@forgeax/engine-import 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 (92) hide show
  1. package/README.md +91 -3
  2. package/dist/__tests__/ies-contract.unit.test.d.ts +2 -0
  3. package/dist/__tests__/ies-contract.unit.test.d.ts.map +1 -0
  4. package/dist/__tests__/ies-producer.unit.test.d.ts +2 -0
  5. package/dist/__tests__/ies-producer.unit.test.d.ts.map +1 -0
  6. package/dist/__tests__/mesh-lod-contract.unit.test.d.ts +2 -0
  7. package/dist/__tests__/mesh-lod-contract.unit.test.d.ts.map +1 -0
  8. package/dist/__tests__/mesh-lod-reimport.integration.test.d.ts +2 -0
  9. package/dist/__tests__/mesh-lod-reimport.integration.test.d.ts.map +1 -0
  10. package/dist/__tests__/scriptable-pack-build.unit.test.d.ts +2 -0
  11. package/dist/__tests__/scriptable-pack-build.unit.test.d.ts.map +1 -0
  12. package/dist/__tests__/scriptable-pack-file-snapshot.unit.test.d.ts +2 -0
  13. package/dist/__tests__/scriptable-pack-file-snapshot.unit.test.d.ts.map +1 -0
  14. package/dist/__tests__/source-package-publication-lod.integration.test.d.ts +2 -0
  15. package/dist/__tests__/source-package-publication-lod.integration.test.d.ts.map +1 -0
  16. package/dist/browser.d.ts +1 -0
  17. package/dist/browser.d.ts.map +1 -1
  18. package/dist/browser.mjs +230 -3
  19. package/dist/browser.mjs.map +1 -1
  20. package/dist/build-production.d.ts +1 -8
  21. package/dist/build-production.d.ts.map +1 -1
  22. package/dist/ies/ies-importer.d.ts +4 -0
  23. package/dist/ies/ies-importer.d.ts.map +1 -0
  24. package/dist/ies/parse-lm63.d.ts +13 -0
  25. package/dist/ies/parse-lm63.d.ts.map +1 -0
  26. package/dist/ies/resample-type-c.d.ts +4 -0
  27. package/dist/ies/resample-type-c.d.ts.map +1 -0
  28. package/dist/import-runner.d.ts.map +1 -1
  29. package/dist/index.d.ts +12 -7
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.mjs +2850 -1157
  32. package/dist/index.mjs.map +1 -1
  33. package/dist/mesh-bin.d.ts.map +1 -1
  34. package/dist/mesh-bin.mjs +29 -1
  35. package/dist/mesh-bin.mjs.map +1 -1
  36. package/dist/mesh-lod.d.ts +60 -0
  37. package/dist/mesh-lod.d.ts.map +1 -0
  38. package/dist/scriptable-pack-build.d.ts +68 -0
  39. package/dist/scriptable-pack-build.d.ts.map +1 -0
  40. package/dist/scriptable-pack-file-snapshot.d.ts +71 -0
  41. package/dist/scriptable-pack-file-snapshot.d.ts.map +1 -0
  42. package/dist/scriptable-pack-host.d.ts +68 -37
  43. package/dist/scriptable-pack-host.d.ts.map +1 -1
  44. package/dist/scriptable-pack-output-producers.d.ts +1 -0
  45. package/dist/scriptable-pack-output-producers.d.ts.map +1 -1
  46. package/dist/scriptable-pack-staged-snapshot.d.ts +2 -2
  47. package/dist/scriptable-pack-staged-snapshot.d.ts.map +1 -1
  48. package/dist/scriptable-pack.d.ts +15 -27
  49. package/dist/scriptable-pack.d.ts.map +1 -1
  50. package/dist/source-package-errors.d.ts.map +1 -1
  51. package/dist/source-package-publication.d.ts +7 -0
  52. package/dist/source-package-publication.d.ts.map +1 -1
  53. package/package.json +8 -7
  54. package/src/__tests__/ies-contract.unit.test.ts +31 -0
  55. package/src/__tests__/ies-producer.unit.test.ts +104 -0
  56. package/src/__tests__/mesh-bin.test.ts +69 -0
  57. package/src/__tests__/mesh-lod-contract.unit.test.ts +92 -0
  58. package/src/__tests__/mesh-lod-reimport.integration.test.ts +15 -0
  59. package/src/__tests__/scriptable-pack-build.unit.test.ts +267 -0
  60. package/src/__tests__/scriptable-pack-file-snapshot.unit.test.ts +177 -0
  61. package/src/__tests__/scriptable-pack-host.unit.test.ts +196 -4
  62. package/src/__tests__/scriptable-pack-production-producers.unit.test.ts +2 -0
  63. package/src/__tests__/scriptable-pack-staged-snapshot.unit.test.ts +43 -12
  64. package/src/__tests__/source-package-publication-lod.integration.test.ts +29 -0
  65. package/src/__tests__/source-package-publication.integration.test.ts +167 -1
  66. package/src/browser.ts +5 -0
  67. package/src/build-production.ts +491 -140
  68. package/src/ies/ies-importer.ts +126 -0
  69. package/src/ies/parse-lm63.ts +98 -0
  70. package/src/ies/resample-type-c.ts +108 -0
  71. package/src/import-runner.ts +58 -0
  72. package/src/index.ts +47 -16
  73. package/src/mesh-bin.ts +38 -0
  74. package/src/mesh-lod.ts +265 -0
  75. package/src/scriptable-pack-build.ts +725 -0
  76. package/src/scriptable-pack-file-snapshot.ts +346 -0
  77. package/src/scriptable-pack-host.ts +785 -340
  78. package/src/scriptable-pack-output-producers.ts +55 -0
  79. package/src/scriptable-pack-staged-snapshot.ts +12 -11
  80. package/src/scriptable-pack.ts +18 -486
  81. package/src/source-package-errors.ts +6 -0
  82. package/src/source-package-publication.ts +273 -11
  83. package/dist/.tsbuildinfo +0 -1
  84. package/dist/__tests__/scriptable-pack-product.contract.test.d.ts +0 -2
  85. package/dist/__tests__/scriptable-pack-product.contract.test.d.ts.map +0 -1
  86. package/dist/__tests__/scriptable-pack.integration.test.d.ts +0 -2
  87. package/dist/__tests__/scriptable-pack.integration.test.d.ts.map +0 -1
  88. package/dist/scriptable-source-package.d.ts +0 -14
  89. package/dist/scriptable-source-package.d.ts.map +0 -1
  90. package/src/__tests__/scriptable-pack-product.contract.test.ts +0 -46
  91. package/src/__tests__/scriptable-pack.integration.test.ts +0 -435
  92. package/src/scriptable-source-package.ts +0 -88
@@ -44,7 +44,14 @@ describe('ScriptablePack staged asset snapshot source', () => {
44
44
  guids: [DEPENDENCY],
45
45
  async build() {
46
46
  builds.push('dependency');
47
- return ok([{ guid: DEPENDENCY, asset: mesh(2), digest: 'sha256:dependency' }]);
47
+ return ok([
48
+ {
49
+ guid: DEPENDENCY,
50
+ sourceKey: 'dependency',
51
+ asset: mesh(2),
52
+ digest: 'sha256:dependency',
53
+ },
54
+ ]);
48
55
  },
49
56
  },
50
57
  {
@@ -56,7 +63,7 @@ describe('ScriptablePack staged asset snapshot source', () => {
56
63
  if (!dependency.ok) return dependency;
57
64
  builds.push('root:end');
58
65
  const value = dependency.value.asset as MeshAsset;
59
- return ok([{ guid: ROOT, asset: mesh(value.vertices[0] ?? 0) }]);
66
+ return ok([{ guid: ROOT, sourceKey: 'root', asset: mesh(value.vertices[0] ?? 0) }]);
60
67
  },
61
68
  },
62
69
  ];
@@ -81,7 +88,9 @@ describe('ScriptablePack staged asset snapshot source', () => {
81
88
  guids: [ROOT],
82
89
  async build(source) {
83
90
  const result = await source.readByGuid(DEPENDENCY);
84
- return result.ok ? ok([{ guid: ROOT, asset: result.value.asset }]) : result;
91
+ return result.ok
92
+ ? ok([{ guid: ROOT, sourceKey: 'root', asset: result.value.asset }])
93
+ : result;
85
94
  },
86
95
  },
87
96
  {
@@ -89,7 +98,9 @@ describe('ScriptablePack staged asset snapshot source', () => {
89
98
  guids: [DEPENDENCY],
90
99
  async build(source) {
91
100
  const result = await source.readByGuid(ROOT);
92
- return result.ok ? ok([{ guid: DEPENDENCY, asset: result.value.asset }]) : result;
101
+ return result.ok
102
+ ? ok([{ guid: DEPENDENCY, sourceKey: 'dependency', asset: result.value.asset }])
103
+ : result;
93
104
  },
94
105
  },
95
106
  ];
@@ -101,8 +112,11 @@ describe('ScriptablePack staged asset snapshot source', () => {
101
112
  expect(result).toMatchObject({
102
113
  ok: false,
103
114
  error: {
104
- code: 'pack-source-build-cycle',
105
- actual: 'a.pack.ts -> b.pack.ts -> a.pack.ts',
115
+ code: 'pack-content-dependency-stalled',
116
+ detail: {
117
+ waitingGuids: [AssetGuid.format(ROOT)],
118
+ pendingSubjects: ['a.pack.ts', 'b.pack.ts', 'a.pack.ts'],
119
+ },
106
120
  },
107
121
  });
108
122
  });
@@ -116,7 +130,9 @@ describe('ScriptablePack staged asset snapshot source', () => {
116
130
  guids: [ROOT],
117
131
  async build(ownerSource) {
118
132
  const result = await ownerSource.readByGuid(DEPENDENCY);
119
- return result.ok ? ok([{ guid: ROOT, asset: result.value.asset }]) : result;
133
+ return result.ok
134
+ ? ok([{ guid: ROOT, sourceKey: 'root', asset: result.value.asset }])
135
+ : result;
120
136
  },
121
137
  },
122
138
  ],
@@ -134,7 +150,7 @@ describe('ScriptablePack staged asset snapshot source', () => {
134
150
  it('resolves declared direct Pack outputs in the same generation', async () => {
135
151
  const source = createScriptablePackStagedAssetSnapshotSource({
136
152
  generation: 3,
137
- declaredExternalOutputs: [{ guid: DEPENDENCY, asset: mesh(7) }],
153
+ declaredExternalOutputs: [{ guid: DEPENDENCY, sourceKey: 'dependency', asset: mesh(7) }],
138
154
  owners: [
139
155
  {
140
156
  id: 'root.pack.ts',
@@ -145,6 +161,7 @@ describe('ScriptablePack staged asset snapshot source', () => {
145
161
  ? ok([
146
162
  {
147
163
  guid: ROOT,
164
+ sourceKey: 'root',
148
165
  asset: mesh((dependency.value.asset as MeshAsset).vertices[0] ?? 0),
149
166
  },
150
167
  ])
@@ -178,7 +195,14 @@ describe('ScriptablePack staged asset snapshot source', () => {
178
195
  guids: [DEPENDENCY],
179
196
  async build() {
180
197
  dependencyBuilds += 1;
181
- return ok([{ guid: DEPENDENCY, asset: mesh(2), digest: 'sha256:dependency' }]);
198
+ return ok([
199
+ {
200
+ guid: DEPENDENCY,
201
+ sourceKey: 'dependency',
202
+ asset: mesh(2),
203
+ digest: 'sha256:dependency',
204
+ },
205
+ ]);
182
206
  },
183
207
  },
184
208
  {
@@ -192,7 +216,12 @@ describe('ScriptablePack staged asset snapshot source', () => {
192
216
  await repairBuildGate;
193
217
  const value = dependency.value.asset as MeshAsset;
194
218
  return ok([
195
- { guid: ROOT, asset: mesh((value.vertices[0] ?? 0) + 1), digest: 'sha256:root' },
219
+ {
220
+ guid: ROOT,
221
+ sourceKey: 'root',
222
+ asset: mesh((value.vertices[0] ?? 0) + 1),
223
+ digest: 'sha256:root',
224
+ },
196
225
  ]);
197
226
  },
198
227
  },
@@ -201,7 +230,9 @@ describe('ScriptablePack staged asset snapshot source', () => {
201
230
  guids: [HEALTHY],
202
231
  async build() {
203
232
  healthyBuilds += 1;
204
- return ok([{ guid: HEALTHY, asset: mesh(7), digest: 'sha256:healthy' }]);
233
+ return ok([
234
+ { guid: HEALTHY, sourceKey: 'healthy', asset: mesh(7), digest: 'sha256:healthy' },
235
+ ]);
205
236
  },
206
237
  },
207
238
  ];
@@ -272,7 +303,7 @@ describe('ScriptablePack staged asset snapshot source', () => {
272
303
  id: 'mesh.pack.ts',
273
304
  guids: [ROOT],
274
305
  async build() {
275
- return ok([{ guid: ROOT, asset: mesh(3), digest: 'sha256:mesh' }]);
306
+ return ok([{ guid: ROOT, sourceKey: 'mesh', asset: mesh(3), digest: 'sha256:mesh' }]);
276
307
  },
277
308
  };
278
309
  const first = await createScriptablePackStagedAssetSnapshotSource({
@@ -0,0 +1,29 @@
1
+ import { mkdtemp, rm } from 'node:fs/promises';
2
+ import { tmpdir } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { afterEach, describe, expect, it } from 'vitest';
5
+ import { publishImportPublication } from '../source-package-publication.js';
6
+
7
+ describe('LOD publication boundary', () => {
8
+ const roots: string[] = [];
9
+
10
+ afterEach(async () => {
11
+ await Promise.all(roots.splice(0).map((root) => rm(root, { recursive: true, force: true })));
12
+ });
13
+
14
+ it('publishes the complete root package through the shared host', async () => {
15
+ const root = await mkdtemp(join(tmpdir(), 'forgeax-lod-publication-'));
16
+ roots.push(root);
17
+ const guid = '019e3969-1d48-7c3b-ac24-6d68f457065f';
18
+ const result = await publishImportPublication({
19
+ root,
20
+ guid,
21
+ desiredKey: 'a'.repeat(64),
22
+ pack: { lods: [{ meshGuid: 'lod-1', screenCoverage: 0.5 }] },
23
+ previousCatalog: [],
24
+ nextCatalog: [],
25
+ publishedGuids: [guid, 'lod-1'],
26
+ });
27
+ expect(result.ok).toBe(true);
28
+ });
29
+ });
@@ -1,6 +1,8 @@
1
- import { mkdtemp, rm } from 'node:fs/promises';
1
+ import { createHash } from 'node:crypto';
2
+ import { mkdtemp, readdir, readFile, rm } from 'node:fs/promises';
2
3
  import { tmpdir } from 'node:os';
3
4
  import { join } from 'node:path';
5
+ import { DdcEntryStore } from '@forgeax/engine-ddc';
4
6
  import { afterEach, describe, expect, it } from 'vitest';
5
7
  import {
6
8
  commitImportPublication,
@@ -11,6 +13,40 @@ import {
11
13
 
12
14
  const GUID = '019e3969-1d48-7c3b-ac24-6d68f457065f';
13
15
  const KEY = 'a'.repeat(64);
16
+ const ARTIFACT_PATH = `${GUID}/body.bin`;
17
+ const ARTIFACT_BYTES = new Uint8Array([9, 8, 7]);
18
+ const ARTIFACT_DIGEST = `sha256:${createHash('sha256').update(ARTIFACT_BYTES).digest('hex')}`;
19
+
20
+ const validArtifact = {
21
+ path: ARTIFACT_PATH,
22
+ mediaType: 'application/octet-stream',
23
+ bytes: ARTIFACT_BYTES,
24
+ };
25
+
26
+ const validDescriptor = {
27
+ path: ARTIFACT_PATH,
28
+ mediaType: validArtifact.mediaType,
29
+ byteLength: ARTIFACT_BYTES.byteLength,
30
+ integrity: { algorithm: 'sha256' as const, digest: ARTIFACT_DIGEST },
31
+ };
32
+
33
+ function closurePack(
34
+ artifacts: Readonly<Record<string, unknown>> = { body: validDescriptor },
35
+ ): Record<string, unknown> {
36
+ return {
37
+ schemaVersion: '2.0.0',
38
+ kind: 'internal-text-package',
39
+ assets: [
40
+ {
41
+ guid: GUID,
42
+ kind: 'fixture',
43
+ payload: { value: 'same' },
44
+ refs: [],
45
+ artifacts,
46
+ },
47
+ ],
48
+ };
49
+ }
14
50
 
15
51
  function input(root: string, desiredKey = KEY): ImportPublicationInput {
16
52
  return {
@@ -72,4 +108,134 @@ describe('source package publication concurrency', () => {
72
108
  error: { code: 'source-package-ddc-failed', detail: 'DDC lifecycle commit returned stale' },
73
109
  });
74
110
  });
111
+
112
+ it('persists concurrent equivalent transports without sharing a temporary path', async () => {
113
+ const root = await mkdtemp(join(tmpdir(), 'forgeax-source-publication-'));
114
+ roots.push(root);
115
+ const transport = {
116
+ path: join(root, 'runtime', `${GUID}.pack.json`),
117
+ body: JSON.stringify({ guid: GUID, value: 'concurrent' }),
118
+ };
119
+ const staged = await Promise.all(
120
+ Array.from({ length: 8 }, () => stageImportPublication({ ...input(root), transport })),
121
+ );
122
+ expect(staged.every((candidate) => candidate.ok)).toBe(true);
123
+ if (staged.some((candidate) => !candidate.ok)) return;
124
+
125
+ const results = await Promise.all(
126
+ staged.map((candidate) =>
127
+ candidate.ok ? commitImportPublication(candidate.candidate) : Promise.resolve(candidate),
128
+ ),
129
+ );
130
+ expect(results.every((result) => result.ok && result.transportPersisted)).toBe(true);
131
+ expect(await readFile(transport.path, 'utf8')).toBe(transport.body);
132
+ expect((await readdir(join(root, 'runtime'))).some((name) => name.includes('.tmp'))).toBe(
133
+ false,
134
+ );
135
+ });
136
+
137
+ it('publishes the complete transport artifact closure into the DDC entry', async () => {
138
+ const root = await mkdtemp(join(tmpdir(), 'forgeax-source-publication-'));
139
+ roots.push(root);
140
+ const pack = closurePack();
141
+ const publicationInput = {
142
+ ...input(root),
143
+ pack,
144
+ transport: {
145
+ path: join(root, 'runtime', `${GUID}.pack.json`),
146
+ body: JSON.stringify(pack),
147
+ artifacts: [validArtifact],
148
+ },
149
+ };
150
+
151
+ const staged = await stageImportPublication(publicationInput);
152
+ expect(staged.ok).toBe(true);
153
+ if (!staged.ok) return;
154
+ await expect(commitImportPublication(staged.candidate)).resolves.toMatchObject({ ok: true });
155
+
156
+ const entry = await new DdcEntryStore(root).read(KEY);
157
+ expect(entry?.artifacts[ARTIFACT_PATH]).toMatchObject({
158
+ mediaType: validArtifact.mediaType,
159
+ bytes: validArtifact.bytes,
160
+ });
161
+ });
162
+
163
+ it.each([
164
+ {
165
+ label: 'missing body',
166
+ pack: closurePack(),
167
+ artifacts: [],
168
+ },
169
+ {
170
+ label: 'media type mismatch',
171
+ pack: closurePack(),
172
+ artifacts: [{ ...validArtifact, mediaType: 'application/x-wrong' }],
173
+ },
174
+ {
175
+ label: 'byte length mismatch',
176
+ pack: closurePack(),
177
+ artifacts: [{ ...validArtifact, bytes: new Uint8Array([9, 8]) }],
178
+ },
179
+ {
180
+ label: 'sha256 mismatch',
181
+ pack: closurePack({
182
+ body: {
183
+ ...validDescriptor,
184
+ integrity: { algorithm: 'sha256' as const, digest: 'sha256:bad' },
185
+ },
186
+ }),
187
+ artifacts: [validArtifact],
188
+ },
189
+ {
190
+ label: 'duplicate transport path',
191
+ pack: closurePack(),
192
+ artifacts: [validArtifact, validArtifact],
193
+ },
194
+ {
195
+ label: 'duplicate descriptor path',
196
+ pack: closurePack({ body: validDescriptor, alias: { ...validDescriptor } }),
197
+ artifacts: [validArtifact],
198
+ },
199
+ {
200
+ label: 'unexpected transport body',
201
+ pack: closurePack(),
202
+ artifacts: [
203
+ validArtifact,
204
+ {
205
+ path: `${GUID}/extra.bin`,
206
+ mediaType: 'application/octet-stream',
207
+ bytes: new Uint8Array([1]),
208
+ },
209
+ ],
210
+ },
211
+ {
212
+ label: 'transport body mismatch',
213
+ pack: closurePack(),
214
+ body: JSON.stringify({ schemaVersion: '2.0.0', kind: 'internal-text-package', assets: [] }),
215
+ artifacts: [validArtifact],
216
+ },
217
+ ] as const)('rejects $label before advancing the DDC head', async ({ pack, artifacts, body }) => {
218
+ const root = await mkdtemp(join(tmpdir(), 'forgeax-source-publication-'));
219
+ roots.push(root);
220
+ const publicationInput: ImportPublicationInput = {
221
+ ...input(root),
222
+ pack,
223
+ transport: {
224
+ path: join(root, 'runtime', `${GUID}.pack.json`),
225
+ body: body ?? JSON.stringify(pack),
226
+ artifacts,
227
+ },
228
+ };
229
+
230
+ const staged = await stageImportPublication(publicationInput);
231
+
232
+ expect(staged).toMatchObject({
233
+ ok: false,
234
+ error: {
235
+ code: 'source-package-publication-invalid',
236
+ },
237
+ head: { state: 'missing' },
238
+ });
239
+ expect(await new DdcEntryStore(root).listKeys()).toEqual([]);
240
+ });
75
241
  });
package/src/browser.ts CHANGED
@@ -12,3 +12,8 @@ export {
12
12
  } from './import-runner.js';
13
13
  export { ImporterRegistry } from './importer-registry.js';
14
14
  export { packMeshBinV4 } from './mesh-bin.js';
15
+ export {
16
+ deriveDefaultLodScreenCoverages,
17
+ reconcileMeshLodMeta,
18
+ validateMeshLodContract,
19
+ } from './mesh-lod.js';