@flighthq/scene2d-resources 0.3.0-edge.1458.0c01b63

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (44) hide show
  1. package/dist/builtInScene2DDocumentImporters.d.ts +4 -0
  2. package/dist/builtInScene2DDocumentImporters.d.ts.map +1 -0
  3. package/dist/builtInScene2DDocumentImporters.js +43 -0
  4. package/dist/builtInScene2DDocumentImporters.js.map +1 -0
  5. package/dist/contract.d.ts +7 -0
  6. package/dist/contract.d.ts.map +1 -0
  7. package/dist/contract.js +7 -0
  8. package/dist/contract.js.map +1 -0
  9. package/dist/index.d.ts +2 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +2 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/loadScene2DResources.d.ts +3 -0
  14. package/dist/loadScene2DResources.d.ts.map +1 -0
  15. package/dist/loadScene2DResources.js +47 -0
  16. package/dist/loadScene2DResources.js.map +1 -0
  17. package/dist/resolveScene2DResources.d.ts +3 -0
  18. package/dist/resolveScene2DResources.d.ts.map +1 -0
  19. package/dist/resolveScene2DResources.js +31 -0
  20. package/dist/resolveScene2DResources.js.map +1 -0
  21. package/dist/scene2DContentReference.d.ts +3 -0
  22. package/dist/scene2DContentReference.d.ts.map +1 -0
  23. package/dist/scene2DContentReference.js +13 -0
  24. package/dist/scene2DContentReference.js.map +1 -0
  25. package/dist/scene2DDocument.d.ts +5 -0
  26. package/dist/scene2DDocument.d.ts.map +1 -0
  27. package/dist/scene2DDocument.js +13 -0
  28. package/dist/scene2DDocument.js.map +1 -0
  29. package/dist/scene2DDocumentImporterRegistry.d.ts +6 -0
  30. package/dist/scene2DDocumentImporterRegistry.d.ts.map +1 -0
  31. package/dist/scene2DDocumentImporterRegistry.js +32 -0
  32. package/dist/scene2DDocumentImporterRegistry.js.map +1 -0
  33. package/dist/scene2DDocumentSource.d.ts +3 -0
  34. package/dist/scene2DDocumentSource.d.ts.map +1 -0
  35. package/dist/scene2DDocumentSource.js +12 -0
  36. package/dist/scene2DDocumentSource.js.map +1 -0
  37. package/package.json +51 -0
  38. package/src/builtInScene2DDocumentImporters.test.ts +59 -0
  39. package/src/loadScene2DResources.test.ts +81 -0
  40. package/src/resolveScene2DResources.test.ts +57 -0
  41. package/src/scene2DContentReference.test.ts +38 -0
  42. package/src/scene2DDocument.test.ts +44 -0
  43. package/src/scene2DDocumentImporterRegistry.test.ts +62 -0
  44. package/src/scene2DDocumentSource.test.ts +32 -0
@@ -0,0 +1,4 @@
1
+ import type { Scene2DDocumentImporterRegistry } from '@flighthq/types/contract';
2
+ export declare function registerLottieScene2DDocumentImporter(registry: Scene2DDocumentImporterRegistry): void;
3
+ export declare function registerSvgScene2DDocumentImporter(registry: Scene2DDocumentImporterRegistry): void;
4
+ //# sourceMappingURL=builtInScene2DDocumentImporters.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builtInScene2DDocumentImporters.d.ts","sourceRoot":"","sources":["../src/builtInScene2DDocumentImporters.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAIV,+BAA+B,EAChC,MAAM,0BAA0B,CAAC;AAMlC,wBAAgB,qCAAqC,CAAC,QAAQ,EAAE,+BAA+B,GAAG,IAAI,CAErG;AAED,wBAAgB,kCAAkC,CAAC,QAAQ,EAAE,+BAA+B,GAAG,IAAI,CAElG"}
@@ -0,0 +1,43 @@
1
+ import { createScene2DFromLottieDocument, createScene2DFromSvgDocument } from '@flighthq/scene2d-formats/contract';
2
+ import { ImportDiagnosticSeverity } from '@flighthq/types/contract';
3
+ import { createScene2DDocument } from './scene2DDocument';
4
+ import { registerScene2DDocumentImporter } from './scene2DDocumentImporterRegistry';
5
+ export function registerLottieScene2DDocumentImporter(registry) {
6
+ registerScene2DDocumentImporter(registry, 'lottie', matchesLottieDocument, importLottieDocument);
7
+ }
8
+ export function registerSvgScene2DDocumentImporter(registry) {
9
+ registerScene2DDocumentImporter(registry, 'svg', matchesSvgDocument, importSvgDocument);
10
+ }
11
+ function importLottieDocument(source, _context) {
12
+ const diagnostics = [];
13
+ const result = createScene2DFromLottieDocument(decodeText(source), diagnostics);
14
+ if (isInvalidDocument(diagnostics, 'lottie.invalid-document'))
15
+ return null;
16
+ return createScene2DDocument(result.root, [], 'lottie');
17
+ }
18
+ function importSvgDocument(source, _context) {
19
+ const diagnostics = [];
20
+ const root = createScene2DFromSvgDocument(decodeText(source), diagnostics);
21
+ if (isInvalidDocument(diagnostics, 'svg.invalid-document'))
22
+ return null;
23
+ return createScene2DDocument(root, [], 'svg');
24
+ }
25
+ function matchesLottieDocument(source, context) {
26
+ if (context.mimeType === 'application/lottie+json')
27
+ return true;
28
+ const text = decodeText(source).trimStart();
29
+ return text.startsWith('{') && text.includes('"layers"') && text.includes('"fr"');
30
+ }
31
+ function matchesSvgDocument(source, context) {
32
+ if (context.mimeType === 'image/svg+xml')
33
+ return true;
34
+ return /^(?:<\?xml[^>]*>\s*)?<svg(?:\s|>)/i.test(decodeText(source).trimStart());
35
+ }
36
+ function decodeText(source) {
37
+ return _decoder.decode(source);
38
+ }
39
+ function isInvalidDocument(diagnostics, kind) {
40
+ return diagnostics.some((diagnostic) => diagnostic.kind === kind && diagnostic.severity === ImportDiagnosticSeverity.Reject);
41
+ }
42
+ const _decoder = new TextDecoder();
43
+ //# sourceMappingURL=builtInScene2DDocumentImporters.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"builtInScene2DDocumentImporters.js","sourceRoot":"","sources":["../src/builtInScene2DDocumentImporters.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,+BAA+B,EAAE,4BAA4B,EAAE,MAAM,oCAAoC,CAAC;AAOnH,OAAO,EAAE,wBAAwB,EAAE,MAAM,0BAA0B,CAAC;AAEpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,mBAAmB,CAAC;AAC1D,OAAO,EAAE,+BAA+B,EAAE,MAAM,mCAAmC,CAAC;AAEpF,MAAM,UAAU,qCAAqC,CAAC,QAAyC;IAC7F,+BAA+B,CAAC,QAAQ,EAAE,QAAQ,EAAE,qBAAqB,EAAE,oBAAoB,CAAC,CAAC;AACnG,CAAC;AAED,MAAM,UAAU,kCAAkC,CAAC,QAAyC;IAC1F,+BAA+B,CAAC,QAAQ,EAAE,KAAK,EAAE,kBAAkB,EAAE,iBAAiB,CAAC,CAAC;AAC1F,CAAC;AAED,SAAS,oBAAoB,CAC3B,MAAkB,EAClB,QAAgD;IAEhD,MAAM,WAAW,GAAuB,EAAE,CAAC;IAC3C,MAAM,MAAM,GAAG,+BAA+B,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,CAAC;IAChF,IAAI,iBAAiB,CAAC,WAAW,EAAE,yBAAyB,CAAC;QAAE,OAAO,IAAI,CAAC;IAC3E,OAAO,qBAAqB,CAAC,MAAM,CAAC,IAAI,EAAE,EAAE,EAAE,QAAQ,CAAC,CAAC;AAC1D,CAAC;AAED,SAAS,iBAAiB,CACxB,MAAkB,EAClB,QAAgD;IAEhD,MAAM,WAAW,GAAuB,EAAE,CAAC;IAC3C,MAAM,IAAI,GAAG,4BAA4B,CAAC,UAAU,CAAC,MAAM,CAAC,EAAE,WAAW,CAAC,CAAC;IAC3E,IAAI,iBAAiB,CAAC,WAAW,EAAE,sBAAsB,CAAC;QAAE,OAAO,IAAI,CAAC;IACxE,OAAO,qBAAqB,CAAC,IAAI,EAAE,EAAE,EAAE,KAAK,CAAC,CAAC;AAChD,CAAC;AAED,SAAS,qBAAqB,CAAC,MAAkB,EAAE,OAA+C;IAChG,IAAI,OAAO,CAAC,QAAQ,KAAK,yBAAyB;QAAE,OAAO,IAAI,CAAC;IAChE,MAAM,IAAI,GAAG,UAAU,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,CAAC;IAC5C,OAAO,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC;AACpF,CAAC;AAED,SAAS,kBAAkB,CAAC,MAAkB,EAAE,OAA+C;IAC7F,IAAI,OAAO,CAAC,QAAQ,KAAK,eAAe;QAAE,OAAO,IAAI,CAAC;IACtD,OAAO,oCAAoC,CAAC,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,CAAC,CAAC;AACnF,CAAC;AAED,SAAS,UAAU,CAAC,MAAkB;IACpC,OAAO,QAAQ,CAAC,MAAM,CAAC,MAAM,CAAC,CAAC;AACjC,CAAC;AAED,SAAS,iBAAiB,CAAC,WAAyC,EAAE,IAAY;IAChF,OAAO,WAAW,CAAC,IAAI,CACrB,CAAC,UAAU,EAAE,EAAE,CAAC,UAAU,CAAC,IAAI,KAAK,IAAI,IAAI,UAAU,CAAC,QAAQ,KAAK,wBAAwB,CAAC,MAAM,CACpG,CAAC;AACJ,CAAC;AAED,MAAM,QAAQ,GAAG,IAAI,WAAW,EAAE,CAAC"}
@@ -0,0 +1,7 @@
1
+ export * from './builtInScene2DDocumentImporters';
2
+ export * from './loadScene2DResources';
3
+ export * from './resolveScene2DResources';
4
+ export * from './scene2DDocument';
5
+ export * from './scene2DDocumentImporterRegistry';
6
+ export * from './scene2DDocumentSource';
7
+ //# sourceMappingURL=contract.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.d.ts","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,mCAAmC,CAAC;AAClD,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAC;AAClC,cAAc,mCAAmC,CAAC;AAClD,cAAc,yBAAyB,CAAC"}
@@ -0,0 +1,7 @@
1
+ export * from './builtInScene2DDocumentImporters';
2
+ export * from './loadScene2DResources';
3
+ export * from './resolveScene2DResources';
4
+ export * from './scene2DDocument';
5
+ export * from './scene2DDocumentImporterRegistry';
6
+ export * from './scene2DDocumentSource';
7
+ //# sourceMappingURL=contract.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract.js","sourceRoot":"","sources":["../src/contract.ts"],"names":[],"mappings":"AAAA,cAAc,mCAAmC,CAAC;AAClD,cAAc,wBAAwB,CAAC;AACvC,cAAc,2BAA2B,CAAC;AAC1C,cAAc,mBAAmB,CAAC;AAClC,cAAc,mCAAmC,CAAC;AAClD,cAAc,yBAAyB,CAAC"}
@@ -0,0 +1,2 @@
1
+ export { createScene2DAssetReference, createScene2DDocument, createScene2DDocumentFromBytes, createScene2DDocumentImporterRegistry, createScene2DSlotReference, loadScene2DDocumentFromUrl, loadScene2DResources, registerLottieScene2DDocumentImporter, registerScene2DDocumentImporter, registerSvgScene2DDocumentImporter, resolveScene2DResources, unregisterScene2DDocumentImporter, } from './contract';
2
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,2BAA2B,EAC3B,qBAAqB,EACrB,8BAA8B,EAC9B,qCAAqC,EACrC,0BAA0B,EAC1B,0BAA0B,EAC1B,oBAAoB,EACpB,qCAAqC,EACrC,+BAA+B,EAC/B,kCAAkC,EAClC,uBAAuB,EACvB,iCAAiC,GAClC,MAAM,YAAY,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,2 @@
1
+ export { createScene2DAssetReference, createScene2DDocument, createScene2DDocumentFromBytes, createScene2DDocumentImporterRegistry, createScene2DSlotReference, loadScene2DDocumentFromUrl, loadScene2DResources, registerLottieScene2DDocumentImporter, registerScene2DDocumentImporter, registerSvgScene2DDocumentImporter, resolveScene2DResources, unregisterScene2DDocumentImporter, } from './contract';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,2BAA2B,EAC3B,qBAAqB,EACrB,8BAA8B,EAC9B,qCAAqC,EACrC,0BAA0B,EAC1B,0BAA0B,EAC1B,oBAAoB,EACpB,qCAAqC,EACrC,+BAA+B,EAC/B,kCAAkC,EAClC,uBAAuB,EACvB,iCAAiC,GAClC,MAAM,YAAY,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { LoadScene2DResourcesOptions, Scene2DDocument, Scene2DResources } from '@flighthq/types/contract';
2
+ export declare function loadScene2DResources(document: Scene2DDocument, options: Readonly<LoadScene2DResourcesOptions>): Promise<Scene2DResources>;
3
+ //# sourceMappingURL=loadScene2DResources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadScene2DResources.d.ts","sourceRoot":"","sources":["../src/loadScene2DResources.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,2BAA2B,EAG3B,eAAe,EAEf,gBAAgB,EACjB,MAAM,0BAA0B,CAAC;AAOlC,wBAAsB,oBAAoB,CACxC,QAAQ,EAAE,eAAe,EACzB,OAAO,EAAE,QAAQ,CAAC,2BAA2B,CAAC,GAC7C,OAAO,CAAC,gBAAgB,CAAC,CAuB3B"}
@@ -0,0 +1,47 @@
1
+ import { emitSignal } from '@flighthq/signals/contract';
2
+ import { Scene2DContentReferenceKind } from '@flighthq/types/contract';
3
+ import { setScene2DContentReferenceContent } from './scene2DContentReference';
4
+ // Operation-scoped asynchronous boundary. Slot fills remain synchronous and code-driven; only asset
5
+ // references cross the caller-supplied Promise seam. No resolver state survives this call.
6
+ export async function loadScene2DResources(document, options) {
7
+ const selected = document.references.filter((reference) => options.select === undefined || options.select(reference));
8
+ const signal = options.signal ?? new AbortController().signal;
9
+ let loaded = 0;
10
+ const loads = selected.map(async (reference) => {
11
+ try {
12
+ if (reference.kind === Scene2DContentReferenceKind.Slot) {
13
+ return options.resolveSlotContent?.(reference.name, reference.linkage) ?? null;
14
+ }
15
+ return await options.loadAssetContent(reference.name, reference.uri, signal);
16
+ }
17
+ finally {
18
+ loaded++;
19
+ emitScene2DResourceLoadProgress(options, loaded, selected.length, reference);
20
+ }
21
+ });
22
+ const contents = await Promise.all(loads);
23
+ const resolved = [];
24
+ const unresolved = [];
25
+ for (let i = 0; i < selected.length; i++) {
26
+ reconcileLoadedScene2DContent(selected[i], contents[i], resolved, unresolved);
27
+ }
28
+ return { document, resolved, root: document.root, unresolved };
29
+ }
30
+ function emitScene2DResourceLoadProgress(options, loaded, total, reference) {
31
+ if (options.progress === undefined)
32
+ return;
33
+ emitSignal(options.progress, {
34
+ kind: reference.kind,
35
+ loaded,
36
+ name: reference.name,
37
+ total,
38
+ });
39
+ }
40
+ function reconcileLoadedScene2DContent(reference, content, resolved, unresolved) {
41
+ setScene2DContentReferenceContent(reference, content);
42
+ if (content === null)
43
+ unresolved.push(reference);
44
+ else
45
+ resolved.push({ content, reference });
46
+ }
47
+ //# sourceMappingURL=loadScene2DResources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"loadScene2DResources.js","sourceRoot":"","sources":["../src/loadScene2DResources.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,4BAA4B,CAAC;AASxD,OAAO,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAEvE,OAAO,EAAE,iCAAiC,EAAE,MAAM,2BAA2B,CAAC;AAE9E,oGAAoG;AACpG,2FAA2F;AAC3F,MAAM,CAAC,KAAK,UAAU,oBAAoB,CACxC,QAAyB,EACzB,OAA8C;IAE9C,MAAM,QAAQ,GAAG,QAAQ,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,OAAO,CAAC,MAAM,KAAK,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC;IACtH,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,IAAI,IAAI,eAAe,EAAE,CAAC,MAAM,CAAC;IAC9D,IAAI,MAAM,GAAG,CAAC,CAAC;IAEf,MAAM,KAAK,GAAG,QAAQ,CAAC,GAAG,CAAC,KAAK,EAAE,SAAS,EAA0B,EAAE;QACrE,IAAI,CAAC;YACH,IAAI,SAAS,CAAC,IAAI,KAAK,2BAA2B,CAAC,IAAI,EAAE,CAAC;gBACxD,OAAO,OAAO,CAAC,kBAAkB,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;YACjF,CAAC;YACD,OAAO,MAAM,OAAO,CAAC,gBAAgB,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;QAC/E,CAAC;gBAAS,CAAC;YACT,MAAM,EAAE,CAAC;YACT,+BAA+B,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC,CAAC;QAC/E,CAAC;IACH,CAAC,CAAC,CAAC;IACH,MAAM,QAAQ,GAAG,MAAM,OAAO,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAgC,EAAE,CAAC;IACjD,MAAM,UAAU,GAA8B,EAAE,CAAC;IACjD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACzC,6BAA6B,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;IAChF,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC;AACjE,CAAC;AAED,SAAS,+BAA+B,CACtC,OAA8C,EAC9C,MAAc,EACd,KAAa,EACb,SAA4C;IAE5C,IAAI,OAAO,CAAC,QAAQ,KAAK,SAAS;QAAE,OAAO;IAC3C,UAAU,CAAC,OAAO,CAAC,QAAQ,EAAE;QAC3B,IAAI,EAAE,SAAS,CAAC,IAAI;QACpB,MAAM;QACN,IAAI,EAAE,SAAS,CAAC,IAAI;QACpB,KAAK;KACN,CAAC,CAAC;AACL,CAAC;AAED,SAAS,6BAA6B,CACpC,SAAkC,EAClC,OAAsB,EACtB,QAAqC,EACrC,UAAqC;IAErC,iCAAiC,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;IACtD,IAAI,OAAO,KAAK,IAAI;QAAE,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;;QAC5C,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;AAC7C,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { ResolveScene2DResourcesOptions, Scene2DDocument, Scene2DResources } from '@flighthq/types/contract';
2
+ export declare function resolveScene2DResources(document: Scene2DDocument, options?: Readonly<ResolveScene2DResourcesOptions>): Scene2DResources;
3
+ //# sourceMappingURL=resolveScene2DResources.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveScene2DResources.d.ts","sourceRoot":"","sources":["../src/resolveScene2DResources.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,8BAA8B,EAE9B,eAAe,EACf,gBAAgB,EACjB,MAAM,0BAA0B,CAAC;AAQlC,wBAAgB,uBAAuB,CACrC,QAAQ,EAAE,eAAe,EACzB,OAAO,CAAC,EAAE,QAAQ,CAAC,8BAA8B,CAAC,GACjD,gBAAgB,CAgBlB"}
@@ -0,0 +1,31 @@
1
+ import { Scene2DContentReferenceKind } from '@flighthq/types/contract';
2
+ import { setScene2DContentReferenceContent } from './scene2DContentReference';
3
+ // Reconciles one caller-selected working set entirely synchronously. Asset content must already be
4
+ // available through resolveAssetContent; application slots resolve by name/linkage. This function performs
5
+ // no fetch, decode, Promise scheduling, or registry dispatch.
6
+ export function resolveScene2DResources(document, options) {
7
+ const resolved = [];
8
+ const unresolved = [];
9
+ for (let i = 0; i < document.references.length; i++) {
10
+ const reference = document.references[i];
11
+ if (options?.select !== undefined && !options.select(reference))
12
+ continue;
13
+ const content = resolveScene2DContentReference(reference, options);
14
+ if (content === null) {
15
+ setScene2DContentReferenceContent(reference, null);
16
+ unresolved.push(reference);
17
+ }
18
+ else {
19
+ setScene2DContentReferenceContent(reference, content);
20
+ resolved.push({ content, reference });
21
+ }
22
+ }
23
+ return { document, resolved, root: document.root, unresolved };
24
+ }
25
+ function resolveScene2DContentReference(reference, options) {
26
+ if (reference.kind === Scene2DContentReferenceKind.Asset) {
27
+ return options?.resolveAssetContent?.(reference.name, reference.uri) ?? null;
28
+ }
29
+ return options?.resolveSlotContent?.(reference.name, reference.linkage) ?? null;
30
+ }
31
+ //# sourceMappingURL=resolveScene2DResources.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"resolveScene2DResources.js","sourceRoot":"","sources":["../src/resolveScene2DResources.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAEvE,OAAO,EAAE,iCAAiC,EAAE,MAAM,2BAA2B,CAAC;AAE9E,mGAAmG;AACnG,2GAA2G;AAC3G,8DAA8D;AAC9D,MAAM,UAAU,uBAAuB,CACrC,QAAyB,EACzB,OAAkD;IAElD,MAAM,QAAQ,GAAiC,EAAE,CAAC;IAClD,MAAM,UAAU,GAA8B,EAAE,CAAC;IACjD,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,UAAU,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACpD,MAAM,SAAS,GAAG,QAAQ,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QACzC,IAAI,OAAO,EAAE,MAAM,KAAK,SAAS,IAAI,CAAC,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC;YAAE,SAAS;QAC1E,MAAM,OAAO,GAAG,8BAA8B,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;QACnE,IAAI,OAAO,KAAK,IAAI,EAAE,CAAC;YACrB,iCAAiC,CAAC,SAAS,EAAE,IAAI,CAAC,CAAC;YACnD,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC7B,CAAC;aAAM,CAAC;YACN,iCAAiC,CAAC,SAAS,EAAE,OAAO,CAAC,CAAC;YACtD,QAAQ,CAAC,IAAI,CAAC,EAAE,OAAO,EAAE,SAAS,EAAE,CAAC,CAAC;QACxC,CAAC;IACH,CAAC;IACD,OAAO,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,CAAC,IAAI,EAAE,UAAU,EAAE,CAAC;AACjE,CAAC;AAED,SAAS,8BAA8B,CACrC,SAA4C,EAC5C,OAA6D;IAE7D,IAAI,SAAS,CAAC,IAAI,KAAK,2BAA2B,CAAC,KAAK,EAAE,CAAC;QACzD,OAAO,OAAO,EAAE,mBAAmB,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,GAAG,CAAC,IAAI,IAAI,CAAC;IAC/E,CAAC;IACD,OAAO,OAAO,EAAE,kBAAkB,EAAE,CAAC,SAAS,CAAC,IAAI,EAAE,SAAS,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC;AAClF,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Node2D, Scene2DContentReference } from '@flighthq/types/contract';
2
+ export declare function setScene2DContentReferenceContent(reference: Scene2DContentReference, content: Node2D | null): void;
3
+ //# sourceMappingURL=scene2DContentReference.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene2DContentReference.d.ts","sourceRoot":"","sources":["../src/scene2DContentReference.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,MAAM,EAAE,uBAAuB,EAAE,MAAM,0BAA0B,CAAC;AAEhF,wBAAgB,iCAAiC,CAAC,SAAS,EAAE,uBAAuB,EAAE,OAAO,EAAE,MAAM,GAAG,IAAI,GAAG,IAAI,CAQlH"}
@@ -0,0 +1,13 @@
1
+ import { addNodeChild, getNodeParent, removeNodeChild } from '@flighthq/node/contract';
2
+ export function setScene2DContentReferenceContent(reference, content) {
3
+ const previous = reference.content;
4
+ if (previous === content && (content === null || getNodeParent(content) === reference.target))
5
+ return;
6
+ if (previous !== null && getNodeParent(previous) === reference.target) {
7
+ removeNodeChild(reference.target, previous);
8
+ }
9
+ reference.content = content;
10
+ if (content !== null)
11
+ addNodeChild(reference.target, content);
12
+ }
13
+ //# sourceMappingURL=scene2DContentReference.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene2DContentReference.js","sourceRoot":"","sources":["../src/scene2DContentReference.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,yBAAyB,CAAC;AAGvF,MAAM,UAAU,iCAAiC,CAAC,SAAkC,EAAE,OAAsB;IAC1G,MAAM,QAAQ,GAAG,SAAS,CAAC,OAAO,CAAC;IACnC,IAAI,QAAQ,KAAK,OAAO,IAAI,CAAC,OAAO,KAAK,IAAI,IAAI,aAAa,CAAC,OAAO,CAAC,KAAK,SAAS,CAAC,MAAM,CAAC;QAAE,OAAO;IACtG,IAAI,QAAQ,KAAK,IAAI,IAAI,aAAa,CAAC,QAAQ,CAAC,KAAK,SAAS,CAAC,MAAM,EAAE,CAAC;QACtE,eAAe,CAAC,SAAS,CAAC,MAAM,EAAE,QAAQ,CAAC,CAAC;IAC9C,CAAC;IACD,SAAS,CAAC,OAAO,GAAG,OAAO,CAAC;IAC5B,IAAI,OAAO,KAAK,IAAI;QAAE,YAAY,CAAC,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;AAChE,CAAC"}
@@ -0,0 +1,5 @@
1
+ import type { Node2D, Scene2DAssetReference, Scene2DContentReference, Scene2DDocument, Scene2DSlotReference } from '@flighthq/types/contract';
2
+ export declare function createScene2DAssetReference(name: string, uri: string, target: Node2D, required?: boolean): Scene2DAssetReference;
3
+ export declare function createScene2DDocument(root: Node2D, references?: Scene2DContentReference[], sourceKind?: string | null): Scene2DDocument;
4
+ export declare function createScene2DSlotReference(name: string, target: Node2D, linkage?: string | null, required?: boolean): Scene2DSlotReference;
5
+ //# sourceMappingURL=scene2DDocument.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene2DDocument.d.ts","sourceRoot":"","sources":["../src/scene2DDocument.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,MAAM,EACN,qBAAqB,EACrB,uBAAuB,EACvB,eAAe,EACf,oBAAoB,EACrB,MAAM,0BAA0B,CAAC;AAGlC,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,MAAM,EACZ,GAAG,EAAE,MAAM,EACX,MAAM,EAAE,MAAM,EACd,QAAQ,UAAO,GACd,qBAAqB,CAGvB;AAED,wBAAgB,qBAAqB,CACnC,IAAI,EAAE,MAAM,EACZ,UAAU,GAAE,uBAAuB,EAAO,EAC1C,UAAU,GAAE,MAAM,GAAG,IAAW,GAC/B,eAAe,CAEjB;AAED,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,MAAM,EACd,OAAO,GAAE,MAAM,GAAG,IAAW,EAC7B,QAAQ,UAAO,GACd,oBAAoB,CAGtB"}
@@ -0,0 +1,13 @@
1
+ import { Scene2DContentReferenceKind } from '@flighthq/types/contract';
2
+ export function createScene2DAssetReference(name, uri, target, required = true) {
3
+ target.name = name;
4
+ return { content: null, kind: Scene2DContentReferenceKind.Asset, name, required, target, uri };
5
+ }
6
+ export function createScene2DDocument(root, references = [], sourceKind = null) {
7
+ return { references, root, sourceKind };
8
+ }
9
+ export function createScene2DSlotReference(name, target, linkage = null, required = true) {
10
+ target.name = name;
11
+ return { content: null, kind: Scene2DContentReferenceKind.Slot, linkage, name, required, target };
12
+ }
13
+ //# sourceMappingURL=scene2DDocument.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene2DDocument.js","sourceRoot":"","sources":["../src/scene2DDocument.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,2BAA2B,EAAE,MAAM,0BAA0B,CAAC;AAEvE,MAAM,UAAU,2BAA2B,CACzC,IAAY,EACZ,GAAW,EACX,MAAc,EACd,QAAQ,GAAG,IAAI;IAEf,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,2BAA2B,CAAC,KAAK,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,EAAE,CAAC;AACjG,CAAC;AAED,MAAM,UAAU,qBAAqB,CACnC,IAAY,EACZ,aAAwC,EAAE,EAC1C,aAA4B,IAAI;IAEhC,OAAO,EAAE,UAAU,EAAE,IAAI,EAAE,UAAU,EAAE,CAAC;AAC1C,CAAC;AAED,MAAM,UAAU,0BAA0B,CACxC,IAAY,EACZ,MAAc,EACd,UAAyB,IAAI,EAC7B,QAAQ,GAAG,IAAI;IAEf,MAAM,CAAC,IAAI,GAAG,IAAI,CAAC;IACnB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,EAAE,2BAA2B,CAAC,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,EAAE,CAAC;AACpG,CAAC"}
@@ -0,0 +1,6 @@
1
+ import type { Scene2DDocument, Scene2DDocumentImportContext, Scene2DDocumentImporter, Scene2DDocumentImporterMatcher, Scene2DDocumentImporterRegistry } from '@flighthq/types/contract';
2
+ export declare function createScene2DDocumentFromBytes(source: Uint8Array, registry: Readonly<Scene2DDocumentImporterRegistry>, context?: Readonly<Scene2DDocumentImportContext>): Scene2DDocument | null;
3
+ export declare function createScene2DDocumentImporterRegistry(): Scene2DDocumentImporterRegistry;
4
+ export declare function registerScene2DDocumentImporter(registry: Scene2DDocumentImporterRegistry, kind: string, matches: Scene2DDocumentImporterMatcher, importDocument: Scene2DDocumentImporter): void;
5
+ export declare function unregisterScene2DDocumentImporter(registry: Scene2DDocumentImporterRegistry, kind: string): boolean;
6
+ //# sourceMappingURL=scene2DDocumentImporterRegistry.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene2DDocumentImporterRegistry.d.ts","sourceRoot":"","sources":["../src/scene2DDocumentImporterRegistry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,eAAe,EACf,4BAA4B,EAC5B,uBAAuB,EACvB,8BAA8B,EAC9B,+BAA+B,EAChC,MAAM,0BAA0B,CAAC;AAElC,wBAAgB,8BAA8B,CAC5C,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,QAAQ,CAAC,+BAA+B,CAAC,EACnD,OAAO,GAAE,QAAQ,CAAC,4BAA4B,CAAiC,GAC9E,eAAe,GAAG,IAAI,CASxB;AAED,wBAAgB,qCAAqC,IAAI,+BAA+B,CAEvF;AAED,wBAAgB,+BAA+B,CAC7C,QAAQ,EAAE,+BAA+B,EACzC,IAAI,EAAE,MAAM,EACZ,OAAO,EAAE,8BAA8B,EACvC,cAAc,EAAE,uBAAuB,GACtC,IAAI,CAKN;AAED,wBAAgB,iCAAiC,CAAC,QAAQ,EAAE,+BAA+B,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAKlH"}
@@ -0,0 +1,32 @@
1
+ import { createEntity } from '@flighthq/entity/contract';
2
+ export function createScene2DDocumentFromBytes(source, registry, context = { mimeType: null, url: null }) {
3
+ for (let i = 0; i < registry.entries.length; i++) {
4
+ const entry = registry.entries[i];
5
+ if (!entry.matches(source, context))
6
+ continue;
7
+ const document = entry.importDocument(source, context);
8
+ if (document !== null && document.sourceKind === null)
9
+ document.sourceKind = entry.kind;
10
+ return document;
11
+ }
12
+ return null;
13
+ }
14
+ export function createScene2DDocumentImporterRegistry() {
15
+ return createEntity({ entries: [] });
16
+ }
17
+ export function registerScene2DDocumentImporter(registry, kind, matches, importDocument) {
18
+ const index = registry.entries.findIndex((entry) => entry.kind === kind);
19
+ const entry = { importDocument, kind, matches };
20
+ if (index === -1)
21
+ registry.entries.push(entry);
22
+ else
23
+ registry.entries[index] = entry;
24
+ }
25
+ export function unregisterScene2DDocumentImporter(registry, kind) {
26
+ const index = registry.entries.findIndex((entry) => entry.kind === kind);
27
+ if (index === -1)
28
+ return false;
29
+ registry.entries.splice(index, 1);
30
+ return true;
31
+ }
32
+ //# sourceMappingURL=scene2DDocumentImporterRegistry.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene2DDocumentImporterRegistry.js","sourceRoot":"","sources":["../src/scene2DDocumentImporterRegistry.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AASzD,MAAM,UAAU,8BAA8B,CAC5C,MAAkB,EAClB,QAAmD,EACnD,UAAkD,EAAE,QAAQ,EAAE,IAAI,EAAE,GAAG,EAAE,IAAI,EAAE;IAE/E,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,QAAQ,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACjD,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAClC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,MAAM,EAAE,OAAO,CAAC;YAAE,SAAS;QAC9C,MAAM,QAAQ,GAAG,KAAK,CAAC,cAAc,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;QACvD,IAAI,QAAQ,KAAK,IAAI,IAAI,QAAQ,CAAC,UAAU,KAAK,IAAI;YAAE,QAAQ,CAAC,UAAU,GAAG,KAAK,CAAC,IAAI,CAAC;QACxF,OAAO,QAAQ,CAAC;IAClB,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,MAAM,UAAU,qCAAqC;IACnD,OAAO,YAAY,CAAC,EAAE,OAAO,EAAE,EAAE,EAAE,CAAC,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,+BAA+B,CAC7C,QAAyC,EACzC,IAAY,EACZ,OAAuC,EACvC,cAAuC;IAEvC,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IACzE,MAAM,KAAK,GAAG,EAAE,cAAc,EAAE,IAAI,EAAE,OAAO,EAAE,CAAC;IAChD,IAAI,KAAK,KAAK,CAAC,CAAC;QAAE,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;;QAC1C,QAAQ,CAAC,OAAO,CAAC,KAAK,CAAC,GAAG,KAAK,CAAC;AACvC,CAAC;AAED,MAAM,UAAU,iCAAiC,CAAC,QAAyC,EAAE,IAAY;IACvG,MAAM,KAAK,GAAG,QAAQ,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAC;IACzE,IAAI,KAAK,KAAK,CAAC,CAAC;QAAE,OAAO,KAAK,CAAC;IAC/B,QAAQ,CAAC,OAAO,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;IAClC,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,3 @@
1
+ import type { Scene2DDocument, Scene2DDocumentFetcher, Scene2DDocumentImporterRegistry, Scene2DDocumentLoadOptions } from '@flighthq/types/contract';
2
+ export declare function loadScene2DDocumentFromUrl(url: string, registry: Readonly<Scene2DDocumentImporterRegistry>, fetchDocument: Scene2DDocumentFetcher, options?: Readonly<Scene2DDocumentLoadOptions>): Promise<Scene2DDocument | null>;
3
+ //# sourceMappingURL=scene2DDocumentSource.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene2DDocumentSource.d.ts","sourceRoot":"","sources":["../src/scene2DDocumentSource.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,eAAe,EACf,sBAAsB,EACtB,+BAA+B,EAC/B,0BAA0B,EAC3B,MAAM,0BAA0B,CAAC;AAIlC,wBAAsB,0BAA0B,CAC9C,GAAG,EAAE,MAAM,EACX,QAAQ,EAAE,QAAQ,CAAC,+BAA+B,CAAC,EACnD,aAAa,EAAE,sBAAsB,EACrC,OAAO,CAAC,EAAE,QAAQ,CAAC,0BAA0B,CAAC,GAC7C,OAAO,CAAC,eAAe,GAAG,IAAI,CAAC,CAQjC"}
@@ -0,0 +1,12 @@
1
+ import { createScene2DDocumentFromBytes } from './scene2DDocumentImporterRegistry';
2
+ export async function loadScene2DDocumentFromUrl(url, registry, fetchDocument, options) {
3
+ const signal = options?.signal ?? new AbortController().signal;
4
+ const source = await fetchDocument(url, signal, options?.progress ?? null);
5
+ if (source === null)
6
+ return null;
7
+ return createScene2DDocumentFromBytes(source, registry, {
8
+ mimeType: options?.mimeType ?? null,
9
+ url,
10
+ });
11
+ }
12
+ //# sourceMappingURL=scene2DDocumentSource.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"scene2DDocumentSource.js","sourceRoot":"","sources":["../src/scene2DDocumentSource.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,8BAA8B,EAAE,MAAM,mCAAmC,CAAC;AAEnF,MAAM,CAAC,KAAK,UAAU,0BAA0B,CAC9C,GAAW,EACX,QAAmD,EACnD,aAAqC,EACrC,OAA8C;IAE9C,MAAM,MAAM,GAAG,OAAO,EAAE,MAAM,IAAI,IAAI,eAAe,EAAE,CAAC,MAAM,CAAC;IAC/D,MAAM,MAAM,GAAG,MAAM,aAAa,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,QAAQ,IAAI,IAAI,CAAC,CAAC;IAC3E,IAAI,MAAM,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACjC,OAAO,8BAA8B,CAAC,MAAM,EAAE,QAAQ,EAAE;QACtD,QAAQ,EAAE,OAAO,EAAE,QAAQ,IAAI,IAAI;QACnC,GAAG;KACJ,CAAC,CAAC;AACL,CAAC"}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@flighthq/scene2d-resources",
3
+ "version": "0.3.0-edge.1458.0c01b63",
4
+ "repository": {
5
+ "type": "git",
6
+ "url": "https://github.com/flighthq/flight.git",
7
+ "directory": "packages/scene2d-resources"
8
+ },
9
+ "type": "module",
10
+ "main": "dist/index.js",
11
+ "types": "dist/index.d.ts",
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "default": "./dist/index.js"
16
+ },
17
+ "./contract": {
18
+ "types": "./dist/contract.d.ts",
19
+ "default": "./dist/contract.js"
20
+ }
21
+ },
22
+ "files": [
23
+ "dist",
24
+ "src/**/*.test.ts",
25
+ "!dist/**/*.test.js",
26
+ "!dist/**/*.test.d.ts",
27
+ "!dist/**/*.test.js.map",
28
+ "!dist/**/*.test.d.ts.map"
29
+ ],
30
+ "scripts": {
31
+ "build": "tsc -b",
32
+ "clean": "tsc -b --clean",
33
+ "test": "vitest run --config vitest.config.ts",
34
+ "test:watch": "vitest --watch --config vitest.config.ts",
35
+ "prepack": "npm run clean && npm run clean:dist && npm run build",
36
+ "clean:dist": "tsx ../../scripts/clean-package-dist.ts"
37
+ },
38
+ "dependencies": {
39
+ "@flighthq/entity": "0.3.0-edge.1458.0c01b63",
40
+ "@flighthq/node": "0.3.0-edge.1458.0c01b63",
41
+ "@flighthq/scene2d": "0.3.0-edge.1458.0c01b63",
42
+ "@flighthq/scene2d-formats": "0.3.0-edge.1458.0c01b63",
43
+ "@flighthq/signals": "0.3.0-edge.1458.0c01b63",
44
+ "@flighthq/types": "0.3.0-edge.1458.0c01b63"
45
+ },
46
+ "devDependencies": {
47
+ "typescript": "^5.3.0"
48
+ },
49
+ "description": "Renderer-neutral Scene2D documents with explicit named asset and application slot resolution",
50
+ "sideEffects": false
51
+ }
@@ -0,0 +1,59 @@
1
+ import { getNodeChildCount } from '@flighthq/node/contract';
2
+
3
+ import {
4
+ registerLottieScene2DDocumentImporter,
5
+ registerSvgScene2DDocumentImporter,
6
+ } from './builtInScene2DDocumentImporters';
7
+ import {
8
+ createScene2DDocumentFromBytes,
9
+ createScene2DDocumentImporterRegistry,
10
+ } from './scene2DDocumentImporterRegistry';
11
+
12
+ const encode = (source: string) => new TextEncoder().encode(source);
13
+
14
+ describe('registerLottieScene2DDocumentImporter', () => {
15
+ it('adds an opt-in Bodymovin codec without global registration', () => {
16
+ const registry = createScene2DDocumentImporterRegistry();
17
+ registerLottieScene2DDocumentImporter(registry);
18
+ const document = createScene2DDocumentFromBytes(
19
+ encode('{"v":"5.7.0","fr":30,"ip":0,"op":30,"w":100,"h":100,"layers":[]}'),
20
+ registry,
21
+ );
22
+ expect(document?.sourceKind).toBe('lottie');
23
+ });
24
+
25
+ it('preserves the null sentinel when the format importer rejects a document', () => {
26
+ const registry = createScene2DDocumentImporterRegistry();
27
+ registerLottieScene2DDocumentImporter(registry);
28
+ expect(
29
+ createScene2DDocumentFromBytes(encode('{invalid'), registry, {
30
+ mimeType: 'application/lottie+json',
31
+ url: null,
32
+ }),
33
+ ).toBeNull();
34
+ });
35
+ });
36
+
37
+ describe('registerSvgScene2DDocumentImporter', () => {
38
+ it('adds an opt-in SVG codec that produces a document root', () => {
39
+ const registry = createScene2DDocumentImporterRegistry();
40
+ registerSvgScene2DDocumentImporter(registry);
41
+ const document = createScene2DDocumentFromBytes(
42
+ encode('<svg xmlns="http://www.w3.org/2000/svg"><rect width="10" height="20"/></svg>'),
43
+ registry,
44
+ );
45
+ expect(document?.sourceKind).toBe('svg');
46
+ expect(getNodeChildCount(document!.root)).toBe(1);
47
+ });
48
+
49
+ it('preserves the null sentinel when the format importer rejects a document', () => {
50
+ const registry = createScene2DDocumentImporterRegistry();
51
+ registerSvgScene2DDocumentImporter(registry);
52
+ expect(
53
+ createScene2DDocumentFromBytes(encode('<not-svg/>'), registry, {
54
+ mimeType: 'image/svg+xml',
55
+ url: null,
56
+ }),
57
+ ).toBeNull();
58
+ });
59
+ });
@@ -0,0 +1,81 @@
1
+ import { getNodeParent } from '@flighthq/node/contract';
2
+ import { createDisplayObject } from '@flighthq/scene2d/contract';
3
+ import { connectSignal, createSignal } from '@flighthq/signals/contract';
4
+ import type { Scene2DResourceLoadProgress } from '@flighthq/types/contract';
5
+
6
+ import { loadScene2DResources } from './loadScene2DResources';
7
+ import { createScene2DAssetReference, createScene2DDocument, createScene2DSlotReference } from './scene2DDocument';
8
+
9
+ describe('loadScene2DResources', () => {
10
+ it('brackets async assets and synchronous slots in one result', async () => {
11
+ const assetTarget = createDisplayObject();
12
+ const slotTarget = createDisplayObject();
13
+ const assetContent = createDisplayObject();
14
+ const slotContent = createDisplayObject();
15
+ const document = createScene2DDocument(createDisplayObject(), [
16
+ createScene2DAssetReference('background', 'bg.png', assetTarget),
17
+ createScene2DSlotReference('avatar', slotTarget, 'Game.Avatar'),
18
+ ]);
19
+
20
+ const resources = await loadScene2DResources(document, {
21
+ loadAssetContent: async (name, uri) => (name === 'background' && uri === 'bg.png' ? assetContent : null),
22
+ resolveSlotContent: () => slotContent,
23
+ });
24
+
25
+ expect(resources.resolved).toHaveLength(2);
26
+ expect(getNodeParent(assetContent)).toBe(assetTarget);
27
+ expect(getNodeParent(slotContent)).toBe(slotTarget);
28
+ });
29
+
30
+ it('reports operation-scoped completion progress', async () => {
31
+ const progress = createSignal<(event: Readonly<Scene2DResourceLoadProgress>) => void>();
32
+ const events: Scene2DResourceLoadProgress[] = [];
33
+ connectSignal(progress, (event) => events.push({ ...event }));
34
+ const document = createScene2DDocument(createDisplayObject(), [
35
+ createScene2DAssetReference('a', 'a.png', createDisplayObject()),
36
+ createScene2DAssetReference('b', 'b.png', createDisplayObject()),
37
+ ]);
38
+ await loadScene2DResources(document, {
39
+ loadAssetContent: async () => createDisplayObject(),
40
+ progress,
41
+ });
42
+ expect(events.map((event) => event.loaded).sort()).toEqual([1, 2]);
43
+ expect(events.every((event) => event.total === 2)).toBe(true);
44
+ });
45
+
46
+ it('returns resolutions in manifest order when assets settle out of order', async () => {
47
+ let releaseFirst: (content: ReturnType<typeof createDisplayObject>) => void = () => {};
48
+ const first = new Promise<ReturnType<typeof createDisplayObject>>((resolve) => {
49
+ releaseFirst = resolve;
50
+ });
51
+ const secondContent = createDisplayObject();
52
+ const document = createScene2DDocument(createDisplayObject(), [
53
+ createScene2DAssetReference('first', 'first.png', createDisplayObject()),
54
+ createScene2DAssetReference('second', 'second.png', createDisplayObject()),
55
+ ]);
56
+ const operation = loadScene2DResources(document, {
57
+ loadAssetContent: (name) => (name === 'first' ? first : Promise.resolve(secondContent)),
58
+ });
59
+ releaseFirst(createDisplayObject());
60
+ const resources = await operation;
61
+ expect(resources.resolved.map((entry) => entry.reference.name)).toEqual(['first', 'second']);
62
+ });
63
+
64
+ it('relays caller cancellation into the asset loader', async () => {
65
+ const controller = new AbortController();
66
+ controller.abort('stop');
67
+ const received: AbortSignal[] = [];
68
+ const document = createScene2DDocument(createDisplayObject(), [
69
+ createScene2DAssetReference('a', 'a.png', createDisplayObject()),
70
+ ]);
71
+ await loadScene2DResources(document, {
72
+ loadAssetContent: async (_name, _uri, signal) => {
73
+ received.push(signal);
74
+ return null;
75
+ },
76
+ signal: controller.signal,
77
+ });
78
+ expect(received[0].aborted).toBe(true);
79
+ expect(received[0].reason).toBe('stop');
80
+ });
81
+ });
@@ -0,0 +1,57 @@
1
+ import { addNodeChild, getNodeParent } from '@flighthq/node/contract';
2
+ import { createDisplayObject } from '@flighthq/scene2d/contract';
3
+
4
+ import { resolveScene2DResources } from './resolveScene2DResources';
5
+ import { createScene2DAssetReference, createScene2DDocument, createScene2DSlotReference } from './scene2DDocument';
6
+
7
+ describe('resolveScene2DResources', () => {
8
+ it('reconciles asset and application slot content synchronously', () => {
9
+ const root = createDisplayObject();
10
+ const assetTarget = createDisplayObject();
11
+ const slotTarget = createDisplayObject();
12
+ const assetContent = createDisplayObject();
13
+ const slotContent = createDisplayObject();
14
+ const document = createScene2DDocument(root, [
15
+ createScene2DAssetReference('background', 'bg.png', assetTarget),
16
+ createScene2DSlotReference('avatar', slotTarget, 'Game.Avatar'),
17
+ ]);
18
+
19
+ const resources = resolveScene2DResources(document, {
20
+ resolveAssetContent: (name, uri) => (name === 'background' && uri === 'bg.png' ? assetContent : null),
21
+ resolveSlotContent: (name, linkage) => (name === 'avatar' && linkage === 'Game.Avatar' ? slotContent : null),
22
+ });
23
+
24
+ expect(resources.resolved.map((entry) => entry.content)).toEqual([assetContent, slotContent]);
25
+ expect(resources.unresolved).toEqual([]);
26
+ expect(document.references[0].content).toBe(assetContent);
27
+ expect(document.references[1].content).toBe(slotContent);
28
+ expect(getNodeParent(assetContent)).toBe(assetTarget);
29
+ expect(getNodeParent(slotContent)).toBe(slotTarget);
30
+ });
31
+
32
+ it('clears stale managed content when a selected reference is unresolved', () => {
33
+ const target = createDisplayObject();
34
+ const stale = createDisplayObject();
35
+ const reference = createScene2DSlotReference('missing', target);
36
+ reference.content = stale;
37
+ addNodeChild(target, stale);
38
+ const resources = resolveScene2DResources(createScene2DDocument(createDisplayObject(), [reference]));
39
+ expect(resources.unresolved).toEqual([reference]);
40
+ expect(reference.content).toBeNull();
41
+ expect(getNodeParent(stale)).toBeNull();
42
+ });
43
+
44
+ it('leaves references outside the selected working set untouched', () => {
45
+ const target = createDisplayObject();
46
+ const existing = createDisplayObject();
47
+ const reference = createScene2DSlotReference('deferred', target);
48
+ reference.content = existing;
49
+ addNodeChild(target, existing);
50
+ const resources = resolveScene2DResources(createScene2DDocument(createDisplayObject(), [reference]), {
51
+ select: () => false,
52
+ });
53
+ expect(resources.resolved).toEqual([]);
54
+ expect(resources.unresolved).toEqual([]);
55
+ expect(reference.content).toBe(existing);
56
+ });
57
+ });
@@ -0,0 +1,38 @@
1
+ import { addNodeChild, getNodeChildAt, getNodeParent } from '@flighthq/node/contract';
2
+ import { createDisplayObject } from '@flighthq/scene2d/contract';
3
+
4
+ import { setScene2DContentReferenceContent } from './scene2DContentReference';
5
+ import { createScene2DSlotReference } from './scene2DDocument';
6
+
7
+ describe('setScene2DContentReferenceContent', () => {
8
+ it('keeps child order stable when a resolve pass retains the same content', () => {
9
+ const target = createDisplayObject();
10
+ const content = createDisplayObject();
11
+ const overlay = createDisplayObject();
12
+ const reference = createScene2DSlotReference('slot', target);
13
+ setScene2DContentReferenceContent(reference, content);
14
+ addNodeChild(target, overlay);
15
+
16
+ setScene2DContentReferenceContent(reference, content);
17
+
18
+ expect(getNodeChildAt(target, 0)).toBe(content);
19
+ expect(getNodeChildAt(target, 1)).toBe(overlay);
20
+ });
21
+
22
+ it('replaces only the content retained by the manifest reference', () => {
23
+ const target = createDisplayObject();
24
+ const authored = createDisplayObject();
25
+ const first = createDisplayObject();
26
+ const second = createDisplayObject();
27
+ addNodeChild(target, authored);
28
+ const reference = createScene2DSlotReference('slot', target);
29
+
30
+ setScene2DContentReferenceContent(reference, first);
31
+ setScene2DContentReferenceContent(reference, second);
32
+
33
+ expect(reference.content).toBe(second);
34
+ expect(getNodeParent(authored)).toBe(target);
35
+ expect(getNodeParent(first)).toBeNull();
36
+ expect(getNodeParent(second)).toBe(target);
37
+ });
38
+ });
@@ -0,0 +1,44 @@
1
+ import { createDisplayObject } from '@flighthq/scene2d/contract';
2
+ import { Scene2DContentReferenceKind } from '@flighthq/types/contract';
3
+
4
+ import { createScene2DAssetReference, createScene2DDocument, createScene2DSlotReference } from './scene2DDocument';
5
+
6
+ describe('createScene2DAssetReference', () => {
7
+ it('creates an enumerable named asset target', () => {
8
+ const target = createDisplayObject();
9
+ const reference = createScene2DAssetReference('heroImage', 'hero.png', target);
10
+ expect(reference).toEqual({
11
+ content: null,
12
+ kind: Scene2DContentReferenceKind.Asset,
13
+ name: 'heroImage',
14
+ required: true,
15
+ target,
16
+ uri: 'hero.png',
17
+ });
18
+ expect(target.name).toBe('heroImage');
19
+ });
20
+ });
21
+
22
+ describe('createScene2DDocument', () => {
23
+ it('retains the unattached root and an independent manifest', () => {
24
+ const root = createDisplayObject();
25
+ const references = [createScene2DAssetReference('bg', 'bg.png', createDisplayObject())];
26
+ expect(createScene2DDocument(root, references, 'acme')).toEqual({
27
+ references,
28
+ root,
29
+ sourceKind: 'acme',
30
+ });
31
+ });
32
+ });
33
+
34
+ describe('createScene2DSlotReference', () => {
35
+ it('retains instance and linkage identity in the manifest', () => {
36
+ const target = createDisplayObject();
37
+ const reference = createScene2DSlotReference('avatarSlot', target, 'Game.Avatar', false);
38
+ expect(reference.content).toBeNull();
39
+ expect(reference.kind).toBe(Scene2DContentReferenceKind.Slot);
40
+ expect(reference.linkage).toBe('Game.Avatar');
41
+ expect(reference.required).toBe(false);
42
+ expect(target.name).toBe('avatarSlot');
43
+ });
44
+ });
@@ -0,0 +1,62 @@
1
+ import { createDisplayObject } from '@flighthq/scene2d/contract';
2
+
3
+ import { createScene2DDocument } from './scene2DDocument';
4
+ import {
5
+ createScene2DDocumentFromBytes,
6
+ createScene2DDocumentImporterRegistry,
7
+ registerScene2DDocumentImporter,
8
+ unregisterScene2DDocumentImporter,
9
+ } from './scene2DDocumentImporterRegistry';
10
+
11
+ describe('createScene2DDocumentFromBytes', () => {
12
+ it('dispatches to the first matching registered importer', () => {
13
+ const registry = createScene2DDocumentImporterRegistry();
14
+ registerScene2DDocumentImporter(
15
+ registry,
16
+ 'acme',
17
+ (source) => source[0] === 42,
18
+ () => createScene2DDocument(createDisplayObject()),
19
+ );
20
+ const document = createScene2DDocumentFromBytes(new Uint8Array([42]), registry);
21
+ expect(document?.sourceKind).toBe('acme');
22
+ });
23
+
24
+ it('returns null when no importer matches', () => {
25
+ expect(createScene2DDocumentFromBytes(new Uint8Array([0]), createScene2DDocumentImporterRegistry())).toBeNull();
26
+ });
27
+ });
28
+
29
+ describe('createScene2DDocumentImporterRegistry', () => {
30
+ it('creates isolated registries with no implicit codecs', () => {
31
+ const first = createScene2DDocumentImporterRegistry();
32
+ const second = createScene2DDocumentImporterRegistry();
33
+ expect(first.entries).toEqual([]);
34
+ expect(second.entries).not.toBe(first.entries);
35
+ });
36
+ });
37
+
38
+ describe('registerScene2DDocumentImporter', () => {
39
+ it('replaces a kind in place with last-write-wins behavior', () => {
40
+ const registry = createScene2DDocumentImporterRegistry();
41
+ const first = () => null;
42
+ const second = () => createScene2DDocument(createDisplayObject());
43
+ registerScene2DDocumentImporter(registry, 'acme', () => true, first);
44
+ registerScene2DDocumentImporter(registry, 'acme', () => true, second);
45
+ expect(registry.entries).toHaveLength(1);
46
+ expect(registry.entries[0].importDocument).toBe(second);
47
+ });
48
+ });
49
+
50
+ describe('unregisterScene2DDocumentImporter', () => {
51
+ it('removes an existing kind and reports absence', () => {
52
+ const registry = createScene2DDocumentImporterRegistry();
53
+ registerScene2DDocumentImporter(
54
+ registry,
55
+ 'acme',
56
+ () => true,
57
+ () => null,
58
+ );
59
+ expect(unregisterScene2DDocumentImporter(registry, 'acme')).toBe(true);
60
+ expect(unregisterScene2DDocumentImporter(registry, 'acme')).toBe(false);
61
+ });
62
+ });
@@ -0,0 +1,32 @@
1
+ import { createDisplayObject } from '@flighthq/scene2d/contract';
2
+
3
+ import { createScene2DDocument } from './scene2DDocument';
4
+ import {
5
+ createScene2DDocumentImporterRegistry,
6
+ registerScene2DDocumentImporter,
7
+ } from './scene2DDocumentImporterRegistry';
8
+ import { loadScene2DDocumentFromUrl } from './scene2DDocumentSource';
9
+
10
+ describe('loadScene2DDocumentFromUrl', () => {
11
+ it('acquires bytes then stops at the renderer-neutral document', async () => {
12
+ const registry = createScene2DDocumentImporterRegistry();
13
+ registerScene2DDocumentImporter(
14
+ registry,
15
+ 'acme',
16
+ (_source, context) => context.url === 'scene.acme',
17
+ () => createScene2DDocument(createDisplayObject()),
18
+ );
19
+ const document = await loadScene2DDocumentFromUrl(
20
+ 'scene.acme',
21
+ registry,
22
+ async (_url, _signal, _progress) => new Uint8Array([1]),
23
+ );
24
+ expect(document?.sourceKind).toBe('acme');
25
+ });
26
+
27
+ it('returns null when acquisition reports an expected failure', async () => {
28
+ await expect(
29
+ loadScene2DDocumentFromUrl('missing.acme', createScene2DDocumentImporterRegistry(), async () => null),
30
+ ).resolves.toBeNull();
31
+ });
32
+ });