@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.
- 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 +230 -56
- package/dist/index.mjs.map +1 -1
- package/dist/{parameterized-scriptable-pack.d.ts → scriptable-pack-build.d.ts} +15 -15
- package/dist/scriptable-pack-build.d.ts.map +1 -0
- package/dist/scriptable-pack-host.d.ts +13 -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 +5 -5
- package/src/__tests__/source-package-publication.integration.test.ts +141 -0
- package/src/build-production.ts +44 -50
- package/src/index.ts +17 -16
- package/src/{parameterized-scriptable-pack.ts → scriptable-pack-build.ts} +24 -24
- package/src/scriptable-pack-host.ts +38 -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
|
@@ -20,10 +20,10 @@ import type {
|
|
|
20
20
|
ScanSourceDeclaration,
|
|
21
21
|
} from '@forgeax/engine-pack/scanner';
|
|
22
22
|
import {
|
|
23
|
-
type
|
|
23
|
+
type AnyScriptablePackDefinition,
|
|
24
24
|
type DirectPackAssetProjection,
|
|
25
25
|
isScriptablePackAssetKind,
|
|
26
|
-
|
|
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
|
|
67
|
+
export interface ScriptablePackInput {
|
|
68
68
|
readonly sourcePath: string;
|
|
69
69
|
readonly displaySourcePath: string;
|
|
70
|
-
readonly definition:
|
|
70
|
+
readonly definition: AnyScriptablePackDefinition;
|
|
71
71
|
readonly sourceClosure: readonly ScriptablePackSourceClosureEntry[];
|
|
72
72
|
readonly publicationGeneration: number;
|
|
73
73
|
readonly policy:
|
|
74
74
|
| PackageFinalizePolicy
|
|
75
|
-
| ((product:
|
|
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
|
|
84
|
-
readonly product:
|
|
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
|
|
92
|
-
readonly sources: readonly
|
|
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 =
|
|
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:
|
|
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 =
|
|
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
|
|
655
|
+
} as AnyScriptablePackDefinition;
|
|
658
656
|
const outputs = createStandardAssetOutputProducerRegistry();
|
|
659
657
|
outputs.register(createPreExternalizedSceneAssetOutputProducer());
|
|
660
|
-
const built = await
|
|
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:
|
|
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 =
|
|
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
|
|
765
|
-
prepared:
|
|
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
|
|
797
|
-
product:
|
|
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: `
|
|
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
|
|
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
|
|
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
|
|
881
|
-
options:
|
|
882
|
-
): Promise<Result<ReadonlyMap<string,
|
|
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 =
|
|
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:
|
|
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
|
|
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,
|
|
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 =
|
|
934
|
+
const facts = projectScriptablePackPublication(product);
|
|
937
935
|
if (!facts.ok) return facts;
|
|
938
936
|
const revision = await scriptablePackResourceRevision(
|
|
939
937
|
source.displaySourcePath,
|
package/src/scriptable-pack.ts
CHANGED
|
@@ -136,7 +136,8 @@ export interface ScriptablePackExternalEvidence {
|
|
|
136
136
|
readonly digest?: string;
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
|
|
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 +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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"}
|