@forgeax/engine-import 0.1.23 → 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 (29) hide show
  1. package/README.md +5 -5
  2. package/dist/__tests__/scriptable-pack-build.unit.test.d.ts +2 -0
  3. package/dist/__tests__/scriptable-pack-build.unit.test.d.ts.map +1 -0
  4. package/dist/build-production.d.ts.map +1 -1
  5. package/dist/index.d.ts +4 -4
  6. package/dist/index.d.ts.map +1 -1
  7. package/dist/index.mjs +230 -56
  8. package/dist/index.mjs.map +1 -1
  9. package/dist/{parameterized-scriptable-pack.d.ts → scriptable-pack-build.d.ts} +15 -15
  10. package/dist/scriptable-pack-build.d.ts.map +1 -0
  11. package/dist/scriptable-pack-host.d.ts +13 -13
  12. package/dist/scriptable-pack-host.d.ts.map +1 -1
  13. package/dist/scriptable-pack.d.ts +2 -1
  14. package/dist/scriptable-pack.d.ts.map +1 -1
  15. package/dist/source-package-publication.d.ts +7 -0
  16. package/dist/source-package-publication.d.ts.map +1 -1
  17. package/package.json +8 -8
  18. package/src/__tests__/{parameterized-scriptable-pack.unit.test.ts → scriptable-pack-build.unit.test.ts} +11 -14
  19. package/src/__tests__/scriptable-pack-host.unit.test.ts +5 -5
  20. package/src/__tests__/source-package-publication.integration.test.ts +141 -0
  21. package/src/build-production.ts +44 -50
  22. package/src/index.ts +17 -16
  23. package/src/{parameterized-scriptable-pack.ts → scriptable-pack-build.ts} +24 -24
  24. package/src/scriptable-pack-host.ts +38 -40
  25. package/src/scriptable-pack.ts +2 -1
  26. package/src/source-package-publication.ts +242 -10
  27. package/dist/__tests__/parameterized-scriptable-pack.unit.test.d.ts +0 -2
  28. package/dist/__tests__/parameterized-scriptable-pack.unit.test.d.ts.map +0 -1
  29. package/dist/parameterized-scriptable-pack.d.ts.map +0 -1
@@ -20,10 +20,10 @@ import type {
20
20
  ScanSourceDeclaration,
21
21
  } from '@forgeax/engine-pack/scanner';
22
22
  import {
23
- type AnyParameterizedPackDefinition,
23
+ type AnyScriptablePackDefinition,
24
24
  type DirectPackAssetProjection,
25
25
  isScriptablePackAssetKind,
26
- parseParameterizedPackJson,
26
+ parsePackSourceJson,
27
27
  projectDirectPackJson,
28
28
  projectScriptablePackSceneComponents,
29
29
  type ScriptablePackSourceClosureEntry,
@@ -45,17 +45,17 @@ import { AssetError, err, ok } from '@forgeax/engine-types';
45
45
  import type { DdcPack, ImportRunnerFs, RunImportMeta } from './import-runner.js';
46
46
  import type { ImporterRegistry } from './importer-registry.js';
47
47
  import { projectImportProductForBuild } from './pack-projection.js';
48
- import {
49
- buildParameterizedScriptablePack,
50
- buildParameterizedScriptablePackWorklist,
51
- type ParameterizedScriptablePackBuildProduct,
52
- type ParameterizedScriptablePackWorkItem,
53
- } from './parameterized-scriptable-pack.js';
54
48
  import type {
55
49
  ScriptablePackAssetSnapshotSource,
56
50
  ScriptablePackDomainError,
57
51
  ScriptablePackStagedOutput,
58
52
  } from './scriptable-pack.js';
53
+ import {
54
+ buildScriptablePack,
55
+ buildScriptablePackWorklist,
56
+ type ScriptablePackBuildProduct,
57
+ type ScriptablePackBuildWorkItem,
58
+ } from './scriptable-pack-build.js';
59
59
  import {
60
60
  createPreExternalizedSceneAssetOutputProducer,
61
61
  createStandardAssetOutputProducerRegistry,
@@ -64,15 +64,15 @@ import { produceSourcePackage } from './source-package.js';
64
64
 
65
65
  type FinalizedPackageTransport = Awaited<ReturnType<typeof finalizePackageTransportSource>>;
66
66
 
67
- export interface ParameterizedScriptablePackInput {
67
+ export interface ScriptablePackInput {
68
68
  readonly sourcePath: string;
69
69
  readonly displaySourcePath: string;
70
- readonly definition: AnyParameterizedPackDefinition;
70
+ readonly definition: AnyScriptablePackDefinition;
71
71
  readonly sourceClosure: readonly ScriptablePackSourceClosureEntry[];
72
72
  readonly publicationGeneration: number;
73
73
  readonly policy:
74
74
  | PackageFinalizePolicy
75
- | ((product: ParameterizedScriptablePackBuildProduct) => PackageFinalizePolicy);
75
+ | ((product: ScriptablePackBuildProduct) => PackageFinalizePolicy);
76
76
  readonly subjectPackageId?: PackageIdType;
77
77
  readonly values?: Readonly<Record<string, unknown>>;
78
78
  readonly inheritedValues?: Readonly<
@@ -80,16 +80,16 @@ export interface ParameterizedScriptablePackInput {
80
80
  >;
81
81
  }
82
82
 
83
- export interface PreparedParameterizedScriptablePack {
84
- readonly product: ParameterizedScriptablePackBuildProduct;
83
+ export interface PreparedScriptablePack {
84
+ readonly product: ScriptablePackBuildProduct;
85
85
  readonly finalized: FinalizedPackageTransport;
86
86
  readonly facts: ScriptablePackPublicationFacts;
87
87
  readonly revision: ResourceRevision;
88
88
  readonly publication: AssetPublicationEnvelope;
89
89
  }
90
90
 
91
- export interface ParameterizedScriptablePackProductionOptions {
92
- readonly sources: readonly ParameterizedScriptablePackInput[];
91
+ export interface ScriptablePackProductionOptions {
92
+ readonly sources: readonly ScriptablePackInput[];
93
93
  readonly declaredExternalOutputs?: readonly ScriptablePackStagedOutput[];
94
94
  readonly assetSource?: ScriptablePackAssetSnapshotSource;
95
95
  readonly availableGuids?: ReadonlySet<string>;
@@ -211,7 +211,7 @@ export async function declaredPackExternalOutputs(
211
211
  }
212
212
  if (declaration.format !== 'pack.json') continue;
213
213
  if (declaration.value.schemaVersion === '3.0.0') {
214
- const parsed = parseParameterizedPackJson(declaration.value);
214
+ const parsed = parsePackSourceJson(declaration.value);
215
215
  if (!parsed.ok || parsed.value.format !== 'direct') continue;
216
216
  const projected = projectDirectPackJson(parsed.value);
217
217
  if (!projected.ok) continue;
@@ -352,7 +352,7 @@ export interface DirectPackTransport {
352
352
  readonly packageId: string;
353
353
  readonly assets: readonly DirectPackAssetProjection[];
354
354
  };
355
- readonly product: ParameterizedScriptablePackBuildProduct;
355
+ readonly product: ScriptablePackBuildProduct;
356
356
  readonly finalized: FinalizedPackageTransport;
357
357
  readonly facts: ScriptablePackPublicationFacts;
358
358
  readonly revision: ResourceRevision;
@@ -429,9 +429,7 @@ function importedAssetArtifacts(
429
429
  );
430
430
  }
431
431
 
432
- function rawDirectProduct(
433
- input: DirectPackTransportInput,
434
- ): ParameterizedScriptablePackBuildProduct {
432
+ function rawDirectProduct(input: DirectPackTransportInput): ScriptablePackBuildProduct {
435
433
  const inputFingerprint = `sha256:${packageTransportRevision({
436
434
  packageId: input.projected.packageId,
437
435
  sourceRevision: input.sourceRevision,
@@ -623,7 +621,7 @@ export async function prepareDirectPackTransport(
623
621
  projectImportProductForBuild(raw.product),
624
622
  input.policy,
625
623
  );
626
- const facts = projectParameterizedPackPublication(raw);
624
+ const facts = projectScriptablePackPublication(raw);
627
625
  if (!facts.ok) return facts;
628
626
  const displaySourcePath = input.displaySourcePath ?? input.sourcePath;
629
627
  const revision = await scriptablePackResourceRevision(displaySourcePath, raw.inputFingerprint, [
@@ -654,10 +652,10 @@ export async function prepareDirectPackTransport(
654
652
  ]),
655
653
  ),
656
654
  ),
657
- } as AnyParameterizedPackDefinition;
655
+ } as AnyScriptablePackDefinition;
658
656
  const outputs = createStandardAssetOutputProducerRegistry();
659
657
  outputs.register(createPreExternalizedSceneAssetOutputProducer());
660
- const built = await buildParameterizedScriptablePack({
658
+ const built = await buildScriptablePack({
661
659
  definition,
662
660
  sourcePath: input.sourcePath,
663
661
  outputs,
@@ -702,7 +700,7 @@ export async function prepareDirectPackTransport(
702
700
  asset: directAssetForProducer(asset),
703
701
  digest: `sha256:${packageTransportRevision({ kind: asset.kind, payload: asset.payload })}`,
704
702
  }));
705
- const product: ParameterizedScriptablePackBuildProduct = {
703
+ const product: ScriptablePackBuildProduct = {
706
704
  ...built.value,
707
705
  inputFingerprint,
708
706
  stagedOutputs,
@@ -722,7 +720,7 @@ export async function prepareDirectPackTransport(
722
720
  projectImportProductForBuild(product.product),
723
721
  input.policy,
724
722
  );
725
- const facts = projectParameterizedPackPublication(product);
723
+ const facts = projectScriptablePackPublication(product);
726
724
  if (!facts.ok) return facts;
727
725
  const displaySourcePath = input.displaySourcePath ?? input.sourcePath;
728
726
  const revision = await scriptablePackResourceRevision(
@@ -761,8 +759,8 @@ export async function prepareLegacyPackTransport(
761
759
  }
762
760
 
763
761
  /** Materialize a dynamic Pack product through the same dev transport seam. */
764
- export async function materializePreparedParameterizedScriptablePack(
765
- prepared: PreparedParameterizedScriptablePack,
762
+ export async function materializePreparedScriptablePack(
763
+ prepared: PreparedScriptablePack,
766
764
  paths: ScriptablePackTransportPaths,
767
765
  sink: ScriptablePackTransportSink,
768
766
  ): Promise<ReadonlyMap<string, string>> {
@@ -793,8 +791,8 @@ export async function materializePreparedParameterizedScriptablePack(
793
791
  return receipts;
794
792
  }
795
793
 
796
- function projectParameterizedPackPublication(
797
- product: ParameterizedScriptablePackBuildProduct,
794
+ function projectScriptablePackPublication(
795
+ product: ScriptablePackBuildProduct,
798
796
  ): Result<ScriptablePackPublicationFacts, ScriptablePackDomainError> {
799
797
  const assets = new Map(product.product.assets.map((asset) => [asset.guid.toLowerCase(), asset]));
800
798
  const outputs: AssetPublicationOutput[] = [];
@@ -804,7 +802,7 @@ function projectParameterizedPackPublication(
804
802
  if (asset === undefined || staged.digest === undefined || staged.sourceKey === undefined) {
805
803
  return err({
806
804
  code: 'pack-source-output-invalid',
807
- expected: `parameterized Pack publication output ${guid} to include a product, sourceKey, and digest`,
805
+ expected: `ScriptablePack publication output ${guid} to include a product, sourceKey, and digest`,
808
806
  hint: 'repair the dynamic Pack output and rebuild the current generation',
809
807
  detail: { stage: 'publication', guid },
810
808
  });
@@ -857,7 +855,7 @@ function snapshotSourceForStagedOutputs(
857
855
  };
858
856
  }
859
857
 
860
- function composeParameterizedAssetSource(
858
+ function composeScriptablePackAssetSource(
861
859
  staged: import('./scriptable-pack.js').ScriptablePackAssetSnapshotSource | undefined,
862
860
  published: import('./scriptable-pack.js').ScriptablePackAssetSnapshotSource | undefined,
863
861
  ): import('./scriptable-pack.js').ScriptablePackAssetSnapshotSource | undefined {
@@ -873,16 +871,16 @@ function composeParameterizedAssetSource(
873
871
  }
874
872
 
875
873
  /**
876
- * Build parameterized sources in one generation and hand their terminal
874
+ * Build ScriptablePack sources in one generation and hand their terminal
877
875
  * products to the ordinary Pack v2 finalizer. The worklist is deliberately
878
876
  * below publication: it only decides which current-generation products exist.
879
877
  */
880
- export async function produceParameterizedScriptablePackProducts(
881
- options: ParameterizedScriptablePackProductionOptions,
882
- ): Promise<Result<ReadonlyMap<string, PreparedParameterizedScriptablePack>, unknown>> {
878
+ export async function produceScriptablePackProducts(
879
+ options: ScriptablePackProductionOptions,
880
+ ): Promise<Result<ReadonlyMap<string, PreparedScriptablePack>, unknown>> {
883
881
  if (options.sources.length === 0) return ok(new Map());
884
882
  const external = options.declaredExternalOutputs ?? [];
885
- const assetSource = composeParameterizedAssetSource(
883
+ const assetSource = composeScriptablePackAssetSource(
886
884
  snapshotSourceForStagedOutputs(external),
887
885
  options.assetSource,
888
886
  );
@@ -890,7 +888,7 @@ export async function produceParameterizedScriptablePackProducts(
890
888
  ...(options.availableGuids ?? []),
891
889
  ...external.map((output) => AssetGuid.format(output.guid).toLowerCase()),
892
890
  ]);
893
- const workItems: ParameterizedScriptablePackWorkItem[] = options.sources.map((source) => ({
891
+ const workItems: ScriptablePackBuildWorkItem[] = options.sources.map((source) => ({
894
892
  definition: source.definition,
895
893
  sourcePath: source.sourcePath,
896
894
  ...(source.subjectPackageId === undefined ? {} : { subjectPackageId: source.subjectPackageId }),
@@ -898,7 +896,7 @@ export async function produceParameterizedScriptablePackProducts(
898
896
  ...(source.inheritedValues === undefined ? {} : { inheritedValues: source.inheritedValues }),
899
897
  sourceClosure: source.sourceClosure,
900
898
  }));
901
- const worklist = await buildParameterizedScriptablePackWorklist({
899
+ const worklist = await buildScriptablePackWorklist({
902
900
  subjects: workItems,
903
901
  outputs: createStandardAssetOutputProducerRegistry(
904
902
  options.sources.flatMap((source) =>
@@ -917,7 +915,7 @@ export async function produceParameterizedScriptablePackProducts(
917
915
  .filter((product) => product.product.sourceKey !== undefined)
918
916
  .map((product) => [product.product.sourceKey as string, product]),
919
917
  );
920
- const prepared = new Map<string, PreparedParameterizedScriptablePack>();
918
+ const prepared = new Map<string, PreparedScriptablePack>();
921
919
  for (const source of options.sources) {
922
920
  const product = productsBySource.get(source.sourcePath);
923
921
  if (product === undefined) {
@@ -933,7 +931,7 @@ export async function produceParameterizedScriptablePackProducts(
933
931
  projectImportProductForBuild(product.product),
934
932
  policy,
935
933
  );
936
- const facts = projectParameterizedPackPublication(product);
934
+ const facts = projectScriptablePackPublication(product);
937
935
  if (!facts.ok) return facts;
938
936
  const revision = await scriptablePackResourceRevision(
939
937
  source.displaySourcePath,
@@ -136,7 +136,8 @@ export interface ScriptablePackExternalEvidence {
136
136
  readonly digest?: string;
137
137
  }
138
138
 
139
- export interface ScriptablePackBuildProduct {
139
+ /** Shared terminal product envelope for every Pack source form. */
140
+ export interface PackBuildProduct {
140
141
  readonly product: TerminalImportProduct<unknown>;
141
142
  readonly stagedOutputs: readonly ScriptablePackStagedOutput[];
142
143
  readonly externalEvidence: readonly ScriptablePackExternalEvidence[];
@@ -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,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=parameterized-scriptable-pack.unit.test.d.ts.map
@@ -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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"}