@forgeax/engine-import 0.1.23 → 0.1.25
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.
- package/README.md +5 -5
- package/dist/__tests__/scriptable-pack-build.unit.test.d.ts +2 -0
- package/dist/__tests__/scriptable-pack-build.unit.test.d.ts.map +1 -0
- package/dist/build-production.d.ts.map +1 -1
- package/dist/index.d.ts +4 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.mjs +284 -60
- package/dist/index.mjs.map +1 -1
- package/dist/{parameterized-scriptable-pack.d.ts → scriptable-pack-build.d.ts} +20 -15
- package/dist/scriptable-pack-build.d.ts.map +1 -0
- package/dist/scriptable-pack-host.d.ts +15 -13
- package/dist/scriptable-pack-host.d.ts.map +1 -1
- package/dist/scriptable-pack.d.ts +2 -1
- package/dist/scriptable-pack.d.ts.map +1 -1
- package/dist/source-package-publication.d.ts +7 -0
- package/dist/source-package-publication.d.ts.map +1 -1
- package/package.json +8 -8
- package/src/__tests__/{parameterized-scriptable-pack.unit.test.ts → scriptable-pack-build.unit.test.ts} +11 -14
- package/src/__tests__/scriptable-pack-host.unit.test.ts +53 -5
- package/src/__tests__/scriptable-pack-output-producers.unit.test.ts +4 -5
- package/src/__tests__/source-package-publication.integration.test.ts +141 -0
- package/src/build-production.ts +45 -50
- package/src/index.ts +17 -16
- package/src/{parameterized-scriptable-pack.ts → scriptable-pack-build.ts} +91 -27
- package/src/scriptable-pack-host.ts +48 -40
- package/src/scriptable-pack.ts +2 -1
- package/src/source-package-publication.ts +242 -10
- package/dist/__tests__/parameterized-scriptable-pack.unit.test.d.ts +0 -2
- package/dist/__tests__/parameterized-scriptable-pack.unit.test.d.ts.map +0 -1
- package/dist/parameterized-scriptable-pack.d.ts.map +0 -1
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { randomUUID } from 'node:crypto';
|
|
1
|
+
import { createHash, randomUUID } from 'node:crypto';
|
|
2
2
|
import { mkdir, rename, rm, stat, writeFile } from 'node:fs/promises';
|
|
3
3
|
import { dirname } from 'node:path';
|
|
4
4
|
import {
|
|
5
|
+
type DdcArtifact,
|
|
5
6
|
type DdcEntry,
|
|
6
7
|
type DdcGenerationEntryCandidate,
|
|
7
8
|
DdcGenerationSession,
|
|
@@ -9,6 +10,7 @@ import {
|
|
|
9
10
|
DdcLifecycle,
|
|
10
11
|
ddcOutputDigest,
|
|
11
12
|
} from '@forgeax/engine-ddc';
|
|
13
|
+
import { canonicalDdcJson } from '@forgeax/engine-ddc/key';
|
|
12
14
|
import {
|
|
13
15
|
type CatalogEntry,
|
|
14
16
|
err,
|
|
@@ -45,9 +47,17 @@ export interface ImportPublicationInput {
|
|
|
45
47
|
readonly transport?: {
|
|
46
48
|
readonly path: string;
|
|
47
49
|
readonly body: string;
|
|
50
|
+
/** Complete package-relative artifact closure for the published Pack. */
|
|
51
|
+
readonly artifacts?: readonly ImportPublicationArtifact[];
|
|
48
52
|
};
|
|
49
53
|
}
|
|
50
54
|
|
|
55
|
+
export interface ImportPublicationArtifact {
|
|
56
|
+
readonly path: string;
|
|
57
|
+
readonly mediaType: string;
|
|
58
|
+
readonly bytes: Uint8Array;
|
|
59
|
+
}
|
|
60
|
+
|
|
51
61
|
export interface ImportPublicationError {
|
|
52
62
|
readonly code: SourcePackageError['code'];
|
|
53
63
|
readonly expected: string;
|
|
@@ -184,6 +194,225 @@ function importPublicationFailure(error: SourcePackageError): ImportPublicationE
|
|
|
184
194
|
};
|
|
185
195
|
}
|
|
186
196
|
|
|
197
|
+
function publicationArtifacts(
|
|
198
|
+
transport: ImportPublicationInput['transport'],
|
|
199
|
+
): Readonly<Record<string, DdcArtifact>> {
|
|
200
|
+
return Object.fromEntries(
|
|
201
|
+
(transport?.artifacts ?? []).map((artifact) => [
|
|
202
|
+
artifact.path,
|
|
203
|
+
{ mediaType: artifact.mediaType, bytes: artifact.bytes },
|
|
204
|
+
]),
|
|
205
|
+
);
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function publicationContext(input: ImportPublicationInput): SourcePackageErrorContext {
|
|
209
|
+
return {
|
|
210
|
+
sourceMeta: '<import-publication>',
|
|
211
|
+
anchorGuid: input.guid,
|
|
212
|
+
affectedGuids: input.publishedGuids,
|
|
213
|
+
producer: 'source-package/import-publication',
|
|
214
|
+
importer: 'import-publication',
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
/**
|
|
219
|
+
* Validate the Pack-to-transport closure before DDC staging can advance its
|
|
220
|
+
* head. The Pack is the authority for required artifact paths; a missing or
|
|
221
|
+
* mismatched body must never become a published `current` generation.
|
|
222
|
+
*/
|
|
223
|
+
function validatePublicationArtifactClosure(
|
|
224
|
+
input: ImportPublicationInput,
|
|
225
|
+
): Result<Readonly<Record<string, DdcArtifact>>, SourcePackageError> {
|
|
226
|
+
const context = publicationContext(input);
|
|
227
|
+
const pack = input.pack;
|
|
228
|
+
if (
|
|
229
|
+
pack === null ||
|
|
230
|
+
typeof pack !== 'object' ||
|
|
231
|
+
(pack as { readonly schemaVersion?: unknown }).schemaVersion !== '2.0.0' ||
|
|
232
|
+
(pack as { readonly kind?: unknown }).kind !== 'internal-text-package'
|
|
233
|
+
) {
|
|
234
|
+
return ok(publicationArtifacts(input.transport));
|
|
235
|
+
}
|
|
236
|
+
const assets = (pack as { readonly assets?: unknown }).assets;
|
|
237
|
+
if (!Array.isArray(assets)) {
|
|
238
|
+
return err(
|
|
239
|
+
sourcePackageError('source-package-publication-invalid', context, {
|
|
240
|
+
stage: 'route-integrity',
|
|
241
|
+
reason: 'published Pack does not contain an assets array',
|
|
242
|
+
}),
|
|
243
|
+
);
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
const required = new Map<
|
|
247
|
+
string,
|
|
248
|
+
{
|
|
249
|
+
readonly mediaType?: string;
|
|
250
|
+
readonly byteLength?: number;
|
|
251
|
+
readonly integrity?: { readonly algorithm: string; readonly digest: string };
|
|
252
|
+
}
|
|
253
|
+
>();
|
|
254
|
+
for (const asset of assets) {
|
|
255
|
+
if (asset === null || typeof asset !== 'object') {
|
|
256
|
+
return err(
|
|
257
|
+
sourcePackageError('source-package-publication-invalid', context, {
|
|
258
|
+
stage: 'route-integrity',
|
|
259
|
+
reason: 'published Pack contains a non-object asset row',
|
|
260
|
+
}),
|
|
261
|
+
);
|
|
262
|
+
}
|
|
263
|
+
const rawArtifacts = (asset as { readonly artifacts?: unknown }).artifacts;
|
|
264
|
+
if (rawArtifacts === undefined) continue;
|
|
265
|
+
if (rawArtifacts === null || typeof rawArtifacts !== 'object' || Array.isArray(rawArtifacts)) {
|
|
266
|
+
return err(
|
|
267
|
+
sourcePackageError('source-package-publication-invalid', context, {
|
|
268
|
+
stage: 'route-integrity',
|
|
269
|
+
reason: 'published Pack contains an invalid asset artifact map',
|
|
270
|
+
}),
|
|
271
|
+
);
|
|
272
|
+
}
|
|
273
|
+
for (const [localKey, rawDescriptor] of Object.entries(
|
|
274
|
+
rawArtifacts as Record<string, unknown>,
|
|
275
|
+
)) {
|
|
276
|
+
if (rawDescriptor === null || typeof rawDescriptor !== 'object') {
|
|
277
|
+
return err(
|
|
278
|
+
sourcePackageError('source-package-publication-invalid', context, {
|
|
279
|
+
stage: 'route-integrity',
|
|
280
|
+
reason: `artifact descriptor ${localKey} is not an object`,
|
|
281
|
+
}),
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
const descriptor = rawDescriptor as Record<string, unknown>;
|
|
285
|
+
const path = descriptor.path;
|
|
286
|
+
if (typeof path !== 'string' || path.length === 0) {
|
|
287
|
+
return err(
|
|
288
|
+
sourcePackageError('source-package-publication-invalid', context, {
|
|
289
|
+
stage: 'route-integrity',
|
|
290
|
+
reason: `artifact descriptor ${localKey} has no package-relative path`,
|
|
291
|
+
}),
|
|
292
|
+
);
|
|
293
|
+
}
|
|
294
|
+
const mediaType = descriptor.mediaType;
|
|
295
|
+
const byteLength = descriptor.byteLength;
|
|
296
|
+
const integrityValue = descriptor.integrity;
|
|
297
|
+
const integrity =
|
|
298
|
+
integrityValue !== null && typeof integrityValue === 'object'
|
|
299
|
+
? {
|
|
300
|
+
algorithm: (integrityValue as { readonly algorithm?: unknown }).algorithm,
|
|
301
|
+
digest: (integrityValue as { readonly digest?: unknown }).digest,
|
|
302
|
+
}
|
|
303
|
+
: undefined;
|
|
304
|
+
if (
|
|
305
|
+
(mediaType !== undefined && typeof mediaType !== 'string') ||
|
|
306
|
+
(byteLength !== undefined &&
|
|
307
|
+
(!Number.isSafeInteger(byteLength) || (byteLength as number) < 0)) ||
|
|
308
|
+
(integrityValue !== undefined &&
|
|
309
|
+
(integrity === undefined ||
|
|
310
|
+
typeof integrity.algorithm !== 'string' ||
|
|
311
|
+
typeof integrity.digest !== 'string'))
|
|
312
|
+
) {
|
|
313
|
+
return err(
|
|
314
|
+
sourcePackageError('source-package-publication-invalid', context, {
|
|
315
|
+
stage: 'route-integrity',
|
|
316
|
+
reason: `artifact descriptor ${path} has invalid metadata`,
|
|
317
|
+
}),
|
|
318
|
+
);
|
|
319
|
+
}
|
|
320
|
+
if (required.has(path)) {
|
|
321
|
+
return err(
|
|
322
|
+
sourcePackageError('source-package-publication-invalid', context, {
|
|
323
|
+
stage: 'route-integrity',
|
|
324
|
+
reason: `artifact path ${path} is declared more than once`,
|
|
325
|
+
}),
|
|
326
|
+
);
|
|
327
|
+
}
|
|
328
|
+
required.set(path, {
|
|
329
|
+
...(typeof mediaType === 'string' ? { mediaType } : {}),
|
|
330
|
+
...(typeof byteLength === 'number' ? { byteLength } : {}),
|
|
331
|
+
...(integrity !== undefined &&
|
|
332
|
+
typeof integrity.algorithm === 'string' &&
|
|
333
|
+
typeof integrity.digest === 'string'
|
|
334
|
+
? { integrity: { algorithm: integrity.algorithm, digest: integrity.digest } }
|
|
335
|
+
: {}),
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
const available = new Map<string, ImportPublicationArtifact>();
|
|
341
|
+
const duplicatePaths: string[] = [];
|
|
342
|
+
for (const artifact of input.transport?.artifacts ?? []) {
|
|
343
|
+
if (available.has(artifact.path)) duplicatePaths.push(artifact.path);
|
|
344
|
+
available.set(artifact.path, artifact);
|
|
345
|
+
}
|
|
346
|
+
const missing: string[] = [];
|
|
347
|
+
const mismatched: string[] = [...duplicatePaths.map((path) => `${path}: duplicate body`)];
|
|
348
|
+
for (const path of available.keys()) {
|
|
349
|
+
if (!required.has(path)) mismatched.push(`${path}: unexpected body`);
|
|
350
|
+
}
|
|
351
|
+
for (const [path, descriptor] of required) {
|
|
352
|
+
const artifact = available.get(path);
|
|
353
|
+
if (artifact === undefined) {
|
|
354
|
+
missing.push(path);
|
|
355
|
+
continue;
|
|
356
|
+
}
|
|
357
|
+
if (!(artifact.bytes instanceof Uint8Array)) {
|
|
358
|
+
mismatched.push(`${path}: body is not Uint8Array`);
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
if (descriptor.mediaType !== undefined && artifact.mediaType !== descriptor.mediaType) {
|
|
362
|
+
mismatched.push(`${path}: media type mismatch`);
|
|
363
|
+
}
|
|
364
|
+
if (
|
|
365
|
+
descriptor.byteLength !== undefined &&
|
|
366
|
+
artifact.bytes.byteLength !== descriptor.byteLength
|
|
367
|
+
) {
|
|
368
|
+
mismatched.push(`${path}: byte length mismatch`);
|
|
369
|
+
}
|
|
370
|
+
if (descriptor.integrity !== undefined) {
|
|
371
|
+
const actualDigest = `sha256:${createHash('sha256').update(artifact.bytes).digest('hex')}`;
|
|
372
|
+
if (
|
|
373
|
+
descriptor.integrity.algorithm !== 'sha256' ||
|
|
374
|
+
descriptor.integrity.digest !== actualDigest
|
|
375
|
+
) {
|
|
376
|
+
mismatched.push(`${path}: integrity mismatch`);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
if (missing.length > 0 || mismatched.length > 0) {
|
|
381
|
+
return err(
|
|
382
|
+
sourcePackageError('source-package-publication-invalid', context, {
|
|
383
|
+
stage: 'route-integrity',
|
|
384
|
+
reason: 'Pack artifact closure is incomplete or mismatched',
|
|
385
|
+
...(missing.length === 0 ? {} : { missing }),
|
|
386
|
+
...(mismatched.length === 0 ? {} : { unexpected: mismatched }),
|
|
387
|
+
}),
|
|
388
|
+
);
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
if (input.transport !== undefined) {
|
|
392
|
+
let transportedPack: unknown;
|
|
393
|
+
try {
|
|
394
|
+
transportedPack = JSON.parse(input.transport.body) as unknown;
|
|
395
|
+
} catch {
|
|
396
|
+
return err(
|
|
397
|
+
sourcePackageError('source-package-publication-invalid', context, {
|
|
398
|
+
stage: 'route-integrity',
|
|
399
|
+
reason: 'transport body is not valid JSON for the published Pack',
|
|
400
|
+
}),
|
|
401
|
+
);
|
|
402
|
+
}
|
|
403
|
+
if (canonicalDdcJson(transportedPack) !== canonicalDdcJson(pack)) {
|
|
404
|
+
return err(
|
|
405
|
+
sourcePackageError('source-package-publication-invalid', context, {
|
|
406
|
+
stage: 'route-integrity',
|
|
407
|
+
reason: 'transport body does not match the published Pack',
|
|
408
|
+
}),
|
|
409
|
+
);
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
|
|
413
|
+
return ok(publicationArtifacts(input.transport));
|
|
414
|
+
}
|
|
415
|
+
|
|
187
416
|
function projectImportPublication(
|
|
188
417
|
input: ImportPublicationInput,
|
|
189
418
|
head: DdcHead,
|
|
@@ -231,6 +460,15 @@ export async function publishImportPublication(
|
|
|
231
460
|
export async function stageImportPublication(
|
|
232
461
|
input: ImportPublicationInput,
|
|
233
462
|
): Promise<StagedImportPublicationResult> {
|
|
463
|
+
const validatedArtifacts = validatePublicationArtifactClosure(input);
|
|
464
|
+
if (!validatedArtifacts.ok) {
|
|
465
|
+
return {
|
|
466
|
+
ok: false,
|
|
467
|
+
error: importPublicationFailure(validatedArtifacts.error),
|
|
468
|
+
head: await inspectHead(input.root, input.guid, input.desiredKey),
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
const artifacts = validatedArtifacts.value;
|
|
234
472
|
const staged = await stageSourcePackageDdc({
|
|
235
473
|
root: input.root,
|
|
236
474
|
entry: {
|
|
@@ -238,7 +476,7 @@ export async function stageImportPublication(
|
|
|
238
476
|
guid: input.guid,
|
|
239
477
|
payload: input.pack,
|
|
240
478
|
refs: [],
|
|
241
|
-
artifacts
|
|
479
|
+
artifacts,
|
|
242
480
|
receipt: {
|
|
243
481
|
guid: input.guid,
|
|
244
482
|
key: input.desiredKey,
|
|
@@ -248,17 +486,11 @@ export async function stageImportPublication(
|
|
|
248
486
|
guid: input.guid,
|
|
249
487
|
payload: input.pack,
|
|
250
488
|
refs: [],
|
|
251
|
-
artifacts
|
|
489
|
+
artifacts,
|
|
252
490
|
}),
|
|
253
491
|
},
|
|
254
492
|
},
|
|
255
|
-
context:
|
|
256
|
-
sourceMeta: '<import-publication>',
|
|
257
|
-
anchorGuid: input.guid,
|
|
258
|
-
affectedGuids: input.publishedGuids,
|
|
259
|
-
producer: 'source-package/import-publication',
|
|
260
|
-
importer: 'import-publication',
|
|
261
|
-
},
|
|
493
|
+
context: publicationContext(input),
|
|
262
494
|
});
|
|
263
495
|
if (!staged.ok) {
|
|
264
496
|
return {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"parameterized-scriptable-pack.unit.test.d.ts","sourceRoot":"","sources":["../../src/__tests__/parameterized-scriptable-pack.unit.test.ts"],"names":[],"mappings":""}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"parameterized-scriptable-pack.d.ts","sourceRoot":"","sources":["../src/parameterized-scriptable-pack.ts"],"names":[],"mappings":"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"}
|