@forgeax/engine-pack 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.
- package/LICENSE +202 -0
- package/README.md +429 -0
- package/dist/.tsbuildinfo +1 -0
- package/dist/__tests__/ai-recovery.unit.test.d.ts +2 -0
- package/dist/__tests__/ai-recovery.unit.test.d.ts.map +1 -0
- package/dist/__tests__/artifact-path.test.d.ts +2 -0
- package/dist/__tests__/artifact-path.test.d.ts.map +1 -0
- package/dist/__tests__/asset-evidence-error-hints.test.d.ts +2 -0
- package/dist/__tests__/asset-evidence-error-hints.test.d.ts.map +1 -0
- package/dist/__tests__/asset-evidence-lifecycle.test.d.ts +2 -0
- package/dist/__tests__/asset-evidence-lifecycle.test.d.ts.map +1 -0
- package/dist/__tests__/asset-evidence-schema.test.d.ts +2 -0
- package/dist/__tests__/asset-evidence-schema.test.d.ts.map +1 -0
- package/dist/__tests__/atlas-cli-region-mismatch.test.d.ts +2 -0
- package/dist/__tests__/atlas-cli-region-mismatch.test.d.ts.map +1 -0
- package/dist/__tests__/atlas-error-code-owner.test-d.d.ts +2 -0
- package/dist/__tests__/atlas-error-code-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/cli-asset-evidence.integration.test.d.ts +2 -0
- package/dist/__tests__/cli-asset-evidence.integration.test.d.ts.map +1 -0
- package/dist/__tests__/cli.unit.test.d.ts +2 -0
- package/dist/__tests__/cli.unit.test.d.ts.map +1 -0
- package/dist/__tests__/evidence-source-inventory.unit.test.d.ts +2 -0
- package/dist/__tests__/evidence-source-inventory.unit.test.d.ts.map +1 -0
- package/dist/__tests__/guid-collision.unit.test.d.ts +2 -0
- package/dist/__tests__/guid-collision.unit.test.d.ts.map +1 -0
- package/dist/__tests__/guid-validator-surface.unit.test.d.ts +2 -0
- package/dist/__tests__/guid-validator-surface.unit.test.d.ts.map +1 -0
- package/dist/__tests__/load-asset-config.test.d.ts +2 -0
- package/dist/__tests__/load-asset-config.test.d.ts.map +1 -0
- package/dist/__tests__/material-cook-dependencies.unit.test.d.ts +2 -0
- package/dist/__tests__/material-cook-dependencies.unit.test.d.ts.map +1 -0
- package/dist/__tests__/material-cook-receipt.unit.test.d.ts +2 -0
- package/dist/__tests__/material-cook-receipt.unit.test.d.ts.map +1 -0
- package/dist/__tests__/material-cook-schema.unit.test.d.ts +2 -0
- package/dist/__tests__/material-cook-schema.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-bin-consumer-surface.unit.test.d.ts +2 -0
- package/dist/__tests__/mesh-bin-consumer-surface.unit.test.d.ts.map +1 -0
- package/dist/__tests__/mesh-bin-contract.unit.test.d.ts +2 -0
- package/dist/__tests__/mesh-bin-contract.unit.test.d.ts.map +1 -0
- package/dist/__tests__/meta-schema-optional.test.d.ts +2 -0
- package/dist/__tests__/meta-schema-optional.test.d.ts.map +1 -0
- package/dist/__tests__/meta-source-overrides.unit.test.d.ts +2 -0
- package/dist/__tests__/meta-source-overrides.unit.test.d.ts.map +1 -0
- package/dist/__tests__/native-cooker-registry.test.d.ts +2 -0
- package/dist/__tests__/native-cooker-registry.test.d.ts.map +1 -0
- package/dist/__tests__/offline-evidence-artifact-verification-owner.test-d.d.ts +2 -0
- package/dist/__tests__/offline-evidence-artifact-verification-owner.test-d.d.ts.map +1 -0
- package/dist/__tests__/offline-evidence.integration.test.d.ts +2 -0
- package/dist/__tests__/offline-evidence.integration.test.d.ts.map +1 -0
- package/dist/__tests__/owner-chain.integration.test.d.ts +2 -0
- package/dist/__tests__/owner-chain.integration.test.d.ts.map +1 -0
- package/dist/__tests__/pack-v2-fixture-migration.test.d.ts +2 -0
- package/dist/__tests__/pack-v2-fixture-migration.test.d.ts.map +1 -0
- package/dist/__tests__/pack-v2-schema.test.d.ts +2 -0
- package/dist/__tests__/pack-v2-schema.test.d.ts.map +1 -0
- package/dist/__tests__/pack.unit.test.d.ts +2 -0
- package/dist/__tests__/pack.unit.test.d.ts.map +1 -0
- package/dist/__tests__/package-finalizer.contract.test.d.ts +2 -0
- package/dist/__tests__/package-finalizer.contract.test.d.ts.map +1 -0
- package/dist/__tests__/producer-contract-schema.unit.test.d.ts +2 -0
- package/dist/__tests__/producer-contract-schema.unit.test.d.ts.map +1 -0
- package/dist/__tests__/resolve-asset-source.test.d.ts +2 -0
- package/dist/__tests__/resolve-asset-source.test.d.ts.map +1 -0
- package/dist/__tests__/runtime-projection.unit.test.d.ts +2 -0
- package/dist/__tests__/runtime-projection.unit.test.d.ts.map +1 -0
- package/dist/__tests__/runtime.browser.test.d.ts +2 -0
- package/dist/__tests__/runtime.browser.test.d.ts.map +1 -0
- package/dist/__tests__/scanner-inventory.contract.test.d.ts +2 -0
- package/dist/__tests__/scanner-inventory.contract.test.d.ts.map +1 -0
- package/dist/__tests__/scanner-inventory.test.d.ts +2 -0
- package/dist/__tests__/scanner-inventory.test.d.ts.map +1 -0
- package/dist/__tests__/scanner-mount-cycle.test.d.ts +2 -0
- package/dist/__tests__/scanner-mount-cycle.test.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack-cli.integration.test.d.ts +2 -0
- package/dist/__tests__/scriptable-pack-cli.integration.test.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack-ddc-key.unit.test.d.ts +2 -0
- package/dist/__tests__/scriptable-pack-ddc-key.unit.test.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack.test-d.d.ts +2 -0
- package/dist/__tests__/scriptable-pack.test-d.d.ts.map +1 -0
- package/dist/__tests__/scriptable-pack.unit.test.d.ts +2 -0
- package/dist/__tests__/scriptable-pack.unit.test.d.ts.map +1 -0
- package/dist/__tests__/topology-actions.unit.test.d.ts +2 -0
- package/dist/__tests__/topology-actions.unit.test.d.ts.map +1 -0
- package/dist/__tests__/topology.unit.test.d.ts +2 -0
- package/dist/__tests__/topology.unit.test.d.ts.map +1 -0
- package/dist/artifact-path.d.ts +9 -0
- package/dist/artifact-path.d.ts.map +1 -0
- package/dist/artifact-path.mjs +53 -0
- package/dist/artifact-path.mjs.map +1 -0
- package/dist/atlas/run-atlas.d.ts +8 -0
- package/dist/atlas/run-atlas.d.ts.map +1 -0
- package/dist/atlas/shelf-pack.d.ts +55 -0
- package/dist/atlas/shelf-pack.d.ts.map +1 -0
- package/dist/bridge.d.ts +2 -0
- package/dist/bridge.d.ts.map +1 -0
- package/dist/bridge.mjs +3 -0
- package/dist/bridge.mjs.map +1 -0
- package/dist/build.d.ts +19 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.mjs +10203 -0
- package/dist/build.mjs.map +1 -0
- package/dist/builtin.d.ts +44 -0
- package/dist/builtin.d.ts.map +1 -0
- package/dist/builtin.mjs +141 -0
- package/dist/builtin.mjs.map +1 -0
- package/dist/catalog-builder.d.ts +43 -0
- package/dist/catalog-builder.d.ts.map +1 -0
- package/dist/catalog-delta.d.ts +11 -0
- package/dist/catalog-delta.d.ts.map +1 -0
- package/dist/catalog-projection.d.ts +91 -0
- package/dist/catalog-projection.d.ts.map +1 -0
- package/dist/cli-asset.d.ts +22 -0
- package/dist/cli-asset.d.ts.map +1 -0
- package/dist/cli-asset.mjs +9697 -0
- package/dist/cli-asset.mjs.map +1 -0
- package/dist/config.d.ts +6 -0
- package/dist/config.d.ts.map +1 -0
- package/dist/config.mjs +29 -0
- package/dist/config.mjs.map +1 -0
- package/dist/deriveAssetName.d.ts +15 -0
- package/dist/deriveAssetName.d.ts.map +1 -0
- package/dist/errors.d.ts +30 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/errors.mjs +19 -0
- package/dist/errors.mjs.map +1 -0
- package/dist/evidence/material-cook.d.ts +92 -0
- package/dist/evidence/material-cook.d.ts.map +1 -0
- package/dist/evidence/offline-evidence.d.ts +25 -0
- package/dist/evidence/offline-evidence.d.ts.map +1 -0
- package/dist/evidence/source-inventory.d.ts +29 -0
- package/dist/evidence/source-inventory.d.ts.map +1 -0
- package/dist/guid.d.ts +45 -0
- package/dist/guid.d.ts.map +1 -0
- package/dist/guid.mjs +94 -0
- package/dist/guid.mjs.map +1 -0
- package/dist/index.d.ts +15 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.mjs +8612 -0
- package/dist/index.mjs.map +1 -0
- package/dist/material/artifact-writer.d.ts +14 -0
- package/dist/material/artifact-writer.d.ts.map +1 -0
- package/dist/material-cook.d.ts +2 -0
- package/dist/material-cook.d.ts.map +1 -0
- package/dist/material-cook.mjs +248 -0
- package/dist/material-cook.mjs.map +1 -0
- package/dist/mesh-bin-contract.d.ts +36 -0
- package/dist/mesh-bin-contract.d.ts.map +1 -0
- package/dist/mesh-bin-contract.mjs +96 -0
- package/dist/mesh-bin-contract.mjs.map +1 -0
- package/dist/name.mjs +22 -0
- package/dist/name.mjs.map +1 -0
- package/dist/native-cooker-registry.d.ts +47 -0
- package/dist/native-cooker-registry.d.ts.map +1 -0
- package/dist/native-cooker.d.ts +3 -0
- package/dist/native-cooker.d.ts.map +1 -0
- package/dist/native-cooker.mjs +142 -0
- package/dist/native-cooker.mjs.map +1 -0
- package/dist/package-finalizer.d.ts +121 -0
- package/dist/package-finalizer.d.ts.map +1 -0
- package/dist/producer-contract.d.ts +19 -0
- package/dist/producer-contract.d.ts.map +1 -0
- package/dist/resolve-asset-source.d.ts +4 -0
- package/dist/resolve-asset-source.d.ts.map +1 -0
- package/dist/resolve-asset-source.mjs +109 -0
- package/dist/resolve-asset-source.mjs.map +1 -0
- package/dist/runtime-projection.d.ts +14 -0
- package/dist/runtime-projection.d.ts.map +1 -0
- package/dist/runtime-publication.d.ts +44 -0
- package/dist/runtime-publication.d.ts.map +1 -0
- package/dist/runtime.d.ts +8 -0
- package/dist/runtime.d.ts.map +1 -0
- package/dist/runtime.mjs +7261 -0
- package/dist/runtime.mjs.map +1 -0
- package/dist/scanner.d.ts +113 -0
- package/dist/scanner.d.ts.map +1 -0
- package/dist/scanner.mjs +8819 -0
- package/dist/scanner.mjs.map +1 -0
- package/dist/schema-compiled.d.ts +12 -0
- package/dist/schema-compiled.d.ts.map +1 -0
- package/dist/schema.mjs +7369 -0
- package/dist/schema.mjs.map +1 -0
- package/dist/scriptable-pack-node.d.ts +62 -0
- package/dist/scriptable-pack-node.d.ts.map +1 -0
- package/dist/scriptable-pack-node.mjs +1674 -0
- package/dist/scriptable-pack-node.mjs.map +1 -0
- package/dist/scriptable-pack-worker.d.ts +2 -0
- package/dist/scriptable-pack-worker.d.ts.map +1 -0
- package/dist/scriptable-pack-worker.mjs +224 -0
- package/dist/scriptable-pack-worker.mjs.map +1 -0
- package/dist/scriptable-pack.d.ts +558 -0
- package/dist/scriptable-pack.d.ts.map +1 -0
- package/dist/scriptable-pack.mjs +672 -0
- package/dist/scriptable-pack.mjs.map +1 -0
- package/dist/topology.d.ts +10 -0
- package/dist/topology.d.ts.map +1 -0
- package/package.json +154 -0
- package/schema/meta.schema.json +173 -0
- package/schema/pack.schema.json +232 -0
- package/src/__tests__/ai-recovery.unit.test.ts +114 -0
- package/src/__tests__/artifact-path.test.ts +41 -0
- package/src/__tests__/asset-evidence-error-hints.test.ts +46 -0
- package/src/__tests__/asset-evidence-lifecycle.test.ts +104 -0
- package/src/__tests__/asset-evidence-schema.test.ts +86 -0
- package/src/__tests__/atlas-cli-region-mismatch.test.ts +134 -0
- package/src/__tests__/atlas-error-code-owner.test-d.ts +23 -0
- package/src/__tests__/cli-asset-evidence.integration.test.ts +172 -0
- package/src/__tests__/cli.unit.test.ts +448 -0
- package/src/__tests__/evidence-source-inventory.unit.test.ts +37 -0
- package/src/__tests__/guid-collision.unit.test.ts +112 -0
- package/src/__tests__/guid-validator-surface.unit.test.ts +20 -0
- package/src/__tests__/load-asset-config.test.ts +121 -0
- package/src/__tests__/material-cook-dependencies.unit.test.ts +112 -0
- package/src/__tests__/material-cook-receipt.unit.test.ts +148 -0
- package/src/__tests__/material-cook-schema.unit.test.ts +152 -0
- package/src/__tests__/mesh-bin-consumer-surface.unit.test.ts +22 -0
- package/src/__tests__/mesh-bin-contract.unit.test.ts +32 -0
- package/src/__tests__/meta-schema-optional.test.ts +116 -0
- package/src/__tests__/meta-source-overrides.unit.test.ts +71 -0
- package/src/__tests__/native-cooker-registry.test.ts +64 -0
- package/src/__tests__/offline-evidence-artifact-verification-owner.test-d.ts +64 -0
- package/src/__tests__/offline-evidence.integration.test.ts +106 -0
- package/src/__tests__/owner-chain.integration.test.ts +78 -0
- package/src/__tests__/pack-v2-fixture-migration.test.ts +87 -0
- package/src/__tests__/pack-v2-schema.test.ts +142 -0
- package/src/__tests__/pack.unit.test.ts +2218 -0
- package/src/__tests__/package-finalizer.contract.test.ts +105 -0
- package/src/__tests__/producer-contract-schema.unit.test.ts +92 -0
- package/src/__tests__/resolve-asset-source.test.ts +121 -0
- package/src/__tests__/runtime-projection.unit.test.ts +87 -0
- package/src/__tests__/runtime.browser.test.ts +15 -0
- package/src/__tests__/scanner-inventory.contract.test.ts +167 -0
- package/src/__tests__/scanner-inventory.test.ts +76 -0
- package/src/__tests__/scanner-mount-cycle.test.ts +232 -0
- package/src/__tests__/scriptable-pack-cli.integration.test.ts +47 -0
- package/src/__tests__/scriptable-pack-ddc-key.unit.test.ts +63 -0
- package/src/__tests__/scriptable-pack.test-d.ts +131 -0
- package/src/__tests__/scriptable-pack.unit.test.ts +789 -0
- package/src/__tests__/topology-actions.unit.test.ts +94 -0
- package/src/__tests__/topology.unit.test.ts +85 -0
- package/src/artifact-path.ts +69 -0
- package/src/atlas/run-atlas.ts +358 -0
- package/src/atlas/shelf-pack.ts +172 -0
- package/src/atlas/upng-ambient.d.ts +13 -0
- package/src/bridge.ts +2 -0
- package/src/build.ts +139 -0
- package/src/builtin.ts +117 -0
- package/src/catalog-builder.ts +313 -0
- package/src/catalog-delta.ts +189 -0
- package/src/catalog-projection.ts +344 -0
- package/src/cli-asset.ts +586 -0
- package/src/config.ts +36 -0
- package/src/deriveAssetName.ts +42 -0
- package/src/errors.ts +47 -0
- package/src/evidence/material-cook.ts +411 -0
- package/src/evidence/offline-evidence.ts +90 -0
- package/src/evidence/source-inventory.ts +193 -0
- package/src/guid.ts +99 -0
- package/src/index.ts +108 -0
- package/src/material/artifact-writer.ts +26 -0
- package/src/material-cook.ts +15 -0
- package/src/mesh-bin-contract.ts +150 -0
- package/src/native-cooker-registry.ts +235 -0
- package/src/native-cooker.ts +2 -0
- package/src/package-finalizer.ts +394 -0
- package/src/producer-contract.ts +277 -0
- package/src/resolve-asset-source.ts +88 -0
- package/src/runtime-projection.ts +100 -0
- package/src/runtime-publication.ts +190 -0
- package/src/runtime.ts +23 -0
- package/src/scanner.ts +715 -0
- package/src/schema/material-cook.schema.json +119 -0
- package/src/schema-compiled.ts +238 -0
- package/src/scriptable-pack-node.ts +1394 -0
- package/src/scriptable-pack-worker.ts +308 -0
- package/src/scriptable-pack.ts +1006 -0
- package/src/topology.ts +157 -0
|
@@ -0,0 +1,277 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AssetSubjectRef,
|
|
3
|
+
ImportedOutputDeclaration,
|
|
4
|
+
ProducerContractDiagnostic,
|
|
5
|
+
ProducerContractResult,
|
|
6
|
+
SourceOverrideMap,
|
|
7
|
+
} from '@forgeax/engine-types';
|
|
8
|
+
import { validateSourceOverrideMap } from '@forgeax/engine-types';
|
|
9
|
+
|
|
10
|
+
type ProducerObject = Record<string, unknown>;
|
|
11
|
+
|
|
12
|
+
function isRecord(value: unknown): value is ProducerObject {
|
|
13
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function hasText(value: unknown): value is string {
|
|
17
|
+
return typeof value === 'string' && value.length > 0;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function issue(
|
|
21
|
+
code: ProducerContractDiagnostic['code'],
|
|
22
|
+
subjectId: string,
|
|
23
|
+
expected: string,
|
|
24
|
+
hint: string,
|
|
25
|
+
actual?: string,
|
|
26
|
+
authority: 'producer' | 'pack' = 'producer',
|
|
27
|
+
): ProducerContractResult<never> {
|
|
28
|
+
const subject: AssetSubjectRef = { type: 'asset', id: subjectId };
|
|
29
|
+
return {
|
|
30
|
+
ok: false,
|
|
31
|
+
error: {
|
|
32
|
+
code,
|
|
33
|
+
subject,
|
|
34
|
+
expected,
|
|
35
|
+
...(actual === undefined ? {} : { actual }),
|
|
36
|
+
hint,
|
|
37
|
+
authority,
|
|
38
|
+
},
|
|
39
|
+
};
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
function validateFacts(
|
|
43
|
+
value: ProducerObject,
|
|
44
|
+
subjectId: string,
|
|
45
|
+
): ProducerContractResult<ProducerObject> {
|
|
46
|
+
if ('packageId' in value && !hasText(value.packageId)) {
|
|
47
|
+
return issue(
|
|
48
|
+
'invalid-producer-fact',
|
|
49
|
+
subjectId,
|
|
50
|
+
'packageId must be a non-empty string',
|
|
51
|
+
'publish a stable package identity',
|
|
52
|
+
);
|
|
53
|
+
}
|
|
54
|
+
if ('provenance' in value) {
|
|
55
|
+
const provenance = value.provenance;
|
|
56
|
+
if (!isRecord(provenance) || !hasText(provenance.provider) || !hasText(provenance.version)) {
|
|
57
|
+
return issue(
|
|
58
|
+
'invalid-producer-fact',
|
|
59
|
+
subjectId,
|
|
60
|
+
'provenance.provider and provenance.version must be non-empty',
|
|
61
|
+
'publish provider identity and version',
|
|
62
|
+
);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if ('revision' in value) {
|
|
66
|
+
const revision = value.revision;
|
|
67
|
+
if (
|
|
68
|
+
!isRecord(revision) ||
|
|
69
|
+
!hasText(revision.digest) ||
|
|
70
|
+
!hasText(revision.rootId) ||
|
|
71
|
+
typeof revision.observedAt !== 'number' ||
|
|
72
|
+
!Number.isFinite(revision.observedAt)
|
|
73
|
+
) {
|
|
74
|
+
return issue(
|
|
75
|
+
'invalid-producer-fact',
|
|
76
|
+
subjectId,
|
|
77
|
+
'revision.digest, revision.rootId and finite revision.observedAt',
|
|
78
|
+
'publish a complete resource revision',
|
|
79
|
+
);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
if ('diagnostics' in value) {
|
|
83
|
+
if (!Array.isArray(value.diagnostics)) {
|
|
84
|
+
return issue(
|
|
85
|
+
'invalid-producer-fact',
|
|
86
|
+
subjectId,
|
|
87
|
+
'diagnostics must be an array',
|
|
88
|
+
'publish structured diagnostics as an array',
|
|
89
|
+
);
|
|
90
|
+
}
|
|
91
|
+
for (const diagnostic of value.diagnostics) {
|
|
92
|
+
if (!isRecord(diagnostic) || !hasText(diagnostic.code)) {
|
|
93
|
+
return issue(
|
|
94
|
+
'invalid-producer-fact',
|
|
95
|
+
subjectId,
|
|
96
|
+
'each diagnostic needs a stable code',
|
|
97
|
+
'publish a machine-readable diagnostic code',
|
|
98
|
+
);
|
|
99
|
+
}
|
|
100
|
+
if (diagnostic.severity === 'blocking') {
|
|
101
|
+
if (!isRecord(diagnostic.subject) || !hasText(diagnostic.subject.id)) {
|
|
102
|
+
return issue(
|
|
103
|
+
'invalid-producer-fact',
|
|
104
|
+
subjectId,
|
|
105
|
+
'blocking diagnostic.subject must identify an object',
|
|
106
|
+
'set subject.type and subject.id',
|
|
107
|
+
);
|
|
108
|
+
}
|
|
109
|
+
if (!hasText(diagnostic.expected) || !hasText(diagnostic.hint)) {
|
|
110
|
+
return issue(
|
|
111
|
+
'invalid-producer-fact',
|
|
112
|
+
subjectId,
|
|
113
|
+
'blocking diagnostics need expected and hint',
|
|
114
|
+
'provide an executable recovery hint',
|
|
115
|
+
);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
return { ok: true, value };
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function validateMetaSourceOverrides(
|
|
124
|
+
value: ProducerObject,
|
|
125
|
+
subjectId: string,
|
|
126
|
+
): ProducerContractResult<ProducerObject> {
|
|
127
|
+
if (!('sourceOverrides' in value)) return { ok: true, value };
|
|
128
|
+
const declaredSourceKeys = Array.isArray(value.subAssets)
|
|
129
|
+
? value.subAssets.flatMap((subAsset) =>
|
|
130
|
+
isRecord(subAsset) && hasText(subAsset.sourceKey) ? [subAsset.sourceKey] : [],
|
|
131
|
+
)
|
|
132
|
+
: [];
|
|
133
|
+
const result = validateSourceOverrideMap(value.sourceOverrides, declaredSourceKeys);
|
|
134
|
+
if (!result.ok) {
|
|
135
|
+
return issue(
|
|
136
|
+
result.error.code,
|
|
137
|
+
subjectId,
|
|
138
|
+
result.error.expected,
|
|
139
|
+
result.error.hint,
|
|
140
|
+
result.error.actual,
|
|
141
|
+
'pack',
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
return {
|
|
145
|
+
ok: true,
|
|
146
|
+
value: { ...value, sourceOverrides: result.value as SourceOverrideMap | undefined },
|
|
147
|
+
};
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Validate one package or imported-output producer declaration.
|
|
152
|
+
*
|
|
153
|
+
* The accepted fields mirror the engine-types POD contract. This function
|
|
154
|
+
* validates that schema without creating a second schema or interpreting
|
|
155
|
+
* diagnostic messages.
|
|
156
|
+
*/
|
|
157
|
+
export function validateProducerContract(value: unknown): ProducerContractResult<ProducerObject> {
|
|
158
|
+
if (!isRecord(value)) {
|
|
159
|
+
return issue(
|
|
160
|
+
'invalid-producer-fact',
|
|
161
|
+
'unknown',
|
|
162
|
+
'producer declaration must be an object',
|
|
163
|
+
'publish a JSON object',
|
|
164
|
+
);
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
const subjectId = hasText(value.guid)
|
|
168
|
+
? value.guid
|
|
169
|
+
: hasText(value.packageId)
|
|
170
|
+
? value.packageId
|
|
171
|
+
: 'unknown';
|
|
172
|
+
const facts = validateFacts(value, subjectId);
|
|
173
|
+
if (!facts.ok) return facts;
|
|
174
|
+
|
|
175
|
+
const metaOverrides = validateMetaSourceOverrides(value, subjectId);
|
|
176
|
+
if (!metaOverrides.ok) return metaOverrides;
|
|
177
|
+
|
|
178
|
+
if ('kind' in value && !hasText(value.kind)) {
|
|
179
|
+
return issue(
|
|
180
|
+
'invalid-producer-fact',
|
|
181
|
+
subjectId,
|
|
182
|
+
'kind must be a non-empty open string',
|
|
183
|
+
'publish the provider-owned asset kind',
|
|
184
|
+
);
|
|
185
|
+
}
|
|
186
|
+
if ('execution' in value && value.execution !== 'direct' && value.execution !== 'cooked') {
|
|
187
|
+
return issue(
|
|
188
|
+
'invalid-producer-fact',
|
|
189
|
+
subjectId,
|
|
190
|
+
'execution must be either direct or cooked',
|
|
191
|
+
'publish the explicit authoring execution mode',
|
|
192
|
+
);
|
|
193
|
+
}
|
|
194
|
+
const hasOutputIdentity = 'kind' in value || 'sourceIndex' in value || 'sourceKey' in value;
|
|
195
|
+
if (!hasOutputIdentity) return facts;
|
|
196
|
+
if (value.sourceKey !== undefined && !hasText(value.sourceKey)) {
|
|
197
|
+
return issue(
|
|
198
|
+
'invalid-source-key',
|
|
199
|
+
subjectId,
|
|
200
|
+
'sourceKey must be a non-empty producer-owned string',
|
|
201
|
+
'publish a stable semantic sourceKey',
|
|
202
|
+
);
|
|
203
|
+
}
|
|
204
|
+
if (
|
|
205
|
+
value.sourceIndex !== undefined &&
|
|
206
|
+
(typeof value.sourceIndex !== 'number' ||
|
|
207
|
+
!Number.isInteger(value.sourceIndex) ||
|
|
208
|
+
value.sourceIndex < 0)
|
|
209
|
+
) {
|
|
210
|
+
return issue(
|
|
211
|
+
'invalid-source-index',
|
|
212
|
+
subjectId,
|
|
213
|
+
'sourceIndex must be a non-negative integer locator',
|
|
214
|
+
'publish a non-negative sourceIndex without using it as identity',
|
|
215
|
+
);
|
|
216
|
+
}
|
|
217
|
+
if (value.sourceKey === undefined && value.sourceIndex !== undefined) {
|
|
218
|
+
return issue(
|
|
219
|
+
'missing-source-key',
|
|
220
|
+
subjectId,
|
|
221
|
+
'sourceKey is required when an output declares sourceIndex',
|
|
222
|
+
'publish a stable semantic key; do not derive identity from sourceIndex',
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
if (
|
|
226
|
+
'compatiblePreviousKinds' in value &&
|
|
227
|
+
(!Array.isArray(value.compatiblePreviousKinds) ||
|
|
228
|
+
value.compatiblePreviousKinds.some((kind) => !hasText(kind)))
|
|
229
|
+
) {
|
|
230
|
+
return issue(
|
|
231
|
+
'invalid-producer-fact',
|
|
232
|
+
subjectId,
|
|
233
|
+
'compatiblePreviousKinds must be an array of non-empty kind strings',
|
|
234
|
+
'declare only producer-verified prior kinds that may preserve the GUID',
|
|
235
|
+
);
|
|
236
|
+
}
|
|
237
|
+
return metaOverrides;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* Validate output topology without ever treating `sourceIndex` as identity.
|
|
242
|
+
*
|
|
243
|
+
* AI-readable callers should use the discriminated result and its structured
|
|
244
|
+
* `code`, `expected`, `actual`, and `hint` fields for recovery decisions.
|
|
245
|
+
*/
|
|
246
|
+
export function validateProducerOutputs(
|
|
247
|
+
outputs: readonly ImportedOutputDeclaration[],
|
|
248
|
+
): ProducerContractResult<readonly ImportedOutputDeclaration[]> {
|
|
249
|
+
const missingKey = outputs.find((output) => output.sourceKey === undefined);
|
|
250
|
+
if (missingKey !== undefined) {
|
|
251
|
+
return issue(
|
|
252
|
+
outputs.length > 1 ? 'source-index-ambiguous' : 'missing-source-key',
|
|
253
|
+
missingKey.guid,
|
|
254
|
+
'every output declaration needs a stable sourceKey',
|
|
255
|
+
'add sourceKey to each output before matching topology',
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
const keys = new Map<string, ImportedOutputDeclaration>();
|
|
259
|
+
for (const output of outputs) {
|
|
260
|
+
const result = validateProducerContract(output);
|
|
261
|
+
if (!result.ok) return result;
|
|
262
|
+
const sourceKey = output.sourceKey;
|
|
263
|
+
if (sourceKey === undefined) continue;
|
|
264
|
+
const prior = keys.get(sourceKey);
|
|
265
|
+
if (prior !== undefined) {
|
|
266
|
+
return issue(
|
|
267
|
+
'duplicate-source-key',
|
|
268
|
+
output.guid,
|
|
269
|
+
'sourceKey values must be unique within one producer package',
|
|
270
|
+
'rename the duplicate semantic output key',
|
|
271
|
+
sourceKey,
|
|
272
|
+
);
|
|
273
|
+
}
|
|
274
|
+
keys.set(sourceKey, output);
|
|
275
|
+
}
|
|
276
|
+
return { ok: true, value: outputs };
|
|
277
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
import { dirname, posix, resolve } from 'node:path';
|
|
2
|
+
import type { Result } from '@forgeax/engine-types';
|
|
3
|
+
import { PACK_ERROR_HINTS, err as resultErr, ok as resultOk } from '@forgeax/engine-types';
|
|
4
|
+
import { deriveAssetName } from './deriveAssetName.js';
|
|
5
|
+
import { PackError } from './errors.js';
|
|
6
|
+
|
|
7
|
+
function deriveSourceName(metaFileName: string): string {
|
|
8
|
+
return metaFileName.replace(/\.meta\.json$/, '');
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
const PATH_REF_RE = /^@([^/]+)\/(.+)$/;
|
|
12
|
+
|
|
13
|
+
function isPathEscape(pathDir: string, resolved: string): boolean {
|
|
14
|
+
const rel = posix.relative(pathDir, resolved);
|
|
15
|
+
return rel.startsWith('..') || posix.isAbsolute(rel);
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export function resolveAssetSource(
|
|
19
|
+
metaPath: string,
|
|
20
|
+
source: string | undefined,
|
|
21
|
+
paths: Record<string, string>,
|
|
22
|
+
): Result<string, PackError> {
|
|
23
|
+
if (source === undefined) {
|
|
24
|
+
const metaDir = dirname(metaPath);
|
|
25
|
+
const metaName = deriveAssetName(metaPath, 1);
|
|
26
|
+
const derived = deriveSourceName(metaName);
|
|
27
|
+
return resultOk(resolve(metaDir, derived));
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (source.startsWith('@')) {
|
|
31
|
+
const match = source.match(PATH_REF_RE);
|
|
32
|
+
if (!match) {
|
|
33
|
+
return resultErr(
|
|
34
|
+
new PackError({
|
|
35
|
+
code: 'pack-malformed-path-ref',
|
|
36
|
+
expected: 'source in format @<name>/<rest>',
|
|
37
|
+
hint: PACK_ERROR_HINTS['pack-malformed-path-ref'],
|
|
38
|
+
detail: {
|
|
39
|
+
code: 'pack-malformed-path-ref',
|
|
40
|
+
reason: 'format',
|
|
41
|
+
rawSource: source,
|
|
42
|
+
expectedFormat: '@<name>/<rest>',
|
|
43
|
+
},
|
|
44
|
+
}),
|
|
45
|
+
);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
const [, name, rest] = match as [string, string, string];
|
|
49
|
+
const pathDir = paths[name];
|
|
50
|
+
|
|
51
|
+
if (pathDir === undefined) {
|
|
52
|
+
return resultErr(
|
|
53
|
+
new PackError({
|
|
54
|
+
code: 'pack-unknown-path',
|
|
55
|
+
expected: 'path name found in package.json#forgeax.assets.paths',
|
|
56
|
+
hint: PACK_ERROR_HINTS['pack-unknown-path'],
|
|
57
|
+
detail: {
|
|
58
|
+
code: 'pack-unknown-path',
|
|
59
|
+
pathName: name,
|
|
60
|
+
knownNames: Object.keys(paths),
|
|
61
|
+
},
|
|
62
|
+
}),
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const resolved = resolve(pathDir, rest);
|
|
67
|
+
|
|
68
|
+
if (isPathEscape(pathDir, resolved)) {
|
|
69
|
+
return resultErr(
|
|
70
|
+
new PackError({
|
|
71
|
+
code: 'pack-malformed-path-ref',
|
|
72
|
+
expected: 'rest path must not escape the declared path directory',
|
|
73
|
+
hint: PACK_ERROR_HINTS['pack-malformed-path-ref'],
|
|
74
|
+
detail: {
|
|
75
|
+
code: 'pack-malformed-path-ref',
|
|
76
|
+
reason: 'escape',
|
|
77
|
+
rawSource: source,
|
|
78
|
+
expectedFormat: '@<name>/<rest>',
|
|
79
|
+
},
|
|
80
|
+
}),
|
|
81
|
+
);
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
return resultOk(resolved);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return resultOk(resolve(dirname(metaPath), source));
|
|
88
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import type {
|
|
2
|
+
AssetEnvelopeV2,
|
|
3
|
+
AssetPublicationTuple,
|
|
4
|
+
AssetRuntimeArtifactDescriptor,
|
|
5
|
+
PackV2,
|
|
6
|
+
PackV2Error,
|
|
7
|
+
Result,
|
|
8
|
+
} from '@forgeax/engine-types';
|
|
9
|
+
import { err, ok } from '@forgeax/engine-types';
|
|
10
|
+
|
|
11
|
+
export interface RuntimeAssetProjectionInput<P = unknown> {
|
|
12
|
+
readonly guid: string;
|
|
13
|
+
readonly kind: string;
|
|
14
|
+
readonly payload: P;
|
|
15
|
+
readonly refs: readonly string[];
|
|
16
|
+
readonly artifacts: Readonly<Record<string, AssetRuntimeArtifactDescriptor>>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface RuntimePackProjectionInput<P = unknown> extends AssetPublicationTuple {
|
|
20
|
+
readonly assets: readonly RuntimeAssetProjectionInput<P>[];
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function invalid(observed: string, expected: string): Result<never, PackV2Error> {
|
|
24
|
+
return err({
|
|
25
|
+
code: 'pack-v2-envelope-invalid',
|
|
26
|
+
expected,
|
|
27
|
+
hint: 'publish one verified Pack v2 envelope with one atomic publication tuple',
|
|
28
|
+
detail: { observed, expected },
|
|
29
|
+
});
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
33
|
+
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function isTuple(value: unknown): value is AssetPublicationTuple {
|
|
37
|
+
return (
|
|
38
|
+
isRecord(value) &&
|
|
39
|
+
typeof value.scopeId === 'string' &&
|
|
40
|
+
value.scopeId.length > 0 &&
|
|
41
|
+
typeof value.generation === 'number' &&
|
|
42
|
+
Number.isInteger(value.generation) &&
|
|
43
|
+
value.generation > 0 &&
|
|
44
|
+
typeof value.digest === 'string' &&
|
|
45
|
+
value.digest.length > 0 &&
|
|
46
|
+
typeof value.outputSetDigest === 'string' &&
|
|
47
|
+
value.outputSetDigest.length > 0
|
|
48
|
+
);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function isAsset(value: unknown): value is RuntimeAssetProjectionInput {
|
|
52
|
+
return (
|
|
53
|
+
isRecord(value) &&
|
|
54
|
+
typeof value.guid === 'string' &&
|
|
55
|
+
value.guid.length > 0 &&
|
|
56
|
+
typeof value.kind === 'string' &&
|
|
57
|
+
value.kind.length > 0 &&
|
|
58
|
+
Array.isArray(value.refs) &&
|
|
59
|
+
value.refs.every((ref) => typeof ref === 'string') &&
|
|
60
|
+
isRecord(value.artifacts)
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/** Project producer facts into the only runtime Pack v2 envelope. */
|
|
65
|
+
export function projectRuntimePack<P = unknown>(value: unknown): Result<PackV2<P>, PackV2Error> {
|
|
66
|
+
if (
|
|
67
|
+
!isRecord(value) ||
|
|
68
|
+
(('schemaVersion' in value || 'kind' in value) &&
|
|
69
|
+
(value.schemaVersion !== '2.0.0' || value.kind !== 'internal-text-package'))
|
|
70
|
+
) {
|
|
71
|
+
return invalid('legacy or malformed pack input', 'runtime Pack v2');
|
|
72
|
+
}
|
|
73
|
+
if (!isTuple(value) || !Array.isArray(value.assets) || !value.assets.every(isAsset)) {
|
|
74
|
+
return invalid('incomplete publication tuple or asset envelope', 'verified runtime Pack v2');
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const seen = new Set<string>();
|
|
78
|
+
for (const asset of value.assets) {
|
|
79
|
+
const guid = asset.guid.toLowerCase();
|
|
80
|
+
if (seen.has(guid)) {
|
|
81
|
+
return err({
|
|
82
|
+
code: 'pack-v2-duplicate-guid',
|
|
83
|
+
expected: 'one asset envelope per GUID',
|
|
84
|
+
hint: 'publish one producer row and recook the package output set',
|
|
85
|
+
detail: { guid: asset.guid, paths: [] },
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
seen.add(guid);
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
return ok({
|
|
92
|
+
schemaVersion: '2.0.0',
|
|
93
|
+
kind: 'internal-text-package',
|
|
94
|
+
scopeId: value.scopeId,
|
|
95
|
+
generation: value.generation,
|
|
96
|
+
digest: value.digest,
|
|
97
|
+
outputSetDigest: value.outputSetDigest,
|
|
98
|
+
assets: value.assets as readonly AssetEnvelopeV2<P>[],
|
|
99
|
+
});
|
|
100
|
+
}
|
|
@@ -0,0 +1,190 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import type {
|
|
3
|
+
AssetPublicationEnvelope,
|
|
4
|
+
AssetPublicationExternalEvidence,
|
|
5
|
+
AssetPublicationOutput,
|
|
6
|
+
} from '@forgeax/engine-types';
|
|
7
|
+
|
|
8
|
+
export interface RuntimePackAssetInput {
|
|
9
|
+
readonly guid: string;
|
|
10
|
+
readonly kind: string;
|
|
11
|
+
readonly name?: string;
|
|
12
|
+
readonly payload: unknown;
|
|
13
|
+
readonly refs?: readonly string[];
|
|
14
|
+
readonly artifacts?: Readonly<Record<string, unknown>>;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export interface RuntimePackInput {
|
|
18
|
+
readonly assets: readonly RuntimePackAssetInput[];
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface RuntimePackEnvelope {
|
|
22
|
+
readonly schemaVersion: '2.0.0';
|
|
23
|
+
readonly kind: 'internal-text-package';
|
|
24
|
+
readonly scopeId: string;
|
|
25
|
+
readonly generation: number;
|
|
26
|
+
readonly digest: string;
|
|
27
|
+
readonly outputSetDigest: string;
|
|
28
|
+
readonly assets: readonly RuntimePackAsset[];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface RuntimePackPublication {
|
|
32
|
+
readonly pack: RuntimePackEnvelope;
|
|
33
|
+
readonly publication: AssetPublicationEnvelope;
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
interface RuntimePackAsset extends RuntimePackAssetInput {
|
|
37
|
+
readonly refs: readonly string[];
|
|
38
|
+
readonly artifacts: Readonly<Record<string, unknown>>;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
export interface RuntimePackPublicationInput {
|
|
42
|
+
readonly pack: RuntimePackInput;
|
|
43
|
+
readonly scopeId: string;
|
|
44
|
+
readonly sourcePath: string;
|
|
45
|
+
readonly sourceRevision: string;
|
|
46
|
+
readonly packageUrl: string;
|
|
47
|
+
readonly inputFingerprint?: string;
|
|
48
|
+
readonly digest?: string;
|
|
49
|
+
readonly generation?: number;
|
|
50
|
+
readonly outputs?: readonly AssetPublicationOutput[];
|
|
51
|
+
readonly externalEvidence?: readonly AssetPublicationExternalEvidence[];
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
function stable(value: unknown): string {
|
|
55
|
+
if (value instanceof Uint8Array) return `bytes:${Buffer.from(value).toString('base64')}`;
|
|
56
|
+
if (Array.isArray(value)) return `[${value.map(stable).join(',')}]`;
|
|
57
|
+
if (value !== null && typeof value === 'object') {
|
|
58
|
+
const record = value as Record<string, unknown>;
|
|
59
|
+
return `{${Object.keys(record)
|
|
60
|
+
.sort()
|
|
61
|
+
.map((key) => `${JSON.stringify(key)}:${stable(record[key])}`)
|
|
62
|
+
.join(',')}}`;
|
|
63
|
+
}
|
|
64
|
+
return JSON.stringify(value) ?? 'null';
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function digest(value: unknown): string {
|
|
68
|
+
return `sha256:${createHash('sha256').update(stable(value)).digest('hex')}`;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function normalizedAssets(pack: RuntimePackInput): readonly RuntimePackAsset[] {
|
|
72
|
+
return pack.assets.map((asset) => {
|
|
73
|
+
const payload = asset.payload;
|
|
74
|
+
const canonicalPayload =
|
|
75
|
+
payload !== null &&
|
|
76
|
+
typeof payload === 'object' &&
|
|
77
|
+
!Array.isArray(payload) &&
|
|
78
|
+
!(payload instanceof Uint8Array) &&
|
|
79
|
+
typeof (payload as Record<string, unknown>).kind !== 'string'
|
|
80
|
+
? { ...(payload as Record<string, unknown>), kind: asset.kind }
|
|
81
|
+
: payload;
|
|
82
|
+
return {
|
|
83
|
+
...asset,
|
|
84
|
+
payload: canonicalPayload,
|
|
85
|
+
refs: [...(asset.refs ?? [])].map((ref) => ref.toLowerCase()),
|
|
86
|
+
artifacts: asset.artifacts ?? {},
|
|
87
|
+
};
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function outputFor(asset: RuntimePackAsset): AssetPublicationOutput {
|
|
92
|
+
return {
|
|
93
|
+
guid: asset.guid.toLowerCase(),
|
|
94
|
+
sourceKey: asset.guid.toLowerCase(),
|
|
95
|
+
kind: asset.kind,
|
|
96
|
+
digest: digest({
|
|
97
|
+
guid: asset.guid.toLowerCase(),
|
|
98
|
+
kind: asset.kind,
|
|
99
|
+
name: asset.name,
|
|
100
|
+
payload: asset.payload,
|
|
101
|
+
refs: asset.refs,
|
|
102
|
+
artifacts: asset.artifacts,
|
|
103
|
+
}),
|
|
104
|
+
refs: asset.refs,
|
|
105
|
+
};
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function outputSetDigest(outputs: readonly AssetPublicationOutput[]): string {
|
|
109
|
+
return digest(
|
|
110
|
+
outputs.map((output) => ({
|
|
111
|
+
guid: output.guid.toLowerCase(),
|
|
112
|
+
sourceKey: output.sourceKey,
|
|
113
|
+
kind: output.kind,
|
|
114
|
+
digest: output.digest,
|
|
115
|
+
refs: [...output.refs].map((guid) => guid.toLowerCase()),
|
|
116
|
+
})),
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function publicationGeneration(
|
|
121
|
+
sourceRevision: string,
|
|
122
|
+
valueDigest: string,
|
|
123
|
+
outputs: string,
|
|
124
|
+
): number {
|
|
125
|
+
const value = createHash('sha256')
|
|
126
|
+
.update(sourceRevision)
|
|
127
|
+
.update('\n')
|
|
128
|
+
.update(valueDigest)
|
|
129
|
+
.update('\n')
|
|
130
|
+
.update(outputs)
|
|
131
|
+
.digest('hex');
|
|
132
|
+
const generation = Number.parseInt(value.slice(0, 8), 16);
|
|
133
|
+
return generation > 0 ? generation : 1;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export function createRuntimePackPublication(
|
|
137
|
+
input: RuntimePackPublicationInput,
|
|
138
|
+
): RuntimePackPublication {
|
|
139
|
+
const assets = normalizedAssets(input.pack);
|
|
140
|
+
const semantic = {
|
|
141
|
+
schemaVersion: '2.0.0' as const,
|
|
142
|
+
kind: 'internal-text-package' as const,
|
|
143
|
+
assets: [...assets].sort((left, right) =>
|
|
144
|
+
left.guid.toLowerCase().localeCompare(right.guid.toLowerCase()),
|
|
145
|
+
),
|
|
146
|
+
};
|
|
147
|
+
const valueDigest = input.digest ?? digest(semantic);
|
|
148
|
+
const outputs = input.outputs ?? assets.map(outputFor);
|
|
149
|
+
const outputDigest = outputSetDigest(outputs);
|
|
150
|
+
const generation =
|
|
151
|
+
input.generation ?? publicationGeneration(input.sourceRevision, valueDigest, outputDigest);
|
|
152
|
+
const externalEvidence = input.externalEvidence ?? [];
|
|
153
|
+
const inputFingerprint = input.inputFingerprint ?? input.sourceRevision;
|
|
154
|
+
const publication: AssetPublicationEnvelope = {
|
|
155
|
+
schemaVersion: 'asset-publication/1',
|
|
156
|
+
sourcePath: input.sourcePath,
|
|
157
|
+
sourceRevision: input.sourceRevision,
|
|
158
|
+
generation,
|
|
159
|
+
digest: valueDigest,
|
|
160
|
+
outputSetDigest: outputDigest,
|
|
161
|
+
outputs,
|
|
162
|
+
receipt: {
|
|
163
|
+
schemaVersion: 'asset-publication-receipt/1',
|
|
164
|
+
sourcePath: input.sourcePath,
|
|
165
|
+
sourceRevision: input.sourceRevision,
|
|
166
|
+
inputFingerprint,
|
|
167
|
+
outputDigest: valueDigest,
|
|
168
|
+
outputSetDigest: outputDigest,
|
|
169
|
+
externalEvidence,
|
|
170
|
+
},
|
|
171
|
+
externalEvidence,
|
|
172
|
+
current: {
|
|
173
|
+
generation,
|
|
174
|
+
digest: valueDigest,
|
|
175
|
+
outputSetDigest: outputDigest,
|
|
176
|
+
packageUrl: input.packageUrl,
|
|
177
|
+
receiptKey: inputFingerprint,
|
|
178
|
+
},
|
|
179
|
+
};
|
|
180
|
+
return {
|
|
181
|
+
pack: {
|
|
182
|
+
...semantic,
|
|
183
|
+
scopeId: input.scopeId,
|
|
184
|
+
generation,
|
|
185
|
+
digest: valueDigest,
|
|
186
|
+
outputSetDigest: outputDigest,
|
|
187
|
+
},
|
|
188
|
+
publication,
|
|
189
|
+
};
|
|
190
|
+
}
|
package/src/runtime.ts
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import type { PackV2, PackV2Error, Result } from '@forgeax/engine-types';
|
|
2
|
+
import { err, ok } from '@forgeax/engine-types';
|
|
3
|
+
|
|
4
|
+
import { validatePackV2 } from './schema-compiled.js';
|
|
5
|
+
|
|
6
|
+
export { validateMeta, validatePack, validatePackV2 } from './schema-compiled.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Parse a Pack v2 envelope without loading the Node-only scanner/evidence
|
|
10
|
+
* barrel. Browser runtime consumers should import this subpath.
|
|
11
|
+
*/
|
|
12
|
+
export function parsePackV2(value: unknown): Result<PackV2, PackV2Error> {
|
|
13
|
+
if (!validatePackV2(value)) {
|
|
14
|
+
return err({
|
|
15
|
+
code: 'pack-v2-envelope-invalid',
|
|
16
|
+
expected: 'a Pack v2 envelope with unique asset GUIDs and valid descriptors',
|
|
17
|
+
hint: 'validate the pack against packages/pack/schema/pack.schema.json and re-cook it',
|
|
18
|
+
detail: { observed: 'invalid pack', expected: 'schemaVersion 2.0.0' },
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return ok(value);
|
|
23
|
+
}
|