@needle-tools/addressing 0.3.4 → 0.4.0

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/Readme.md +62 -0
  2. package/dist/address.d.ts +5 -0
  3. package/dist/address.js +25 -3
  4. package/dist/document/assets.d.ts +27 -0
  5. package/dist/document/assets.js +75 -0
  6. package/dist/document/collectionSelector.d.ts +9 -0
  7. package/dist/document/collectionSelector.js +29 -0
  8. package/dist/document/evaluate.d.ts +72 -0
  9. package/dist/document/evaluate.js +971 -0
  10. package/dist/document/index.d.ts +8 -0
  11. package/dist/document/index.js +8 -0
  12. package/dist/document/inspection.d.ts +30 -0
  13. package/dist/document/inspection.js +130 -0
  14. package/dist/document/runtime.d.ts +71 -0
  15. package/dist/document/runtime.js +1 -0
  16. package/dist/document/types.d.ts +225 -0
  17. package/dist/document/types.js +43 -0
  18. package/dist/document/validation.d.ts +36 -0
  19. package/dist/document/validation.js +219 -0
  20. package/dist/document/write.d.ts +31 -0
  21. package/dist/document/write.js +106 -0
  22. package/dist/id.d.ts +2 -0
  23. package/dist/id.js +4 -0
  24. package/dist/index.d.ts +2 -1
  25. package/dist/index.js +2 -1
  26. package/dist/loadedGltf.d.ts +32 -0
  27. package/dist/loadedGltf.js +62 -0
  28. package/dist/resolve.js +8 -6
  29. package/package.json +9 -1
  30. package/src/address.ts +28 -3
  31. package/src/document/assets.ts +77 -0
  32. package/src/document/collectionSelector.ts +37 -0
  33. package/src/document/evaluate.ts +998 -0
  34. package/src/document/index.ts +8 -0
  35. package/src/document/inspection.ts +123 -0
  36. package/src/document/runtime.ts +66 -0
  37. package/src/document/schema.json +263 -0
  38. package/src/document/types.ts +249 -0
  39. package/src/document/validation.ts +222 -0
  40. package/src/document/write.ts +112 -0
  41. package/src/id.ts +5 -0
  42. package/src/index.ts +3 -0
  43. package/src/loadedGltf.ts +93 -0
  44. package/src/resolve.ts +9 -7
@@ -0,0 +1,77 @@
1
+ import type { ProjectDocument, ProjectDocumentAsset, ProjectDocumentReference } from "./types.js";
2
+
3
+ /** Asset keys and shared resource identities for one document assembly. */
4
+ export class DocumentAssetAssembly {
5
+ private readonly assetBySrc = new Map<string, string>();
6
+ private readonly referenceById = new Map<string, ProjectDocumentReference>();
7
+ private readonly materialBySource = new Map<string, ProjectDocumentReference>();
8
+ private readonly baseMaterialBySource = new Map<string, ProjectDocumentReference>();
9
+
10
+ constructor(
11
+ private readonly assets: Record<string, ProjectDocumentAsset>,
12
+ private readonly references: ProjectDocumentReference[],
13
+ base?: ProjectDocument | null,
14
+ ) {
15
+ for (const [key, asset] of Object.entries(assets)) this.assetBySrc.set(asset.src, key);
16
+ for (const reference of references) {
17
+ this.referenceById.set(reference.id, reference);
18
+ const key = this.materialKey(reference, assets);
19
+ if (key) this.materialBySource.set(key, reference);
20
+ }
21
+ for (const reference of base?.references ?? []) {
22
+ const key = this.materialKey(reference, base?.assets ?? {});
23
+ if (key) this.baseMaterialBySource.set(key, reference);
24
+ }
25
+ }
26
+
27
+ addAsset(input: { src: string; name?: string; kind: string; identity?: { id: string; provider?: string } }): string {
28
+ const existing = this.assetBySrc.get(input.src);
29
+ if (existing !== undefined) return existing;
30
+ const filename = input.src.split("?")[0].split("/").pop() || "material";
31
+ const dot = filename.lastIndexOf(".");
32
+ const stem = input.name || (dot > 0 ? filename.substring(0, dot) : filename);
33
+ let key = stem;
34
+ let n = 2;
35
+ while (key in this.assets) key = `${stem}-${n++}`;
36
+ this.assets[key] = { src: input.src, kind: input.kind, ...(input.identity ? { asset: { ...input.identity } } : {}) };
37
+ this.assetBySrc.set(input.src, key);
38
+ return key;
39
+ }
40
+
41
+ materialEntity(asset: string, material: { id: string; name?: string }, entityId: string): string {
42
+ if (!this.assets[asset]) throw new Error(`Unknown document asset: ${asset}`);
43
+ const key = JSON.stringify([this.assets[asset].src, material.id]);
44
+ const existing = this.materialBySource.get(key);
45
+ if (existing) return existing.id;
46
+ const base = this.baseMaterialBySource.get(key);
47
+ const reference: ProjectDocumentReference = base ? { ...base, asset } : {
48
+ id: entityId, asset, address: { type: "material", id: material.id, ...(material.name ? { name: material.name } : {}) },
49
+ };
50
+ this.addReference(reference);
51
+ this.materialBySource.set(key, reference);
52
+ return reference.id;
53
+ }
54
+
55
+ environmentEntity(asset: string, entityId: string): string {
56
+ if (!this.assets[asset]) throw new Error(`Unknown document asset: ${asset}`);
57
+ this.addReference({ id: entityId, asset, address: { type: "hdr" } });
58
+ return entityId;
59
+ }
60
+
61
+ private addReference(reference: ProjectDocumentReference): void {
62
+ const existing = this.referenceById.get(reference.id);
63
+ if (existing) {
64
+ if (existing.asset !== reference.asset || existing.address?.type !== reference.address?.type || existing.address?.id !== reference.address?.id) {
65
+ throw new Error(`Document reference id '${reference.id}' already identifies another resource`);
66
+ }
67
+ return;
68
+ }
69
+ this.references.push(reference);
70
+ this.referenceById.set(reference.id, reference);
71
+ }
72
+
73
+ private materialKey(reference: ProjectDocumentReference, assets: Record<string, ProjectDocumentAsset>): string | null {
74
+ return reference.address?.type === "material" && reference.address.id && assets[reference.asset]
75
+ ? JSON.stringify([assets[reference.asset].src, reference.address.id]) : null;
76
+ }
77
+ }
@@ -0,0 +1,37 @@
1
+ export interface DocumentCollectionSelectorOptions {
2
+ matchesComponentType?(component: object, name: string): boolean;
3
+ matchesComponentGuid?(component: object, guid: string): boolean;
4
+ deriveComponentGuid?(referenceId: string, fileGuid: string): string;
5
+ referenceId?: string;
6
+ report?(message: string): void;
7
+ }
8
+
9
+ /** Resolve an index or field qualifier; ambiguous identities never select an arbitrary member. */
10
+ export function resolveDocumentCollectionElement(
11
+ collection: readonly unknown[], selector: string, options: DocumentCollectionSelectorOptions = {},
12
+ ): unknown {
13
+ if (/^\d+$/.test(selector)) return collection[Number(selector)];
14
+ const qualifier = /^([A-Za-z_][A-Za-z0-9_-]*):([\s\S]*)$/.exec(selector);
15
+ if (!qualifier) {
16
+ options.report?.(`"${selector}" is neither an index nor a <field>:<value> qualifier`);
17
+ return undefined;
18
+ }
19
+ const [, field, value] = qualifier;
20
+ const elements = collection.filter((element): element is Record<string, unknown> =>
21
+ element !== null && typeof element === "object");
22
+ let matches = elements.filter(element => {
23
+ if (field === "type") return options.matchesComponentType?.(element, value) ?? element.constructor?.name === value;
24
+ if (field === "guid") return options.matchesComponentGuid?.(element, value)
25
+ ?? (element.guid === value || element.needle_editor_guid === value);
26
+ return element[field] === value;
27
+ });
28
+ if (!matches.length && field === "guid" && options.referenceId && options.deriveComponentGuid) {
29
+ const derived = options.deriveComponentGuid(options.referenceId, value);
30
+ matches = elements.filter(element => element.guid === derived);
31
+ }
32
+ if (matches.length > 1) {
33
+ options.report?.(`"${selector}" matched ${matches.length} elements ? the document must name one unambiguously`);
34
+ return undefined;
35
+ }
36
+ return matches[0];
37
+ }