@forgeax/engine-import 0.0.0-dev.8d955ade1c79

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 (118) hide show
  1. package/LICENSE +202 -0
  2. package/README.md +227 -0
  3. package/dist/.tsbuildinfo +1 -0
  4. package/dist/__tests__/decode-image-owner.test-d.d.ts +2 -0
  5. package/dist/__tests__/decode-image-owner.test-d.d.ts.map +1 -0
  6. package/dist/__tests__/import-contract-migration.test.d.ts +2 -0
  7. package/dist/__tests__/import-contract-migration.test.d.ts.map +1 -0
  8. package/dist/__tests__/import-dependencies.test.d.ts +2 -0
  9. package/dist/__tests__/import-dependencies.test.d.ts.map +1 -0
  10. package/dist/__tests__/import-finalizer-digest-retry.test.d.ts +2 -0
  11. package/dist/__tests__/import-finalizer-digest-retry.test.d.ts.map +1 -0
  12. package/dist/__tests__/import-local-artifacts.test.d.ts +2 -0
  13. package/dist/__tests__/import-local-artifacts.test.d.ts.map +1 -0
  14. package/dist/__tests__/import.unit.test.d.ts +2 -0
  15. package/dist/__tests__/import.unit.test.d.ts.map +1 -0
  16. package/dist/__tests__/mesh-bin-contract.test.d.ts +2 -0
  17. package/dist/__tests__/mesh-bin-contract.test.d.ts.map +1 -0
  18. package/dist/__tests__/mesh-bin-morph-v3.test.d.ts +2 -0
  19. package/dist/__tests__/mesh-bin-morph-v3.test.d.ts.map +1 -0
  20. package/dist/__tests__/mesh-bin.test.d.ts +2 -0
  21. package/dist/__tests__/mesh-bin.test.d.ts.map +1 -0
  22. package/dist/__tests__/owner-chain.integration.test.d.ts +2 -0
  23. package/dist/__tests__/owner-chain.integration.test.d.ts.map +1 -0
  24. package/dist/__tests__/scriptable-pack-host.unit.test.d.ts +2 -0
  25. package/dist/__tests__/scriptable-pack-host.unit.test.d.ts.map +1 -0
  26. package/dist/__tests__/scriptable-pack-output-producers.unit.test.d.ts +2 -0
  27. package/dist/__tests__/scriptable-pack-output-producers.unit.test.d.ts.map +1 -0
  28. package/dist/__tests__/scriptable-pack-product.contract.test.d.ts +2 -0
  29. package/dist/__tests__/scriptable-pack-product.contract.test.d.ts.map +1 -0
  30. package/dist/__tests__/scriptable-pack-production-producers.unit.test.d.ts +2 -0
  31. package/dist/__tests__/scriptable-pack-production-producers.unit.test.d.ts.map +1 -0
  32. package/dist/__tests__/scriptable-pack-staged-snapshot.unit.test.d.ts +2 -0
  33. package/dist/__tests__/scriptable-pack-staged-snapshot.unit.test.d.ts.map +1 -0
  34. package/dist/__tests__/scriptable-pack.integration.test.d.ts +2 -0
  35. package/dist/__tests__/scriptable-pack.integration.test.d.ts.map +1 -0
  36. package/dist/__tests__/source-overrides-import-context.unit.test.d.ts +2 -0
  37. package/dist/__tests__/source-overrides-import-context.unit.test.d.ts.map +1 -0
  38. package/dist/__tests__/source-package-publication.integration.test.d.ts +2 -0
  39. package/dist/__tests__/source-package-publication.integration.test.d.ts.map +1 -0
  40. package/dist/browser.d.ts +5 -0
  41. package/dist/browser.d.ts.map +1 -0
  42. package/dist/browser.mjs +782 -0
  43. package/dist/browser.mjs.map +1 -0
  44. package/dist/build-production.d.ts +48 -0
  45. package/dist/build-production.d.ts.map +1 -0
  46. package/dist/catalog-importer-policy.d.ts +13 -0
  47. package/dist/catalog-importer-policy.d.ts.map +1 -0
  48. package/dist/catalog-inventory.d.ts +4 -0
  49. package/dist/catalog-inventory.d.ts.map +1 -0
  50. package/dist/import-product.d.ts +27 -0
  51. package/dist/import-product.d.ts.map +1 -0
  52. package/dist/import-runner.d.ts +136 -0
  53. package/dist/import-runner.d.ts.map +1 -0
  54. package/dist/importer-registry.d.ts +48 -0
  55. package/dist/importer-registry.d.ts.map +1 -0
  56. package/dist/index.d.ts +19 -0
  57. package/dist/index.d.ts.map +1 -0
  58. package/dist/index.mjs +2849 -0
  59. package/dist/index.mjs.map +1 -0
  60. package/dist/mesh-bin.d.ts +19 -0
  61. package/dist/mesh-bin.d.ts.map +1 -0
  62. package/dist/mesh-bin.mjs +182 -0
  63. package/dist/mesh-bin.mjs.map +1 -0
  64. package/dist/pack-projection.d.ts +5 -0
  65. package/dist/pack-projection.d.ts.map +1 -0
  66. package/dist/scriptable-pack-host.d.ts +72 -0
  67. package/dist/scriptable-pack-host.d.ts.map +1 -0
  68. package/dist/scriptable-pack-output-producers.d.ts +7 -0
  69. package/dist/scriptable-pack-output-producers.d.ts.map +1 -0
  70. package/dist/scriptable-pack-staged-snapshot.d.ts +21 -0
  71. package/dist/scriptable-pack-staged-snapshot.d.ts.map +1 -0
  72. package/dist/scriptable-pack.d.ts +141 -0
  73. package/dist/scriptable-pack.d.ts.map +1 -0
  74. package/dist/scriptable-source-package.d.ts +14 -0
  75. package/dist/scriptable-source-package.d.ts.map +1 -0
  76. package/dist/source-package-errors.d.ts +52 -0
  77. package/dist/source-package-errors.d.ts.map +1 -0
  78. package/dist/source-package-publication.d.ts +63 -0
  79. package/dist/source-package-publication.d.ts.map +1 -0
  80. package/dist/source-package.d.ts +60 -0
  81. package/dist/source-package.d.ts.map +1 -0
  82. package/package.json +69 -0
  83. package/src/__tests__/decode-image-owner.test-d.ts +53 -0
  84. package/src/__tests__/import-contract-migration.test.ts +223 -0
  85. package/src/__tests__/import-dependencies.test.ts +65 -0
  86. package/src/__tests__/import-finalizer-digest-retry.test.ts +113 -0
  87. package/src/__tests__/import-local-artifacts.test.ts +102 -0
  88. package/src/__tests__/import.unit.test.ts +429 -0
  89. package/src/__tests__/mesh-bin-contract.test.ts +95 -0
  90. package/src/__tests__/mesh-bin-morph-v3.test.ts +44 -0
  91. package/src/__tests__/mesh-bin.test.ts +102 -0
  92. package/src/__tests__/owner-chain.integration.test.ts +37 -0
  93. package/src/__tests__/scriptable-pack-host.unit.test.ts +86 -0
  94. package/src/__tests__/scriptable-pack-output-producers.unit.test.ts +190 -0
  95. package/src/__tests__/scriptable-pack-product.contract.test.ts +47 -0
  96. package/src/__tests__/scriptable-pack-production-producers.unit.test.ts +488 -0
  97. package/src/__tests__/scriptable-pack-staged-snapshot.unit.test.ts +290 -0
  98. package/src/__tests__/scriptable-pack.integration.test.ts +435 -0
  99. package/src/__tests__/source-overrides-import-context.unit.test.ts +92 -0
  100. package/src/__tests__/source-package-publication.integration.test.ts +75 -0
  101. package/src/browser.ts +14 -0
  102. package/src/build-production.ts +493 -0
  103. package/src/catalog-importer-policy.ts +58 -0
  104. package/src/catalog-inventory.ts +24 -0
  105. package/src/import-product.ts +163 -0
  106. package/src/import-runner.ts +749 -0
  107. package/src/importer-registry.ts +103 -0
  108. package/src/index.ts +175 -0
  109. package/src/mesh-bin.ts +229 -0
  110. package/src/pack-projection.ts +21 -0
  111. package/src/scriptable-pack-host.ts +544 -0
  112. package/src/scriptable-pack-output-producers.ts +456 -0
  113. package/src/scriptable-pack-staged-snapshot.ts +172 -0
  114. package/src/scriptable-pack.ts +626 -0
  115. package/src/scriptable-source-package.ts +88 -0
  116. package/src/source-package-errors.ts +201 -0
  117. package/src/source-package-publication.ts +326 -0
  118. package/src/source-package.ts +187 -0
@@ -0,0 +1,290 @@
1
+ import { AssetGuid } from '@forgeax/engine-pack/guid';
2
+ import type { AssetGuid as AssetGuidType, MeshAsset } from '@forgeax/engine-types';
3
+ import { AssetError, err, ok } from '@forgeax/engine-types';
4
+ import { describe, expect, it } from 'vitest';
5
+ import {
6
+ createScriptablePackStagedAssetSnapshotSource,
7
+ type ScriptablePackStagedOwner,
8
+ } from '../index.js';
9
+
10
+ function guid(value: string): AssetGuidType {
11
+ const parsed = AssetGuid.parse(value);
12
+ if (!parsed.ok) throw parsed.error;
13
+ return parsed.value;
14
+ }
15
+
16
+ function mesh(seed: number): MeshAsset {
17
+ return {
18
+ kind: 'mesh',
19
+ vertices: new Float32Array([seed, 0, 0]),
20
+ attributes: {},
21
+ submeshes: [
22
+ {
23
+ topology: 'triangle-list',
24
+ indexOffset: 0,
25
+ indexCount: 0,
26
+ vertexCount: 1,
27
+ materialSlot: 0,
28
+ },
29
+ ],
30
+ materialSlots: [{ slotName: 'Default' }],
31
+ };
32
+ }
33
+
34
+ const ROOT = guid('019ffa97-2000-7000-8000-000000000001');
35
+ const DEPENDENCY = guid('019ffa97-2000-7000-8000-000000000002');
36
+ const HEALTHY = guid('019ffa97-2000-7000-8000-000000000003');
37
+
38
+ describe('ScriptablePack staged asset snapshot source', () => {
39
+ it('lazily builds owners in one generation, memoizes, and returns private clones', async () => {
40
+ const builds: string[] = [];
41
+ const owners: ScriptablePackStagedOwner[] = [
42
+ {
43
+ id: 'dependency.pack.ts',
44
+ guids: [DEPENDENCY],
45
+ async build() {
46
+ builds.push('dependency');
47
+ return ok([{ guid: DEPENDENCY, asset: mesh(2), digest: 'sha256:dependency' }]);
48
+ },
49
+ },
50
+ {
51
+ id: 'root.pack.ts',
52
+ guids: [ROOT],
53
+ async build(source) {
54
+ builds.push('root:start');
55
+ const dependency = await source.readByGuid(DEPENDENCY);
56
+ if (!dependency.ok) return dependency;
57
+ builds.push('root:end');
58
+ const value = dependency.value.asset as MeshAsset;
59
+ return ok([{ guid: ROOT, asset: mesh(value.vertices[0] ?? 0) }]);
60
+ },
61
+ },
62
+ ];
63
+ const source = createScriptablePackStagedAssetSnapshotSource({ generation: 12, owners });
64
+ const first = await source.readByGuid(ROOT);
65
+ expect(first).toMatchObject({ ok: true, value: { generation: 12 } });
66
+ if (!first.ok) return;
67
+ expect(builds).toEqual(['root:start', 'dependency', 'root:end']);
68
+ expect((first.value.asset as MeshAsset).vertices[0]).toBe(2);
69
+
70
+ (first.value.asset as MeshAsset).vertices[0] = 99;
71
+ const second = await source.readByGuid(ROOT);
72
+ expect(second.ok && (second.value.asset as MeshAsset).vertices[0]).toBe(2);
73
+ expect(builds).toEqual(['root:start', 'dependency', 'root:end']);
74
+ expect(second.ok && second.value.digest).toMatch(/^sha256:/);
75
+ });
76
+
77
+ it('fails a content dependency cycle structurally', async () => {
78
+ const owners: ScriptablePackStagedOwner[] = [
79
+ {
80
+ id: 'a.pack.ts',
81
+ guids: [ROOT],
82
+ async build(source) {
83
+ const result = await source.readByGuid(DEPENDENCY);
84
+ return result.ok ? ok([{ guid: ROOT, asset: result.value.asset }]) : result;
85
+ },
86
+ },
87
+ {
88
+ id: 'b.pack.ts',
89
+ guids: [DEPENDENCY],
90
+ async build(source) {
91
+ const result = await source.readByGuid(ROOT);
92
+ return result.ok ? ok([{ guid: DEPENDENCY, asset: result.value.asset }]) : result;
93
+ },
94
+ },
95
+ ];
96
+ const source = createScriptablePackStagedAssetSnapshotSource({
97
+ generation: 1,
98
+ owners,
99
+ });
100
+ const result = await source.readByGuid(ROOT);
101
+ expect(result).toMatchObject({
102
+ ok: false,
103
+ error: {
104
+ code: 'pack-source-build-cycle',
105
+ actual: 'a.pack.ts -> b.pack.ts -> a.pack.ts',
106
+ },
107
+ });
108
+ });
109
+
110
+ it('fails a content dependency without a local owner', async () => {
111
+ const source = createScriptablePackStagedAssetSnapshotSource({
112
+ generation: 1,
113
+ owners: [
114
+ {
115
+ id: 'root.pack.ts',
116
+ guids: [ROOT],
117
+ async build(ownerSource) {
118
+ const result = await ownerSource.readByGuid(DEPENDENCY);
119
+ return result.ok ? ok([{ guid: ROOT, asset: result.value.asset }]) : result;
120
+ },
121
+ },
122
+ ],
123
+ });
124
+
125
+ await expect(source.readByGuid(ROOT)).resolves.toMatchObject({
126
+ ok: false,
127
+ error: {
128
+ code: 'asset-not-imported',
129
+ expected: 'a staged local owner for the requested GUID',
130
+ },
131
+ });
132
+ });
133
+
134
+ it('resolves declared direct Pack outputs in the same generation', async () => {
135
+ const source = createScriptablePackStagedAssetSnapshotSource({
136
+ generation: 3,
137
+ declaredExternalOutputs: [{ guid: DEPENDENCY, asset: mesh(7) }],
138
+ owners: [
139
+ {
140
+ id: 'root.pack.ts',
141
+ guids: [ROOT],
142
+ async build(ownerSource) {
143
+ const dependency = await ownerSource.readByGuid(DEPENDENCY);
144
+ return dependency.ok
145
+ ? ok([
146
+ {
147
+ guid: ROOT,
148
+ asset: mesh((dependency.value.asset as MeshAsset).vertices[0] ?? 0),
149
+ },
150
+ ])
151
+ : dependency;
152
+ },
153
+ },
154
+ ],
155
+ });
156
+ const result = await source.readByGuid(ROOT);
157
+ expect(result).toMatchObject({ ok: true, value: { generation: 3 } });
158
+ expect(result.ok && (result.value.asset as MeshAsset).vertices[0]).toBe(7);
159
+ });
160
+
161
+ it('retries a refused local owner on the same source and coalesces repaired reads', async () => {
162
+ let rootBuilds = 0;
163
+ let dependencyBuilds = 0;
164
+ let healthyBuilds = 0;
165
+ let repaired = false;
166
+ let releaseRepair!: () => void;
167
+ const repairBuildGate = new Promise<void>((resolve) => {
168
+ releaseRepair = resolve;
169
+ });
170
+ const failure = new AssetError({
171
+ code: 'asset-not-imported',
172
+ expected: 'the repaired local ScriptablePack capability',
173
+ hint: 'repair the local producer capability and retry the same source',
174
+ });
175
+ const owners: ScriptablePackStagedOwner[] = [
176
+ {
177
+ id: 'dependency.pack.ts',
178
+ guids: [DEPENDENCY],
179
+ async build() {
180
+ dependencyBuilds += 1;
181
+ return ok([{ guid: DEPENDENCY, asset: mesh(2), digest: 'sha256:dependency' }]);
182
+ },
183
+ },
184
+ {
185
+ id: 'root.pack.ts',
186
+ guids: [ROOT],
187
+ async build(source) {
188
+ rootBuilds += 1;
189
+ const dependency = await source.readByGuid(DEPENDENCY);
190
+ if (!dependency.ok) return dependency;
191
+ if (!repaired) return err(failure);
192
+ await repairBuildGate;
193
+ const value = dependency.value.asset as MeshAsset;
194
+ return ok([
195
+ { guid: ROOT, asset: mesh((value.vertices[0] ?? 0) + 1), digest: 'sha256:root' },
196
+ ]);
197
+ },
198
+ },
199
+ {
200
+ id: 'healthy.pack.ts',
201
+ guids: [HEALTHY],
202
+ async build() {
203
+ healthyBuilds += 1;
204
+ return ok([{ guid: HEALTHY, asset: mesh(7), digest: 'sha256:healthy' }]);
205
+ },
206
+ },
207
+ ];
208
+ const source = createScriptablePackStagedAssetSnapshotSource({
209
+ generation: 27,
210
+ owners,
211
+ });
212
+
213
+ const refused = await source.readByGuid(ROOT);
214
+ expect(refused).toMatchObject({
215
+ ok: false,
216
+ error: {
217
+ code: 'asset-not-imported',
218
+ expected: 'the repaired local ScriptablePack capability',
219
+ hint: 'repair the local producer capability and retry the same source',
220
+ },
221
+ });
222
+ expect(rootBuilds).toBe(1);
223
+ expect(dependencyBuilds).toBe(1);
224
+
225
+ const healthy = await source.readByGuid(HEALTHY);
226
+ expect(healthy).toMatchObject({
227
+ ok: true,
228
+ value: { generation: 27, digest: 'sha256:healthy' },
229
+ });
230
+ expect(healthyBuilds).toBe(1);
231
+
232
+ repaired = true;
233
+ const repairedReads = [
234
+ source.readByGuid(ROOT),
235
+ source.readByGuid(ROOT),
236
+ source.readByGuid(ROOT),
237
+ ];
238
+ expect(rootBuilds).toBe(2);
239
+ releaseRepair();
240
+ const [first, second, third] = await Promise.all(repairedReads);
241
+ if (
242
+ first === undefined ||
243
+ second === undefined ||
244
+ third === undefined ||
245
+ !first.ok ||
246
+ !second.ok ||
247
+ !third.ok
248
+ )
249
+ throw new Error('repaired concurrent reads must all return snapshots');
250
+ expect(first).toMatchObject({ ok: true, value: { generation: 27, digest: 'sha256:root' } });
251
+ expect(second).toMatchObject({ ok: true, value: { generation: 27, digest: 'sha256:root' } });
252
+ expect(third).toMatchObject({ ok: true, value: { generation: 27, digest: 'sha256:root' } });
253
+ expect((first.value.asset as MeshAsset).vertices[0]).toBe(3);
254
+ expect(rootBuilds).toBe(2);
255
+ expect(dependencyBuilds).toBe(1);
256
+ expect(healthyBuilds).toBe(1);
257
+
258
+ const dependency = await source.readByGuid(DEPENDENCY);
259
+ expect(dependency).toMatchObject({
260
+ ok: true,
261
+ value: { generation: 27, digest: 'sha256:dependency' },
262
+ });
263
+ (first.value.asset as MeshAsset).vertices[0] = 99;
264
+ const memoized = await source.readByGuid(ROOT);
265
+ expect(memoized).toMatchObject({ ok: true, value: { generation: 27, digest: 'sha256:root' } });
266
+ expect(memoized.ok && (memoized.value.asset as MeshAsset).vertices[0]).toBe(3);
267
+ expect(rootBuilds).toBe(2);
268
+ });
269
+
270
+ it('carries the staged generation into every locally built snapshot', async () => {
271
+ const owner: ScriptablePackStagedOwner = {
272
+ id: 'mesh.pack.ts',
273
+ guids: [ROOT],
274
+ async build() {
275
+ return ok([{ guid: ROOT, asset: mesh(3), digest: 'sha256:mesh' }]);
276
+ },
277
+ };
278
+ const first = await createScriptablePackStagedAssetSnapshotSource({
279
+ generation: 8,
280
+ owners: [owner],
281
+ }).readByGuid(ROOT);
282
+ const second = await createScriptablePackStagedAssetSnapshotSource({
283
+ generation: 9,
284
+ owners: [owner],
285
+ }).readByGuid(ROOT);
286
+
287
+ expect(first).toMatchObject({ ok: true, value: { generation: 8, digest: 'sha256:mesh' } });
288
+ expect(second).toMatchObject({ ok: true, value: { generation: 9, digest: 'sha256:mesh' } });
289
+ });
290
+ });