diff --git a/package.json b/package.json index e4b0a670..8d5f086b 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "data-monorepo", - "version": "0.10.7", + "version": "0.10.8", "private": true, "engines": { "node": ">=24" diff --git a/packages/data-ai/.claude-plugin/plugin.json b/packages/data-ai/.claude-plugin/plugin.json index ec7ea367..62a20bf4 100644 --- a/packages/data-ai/.claude-plugin/plugin.json +++ b/packages/data-ai/.claude-plugin/plugin.json @@ -1,6 +1,6 @@ { "name": "adobe-data-ai", - "version": "0.10.7", + "version": "0.10.8", "description": "Architecture skills for @adobe/data — data-oriented modelling, archetype iteration, hot-path performance, and related conventions.", "author": { "name": "Adobe" diff --git a/packages/data-ai/package.json b/packages/data-ai/package.json index 47e5d1cc..72a50bcc 100644 --- a/packages/data-ai/package.json +++ b/packages/data-ai/package.json @@ -1,6 +1,6 @@ { "name": "@adobe/data-ai", - "version": "0.10.7", + "version": "0.10.8", "description": "Cross-agent architecture skills for @adobe/data — installable as a Claude Code plugin or copied into any Agent-Skills-compatible agent (Cursor, Codex).", "type": "module", "private": false, diff --git a/packages/data-gpu/package.json b/packages/data-gpu/package.json index f7aec11c..0364e43b 100644 --- a/packages/data-gpu/package.json +++ b/packages/data-gpu/package.json @@ -1,6 +1,6 @@ { "name": "@adobe/data-gpu", - "version": "0.10.7", + "version": "0.10.8", "description": "Adobe data WebGPU plugins and types for graphics and compute", "type": "module", "private": false, diff --git a/packages/data-lit/package.json b/packages/data-lit/package.json index b60ec0e4..c9c522e1 100644 --- a/packages/data-lit/package.json +++ b/packages/data-lit/package.json @@ -1,6 +1,6 @@ { "name": "@adobe/data-lit", - "version": "0.10.7", + "version": "0.10.8", "description": "Adobe data Lit bindings - hooks, elements, decorators", "type": "module", "private": false, diff --git a/packages/data-persistence/package.json b/packages/data-persistence/package.json index 0292cad5..81e196cb 100644 --- a/packages/data-persistence/package.json +++ b/packages/data-persistence/package.json @@ -1,6 +1,6 @@ { "name": "@adobe/data-persistence", - "version": "0.10.7", + "version": "0.10.8", "description": "Worker-based incremental persistence layer for @adobe/data ECS over OPFS (browser) and node:fs (server).", "type": "module", "sideEffects": false, diff --git a/packages/data-react/package.json b/packages/data-react/package.json index 413e3122..2ae887a1 100644 --- a/packages/data-react/package.json +++ b/packages/data-react/package.json @@ -1,6 +1,6 @@ { "name": "@adobe/data-react", - "version": "0.10.7", + "version": "0.10.8", "description": "Adobe data React bindings — hooks and context for ECS database", "type": "module", "private": false, diff --git a/packages/data-solid/package.json b/packages/data-solid/package.json index 4af8c86c..0587e437 100644 --- a/packages/data-solid/package.json +++ b/packages/data-solid/package.json @@ -1,6 +1,6 @@ { "name": "@adobe/data-solid", - "version": "0.10.7", + "version": "0.10.8", "description": "Adobe data SolidJS bindings — context and provider for ECS database", "type": "module", "private": false, diff --git a/packages/data-sync/package.json b/packages/data-sync/package.json index 1ce510d7..e1d2e770 100644 --- a/packages/data-sync/package.json +++ b/packages/data-sync/package.json @@ -1,6 +1,6 @@ { "name": "@adobe/data-sync", - "version": "0.10.7", + "version": "0.10.8", "description": "Multi-user real-time synchronisation for @adobe/data ECS — server, client, and in-process loopback.", "type": "module", "sideEffects": false, diff --git a/packages/data-testing/package.json b/packages/data-testing/package.json index 4e71176c..781198c6 100644 --- a/packages/data-testing/package.json +++ b/packages/data-testing/package.json @@ -1,6 +1,6 @@ { "name": "@adobe/data-testing", - "version": "0.10.7", + "version": "0.10.8", "description": "Conformance-testing utilities (Match + Conformance runners) for @adobe/data ECS features", "type": "module", "sideEffects": false, diff --git a/packages/data/package.json b/packages/data/package.json index 20db7c62..acb781b3 100644 --- a/packages/data/package.json +++ b/packages/data/package.json @@ -1,6 +1,6 @@ { "name": "@adobe/data", - "version": "0.10.7", + "version": "0.10.8", "description": "Adobe data oriented programming library", "type": "module", "sideEffects": false, diff --git a/packages/data/src/schema/to-type.ts b/packages/data/src/schema/to-type.ts index 66a3cbc6..ebce187e 100644 --- a/packages/data/src/schema/to-type.ts +++ b/packages/data/src/schema/to-type.ts @@ -29,14 +29,6 @@ type FromSchemaInternal = T extends { const: infer ? null : T extends { type: 'blob' } ? Blob - : T extends { type: 'observe' } - ? Observe, Decrement>> - : T extends { type: 'promise' } - ? Promise, Decrement>> - : T extends { type: 'generator' } - ? AsyncGenerator, Decrement>> - : T extends { type: 'function' } - ? FromSchemaFunction> : T extends { type: 'typed-buffer', items: infer Items } ? TypedBuffer> : T extends { type: 'typed-buffer' } @@ -46,6 +38,20 @@ type FromSchemaInternal = T extends { const: infer : T extends { type?: undefined, default: infer D } ? D : T extends { type: 'object' } | { properties: any } ? FromSchemaObject> + // Type-constructor schemas are placed AFTER the data branches on purpose: a + // data schema (every ECS component/resource) must NOT pay extra conditional + // depth for these, because `FromSchemas` runs `ToType` per component across a + // whole plugin `combine` chain, and the added depth tips deep chains past TS's + // stack-depth limit (TS2321). Only observe/promise/generator/function schemas — + // which appear in service schemas, never in the Plugin hot path — fall through. + : T extends { type: 'observe' } + ? Observe, Decrement>> + : T extends { type: 'promise' } + ? Promise, Decrement>> + : T extends { type: 'generator' } + ? AsyncGenerator, Decrement>> + : T extends { type: 'function' } + ? FromSchemaFunction> : any ; diff --git a/packages/data/src/service/async-data-service/create-lazy.test.ts b/packages/data/src/service/async-data-service/create-lazy.test.ts index d677bc0f..070f05de 100644 --- a/packages/data/src/service/async-data-service/create-lazy.test.ts +++ b/packages/data/src/service/async-data-service/create-lazy.test.ts @@ -205,6 +205,29 @@ const validWithPreload = createLazy({ preload: true }); +// ✅ Test 4c: a service that is NOT a valid AsyncDataService (returns non-Data) +// is still accepted — createLazy gates on schema-match alone, not IsValid. +interface NonConformantService extends Service { + fetch: (id: string) => Promise; // Response ∉ Data → not IsValid + count: Observe; // undefined-in-Observe → not IsValid +} +// The service genuinely fails IsValid... +// @ts-expect-error - NonConformantService returns non-Data / undefined-in-Observe +type _NonConformantFailsIsValid = Assert>; +// ...yet createLazy accepts it because the schema correctly describes the members. +const validNonConformant = createLazy({ + load: () => Promise.resolve({} as NonConformantService), + schema: { + type: "object", + properties: { + fetch: { type: "function", signature: { parameters: [{}], returns: { type: "promise", value: {} } } }, + count: { type: "observe", value: {} } + }, + required: ["fetch", "count"], + additionalProperties: false + } +}); + // ============================================================================ // ERROR TESTS (These should produce TypeScript errors) // ============================================================================ diff --git a/packages/data/src/service/async-data-service/create-lazy.ts b/packages/data/src/service/async-data-service/create-lazy.ts index d029b413..9b61332c 100644 --- a/packages/data/src/service/async-data-service/create-lazy.ts +++ b/packages/data/src/service/async-data-service/create-lazy.ts @@ -3,7 +3,7 @@ import { Observe } from "../../observe/index.js"; import { Schema } from "../../schema/index.js"; import { Service } from "../service.js"; -import { IsValidWithCompleteSchema } from "./is-valid-with-complete-schema.js"; +import { EquivalentTypes } from "../../types/types.js"; // ============================================================================ // TYPE INFERENCE HELPERS @@ -38,6 +38,16 @@ type LazyServiceSchema = Schema & { readonly properties?: { readonly [name: string]: LazyMemberSchema }; }; +// createLazy's gate: the schema must completely and correctly describe the loaded +// service's members. This is the actual correctness condition for building the +// wrappers, and it is INTENTIONALLY independent of `AsyncDataService.IsValid` — +// createLazy wraps whatever the schema describes, so a service that returns +// non-`Data` (e.g. a `fetch(): Promise` port) can still be lazily +// chunk-loaded as long as its schema matches. Enforce `IsValid` separately at the +// service's definition site if you also want async-data-service conformance. +type SchemaMatchesService = + EquivalentTypes, Omit>; + // ============================================================================ // RUNTIME WRAPPER KIND // ============================================================================ @@ -87,7 +97,11 @@ function memberKind(member: Schema): WrapKind { * * TypeScript enforces that `schema` describes every member of the loaded service * with the correct wrapper kind; otherwise the `schema` argument fails to type-check - * with a `__createLazyError` marker. Note: member value/parameter schemas authored as `{}` + * with a `__createLazyError` marker. This is the ONLY requirement — createLazy does + * NOT require the service to be a valid `AsyncDataService`, so a service that returns + * non-`Data` (e.g. `Promise`) can still be lazily loaded. Enforce + * `AsyncDataService.IsValid` separately at the service definition site if you want it. + * Note: member value/parameter schemas authored as `{}` * resolve to `any`, so presence and wrapper kind are checked but inner payload * types are only verified where a precise `value`/parameter schema is supplied. * @@ -117,7 +131,7 @@ export function createLazy< load: LoadFn, schema: S, preload?: boolean - } & (IsValidWithCompleteSchema, S> extends true + } & (SchemaMatchesService, S> extends true ? unknown // Inline (not a named type) so the mismatch marker isn't a documented symbol. : { schema: { readonly __createLazyError: "createLazy: schema must completely and correctly describe the loaded service" } })