@forgeax/engine-pack 0.1.21 → 0.1.24

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 (139) hide show
  1. package/README.md +167 -48
  2. package/dist/__tests__/inventory-schema.test.d.ts +2 -0
  3. package/dist/__tests__/inventory-schema.test.d.ts.map +1 -0
  4. package/dist/__tests__/material-cook-receipt-layer-plan.unit.test.d.ts +2 -0
  5. package/dist/__tests__/material-cook-receipt-layer-plan.unit.test.d.ts.map +1 -0
  6. package/dist/__tests__/pack-authoring-gateway.unit.test.d.ts +2 -0
  7. package/dist/__tests__/pack-authoring-gateway.unit.test.d.ts.map +1 -0
  8. package/dist/__tests__/pack-authoring.unit.test.d.ts +2 -0
  9. package/dist/__tests__/pack-authoring.unit.test.d.ts.map +1 -0
  10. package/dist/__tests__/pack-scanner.unit.test.d.ts +2 -0
  11. package/dist/__tests__/pack-scanner.unit.test.d.ts.map +1 -0
  12. package/dist/__tests__/scanner-blacklist.test.d.ts +2 -0
  13. package/dist/__tests__/scanner-blacklist.test.d.ts.map +1 -0
  14. package/dist/build.d.ts +9 -4
  15. package/dist/build.d.ts.map +1 -1
  16. package/dist/build.mjs +3449 -897
  17. package/dist/build.mjs.map +1 -1
  18. package/dist/builtin.mjs +37 -4
  19. package/dist/builtin.mjs.map +1 -1
  20. package/dist/catalog-builder.d.ts.map +1 -1
  21. package/dist/cli-asset.d.ts.map +1 -1
  22. package/dist/cli-asset.mjs +1361 -845
  23. package/dist/cli-asset.mjs.map +1 -1
  24. package/dist/deriveAssetName.d.ts +11 -7
  25. package/dist/deriveAssetName.d.ts.map +1 -1
  26. package/dist/evidence/material-cook.d.ts +11 -0
  27. package/dist/evidence/material-cook.d.ts.map +1 -1
  28. package/dist/evidence/source-inventory.d.ts.map +1 -1
  29. package/dist/guid.d.ts +15 -0
  30. package/dist/guid.d.ts.map +1 -1
  31. package/dist/guid.mjs +53 -5
  32. package/dist/guid.mjs.map +1 -1
  33. package/dist/index.d.ts +4 -2
  34. package/dist/index.d.ts.map +1 -1
  35. package/dist/index.mjs +1297 -562
  36. package/dist/index.mjs.map +1 -1
  37. package/dist/inventory/binding.d.ts +6 -0
  38. package/dist/inventory/binding.d.ts.map +1 -0
  39. package/dist/inventory/declaration.d.ts +24 -0
  40. package/dist/inventory/declaration.d.ts.map +1 -0
  41. package/dist/inventory/index.d.ts +3 -0
  42. package/dist/inventory/index.d.ts.map +1 -0
  43. package/dist/inventory/sync.d.ts +5 -0
  44. package/dist/inventory/sync.d.ts.map +1 -0
  45. package/dist/material-cook.d.ts +1 -1
  46. package/dist/material-cook.d.ts.map +1 -1
  47. package/dist/material-cook.mjs +50 -3
  48. package/dist/material-cook.mjs.map +1 -1
  49. package/dist/name.mjs +4 -7
  50. package/dist/name.mjs.map +1 -1
  51. package/dist/pack-authoring-node.d.ts +26 -0
  52. package/dist/pack-authoring-node.d.ts.map +1 -0
  53. package/dist/pack-authoring-node.mjs +11089 -0
  54. package/dist/pack-authoring-node.mjs.map +1 -0
  55. package/dist/pack-authoring.d.ts +230 -0
  56. package/dist/pack-authoring.d.ts.map +1 -0
  57. package/dist/pack-authoring.mjs +1137 -0
  58. package/dist/pack-authoring.mjs.map +1 -0
  59. package/dist/package-finalizer.d.ts +2 -20
  60. package/dist/package-finalizer.d.ts.map +1 -1
  61. package/dist/resolve-asset-source.d.ts +1 -3
  62. package/dist/resolve-asset-source.d.ts.map +1 -1
  63. package/dist/resolve-asset-source.mjs +6 -81
  64. package/dist/resolve-asset-source.mjs.map +1 -1
  65. package/dist/runtime.mjs +77 -7
  66. package/dist/runtime.mjs.map +1 -1
  67. package/dist/scanner.d.ts +18 -7
  68. package/dist/scanner.d.ts.map +1 -1
  69. package/dist/scanner.mjs +1164 -687
  70. package/dist/scanner.mjs.map +1 -1
  71. package/dist/schema-compiled.d.ts.map +1 -1
  72. package/dist/schema.mjs +79 -7
  73. package/dist/schema.mjs.map +1 -1
  74. package/dist/scriptable-pack-node.d.ts +7 -31
  75. package/dist/scriptable-pack-node.d.ts.map +1 -1
  76. package/dist/scriptable-pack-node.mjs +572 -1019
  77. package/dist/scriptable-pack-node.mjs.map +1 -1
  78. package/dist/scriptable-pack-worker.mjs +29 -11
  79. package/dist/scriptable-pack-worker.mjs.map +1 -1
  80. package/dist/scriptable-pack.d.ts +20 -532
  81. package/dist/scriptable-pack.d.ts.map +1 -1
  82. package/dist/scriptable-pack.mjs +1038 -527
  83. package/dist/scriptable-pack.mjs.map +1 -1
  84. package/package.json +2 -7
  85. package/schema/meta.schema.json +1 -1
  86. package/schema/pack.schema.json +76 -6
  87. package/src/__tests__/guid-collision.unit.test.ts +18 -9
  88. package/src/__tests__/inventory-schema.test.ts +109 -0
  89. package/src/__tests__/material-cook-receipt-layer-plan.unit.test.ts +16 -0
  90. package/src/__tests__/material-cook-schema.unit.test.ts +62 -0
  91. package/src/__tests__/pack-authoring-gateway.unit.test.ts +249 -0
  92. package/src/__tests__/pack-authoring.unit.test.ts +266 -0
  93. package/src/__tests__/pack-scanner.unit.test.ts +123 -0
  94. package/src/__tests__/pack.unit.test.ts +1 -238
  95. package/src/__tests__/package-finalizer.contract.test.ts +55 -0
  96. package/src/__tests__/resolve-asset-source.test.ts +15 -98
  97. package/src/__tests__/scanner-blacklist.test.ts +55 -0
  98. package/src/__tests__/scanner-inventory.contract.test.ts +11 -14
  99. package/src/__tests__/scanner-inventory.test.ts +23 -10
  100. package/src/__tests__/scriptable-pack-cli.integration.test.ts +4 -9
  101. package/src/__tests__/topology.unit.test.ts +23 -0
  102. package/src/build.ts +28 -18
  103. package/src/catalog-builder.ts +60 -33
  104. package/src/cli-asset.ts +68 -83
  105. package/src/deriveAssetName.ts +14 -13
  106. package/src/evidence/material-cook.ts +79 -3
  107. package/src/evidence/source-inventory.ts +34 -21
  108. package/src/guid.ts +65 -4
  109. package/src/index.ts +15 -11
  110. package/src/inventory/binding.ts +25 -0
  111. package/src/inventory/declaration.ts +168 -0
  112. package/src/inventory/index.ts +18 -0
  113. package/src/inventory/sync.ts +22 -0
  114. package/src/material-cook.ts +1 -0
  115. package/src/pack-authoring-node.ts +1966 -0
  116. package/src/pack-authoring.ts +1478 -0
  117. package/src/package-finalizer.ts +2 -42
  118. package/src/resolve-asset-source.ts +4 -76
  119. package/src/scanner.ts +236 -56
  120. package/src/schema/material-cook.schema.json +4 -1
  121. package/src/schema-compiled.ts +2 -0
  122. package/src/scriptable-pack-node.ts +108 -719
  123. package/src/scriptable-pack-worker.ts +48 -16
  124. package/src/scriptable-pack.ts +28 -939
  125. package/dist/.tsbuildinfo +0 -1
  126. package/dist/__tests__/load-asset-config.test.d.ts +0 -2
  127. package/dist/__tests__/load-asset-config.test.d.ts.map +0 -1
  128. package/dist/__tests__/scriptable-pack.test-d.d.ts +0 -2
  129. package/dist/__tests__/scriptable-pack.test-d.d.ts.map +0 -1
  130. package/dist/__tests__/scriptable-pack.unit.test.d.ts +0 -2
  131. package/dist/__tests__/scriptable-pack.unit.test.d.ts.map +0 -1
  132. package/dist/config.d.ts +0 -6
  133. package/dist/config.d.ts.map +0 -1
  134. package/dist/config.mjs +0 -29
  135. package/dist/config.mjs.map +0 -1
  136. package/src/__tests__/load-asset-config.test.ts +0 -121
  137. package/src/__tests__/scriptable-pack.test-d.ts +0 -131
  138. package/src/__tests__/scriptable-pack.unit.test.ts +0 -789
  139. package/src/config.ts +0 -36
package/src/guid.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import { sha1 } from '@noble/hashes/legacy.js';
1
2
  import { uuidv7obj } from 'uuidv7';
2
3
  import { PackError } from './errors.js';
3
4
 
@@ -8,6 +9,9 @@ import { PackError } from './errors.js';
8
9
  */
9
10
  export type AssetGuid = Uint8Array & { readonly __guidBrand: 'AssetGuid' };
10
11
 
12
+ /** Pack-level identity. It is intentionally not assignable to AssetGuid. */
13
+ export type PackageId = Uint8Array & { readonly __packageIdBrand: 'PackageId' };
14
+
11
15
  /** Minimal Result alias for this module (structurally compatible with ScanResult). */
12
16
  export type GuidResult<T, E> =
13
17
  | { readonly ok: true; readonly value: T }
@@ -27,6 +31,13 @@ function bytesToDashForm(bytes: Uint8Array): string {
27
31
 
28
32
  const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
29
33
 
34
+ /** Stable author-owned output identity. Paths and payloads are not identity inputs. */
35
+ export const PACK_SOURCE_KEY_RE = /^[a-z0-9][a-z0-9._-]*(\/[a-z0-9][a-z0-9._-]*)*$/;
36
+
37
+ export function isValidPackSourceKey(value: unknown): value is string {
38
+ return typeof value === 'string' && PACK_SOURCE_KEY_RE.test(value);
39
+ }
40
+
30
41
  export function isValidAssetGuidString(value: unknown): value is string {
31
42
  return typeof value === 'string' && UUID_RE.test(value);
32
43
  }
@@ -40,6 +51,54 @@ function dashFormToBytes(dashForm: string): Uint8Array {
40
51
  return bytes;
41
52
  }
42
53
 
54
+ function randomUuidBytes(): Uint8Array {
55
+ const uuid = uuidv7obj();
56
+ const bytes = new Uint8Array(16);
57
+ bytes.set(uuid.bytes);
58
+ return bytes;
59
+ }
60
+
61
+ function packageId(value: Uint8Array): PackageId {
62
+ return value as PackageId;
63
+ }
64
+
65
+ /** Utilities for the pack-level UUID identity. */
66
+ export const PackageId = {
67
+ parse(dashForm: string): GuidResult<PackageId, PackError> {
68
+ const parsed = AssetGuid.parse(dashForm);
69
+ return parsed.ok ? { ok: true, value: packageId(parsed.value) } : parsed;
70
+ },
71
+ format(value: PackageId): string {
72
+ return bytesToDashForm(value);
73
+ },
74
+ random(): PackageId {
75
+ return packageId(randomUuidBytes());
76
+ },
77
+ } as const;
78
+
79
+ function derivedGuid(namespace: PackageId, sourceKey: string): AssetGuid {
80
+ if (!(namespace instanceof Uint8Array) || namespace.byteLength !== 16) {
81
+ throw new TypeError('AssetGuid.derive requires a 16-byte PackageId');
82
+ }
83
+ if (!isValidPackSourceKey(sourceKey)) {
84
+ const error = new TypeError(
85
+ `AssetGuid.derive received invalid sourceKey ${JSON.stringify(sourceKey)}`,
86
+ ) as TypeError & { readonly code?: string };
87
+ Object.defineProperty(error, 'code', { value: 'pack-source-key-invalid' });
88
+ throw error;
89
+ }
90
+ const name = new TextEncoder().encode(sourceKey);
91
+ const input = new Uint8Array(namespace.byteLength + name.byteLength);
92
+ input.set(namespace, 0);
93
+ input.set(name, namespace.byteLength);
94
+ const digest = sha1(input);
95
+ const result = digest.slice(0, 16);
96
+ // RFC 4122 §4.1.1 / §4.1.3: UUID version 5, RFC variant.
97
+ result[6] = ((result[6] ?? 0) & 0x0f) | 0x50;
98
+ result[8] = ((result[8] ?? 0) & 0x3f) | 0x80;
99
+ return brand(result);
100
+ }
101
+
43
102
  /**
44
103
  * Utilities for the AssetGuid branded 16-byte UUID type.
45
104
  * Declare the type via `import type { AssetGuid } from '@forgeax/engine-pack/guid'`
@@ -91,9 +150,11 @@ export const AssetGuid = {
91
150
  * Works in both Node.js and browser environments.
92
151
  */
93
152
  random(): AssetGuid {
94
- const uuid = uuidv7obj();
95
- const bytes = new Uint8Array(16);
96
- bytes.set(uuid.bytes);
97
- return brand(bytes);
153
+ return brand(randomUuidBytes());
154
+ },
155
+
156
+ /** Derive the stable UUIDv5 projection for one Pack subject and sourceKey. */
157
+ derive(namespace: PackageId, sourceKey: string): AssetGuid {
158
+ return derivedGuid(namespace, sourceKey);
98
159
  },
99
160
  } as const;
package/src/index.ts CHANGED
@@ -7,6 +7,8 @@ export {
7
7
  collectMaterialCookRefs,
8
8
  createMaterialArtifactDigest,
9
9
  createMaterialCookIdentity,
10
+ isStandardMaterialRecord,
11
+ isStandardRootModule,
10
12
  type MaterialCookArtifact,
11
13
  type MaterialCookIdentity,
12
14
  type MaterialCookIdentityExpectation,
@@ -15,6 +17,7 @@ export {
15
17
  type MaterialCookRecordError,
16
18
  type MaterialCookRefs,
17
19
  type MaterialCookWasmProvenance,
20
+ materialLayerPlanIdentity,
18
21
  projectCookedMaterialRecord,
19
22
  serializeCookedMaterialRecord,
20
23
  serializeMaterialCookReceipt,
@@ -22,6 +25,13 @@ export {
22
25
  validateMaterialCookReceipt,
23
26
  } from './evidence/material-cook.js';
24
27
  export { buildOfflineAssetEvidence, packageVerification } from './evidence/offline-evidence.js';
28
+ export {
29
+ AssetGuid,
30
+ isValidAssetGuidString,
31
+ isValidPackSourceKey,
32
+ PACK_SOURCE_KEY_RE,
33
+ PackageId,
34
+ } from './guid.js';
25
35
  export {
26
36
  decodeMeshBinHeader,
27
37
  MESH_BIN_DIGEST_BYTES,
@@ -33,24 +43,18 @@ export {
33
43
  type MeshBinHeaderV4,
34
44
  writeMeshBinHeader,
35
45
  } from './mesh-bin-contract.js';
46
+ export * from './pack-authoring.js';
36
47
  export { validateProducerContract, validateProducerOutputs } from './producer-contract.js';
37
48
  export {
38
49
  type AssetReader,
39
50
  isScriptablePackAssetKind,
40
- projectScriptablePackMeta,
51
+ projectScriptablePackSceneComponents,
41
52
  SCRIPTABLE_PACK_ASSET_KINDS,
42
- type ScriptablePackAssetDeclaration,
43
- type ScriptablePackAssetDeclarations,
44
- type ScriptablePackAssetFor,
45
53
  type ScriptablePackAssetKind,
46
- type ScriptablePackDefinition,
47
- type ScriptablePackError,
48
- type ScriptablePackExternalAssets,
49
- type ScriptablePackMetaJson,
50
- type ScriptablePackOutputs,
51
- type ScriptablePackPublicationEnvelope,
52
54
  type ScriptablePackReadError,
53
- validateScriptablePackDefinition,
55
+ type ScriptablePackSceneComponent,
56
+ type ScriptablePackSceneComponentInput,
57
+ type ScriptablePackSourceClosureEntry,
54
58
  } from './scriptable-pack.js';
55
59
  export { calculateTopologyDiff, diffTopology } from './topology.js';
56
60
 
@@ -0,0 +1,25 @@
1
+ import type { AssetRef, SceneEntityRef } from '@forgeax/engine-types';
2
+ import type { AuthorInventory } from './declaration.js';
3
+
4
+ export function projectAssetRefs(inventory: AuthorInventory): readonly AssetRef[] {
5
+ return inventory.declarations.map(
6
+ (row) =>
7
+ ({
8
+ guid: row.guid,
9
+ sourceKey: row.sourceKey,
10
+ kind: row.kind,
11
+ }) as AssetRef,
12
+ );
13
+ }
14
+
15
+ /** Derive instance-relative scene refs from the same validated author rows. */
16
+ export function projectSceneEntityRefs(inventory: AuthorInventory): readonly SceneEntityRef[] {
17
+ return inventory.declarations.flatMap((row) =>
18
+ row.sceneBindings === undefined
19
+ ? []
20
+ : row.sceneBindings.map((bindingKey) => ({
21
+ sceneSourceKey: row.sourceKey,
22
+ bindingKey,
23
+ })),
24
+ );
25
+ }
@@ -0,0 +1,168 @@
1
+ import { err, ok, type Result } from '@forgeax/engine-types';
2
+
3
+ export interface AuthorInventoryRow {
4
+ readonly guid: string;
5
+ readonly sourceKey: string;
6
+ readonly kind: string;
7
+ readonly payload: Readonly<Record<string, unknown>>;
8
+ readonly refs: readonly string[];
9
+ readonly sceneBindings?: readonly string[];
10
+ }
11
+
12
+ export interface AuthorInventory {
13
+ readonly declarations: readonly AuthorInventoryRow[];
14
+ }
15
+
16
+ export type InventoryErrorCode =
17
+ | 'inventory-source-key-missing'
18
+ | 'inventory-source-key-duplicate'
19
+ | 'inventory-guid-duplicate'
20
+ | 'inventory-scene-binding-missing'
21
+ | 'inventory-scene-binding-duplicate';
22
+
23
+ export type InventoryError = {
24
+ readonly code: InventoryErrorCode;
25
+ readonly expected: string;
26
+ readonly hint: string;
27
+ readonly detail: {
28
+ readonly guid?: string;
29
+ readonly sourceKey?: string;
30
+ };
31
+ };
32
+
33
+ function failure(
34
+ code: InventoryErrorCode,
35
+ expected: string,
36
+ hint: string,
37
+ detail: InventoryError['detail'],
38
+ ): Result<never, InventoryError> {
39
+ return err({ code, expected, hint, detail });
40
+ }
41
+
42
+ export function validateAuthorInventory(value: unknown): Result<AuthorInventory, InventoryError> {
43
+ if (
44
+ typeof value !== 'object' ||
45
+ value === null ||
46
+ !Array.isArray((value as { readonly declarations?: unknown }).declarations)
47
+ ) {
48
+ return failure(
49
+ 'inventory-source-key-missing',
50
+ 'an author inventory with declaration rows',
51
+ 'declare each author asset in the source inventory',
52
+ {},
53
+ );
54
+ }
55
+
56
+ const guids = new Set<string>();
57
+ const sourceKeys = new Set<string>();
58
+ const declarations: AuthorInventoryRow[] = [];
59
+ const rows = (value as { readonly declarations: readonly unknown[] }).declarations;
60
+ for (const raw of rows) {
61
+ if (typeof raw !== 'object' || raw === null) {
62
+ return failure(
63
+ 'inventory-source-key-missing',
64
+ 'each declaration has a non-empty sourceKey',
65
+ 'add sourceKey to the author declaration before projection',
66
+ {},
67
+ );
68
+ }
69
+ const row = raw as Record<string, unknown>;
70
+ const guid = typeof row.guid === 'string' ? row.guid : undefined;
71
+ const sourceKey = typeof row.sourceKey === 'string' ? row.sourceKey : undefined;
72
+ if (
73
+ guid === undefined ||
74
+ sourceKey === undefined ||
75
+ sourceKey.trim().length === 0 ||
76
+ sourceKey !== sourceKey.trim()
77
+ ) {
78
+ return failure(
79
+ 'inventory-source-key-missing',
80
+ 'each declaration has a non-empty sourceKey',
81
+ 'add sourceKey to the author declaration before projection',
82
+ {
83
+ ...(guid === undefined ? {} : { guid }),
84
+ ...(sourceKey === undefined ? {} : { sourceKey }),
85
+ },
86
+ );
87
+ }
88
+ const normalizedGuid = guid.toLowerCase();
89
+ if (guids.has(normalizedGuid)) {
90
+ return failure(
91
+ 'inventory-guid-duplicate',
92
+ 'every GUID identifies exactly one author declaration',
93
+ 'remove or rename the duplicate GUID before publishing the inventory',
94
+ { guid, sourceKey },
95
+ );
96
+ }
97
+ if (sourceKeys.has(sourceKey)) {
98
+ return failure(
99
+ 'inventory-source-key-duplicate',
100
+ 'every sourceKey identifies exactly one author declaration',
101
+ 'rename the duplicate semantic sourceKey before publishing the inventory',
102
+ { guid, sourceKey },
103
+ );
104
+ }
105
+ if (row.kind === 'scene') {
106
+ const payload = row.payload;
107
+ const payloadRecord =
108
+ typeof payload === 'object' && payload !== null
109
+ ? (payload as Record<string, unknown>)
110
+ : undefined;
111
+ const entities =
112
+ payloadRecord !== undefined && Array.isArray(payloadRecord.entities)
113
+ ? payloadRecord.entities
114
+ : [];
115
+ const bindings = new Set<string>();
116
+ for (const entity of entities) {
117
+ const bindingKey =
118
+ typeof entity === 'object' && entity !== null && typeof entity.bindingKey === 'string'
119
+ ? entity.bindingKey
120
+ : undefined;
121
+ if (bindingKey !== undefined && bindingKey.length === 0) {
122
+ return failure(
123
+ 'inventory-scene-binding-missing',
124
+ 'each declared scene bindingKey is non-empty',
125
+ 'remove the empty bindingKey or declare a stable key in the scene producer',
126
+ { sourceKey },
127
+ );
128
+ }
129
+ if (bindingKey === undefined) continue;
130
+ if (bindings.has(bindingKey)) {
131
+ return failure(
132
+ 'inventory-scene-binding-duplicate',
133
+ 'bindingKey values are unique within one scene',
134
+ 'rename the duplicate bindingKey in the scene producer',
135
+ { sourceKey },
136
+ );
137
+ }
138
+ bindings.add(bindingKey);
139
+ }
140
+ }
141
+ guids.add(normalizedGuid);
142
+ sourceKeys.add(sourceKey);
143
+ const sceneBindings =
144
+ row.kind === 'scene' && typeof row.payload === 'object' && row.payload !== null
145
+ ? (row.payload as { readonly entities?: readonly unknown[] }).entities?.flatMap(
146
+ (entity) => {
147
+ if (typeof entity !== 'object' || entity === null) return [];
148
+ const key = (entity as { readonly bindingKey?: unknown }).bindingKey;
149
+ return typeof key === 'string' && key.length > 0 ? [key] : [];
150
+ },
151
+ )
152
+ : undefined;
153
+ declarations.push({
154
+ guid,
155
+ sourceKey,
156
+ kind: typeof row.kind === 'string' ? row.kind : 'unknown',
157
+ payload:
158
+ typeof row.payload === 'object' && row.payload !== null
159
+ ? (row.payload as Readonly<Record<string, unknown>>)
160
+ : {},
161
+ refs: Array.isArray(row.refs)
162
+ ? row.refs.filter((ref): ref is string => typeof ref === 'string')
163
+ : [],
164
+ ...(sceneBindings === undefined ? {} : { sceneBindings }),
165
+ });
166
+ }
167
+ return ok({ declarations });
168
+ }
@@ -0,0 +1,18 @@
1
+ export { validateProducerContract, validateProducerOutputs } from '../producer-contract.js';
2
+ export {
3
+ type InventoryDeclaration,
4
+ type MetaInventoryDocument,
5
+ type MetaInventorySubAsset,
6
+ type PackInventoryAsset,
7
+ type PackInventoryDocument,
8
+ SCANNER_BLACKLIST,
9
+ type ScanInventory,
10
+ type ScanOptions,
11
+ type ScanResult,
12
+ type ScanSourceDeclaration,
13
+ type ScriptablePackInventoryDeclaration,
14
+ type ScriptablePackScanOptions,
15
+ STANDARD_SCRIPTABLE_PACK_SCAN_OPTIONS,
16
+ scan,
17
+ scanInventory,
18
+ } from '../scanner.js';
@@ -0,0 +1,22 @@
1
+ import type { AuthorInventory } from './declaration.js';
2
+
3
+ export type InventoryDigest = string;
4
+
5
+ export function inventoryDigest(inventory: AuthorInventory): InventoryDigest {
6
+ return inventory.declarations
7
+ .map(
8
+ (row) => `${row.guid}\0${row.sourceKey}\0${row.kind}\0${row.sceneBindings?.join('\0') ?? ''}`,
9
+ )
10
+ .sort()
11
+ .join('\n');
12
+ }
13
+
14
+ export function syncAuthorInventory(inventory: AuthorInventory): AuthorInventory {
15
+ return {
16
+ declarations: inventory.declarations.map((row) => ({
17
+ ...row,
18
+ refs: [...row.refs],
19
+ ...(row.sceneBindings === undefined ? {} : { sceneBindings: [...row.sceneBindings] }),
20
+ })),
21
+ };
22
+ }
@@ -8,6 +8,7 @@ export {
8
8
  type MaterialCookRecordError,
9
9
  type MaterialCookRefs,
10
10
  type MaterialCookWasmProvenance,
11
+ materialLayerPlanIdentity,
11
12
  projectCookedMaterialRecord,
12
13
  serializeCookedMaterialRecord,
13
14
  serializeMaterialCookReceipt,