@forgeax/engine-pack 0.1.3 → 0.1.6
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 +52 -4
- package/dist/.tsbuildinfo +1 -1
- 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__/package-finalizer.contract.test.d.ts +2 -0
- package/dist/__tests__/package-finalizer.contract.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/artifact-path.mjs +53 -0
- package/dist/artifact-path.mjs.map +1 -0
- package/dist/atlas/shelf-pack.d.ts.map +1 -1
- package/dist/build.d.ts +19 -0
- package/dist/build.d.ts.map +1 -0
- package/dist/build.mjs +10168 -0
- package/dist/build.mjs.map +1 -0
- package/dist/builtin.d.ts +20 -0
- package/dist/builtin.d.ts.map +1 -1
- 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.mjs +231 -484
- package/dist/cli-asset.mjs.map +1 -1
- package/dist/index.mjs +83 -1
- package/dist/index.mjs.map +1 -1
- package/dist/material-cook.d.ts +2 -0
- package/dist/material-cook.d.ts.map +1 -0
- package/dist/material-cook.mjs +193 -0
- package/dist/material-cook.mjs.map +1 -0
- package/dist/mesh-bin-contract.mjs +96 -0
- package/dist/mesh-bin-contract.mjs.map +1 -0
- package/dist/native-cooker-registry.d.ts +19 -0
- package/dist/native-cooker-registry.d.ts.map +1 -1
- package/dist/native-cooker.mjs +37 -0
- package/dist/native-cooker.mjs.map +1 -1
- package/dist/package-finalizer.d.ts +121 -0
- package/dist/package-finalizer.d.ts.map +1 -0
- package/dist/resolve-asset-source.d.ts.map +1 -1
- package/dist/resolve-asset-source.mjs +16 -6
- package/dist/resolve-asset-source.mjs.map +1 -1
- 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 +87 -54
- package/dist/scanner.d.ts.map +1 -1
- package/dist/scanner.mjs +258 -454
- package/dist/scanner.mjs.map +1 -1
- package/dist/scriptable-pack-node.d.ts +3 -8
- package/dist/scriptable-pack-node.d.ts.map +1 -1
- package/dist/scriptable-pack-node.mjs +93 -9
- package/dist/scriptable-pack-node.mjs.map +1 -1
- package/dist/scriptable-pack-worker.mjs +1 -0
- package/dist/scriptable-pack-worker.mjs.map +1 -1
- package/dist/scriptable-pack.d.ts +33 -0
- package/dist/scriptable-pack.d.ts.map +1 -1
- package/dist/scriptable-pack.mjs +91 -2
- package/dist/scriptable-pack.mjs.map +1 -1
- package/dist/topology.d.ts +1 -1
- package/dist/topology.d.ts.map +1 -1
- package/package.json +27 -2
- package/src/__tests__/mesh-bin-consumer-surface.unit.test.ts +2 -1
- package/src/__tests__/owner-chain.integration.test.ts +78 -0
- package/src/__tests__/package-finalizer.contract.test.ts +105 -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 +16 -12
- package/src/__tests__/scriptable-pack.unit.test.ts +42 -0
- package/src/atlas/shelf-pack.ts +20 -24
- package/src/build.ts +139 -0
- package/src/builtin.ts +38 -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 +1 -5
- package/src/index.ts +2 -2
- package/src/material-cook.ts +13 -0
- package/src/native-cooker-registry.ts +72 -0
- package/src/package-finalizer.ts +394 -0
- package/src/resolve-asset-source.ts +2 -7
- package/src/runtime-publication.ts +190 -0
- package/src/runtime.ts +23 -0
- package/src/scanner.ts +289 -290
- package/src/scriptable-pack-node.ts +27 -23
- package/src/scriptable-pack-worker.ts +2 -0
- package/src/scriptable-pack.ts +123 -1
- package/src/topology.ts +2 -1
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
export { type CookedMaterialRecord, collectMaterialCookRefs, createMaterialArtifactDigest, type MaterialCookArtifact, type MaterialCookReceipt, type MaterialCookRecordError, type MaterialCookRefs, projectCookedMaterialRecord, serializeCookedMaterialRecord, serializeMaterialCookReceipt, validateCookedMaterialRecord, } from './evidence/material-cook.js';
|
|
2
|
+
//# sourceMappingURL=material-cook.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"material-cook.d.ts","sourceRoot":"","sources":["../src/material-cook.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,oBAAoB,EACzB,uBAAuB,EACvB,4BAA4B,EAC5B,KAAK,oBAAoB,EACzB,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,KAAK,gBAAgB,EACrB,2BAA2B,EAC3B,6BAA6B,EAC7B,4BAA4B,EAC5B,4BAA4B,GAC7B,MAAM,6BAA6B,CAAC"}
|
|
@@ -0,0 +1,193 @@
|
|
|
1
|
+
import { MATERIAL_TEXTURE_SLOTS, ok, err } from '@forgeax/engine-types';
|
|
2
|
+
import { sha256 } from '@noble/hashes/sha2.js';
|
|
3
|
+
import { bytesToHex } from '@noble/hashes/utils.js';
|
|
4
|
+
|
|
5
|
+
// src/evidence/material-cook.ts
|
|
6
|
+
function unique(values) {
|
|
7
|
+
return [...new Set(values)].sort();
|
|
8
|
+
}
|
|
9
|
+
function guidText(value) {
|
|
10
|
+
return typeof value === "string" ? value : Array.from(value, (byte) => byte.toString(16).padStart(2, "0")).join("");
|
|
11
|
+
}
|
|
12
|
+
function cookGuidText(value) {
|
|
13
|
+
return typeof value === "number" ? void 0 : guidText(value);
|
|
14
|
+
}
|
|
15
|
+
function textureValues(values, textureFields) {
|
|
16
|
+
return Object.entries(values ?? {}).flatMap(([name, value]) => {
|
|
17
|
+
if (value === null) return [];
|
|
18
|
+
if (typeof value === "string") {
|
|
19
|
+
return textureFields.has(name) ? [{ texture: value }] : [];
|
|
20
|
+
}
|
|
21
|
+
return value !== null && typeof value === "object" && !Array.isArray(value) && "texture" in value ? [value] : [];
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
function collectMaterialCookRefs(material) {
|
|
25
|
+
const textureFields = material.parameters === void 0 ? new Set(MATERIAL_TEXTURE_SLOTS) : new Set(
|
|
26
|
+
material.parameters.filter((parameter) => parameter.type === "texture").map((parameter) => parameter.name)
|
|
27
|
+
);
|
|
28
|
+
const textures = textureValues(material.values, textureFields);
|
|
29
|
+
return {
|
|
30
|
+
parent: material.parent ? [guidText(material.parent)] : [],
|
|
31
|
+
textures: unique(
|
|
32
|
+
textures.flatMap((value) => {
|
|
33
|
+
const guid = cookGuidText(value.texture);
|
|
34
|
+
return guid === void 0 ? [] : [guid];
|
|
35
|
+
})
|
|
36
|
+
),
|
|
37
|
+
samplers: unique(
|
|
38
|
+
textures.flatMap((value) => {
|
|
39
|
+
if (value.sampler === void 0) return [];
|
|
40
|
+
const guid = cookGuidText(value.sampler);
|
|
41
|
+
return guid === void 0 ? [] : [guid];
|
|
42
|
+
})
|
|
43
|
+
),
|
|
44
|
+
modules: unique((material.passes ?? []).map((pass) => pass.program.module))
|
|
45
|
+
};
|
|
46
|
+
}
|
|
47
|
+
function createMaterialArtifactDigest(bytes) {
|
|
48
|
+
return `sha256:${bytesToHex(sha256(bytes))}`;
|
|
49
|
+
}
|
|
50
|
+
function jsonValue(value) {
|
|
51
|
+
if (value instanceof Uint8Array) return [...value];
|
|
52
|
+
if (Array.isArray(value)) return value.map(jsonValue);
|
|
53
|
+
if (value !== null && typeof value === "object") {
|
|
54
|
+
return Object.fromEntries(
|
|
55
|
+
Object.entries(value).filter(([, entry]) => entry !== void 0).sort(([a], [b]) => a.localeCompare(b)).map(([key, entry]) => [key, jsonValue(entry)])
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
return value;
|
|
59
|
+
}
|
|
60
|
+
function serializeCookedMaterialRecord(record) {
|
|
61
|
+
return JSON.stringify(jsonValue(record));
|
|
62
|
+
}
|
|
63
|
+
function serializeMaterialCookReceipt(receipt) {
|
|
64
|
+
return JSON.stringify(jsonValue({ ...receipt, sourceClosure: unique(receipt.sourceClosure) }));
|
|
65
|
+
}
|
|
66
|
+
function invalid(field) {
|
|
67
|
+
return err({
|
|
68
|
+
code: "material-cook-record-invalid",
|
|
69
|
+
expected: "a complete material-cook/2 record with derived layout identity",
|
|
70
|
+
hint: "re-cook the material and publish its record, artifact, references, and receipt together",
|
|
71
|
+
detail: { field }
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function validateMaterialCookReceipt(value, expected = {}) {
|
|
75
|
+
if (value === null || typeof value !== "object") return invalid("receipt");
|
|
76
|
+
const candidate = value;
|
|
77
|
+
if (candidate.schemaVersion !== "material-cook/2") return invalid("receipt.schemaVersion");
|
|
78
|
+
if (typeof candidate.layoutIdentity !== "string" || candidate.layoutIdentity.length === 0) {
|
|
79
|
+
return invalid("receipt.layoutIdentity");
|
|
80
|
+
}
|
|
81
|
+
if (candidate.derivedInterface === null || typeof candidate.derivedInterface !== "object") {
|
|
82
|
+
return invalid("receipt.derivedInterface");
|
|
83
|
+
}
|
|
84
|
+
const derivedInterface = candidate.derivedInterface;
|
|
85
|
+
if (derivedInterface.layoutIdentity !== candidate.layoutIdentity) {
|
|
86
|
+
return invalid("receipt.derivedInterface.layoutIdentity");
|
|
87
|
+
}
|
|
88
|
+
for (const field of [
|
|
89
|
+
"sourceClosure",
|
|
90
|
+
"profile",
|
|
91
|
+
"compilerVersion",
|
|
92
|
+
"inputDigest",
|
|
93
|
+
"outputDigest"
|
|
94
|
+
]) {
|
|
95
|
+
const fieldValue = candidate[field];
|
|
96
|
+
if (field === "sourceClosure" && !Array.isArray(fieldValue) || field !== "sourceClosure" && typeof fieldValue !== "string") {
|
|
97
|
+
return invalid(`receipt.${field}`);
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
const sourceClosure = candidate.sourceClosure;
|
|
101
|
+
if (Array.isArray(sourceClosure) && sourceClosure.some((path) => typeof path !== "string")) {
|
|
102
|
+
return invalid("receipt.sourceClosure");
|
|
103
|
+
}
|
|
104
|
+
if (expected.layoutIdentity !== void 0 && candidate.layoutIdentity !== expected.layoutIdentity) {
|
|
105
|
+
return invalid("receipt.layoutIdentity");
|
|
106
|
+
}
|
|
107
|
+
if (expected.artifactDigest !== void 0 && candidate.outputDigest !== expected.artifactDigest) {
|
|
108
|
+
return invalid("receipt.outputDigest");
|
|
109
|
+
}
|
|
110
|
+
if (expected.inputDigest !== void 0 && candidate.inputDigest !== expected.inputDigest) {
|
|
111
|
+
return invalid("receipt.inputDigest");
|
|
112
|
+
}
|
|
113
|
+
if (candidate.publicationGeneration !== void 0 && (typeof candidate.publicationGeneration !== "number" || !Number.isSafeInteger(candidate.publicationGeneration) || candidate.publicationGeneration < 1)) {
|
|
114
|
+
return invalid("receipt.publicationGeneration");
|
|
115
|
+
}
|
|
116
|
+
return ok({
|
|
117
|
+
schemaVersion: "material-cook/2",
|
|
118
|
+
sourceClosure: candidate.sourceClosure,
|
|
119
|
+
profile: candidate.profile,
|
|
120
|
+
compilerVersion: candidate.compilerVersion,
|
|
121
|
+
inputDigest: candidate.inputDigest,
|
|
122
|
+
outputDigest: candidate.outputDigest,
|
|
123
|
+
layoutIdentity: candidate.layoutIdentity,
|
|
124
|
+
derivedInterface: { layoutIdentity: derivedInterface.layoutIdentity },
|
|
125
|
+
...candidate.publicationGeneration === void 0 ? {} : { publicationGeneration: candidate.publicationGeneration }
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
function validateCookedMaterialRecord(value) {
|
|
129
|
+
if (value === null || typeof value !== "object") return invalid("record");
|
|
130
|
+
const candidate = value;
|
|
131
|
+
if (candidate.schemaVersion !== "material-cook/2") return invalid("schemaVersion");
|
|
132
|
+
if (typeof candidate.guid !== "string" || !candidate.guid) return invalid("guid");
|
|
133
|
+
if (candidate.materialGuid !== void 0 && typeof candidate.materialGuid !== "string")
|
|
134
|
+
return invalid("materialGuid");
|
|
135
|
+
if (candidate.publicationGeneration !== void 0 && (typeof candidate.publicationGeneration !== "number" || !Number.isSafeInteger(candidate.publicationGeneration) || candidate.publicationGeneration < 1))
|
|
136
|
+
return invalid("publicationGeneration");
|
|
137
|
+
if (candidate.specializationKey !== void 0 && typeof candidate.specializationKey !== "string")
|
|
138
|
+
return invalid("specializationKey");
|
|
139
|
+
if (candidate.artifactDigest !== void 0 && typeof candidate.artifactDigest !== "string")
|
|
140
|
+
return invalid("artifactDigest");
|
|
141
|
+
if (candidate.sourceClosure !== void 0 && (!Array.isArray(candidate.sourceClosure) || candidate.sourceClosure.some((path) => typeof path !== "string")))
|
|
142
|
+
return invalid("sourceClosure");
|
|
143
|
+
if (candidate.parameterContract !== void 0) {
|
|
144
|
+
if (candidate.parameterContract === null || typeof candidate.parameterContract !== "object")
|
|
145
|
+
return invalid("parameterContract");
|
|
146
|
+
const parameterContract = candidate.parameterContract;
|
|
147
|
+
if (!Array.isArray(parameterContract.parameters))
|
|
148
|
+
return invalid("parameterContract.parameters");
|
|
149
|
+
if (parameterContract.values === null || typeof parameterContract.values !== "object" || Array.isArray(parameterContract.values))
|
|
150
|
+
return invalid("parameterContract.values");
|
|
151
|
+
}
|
|
152
|
+
if (candidate.resolved === null || typeof candidate.resolved !== "object")
|
|
153
|
+
return invalid("resolved");
|
|
154
|
+
if (candidate.refs === null || typeof candidate.refs !== "object") return invalid("refs");
|
|
155
|
+
if (candidate.artifact === null || typeof candidate.artifact !== "object")
|
|
156
|
+
return invalid("artifact");
|
|
157
|
+
if (candidate.receipt === null || typeof candidate.receipt !== "object")
|
|
158
|
+
return invalid("receipt");
|
|
159
|
+
const artifact = candidate.artifact;
|
|
160
|
+
if (typeof artifact.digest !== "string" || !Array.isArray(artifact.bytes) && !ArrayBuffer.isView(artifact.bytes)) {
|
|
161
|
+
return invalid("artifact");
|
|
162
|
+
}
|
|
163
|
+
const receiptResult = validateMaterialCookReceipt(candidate.receipt, {
|
|
164
|
+
artifactDigest: artifact.digest
|
|
165
|
+
});
|
|
166
|
+
if (!receiptResult.ok) return receiptResult;
|
|
167
|
+
if (candidate.artifactDigest !== void 0 && candidate.artifactDigest !== artifact.digest)
|
|
168
|
+
return invalid("artifactDigest");
|
|
169
|
+
if (candidate.publicationGeneration !== void 0 && receiptResult.value.publicationGeneration !== candidate.publicationGeneration)
|
|
170
|
+
return invalid("receipt.publicationGeneration");
|
|
171
|
+
const normalized = {
|
|
172
|
+
...candidate,
|
|
173
|
+
artifact: {
|
|
174
|
+
...artifact,
|
|
175
|
+
bytes: ArrayBuffer.isView(artifact.bytes) ? Uint8Array.from(artifact.bytes) : Uint8Array.from(artifact.bytes)
|
|
176
|
+
},
|
|
177
|
+
receipt: receiptResult.value
|
|
178
|
+
};
|
|
179
|
+
return ok(normalized);
|
|
180
|
+
}
|
|
181
|
+
function projectCookedMaterialRecord(record) {
|
|
182
|
+
return {
|
|
183
|
+
resolved: record.resolved,
|
|
184
|
+
refs: record.refs,
|
|
185
|
+
artifact: record.artifact,
|
|
186
|
+
receipt: record.receipt,
|
|
187
|
+
schemaVersion: record.schemaVersion
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
export { collectMaterialCookRefs, createMaterialArtifactDigest, projectCookedMaterialRecord, serializeCookedMaterialRecord, serializeMaterialCookReceipt, validateCookedMaterialRecord };
|
|
192
|
+
//# sourceMappingURL=material-cook.mjs.map
|
|
193
|
+
//# sourceMappingURL=material-cook.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/evidence/material-cook.ts"],"names":[],"mappings":"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","file":"material-cook.mjs","sourcesContent":["import type {\n MaterialAsset,\n MaterialParameter,\n MaterialPass,\n MaterialTextureReference,\n MaterialTextureValue,\n MaterialValue,\n Result,\n} from '@forgeax/engine-types';\nimport { err, MATERIAL_TEXTURE_SLOTS, ok } from '@forgeax/engine-types';\nimport { sha256 } from '@noble/hashes/sha2.js';\nimport { bytesToHex } from '@noble/hashes/utils.js';\n\nexport interface MaterialCookRefs {\n readonly parent: readonly string[];\n readonly textures: readonly string[];\n readonly samplers: readonly string[];\n readonly modules: readonly string[];\n}\n\nexport interface MaterialCookArtifact {\n readonly mediaType: string;\n readonly path: string;\n readonly digest: string;\n readonly bytes: Uint8Array;\n}\n\nexport interface MaterialCookReceipt {\n readonly schemaVersion: 'material-cook/2';\n readonly sourceClosure: readonly string[];\n readonly profile: string;\n readonly compilerVersion: string;\n readonly inputDigest: string;\n readonly outputDigest: string;\n readonly layoutIdentity: string;\n readonly derivedInterface: { readonly layoutIdentity: string };\n readonly publicationGeneration?: number;\n}\n\nexport interface MaterialParameterContract {\n readonly parameters: readonly MaterialParameter[];\n readonly values: Readonly<Record<string, MaterialValue | null>>;\n}\n\nexport interface CookedMaterialRecord {\n readonly schemaVersion: 'material-cook/2';\n readonly guid: string;\n readonly authored?: MaterialAsset;\n readonly materialGuid?: string;\n readonly publicationGeneration?: number;\n readonly specializationKey?: string;\n readonly artifactDigest?: string;\n readonly sourceClosure?: readonly string[];\n readonly parameterContract?: MaterialParameterContract;\n readonly resolved: {\n readonly passes: readonly MaterialPass[];\n readonly parameters: readonly MaterialParameter[];\n readonly values: Readonly<Record<string, MaterialValue | null>>;\n };\n readonly refs: MaterialCookRefs;\n readonly artifact: MaterialCookArtifact;\n readonly receipt: MaterialCookReceipt;\n}\n\nexport interface MaterialCookRecordError {\n readonly code: 'material-cook-record-invalid';\n readonly expected: string;\n readonly hint: string;\n readonly detail: { readonly field: string };\n}\n\nexport interface MaterialCookIdentityExpectation {\n readonly layoutIdentity?: string;\n readonly artifactDigest?: string;\n readonly inputDigest?: string;\n}\n\nfunction unique(values: readonly string[]): readonly string[] {\n return [...new Set(values)].sort();\n}\n\nfunction guidText(value: string | Uint8Array): string {\n return typeof value === 'string'\n ? value\n : Array.from(value, (byte) => byte.toString(16).padStart(2, '0')).join('');\n}\n\nfunction cookGuidText(value: MaterialTextureReference): string | undefined {\n return typeof value === 'number' ? undefined : guidText(value);\n}\n\nfunction textureValues(\n values: Readonly<Record<string, MaterialValue | null>> | undefined,\n textureFields: ReadonlySet<string>,\n): readonly MaterialTextureValue[] {\n return Object.entries(values ?? {}).flatMap(([name, value]) => {\n if (value === null) return [];\n if (typeof value === 'string') {\n return textureFields.has(name) ? [{ texture: value }] : [];\n }\n return value !== null &&\n typeof value === 'object' &&\n !Array.isArray(value) &&\n 'texture' in value\n ? [value as MaterialTextureValue]\n : [];\n });\n}\n\nexport function collectMaterialCookRefs(material: Partial<MaterialAsset>): MaterialCookRefs {\n const textureFields =\n material.parameters === undefined\n ? new Set<string>(MATERIAL_TEXTURE_SLOTS)\n : new Set(\n material.parameters\n .filter((parameter) => parameter.type === 'texture')\n .map((parameter) => parameter.name),\n );\n const textures = textureValues(material.values, textureFields);\n return {\n parent: material.parent ? [guidText(material.parent)] : [],\n textures: unique(\n textures.flatMap((value) => {\n const guid = cookGuidText(value.texture);\n return guid === undefined ? [] : [guid];\n }),\n ),\n samplers: unique(\n textures.flatMap((value) => {\n if (value.sampler === undefined) return [];\n const guid = cookGuidText(value.sampler);\n return guid === undefined ? [] : [guid];\n }),\n ),\n modules: unique((material.passes ?? []).map((pass) => pass.program.module)),\n };\n}\n\nexport function createMaterialArtifactDigest(bytes: Uint8Array): string {\n return `sha256:${bytesToHex(sha256(bytes))}`;\n}\n\nfunction jsonValue(value: unknown): unknown {\n if (value instanceof Uint8Array) return [...value];\n if (Array.isArray(value)) return value.map(jsonValue);\n if (value !== null && typeof value === 'object') {\n return Object.fromEntries(\n Object.entries(value)\n .filter(([, entry]) => entry !== undefined)\n .sort(([a], [b]) => a.localeCompare(b))\n .map(([key, entry]) => [key, jsonValue(entry)]),\n );\n }\n return value;\n}\n\nexport function serializeCookedMaterialRecord(record: CookedMaterialRecord): string {\n return JSON.stringify(jsonValue(record));\n}\n\nexport function serializeMaterialCookReceipt(receipt: MaterialCookReceipt): string {\n return JSON.stringify(jsonValue({ ...receipt, sourceClosure: unique(receipt.sourceClosure) }));\n}\n\nfunction invalid(field: string): Result<never, MaterialCookRecordError> {\n return err({\n code: 'material-cook-record-invalid',\n expected: 'a complete material-cook/2 record with derived layout identity',\n hint: 're-cook the material and publish its record, artifact, references, and receipt together',\n detail: { field },\n });\n}\n\nexport function validateMaterialCookReceipt(\n value: unknown,\n expected: MaterialCookIdentityExpectation = {},\n): Result<MaterialCookReceipt, MaterialCookRecordError> {\n if (value === null || typeof value !== 'object') return invalid('receipt');\n const candidate = value as Record<string, unknown>;\n if (candidate.schemaVersion !== 'material-cook/2') return invalid('receipt.schemaVersion');\n if (typeof candidate.layoutIdentity !== 'string' || candidate.layoutIdentity.length === 0) {\n return invalid('receipt.layoutIdentity');\n }\n if (candidate.derivedInterface === null || typeof candidate.derivedInterface !== 'object') {\n return invalid('receipt.derivedInterface');\n }\n const derivedInterface = candidate.derivedInterface as Record<string, unknown>;\n if (derivedInterface.layoutIdentity !== candidate.layoutIdentity) {\n return invalid('receipt.derivedInterface.layoutIdentity');\n }\n for (const field of [\n 'sourceClosure',\n 'profile',\n 'compilerVersion',\n 'inputDigest',\n 'outputDigest',\n ]) {\n const fieldValue = candidate[field];\n if (\n (field === 'sourceClosure' && !Array.isArray(fieldValue)) ||\n (field !== 'sourceClosure' && typeof fieldValue !== 'string')\n ) {\n return invalid(`receipt.${field}`);\n }\n }\n const sourceClosure = candidate.sourceClosure;\n if (Array.isArray(sourceClosure) && sourceClosure.some((path) => typeof path !== 'string')) {\n return invalid('receipt.sourceClosure');\n }\n if (\n expected.layoutIdentity !== undefined &&\n candidate.layoutIdentity !== expected.layoutIdentity\n ) {\n return invalid('receipt.layoutIdentity');\n }\n if (expected.artifactDigest !== undefined && candidate.outputDigest !== expected.artifactDigest) {\n return invalid('receipt.outputDigest');\n }\n if (expected.inputDigest !== undefined && candidate.inputDigest !== expected.inputDigest) {\n return invalid('receipt.inputDigest');\n }\n if (\n candidate.publicationGeneration !== undefined &&\n (typeof candidate.publicationGeneration !== 'number' ||\n !Number.isSafeInteger(candidate.publicationGeneration) ||\n candidate.publicationGeneration < 1)\n ) {\n return invalid('receipt.publicationGeneration');\n }\n return ok({\n schemaVersion: 'material-cook/2',\n sourceClosure: candidate.sourceClosure as string[],\n profile: candidate.profile as string,\n compilerVersion: candidate.compilerVersion as string,\n inputDigest: candidate.inputDigest as string,\n outputDigest: candidate.outputDigest as string,\n layoutIdentity: candidate.layoutIdentity,\n derivedInterface: { layoutIdentity: derivedInterface.layoutIdentity as string },\n ...(candidate.publicationGeneration === undefined\n ? {}\n : { publicationGeneration: candidate.publicationGeneration as number }),\n });\n}\n\nexport function validateCookedMaterialRecord(\n value: unknown,\n): Result<CookedMaterialRecord, MaterialCookRecordError> {\n if (value === null || typeof value !== 'object') return invalid('record');\n const candidate = value as Record<string, unknown>;\n if (candidate.schemaVersion !== 'material-cook/2') return invalid('schemaVersion');\n if (typeof candidate.guid !== 'string' || !candidate.guid) return invalid('guid');\n if (candidate.materialGuid !== undefined && typeof candidate.materialGuid !== 'string')\n return invalid('materialGuid');\n if (\n candidate.publicationGeneration !== undefined &&\n (typeof candidate.publicationGeneration !== 'number' ||\n !Number.isSafeInteger(candidate.publicationGeneration) ||\n candidate.publicationGeneration < 1)\n )\n return invalid('publicationGeneration');\n if (candidate.specializationKey !== undefined && typeof candidate.specializationKey !== 'string')\n return invalid('specializationKey');\n if (candidate.artifactDigest !== undefined && typeof candidate.artifactDigest !== 'string')\n return invalid('artifactDigest');\n if (\n candidate.sourceClosure !== undefined &&\n (!Array.isArray(candidate.sourceClosure) ||\n candidate.sourceClosure.some((path) => typeof path !== 'string'))\n )\n return invalid('sourceClosure');\n if (candidate.parameterContract !== undefined) {\n if (candidate.parameterContract === null || typeof candidate.parameterContract !== 'object')\n return invalid('parameterContract');\n const parameterContract = candidate.parameterContract as Record<string, unknown>;\n if (!Array.isArray(parameterContract.parameters))\n return invalid('parameterContract.parameters');\n if (\n parameterContract.values === null ||\n typeof parameterContract.values !== 'object' ||\n Array.isArray(parameterContract.values)\n )\n return invalid('parameterContract.values');\n }\n if (candidate.resolved === null || typeof candidate.resolved !== 'object')\n return invalid('resolved');\n if (candidate.refs === null || typeof candidate.refs !== 'object') return invalid('refs');\n if (candidate.artifact === null || typeof candidate.artifact !== 'object')\n return invalid('artifact');\n if (candidate.receipt === null || typeof candidate.receipt !== 'object')\n return invalid('receipt');\n const artifact = candidate.artifact as Record<string, unknown>;\n if (\n typeof artifact.digest !== 'string' ||\n (!Array.isArray(artifact.bytes) && !ArrayBuffer.isView(artifact.bytes))\n ) {\n return invalid('artifact');\n }\n const receiptResult = validateMaterialCookReceipt(candidate.receipt, {\n artifactDigest: artifact.digest as string,\n });\n if (!receiptResult.ok) return receiptResult;\n if (candidate.artifactDigest !== undefined && candidate.artifactDigest !== artifact.digest)\n return invalid('artifactDigest');\n if (\n candidate.publicationGeneration !== undefined &&\n receiptResult.value.publicationGeneration !== candidate.publicationGeneration\n )\n return invalid('receipt.publicationGeneration');\n const normalized = {\n ...candidate,\n artifact: {\n ...artifact,\n bytes: ArrayBuffer.isView(artifact.bytes)\n ? Uint8Array.from(artifact.bytes as Uint8Array)\n : Uint8Array.from(artifact.bytes as number[]),\n },\n receipt: receiptResult.value,\n } as CookedMaterialRecord;\n return ok(normalized);\n}\n\nexport function projectCookedMaterialRecord(\n record: CookedMaterialRecord,\n): Omit<CookedMaterialRecord, 'guid' | 'authored'> {\n return {\n resolved: record.resolved,\n refs: record.refs,\n artifact: record.artifact,\n receipt: record.receipt,\n schemaVersion: record.schemaVersion,\n };\n}\n"]}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
// src/mesh-bin-contract.ts
|
|
2
|
+
var MESH_BIN_VERSION = 4;
|
|
3
|
+
var MESH_BIN_PROJECTION_VERSION = 1;
|
|
4
|
+
var MESH_BIN_HEADER_V4_BYTES = 80;
|
|
5
|
+
var MESH_BIN_DIGEST_BYTES = 32;
|
|
6
|
+
function failure(code, sourceKey, expected, actual) {
|
|
7
|
+
return {
|
|
8
|
+
ok: false,
|
|
9
|
+
error: {
|
|
10
|
+
code,
|
|
11
|
+
subject: "mesh-bin",
|
|
12
|
+
sourceKey,
|
|
13
|
+
expected,
|
|
14
|
+
actual,
|
|
15
|
+
recovery: "re-cook the source with its Meta sidecar through the build-time importer"
|
|
16
|
+
}
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
function digestBytes(digest) {
|
|
20
|
+
const bytes = new Uint8Array(MESH_BIN_DIGEST_BYTES);
|
|
21
|
+
bytes.set(new TextEncoder().encode(digest).subarray(0, MESH_BIN_DIGEST_BYTES));
|
|
22
|
+
return bytes;
|
|
23
|
+
}
|
|
24
|
+
function readDigest(bytes) {
|
|
25
|
+
return new TextDecoder().decode(bytes).replace(/\0+$/u, "");
|
|
26
|
+
}
|
|
27
|
+
function writeMeshBinHeader(header, out) {
|
|
28
|
+
if (out.byteLength < MESH_BIN_HEADER_V4_BYTES) {
|
|
29
|
+
throw new RangeError("mesh-bin v4 header output is truncated");
|
|
30
|
+
}
|
|
31
|
+
const view = new DataView(out.buffer, out.byteOffset, out.byteLength);
|
|
32
|
+
view.setUint32(0, header.version, true);
|
|
33
|
+
view.setUint32(4, header.projectionVersion, true);
|
|
34
|
+
view.setUint32(8, header.mask, true);
|
|
35
|
+
view.setUint32(12, header.stride, true);
|
|
36
|
+
view.setUint32(16, header.vertexCount, true);
|
|
37
|
+
view.setUint32(20, header.vertexBytes, true);
|
|
38
|
+
view.setUint32(24, header.indexCount, true);
|
|
39
|
+
view.setUint32(28, header.indexWidth, true);
|
|
40
|
+
view.setUint32(32, header.indexBytes, true);
|
|
41
|
+
view.setUint32(36, header.jsonBytes, true);
|
|
42
|
+
out.set(digestBytes(header.digest), 48);
|
|
43
|
+
}
|
|
44
|
+
function decodeMeshBinHeader(bytes, sourceKey = "<unknown mesh source>") {
|
|
45
|
+
if (bytes.byteLength < MESH_BIN_HEADER_V4_BYTES) {
|
|
46
|
+
return failure(
|
|
47
|
+
"mesh-bin-header-truncated",
|
|
48
|
+
sourceKey,
|
|
49
|
+
`mesh-bin v4 header (${MESH_BIN_HEADER_V4_BYTES} bytes)`,
|
|
50
|
+
`${bytes.byteLength} bytes`
|
|
51
|
+
);
|
|
52
|
+
}
|
|
53
|
+
const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);
|
|
54
|
+
const version = view.getUint32(0, true);
|
|
55
|
+
if (version !== MESH_BIN_VERSION) {
|
|
56
|
+
return failure("mesh-bin-version-unsupported", sourceKey, "mesh-bin v4", `version ${version}`);
|
|
57
|
+
}
|
|
58
|
+
const projectionVersion = view.getUint32(4, true);
|
|
59
|
+
const mask = view.getUint32(8, true);
|
|
60
|
+
const stride = view.getUint32(12, true);
|
|
61
|
+
const vertexCount = view.getUint32(16, true);
|
|
62
|
+
const vertexBytes = view.getUint32(20, true);
|
|
63
|
+
const indexCount = view.getUint32(24, true);
|
|
64
|
+
const indexWidth = view.getUint32(28, true);
|
|
65
|
+
const indexBytes = view.getUint32(32, true);
|
|
66
|
+
const jsonBytes = view.getUint32(36, true);
|
|
67
|
+
const digest = readDigest(bytes.subarray(48, 48 + MESH_BIN_DIGEST_BYTES));
|
|
68
|
+
if (projectionVersion !== MESH_BIN_PROJECTION_VERSION || mask === 0 || stride === 0 || !Number.isSafeInteger(vertexCount) || !Number.isSafeInteger(vertexBytes) || !Number.isSafeInteger(indexCount) || !Number.isSafeInteger(indexBytes) || !Number.isSafeInteger(jsonBytes) || indexCount === 0 && indexWidth !== 0 || indexCount > 0 && indexWidth !== 2 && indexWidth !== 4 || indexBytes !== indexCount * indexWidth || vertexBytes !== vertexCount * stride || digest.length === 0) {
|
|
69
|
+
return failure(
|
|
70
|
+
"mesh-bin-header-invalid",
|
|
71
|
+
sourceKey,
|
|
72
|
+
"safe v4 projection, stride, cardinality, and payload byte lengths",
|
|
73
|
+
`projection=${projectionVersion}; mask=${mask}; stride=${stride}; vertexBytes=${vertexBytes}; indexBytes=${indexBytes}; digest=${digest}`
|
|
74
|
+
);
|
|
75
|
+
}
|
|
76
|
+
return {
|
|
77
|
+
ok: true,
|
|
78
|
+
value: {
|
|
79
|
+
version: 4,
|
|
80
|
+
projectionVersion: 1,
|
|
81
|
+
mask,
|
|
82
|
+
digest,
|
|
83
|
+
stride,
|
|
84
|
+
vertexCount,
|
|
85
|
+
vertexBytes,
|
|
86
|
+
indexCount,
|
|
87
|
+
indexWidth,
|
|
88
|
+
indexBytes,
|
|
89
|
+
jsonBytes
|
|
90
|
+
}
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export { MESH_BIN_DIGEST_BYTES, MESH_BIN_HEADER_V4_BYTES, MESH_BIN_PROJECTION_VERSION, MESH_BIN_VERSION, decodeMeshBinHeader, writeMeshBinHeader };
|
|
95
|
+
//# sourceMappingURL=mesh-bin-contract.mjs.map
|
|
96
|
+
//# sourceMappingURL=mesh-bin-contract.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/mesh-bin-contract.ts"],"names":[],"mappings":"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","file":"mesh-bin-contract.mjs","sourcesContent":["/** Canonical mesh binary wire facts shared by build-time and runtime owners. */\n\nexport const MESH_BIN_VERSION = 4 as const;\nexport const MESH_BIN_PROJECTION_VERSION = 1 as const;\nexport const MESH_BIN_HEADER_V4_BYTES = 80;\nexport const MESH_BIN_DIGEST_BYTES = 32;\n\nexport interface MeshBinHeaderV4 {\n readonly version: 4;\n readonly projectionVersion: 1;\n readonly mask: number;\n readonly digest: string;\n readonly stride: number;\n readonly vertexCount: number;\n readonly vertexBytes: number;\n readonly indexCount: number;\n readonly indexWidth: 0 | 2 | 4;\n readonly indexBytes: number;\n readonly jsonBytes: number;\n}\n\nexport interface MeshBinContractError {\n readonly code:\n | 'mesh-bin-header-truncated'\n | 'mesh-bin-version-unsupported'\n | 'mesh-bin-header-invalid';\n readonly subject: 'mesh-bin';\n readonly sourceKey: string;\n readonly expected: string;\n readonly actual: string;\n readonly recovery: string;\n}\n\nexport type MeshBinHeaderResult =\n | { readonly ok: true; readonly value: MeshBinHeaderV4 }\n | { readonly ok: false; readonly error: MeshBinContractError };\n\nfunction failure(\n code: MeshBinContractError['code'],\n sourceKey: string,\n expected: string,\n actual: string,\n): MeshBinHeaderResult {\n return {\n ok: false,\n error: {\n code,\n subject: 'mesh-bin',\n sourceKey,\n expected,\n actual,\n recovery: 're-cook the source with its Meta sidecar through the build-time importer',\n },\n };\n}\n\nfunction digestBytes(digest: string): Uint8Array {\n const bytes = new Uint8Array(MESH_BIN_DIGEST_BYTES);\n bytes.set(new TextEncoder().encode(digest).subarray(0, MESH_BIN_DIGEST_BYTES));\n return bytes;\n}\n\nfunction readDigest(bytes: Uint8Array): string {\n return new TextDecoder().decode(bytes).replace(/\\0+$/u, '');\n}\n\nexport function writeMeshBinHeader(header: MeshBinHeaderV4, out: Uint8Array): void {\n if (out.byteLength < MESH_BIN_HEADER_V4_BYTES) {\n throw new RangeError('mesh-bin v4 header output is truncated');\n }\n const view = new DataView(out.buffer, out.byteOffset, out.byteLength);\n view.setUint32(0, header.version, true);\n view.setUint32(4, header.projectionVersion, true);\n view.setUint32(8, header.mask, true);\n view.setUint32(12, header.stride, true);\n view.setUint32(16, header.vertexCount, true);\n view.setUint32(20, header.vertexBytes, true);\n view.setUint32(24, header.indexCount, true);\n view.setUint32(28, header.indexWidth, true);\n view.setUint32(32, header.indexBytes, true);\n view.setUint32(36, header.jsonBytes, true);\n out.set(digestBytes(header.digest), 48);\n}\n\nexport function decodeMeshBinHeader(\n bytes: Uint8Array,\n sourceKey = '<unknown mesh source>',\n): MeshBinHeaderResult {\n if (bytes.byteLength < MESH_BIN_HEADER_V4_BYTES) {\n return failure(\n 'mesh-bin-header-truncated',\n sourceKey,\n `mesh-bin v4 header (${MESH_BIN_HEADER_V4_BYTES} bytes)`,\n `${bytes.byteLength} bytes`,\n );\n }\n const view = new DataView(bytes.buffer, bytes.byteOffset, bytes.byteLength);\n const version = view.getUint32(0, true);\n if (version !== MESH_BIN_VERSION) {\n return failure('mesh-bin-version-unsupported', sourceKey, 'mesh-bin v4', `version ${version}`);\n }\n const projectionVersion = view.getUint32(4, true);\n const mask = view.getUint32(8, true);\n const stride = view.getUint32(12, true);\n const vertexCount = view.getUint32(16, true);\n const vertexBytes = view.getUint32(20, true);\n const indexCount = view.getUint32(24, true);\n const indexWidth = view.getUint32(28, true);\n const indexBytes = view.getUint32(32, true);\n const jsonBytes = view.getUint32(36, true);\n const digest = readDigest(bytes.subarray(48, 48 + MESH_BIN_DIGEST_BYTES));\n if (\n projectionVersion !== MESH_BIN_PROJECTION_VERSION ||\n mask === 0 ||\n stride === 0 ||\n !Number.isSafeInteger(vertexCount) ||\n !Number.isSafeInteger(vertexBytes) ||\n !Number.isSafeInteger(indexCount) ||\n !Number.isSafeInteger(indexBytes) ||\n !Number.isSafeInteger(jsonBytes) ||\n (indexCount === 0 && indexWidth !== 0) ||\n (indexCount > 0 && indexWidth !== 2 && indexWidth !== 4) ||\n indexBytes !== indexCount * indexWidth ||\n vertexBytes !== vertexCount * stride ||\n digest.length === 0\n ) {\n return failure(\n 'mesh-bin-header-invalid',\n sourceKey,\n 'safe v4 projection, stride, cardinality, and payload byte lengths',\n `projection=${projectionVersion}; mask=${mask}; stride=${stride}; vertexBytes=${vertexBytes}; indexBytes=${indexBytes}; digest=${digest}`,\n );\n }\n return {\n ok: true,\n value: {\n version: 4,\n projectionVersion: 1,\n mask,\n digest,\n stride,\n vertexCount,\n vertexBytes,\n indexCount,\n indexWidth: indexWidth as 0 | 2 | 4,\n indexBytes,\n jsonBytes,\n },\n };\n}\n"]}
|
|
@@ -16,6 +16,24 @@ export interface NativeCooker<P = unknown, I = unknown> {
|
|
|
16
16
|
readonly discover?: (input: unknown) => I | Promise<I>;
|
|
17
17
|
cook(input: I): NativeCookDraft<P> | Promise<NativeCookDraft<P>>;
|
|
18
18
|
}
|
|
19
|
+
export interface NativeCookTransactionSnapshot<P = unknown> {
|
|
20
|
+
readonly draft: NativeCookDraft<P>;
|
|
21
|
+
readonly generation: number;
|
|
22
|
+
}
|
|
23
|
+
export interface NativeCookTransactionOptions<P = unknown, I = unknown> {
|
|
24
|
+
readonly key: string;
|
|
25
|
+
readonly input: I;
|
|
26
|
+
readonly previous?: NativeCookTransactionSnapshot<P>;
|
|
27
|
+
readonly validate?: (draft: NativeCookDraft<P>) => string | undefined;
|
|
28
|
+
readonly publish?: (draft: NativeCookDraft<P>, generation: number) => void | Promise<void>;
|
|
29
|
+
}
|
|
30
|
+
export interface NativeCookTransactionResult<P = unknown> extends NativeCookTransactionSnapshot<P> {
|
|
31
|
+
readonly status: 'committed' | 'recovered';
|
|
32
|
+
readonly lastKnownGood: NativeCookDraft<P>;
|
|
33
|
+
readonly candidateGeneration: number;
|
|
34
|
+
readonly lastKnownGoodGeneration: number;
|
|
35
|
+
readonly recoveryHint?: string;
|
|
36
|
+
}
|
|
19
37
|
/** Injectable build-time table for native authored Pack producers. */
|
|
20
38
|
export declare class NativeCookerRegistry {
|
|
21
39
|
private readonly cookers;
|
|
@@ -24,5 +42,6 @@ export declare class NativeCookerRegistry {
|
|
|
24
42
|
registeredCookers(): readonly string[];
|
|
25
43
|
runDraft<P = unknown, I = unknown>(key: string, input: I): Promise<Result<NativeCookDraft<P>, AssetStageError>>;
|
|
26
44
|
run<P = unknown, I = unknown>(key: string, input: I): Promise<Result<CookProduct<P>, AssetStageError>>;
|
|
45
|
+
runTransaction<P = unknown, I = unknown>(options: NativeCookTransactionOptions<P, I>): Promise<Result<NativeCookTransactionResult<P>, AssetStageError>>;
|
|
27
46
|
}
|
|
28
47
|
//# sourceMappingURL=native-cooker-registry.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-cooker-registry.d.ts","sourceRoot":"","sources":["../src/native-cooker-registry.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAEV,UAAU,EACV,eAAe,EACf,WAAW,EACX,MAAM,EACP,MAAM,uBAAuB,CAAC;AAG/B,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,SAAS,EAAE,MAAM,CAAC;IAC3B,QAAQ,CAAC,UAAU,CAAC,EAAE,UAAU,CAAC;IACjC,QAAQ,CAAC,KAAK,EAAE,UAAU,CAAC;CAC5B;AAED,MAAM,WAAW,eAAe,CAAC,CAAC,GAAG,OAAO;IAC1C,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC;IACpB,QAAQ,CAAC,IAAI,EAAE,SAAS,MAAM,EAAE,CAAC;IACjC,QAAQ,CAAC,SAAS,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,kBAAkB,CAAC,CAAC,CAAC;IACjE,QAAQ,CAAC,gBAAgB,EAAE,MAAM,CAAC;CACnC;AAED,MAAM,WAAW,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO;IACpD,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,OAAO,KAAK,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IACvD,IAAI,CAAC,KAAK,EAAE,CAAC,GAAG,eAAe,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,CAAC;CAClE;
|
|
1
|
+
{"version":3,"file":"native-cooker-registry.d.ts","sourceRoot":"","sources":["../src/native-cooker-registry.ts"],"names":[],"mappings":"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"}
|
package/dist/native-cooker.mjs
CHANGED
|
@@ -31,6 +31,19 @@ function failure(key, detail) {
|
|
|
31
31
|
recovery: { action: `rerun native cooker ${key}`, retryable: true }
|
|
32
32
|
});
|
|
33
33
|
}
|
|
34
|
+
function transactionFailure(key, reason) {
|
|
35
|
+
return failure(key, { guid: "unknown", producer: reason });
|
|
36
|
+
}
|
|
37
|
+
function recovered(key, previous) {
|
|
38
|
+
return {
|
|
39
|
+
...previous,
|
|
40
|
+
status: "recovered",
|
|
41
|
+
lastKnownGood: previous.draft,
|
|
42
|
+
candidateGeneration: previous.generation + 1,
|
|
43
|
+
lastKnownGoodGeneration: previous.generation,
|
|
44
|
+
recoveryHint: `repair ${key} and submit a new candidate generation`
|
|
45
|
+
};
|
|
46
|
+
}
|
|
34
47
|
var NativeCookerRegistry = class {
|
|
35
48
|
cookers = /* @__PURE__ */ new Map();
|
|
36
49
|
register(cooker) {
|
|
@@ -98,6 +111,30 @@ var NativeCookerRegistry = class {
|
|
|
98
111
|
};
|
|
99
112
|
return ok(product);
|
|
100
113
|
}
|
|
114
|
+
async runTransaction(options) {
|
|
115
|
+
const candidate = await this.runDraft(options.key, options.input);
|
|
116
|
+
if (!candidate.ok) {
|
|
117
|
+
return options.previous === void 0 ? candidate : ok(recovered(options.key, options.previous));
|
|
118
|
+
}
|
|
119
|
+
const invalidReason = options.validate?.(candidate.value);
|
|
120
|
+
if (invalidReason !== void 0) {
|
|
121
|
+
return options.previous === void 0 ? transactionFailure(options.key, invalidReason) : ok(recovered(options.key, options.previous));
|
|
122
|
+
}
|
|
123
|
+
const generation = (options.previous?.generation ?? 0) + 1;
|
|
124
|
+
try {
|
|
125
|
+
await options.publish?.(candidate.value, generation);
|
|
126
|
+
} catch {
|
|
127
|
+
return options.previous === void 0 ? transactionFailure(options.key, "atomic producer publication failed") : ok(recovered(options.key, options.previous));
|
|
128
|
+
}
|
|
129
|
+
return ok({
|
|
130
|
+
draft: candidate.value,
|
|
131
|
+
generation,
|
|
132
|
+
status: "committed",
|
|
133
|
+
lastKnownGood: candidate.value,
|
|
134
|
+
candidateGeneration: generation,
|
|
135
|
+
lastKnownGoodGeneration: generation
|
|
136
|
+
});
|
|
137
|
+
}
|
|
101
138
|
};
|
|
102
139
|
|
|
103
140
|
export { NativeCookerRegistry };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/native-cooker-registry.ts"],"names":[],"mappings":"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","file":"native-cooker.mjs","sourcesContent":["import { createHash } from 'node:crypto';\nimport type {\n ArtifactDescriptor,\n AssetCodec,\n AssetStageError,\n CookProduct,\n Result,\n} from '@forgeax/engine-types';\nimport { err, ok } from '@forgeax/engine-types';\n\nexport interface NativeCookArtifact {\n readonly mediaType: string;\n readonly assetCodec?: AssetCodec;\n readonly bytes: Uint8Array;\n}\n\nexport interface NativeCookDraft<P = unknown> {\n readonly guid: string;\n readonly payload: P;\n readonly refs: readonly string[];\n readonly artifacts: Readonly<Record<string, NativeCookArtifact>>;\n readonly inputFingerprint: string;\n}\n\nexport interface NativeCooker<P = unknown, I = unknown> {\n readonly key: string;\n readonly discover?: (input: unknown) => I | Promise<I>;\n cook(input: I): NativeCookDraft<P> | Promise<NativeCookDraft<P>>;\n}\n\nfunction canonicalize(value: unknown): unknown {\n if (value instanceof Uint8Array) return Buffer.from(value).toString('base64');\n if (Array.isArray(value)) return value.map(canonicalize);\n if (typeof value !== 'object' || value === null) return value;\n return Object.fromEntries(\n Object.entries(value)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([key, nested]) => [key, canonicalize(nested)]),\n );\n}\n\nfunction digestDraft<P>(draft: NativeCookDraft<P>): string {\n const bytes = JSON.stringify(\n canonicalize({\n guid: draft.guid,\n payload: draft.payload,\n refs: draft.refs,\n artifacts: draft.artifacts,\n }),\n );\n return `sha256:${createHash('sha256').update(bytes).digest('hex')}`;\n}\n\ntype NativeCookErrorDetail = Extract<\n AssetStageError['detail'],\n { readonly guid: string; readonly producer: string }\n>;\n\nfunction failure(key: string, detail: NativeCookErrorDetail): Result<never, AssetStageError> {\n return err({\n stage: 'native-cook',\n code: 'native-cook-failed',\n expected: 'a registered native cooker to produce a complete draft',\n hint: 'register the cooker or repair its draft, then rerun recovery',\n detail,\n recovery: { action: `rerun native cooker ${key}`, retryable: true },\n });\n}\n\n/** Injectable build-time table for native authored Pack producers. */\nexport class NativeCookerRegistry {\n private readonly cookers = new Map<string, NativeCooker>();\n\n register(cooker: NativeCooker): void {\n if (typeof cooker.key !== 'string' || cooker.key.length === 0) {\n throw new TypeError('NativeCookerRegistry.register: key must be a non-empty string');\n }\n if (typeof cooker.cook !== 'function') {\n throw new TypeError(`NativeCookerRegistry.register: cooker ${cooker.key} must expose cook`);\n }\n this.cookers.set(cooker.key, cooker);\n }\n\n get(key: string): NativeCooker | undefined {\n return this.cookers.get(key);\n }\n\n registeredCookers(): readonly string[] {\n return [...this.cookers.keys()];\n }\n\n async runDraft<P = unknown, I = unknown>(\n key: string,\n input: I,\n ): Promise<Result<NativeCookDraft<P>, AssetStageError>> {\n const cooker = this.cookers.get(key) as NativeCooker<P, I> | undefined;\n if (cooker === undefined) {\n return failure(key, { guid: 'unknown', producer: key });\n }\n let draft: NativeCookDraft<P>;\n try {\n const typedInput = cooker.discover === undefined ? input : await cooker.discover(input);\n draft = await cooker.cook(typedInput);\n } catch (error) {\n return failure(key, { guid: 'unknown', producer: String(error) });\n }\n if (\n draft.guid.length === 0 ||\n draft.inputFingerprint.length === 0 ||\n !Array.isArray(draft.refs) ||\n typeof draft.payload !== 'object' ||\n draft.payload === null ||\n typeof draft.artifacts !== 'object' ||\n draft.artifacts === null ||\n Object.entries(draft.artifacts).some(\n ([, artifact]) =>\n artifact === null ||\n typeof artifact !== 'object' ||\n typeof artifact.mediaType !== 'string' ||\n artifact.mediaType.length === 0 ||\n !(artifact.bytes instanceof Uint8Array),\n )\n ) {\n return failure(key, { guid: draft.guid || 'unknown', producer: key });\n }\n return ok(draft);\n }\n\n async run<P = unknown, I = unknown>(\n key: string,\n input: I,\n ): Promise<Result<CookProduct<P>, AssetStageError>> {\n const draftResult = await this.runDraft<P, I>(key, input);\n if (!draftResult.ok) return draftResult;\n const draft = draftResult.value;\n const digest = digestDraft(draft);\n const artifacts: Record<string, ArtifactDescriptor> = {};\n for (const [path, artifact] of Object.entries(draft.artifacts)) {\n artifacts[path] = {\n path,\n mediaType: artifact.mediaType,\n ...(artifact.assetCodec === undefined ? {} : { assetCodec: artifact.assetCodec }),\n byteLength: artifact.bytes.byteLength,\n };\n }\n const receipt = {\n guid: draft.guid,\n origin: 'authoredPack' as const,\n status: 'succeeded' as const,\n inputFingerprint: draft.inputFingerprint,\n outputDigest: digest,\n };\n const product: CookProduct<P> = {\n guid: draft.guid,\n payload: draft.payload,\n refs: draft.refs,\n artifacts,\n digest,\n receipt,\n };\n return ok(product);\n }\n}\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/native-cooker-registry.ts"],"names":[],"mappings":"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","file":"native-cooker.mjs","sourcesContent":["import { createHash } from 'node:crypto';\nimport type {\n ArtifactDescriptor,\n AssetCodec,\n AssetStageError,\n CookProduct,\n Result,\n} from '@forgeax/engine-types';\nimport { err, ok } from '@forgeax/engine-types';\n\nexport interface NativeCookArtifact {\n readonly mediaType: string;\n readonly assetCodec?: AssetCodec;\n readonly bytes: Uint8Array;\n}\n\nexport interface NativeCookDraft<P = unknown> {\n readonly guid: string;\n readonly payload: P;\n readonly refs: readonly string[];\n readonly artifacts: Readonly<Record<string, NativeCookArtifact>>;\n readonly inputFingerprint: string;\n}\n\nexport interface NativeCooker<P = unknown, I = unknown> {\n readonly key: string;\n readonly discover?: (input: unknown) => I | Promise<I>;\n cook(input: I): NativeCookDraft<P> | Promise<NativeCookDraft<P>>;\n}\n\nexport interface NativeCookTransactionSnapshot<P = unknown> {\n readonly draft: NativeCookDraft<P>;\n readonly generation: number;\n}\n\nexport interface NativeCookTransactionOptions<P = unknown, I = unknown> {\n readonly key: string;\n readonly input: I;\n readonly previous?: NativeCookTransactionSnapshot<P>;\n readonly validate?: (draft: NativeCookDraft<P>) => string | undefined;\n readonly publish?: (draft: NativeCookDraft<P>, generation: number) => void | Promise<void>;\n}\n\nexport interface NativeCookTransactionResult<P = unknown> extends NativeCookTransactionSnapshot<P> {\n readonly status: 'committed' | 'recovered';\n readonly lastKnownGood: NativeCookDraft<P>;\n readonly candidateGeneration: number;\n readonly lastKnownGoodGeneration: number;\n readonly recoveryHint?: string;\n}\n\nfunction canonicalize(value: unknown): unknown {\n if (value instanceof Uint8Array) return Buffer.from(value).toString('base64');\n if (Array.isArray(value)) return value.map(canonicalize);\n if (typeof value !== 'object' || value === null) return value;\n return Object.fromEntries(\n Object.entries(value)\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([key, nested]) => [key, canonicalize(nested)]),\n );\n}\n\nfunction digestDraft<P>(draft: NativeCookDraft<P>): string {\n const bytes = JSON.stringify(\n canonicalize({\n guid: draft.guid,\n payload: draft.payload,\n refs: draft.refs,\n artifacts: draft.artifacts,\n }),\n );\n return `sha256:${createHash('sha256').update(bytes).digest('hex')}`;\n}\n\ntype NativeCookErrorDetail = Extract<\n AssetStageError['detail'],\n { readonly guid: string; readonly producer: string }\n>;\n\nfunction failure(key: string, detail: NativeCookErrorDetail): Result<never, AssetStageError> {\n return err({\n stage: 'native-cook',\n code: 'native-cook-failed',\n expected: 'a registered native cooker to produce a complete draft',\n hint: 'register the cooker or repair its draft, then rerun recovery',\n detail,\n recovery: { action: `rerun native cooker ${key}`, retryable: true },\n });\n}\n\nfunction transactionFailure(key: string, reason: string): Result<never, AssetStageError> {\n return failure(key, { guid: 'unknown', producer: reason });\n}\n\nfunction recovered<P>(\n key: string,\n previous: NativeCookTransactionSnapshot<P>,\n): NativeCookTransactionResult<P> {\n return {\n ...previous,\n status: 'recovered',\n lastKnownGood: previous.draft,\n candidateGeneration: previous.generation + 1,\n lastKnownGoodGeneration: previous.generation,\n recoveryHint: `repair ${key} and submit a new candidate generation`,\n };\n}\n\n/** Injectable build-time table for native authored Pack producers. */\nexport class NativeCookerRegistry {\n private readonly cookers = new Map<string, NativeCooker>();\n\n register(cooker: NativeCooker): void {\n if (typeof cooker.key !== 'string' || cooker.key.length === 0) {\n throw new TypeError('NativeCookerRegistry.register: key must be a non-empty string');\n }\n if (typeof cooker.cook !== 'function') {\n throw new TypeError(`NativeCookerRegistry.register: cooker ${cooker.key} must expose cook`);\n }\n this.cookers.set(cooker.key, cooker);\n }\n\n get(key: string): NativeCooker | undefined {\n return this.cookers.get(key);\n }\n\n registeredCookers(): readonly string[] {\n return [...this.cookers.keys()];\n }\n\n async runDraft<P = unknown, I = unknown>(\n key: string,\n input: I,\n ): Promise<Result<NativeCookDraft<P>, AssetStageError>> {\n const cooker = this.cookers.get(key) as NativeCooker<P, I> | undefined;\n if (cooker === undefined) {\n return failure(key, { guid: 'unknown', producer: key });\n }\n let draft: NativeCookDraft<P>;\n try {\n const typedInput = cooker.discover === undefined ? input : await cooker.discover(input);\n draft = await cooker.cook(typedInput);\n } catch (error) {\n return failure(key, { guid: 'unknown', producer: String(error) });\n }\n if (\n draft.guid.length === 0 ||\n draft.inputFingerprint.length === 0 ||\n !Array.isArray(draft.refs) ||\n typeof draft.payload !== 'object' ||\n draft.payload === null ||\n typeof draft.artifacts !== 'object' ||\n draft.artifacts === null ||\n Object.entries(draft.artifacts).some(\n ([, artifact]) =>\n artifact === null ||\n typeof artifact !== 'object' ||\n typeof artifact.mediaType !== 'string' ||\n artifact.mediaType.length === 0 ||\n !(artifact.bytes instanceof Uint8Array),\n )\n ) {\n return failure(key, { guid: draft.guid || 'unknown', producer: key });\n }\n return ok(draft);\n }\n\n async run<P = unknown, I = unknown>(\n key: string,\n input: I,\n ): Promise<Result<CookProduct<P>, AssetStageError>> {\n const draftResult = await this.runDraft<P, I>(key, input);\n if (!draftResult.ok) return draftResult;\n const draft = draftResult.value;\n const digest = digestDraft(draft);\n const artifacts: Record<string, ArtifactDescriptor> = {};\n for (const [path, artifact] of Object.entries(draft.artifacts)) {\n artifacts[path] = {\n path,\n mediaType: artifact.mediaType,\n ...(artifact.assetCodec === undefined ? {} : { assetCodec: artifact.assetCodec }),\n byteLength: artifact.bytes.byteLength,\n };\n }\n const receipt = {\n guid: draft.guid,\n origin: 'authoredPack' as const,\n status: 'succeeded' as const,\n inputFingerprint: draft.inputFingerprint,\n outputDigest: digest,\n };\n const product: CookProduct<P> = {\n guid: draft.guid,\n payload: draft.payload,\n refs: draft.refs,\n artifacts,\n digest,\n receipt,\n };\n return ok(product);\n }\n\n async runTransaction<P = unknown, I = unknown>(\n options: NativeCookTransactionOptions<P, I>,\n ): Promise<Result<NativeCookTransactionResult<P>, AssetStageError>> {\n const candidate = await this.runDraft<P, I>(options.key, options.input);\n if (!candidate.ok) {\n return options.previous === undefined\n ? candidate\n : ok(recovered(options.key, options.previous));\n }\n const invalidReason = options.validate?.(candidate.value);\n if (invalidReason !== undefined) {\n return options.previous === undefined\n ? transactionFailure(options.key, invalidReason)\n : ok(recovered(options.key, options.previous));\n }\n const generation = (options.previous?.generation ?? 0) + 1;\n try {\n await options.publish?.(candidate.value, generation);\n } catch {\n return options.previous === undefined\n ? transactionFailure(options.key, 'atomic producer publication failed')\n : ok(recovered(options.key, options.previous));\n }\n return ok({\n draft: candidate.value,\n generation,\n status: 'committed',\n lastKnownGood: candidate.value,\n candidateGeneration: generation,\n lastKnownGoodGeneration: generation,\n });\n }\n}\n"]}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
import type { AssetCodec, AssetRelation, CookReceipt } from '@forgeax/engine-types';
|
|
2
|
+
export interface PackageArtifactBody {
|
|
3
|
+
readonly mediaType: string;
|
|
4
|
+
readonly assetCodec?: AssetCodec;
|
|
5
|
+
readonly bytes: Uint8Array;
|
|
6
|
+
}
|
|
7
|
+
export interface PackageProductAsset {
|
|
8
|
+
readonly guid: string;
|
|
9
|
+
readonly kind: string;
|
|
10
|
+
readonly name?: string;
|
|
11
|
+
readonly payload: Readonly<Record<string, unknown>>;
|
|
12
|
+
readonly refs: readonly string[];
|
|
13
|
+
readonly artifacts: Readonly<Record<string, PackageArtifactBody>>;
|
|
14
|
+
}
|
|
15
|
+
export interface PackageProduct {
|
|
16
|
+
readonly assets: readonly PackageProductAsset[];
|
|
17
|
+
readonly receipts: readonly CookReceipt[];
|
|
18
|
+
readonly diagnostics: readonly unknown[];
|
|
19
|
+
readonly sourceRevision: string;
|
|
20
|
+
readonly sourceKey?: string;
|
|
21
|
+
}
|
|
22
|
+
/** Minimal authored Pack v1/v2 shape consumed by the file transport seam. */
|
|
23
|
+
export interface AuthoredPackAssetInput {
|
|
24
|
+
readonly guid: string;
|
|
25
|
+
readonly kind: string;
|
|
26
|
+
readonly name?: string;
|
|
27
|
+
readonly execution?: 'direct' | 'cooked';
|
|
28
|
+
readonly sourceKey?: string;
|
|
29
|
+
readonly sourceIndex?: number;
|
|
30
|
+
readonly relations?: readonly AssetRelation[];
|
|
31
|
+
readonly payload: Record<string, unknown>;
|
|
32
|
+
readonly refs?: readonly string[];
|
|
33
|
+
readonly artifacts?: Readonly<Record<string, unknown>>;
|
|
34
|
+
}
|
|
35
|
+
export interface AuthoredPackInput {
|
|
36
|
+
readonly schemaVersion?: string;
|
|
37
|
+
readonly assets?: readonly AuthoredPackAssetInput[];
|
|
38
|
+
}
|
|
39
|
+
export interface PackageDocumentArtifact {
|
|
40
|
+
readonly path: string;
|
|
41
|
+
readonly mediaType: string;
|
|
42
|
+
readonly assetCodec?: AssetCodec;
|
|
43
|
+
readonly byteLength: number;
|
|
44
|
+
readonly integrity: {
|
|
45
|
+
readonly algorithm: 'sha256';
|
|
46
|
+
readonly digest: string;
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
export interface PackageDocumentAsset {
|
|
50
|
+
readonly guid: string;
|
|
51
|
+
readonly kind: string;
|
|
52
|
+
readonly name?: string;
|
|
53
|
+
readonly payload: Record<string, unknown>;
|
|
54
|
+
readonly refs: readonly string[];
|
|
55
|
+
readonly artifacts: Readonly<Record<string, PackageDocumentArtifact>>;
|
|
56
|
+
}
|
|
57
|
+
export interface PackageDocument {
|
|
58
|
+
readonly schemaVersion: '2.0.0';
|
|
59
|
+
readonly kind: 'internal-text-package';
|
|
60
|
+
readonly assets: readonly PackageDocumentAsset[];
|
|
61
|
+
}
|
|
62
|
+
/** Upgrade the legacy authored Pack envelope at the Pack schema boundary. */
|
|
63
|
+
export declare function upgradeLegacyAuthoredPack(pack: AuthoredPackInput): AuthoredPackInput;
|
|
64
|
+
export interface PackageFinalizePolicy {
|
|
65
|
+
readonly base: string;
|
|
66
|
+
readonly packagePath: string;
|
|
67
|
+
readonly artifactPath: (guid: string, localKey: string) => string;
|
|
68
|
+
readonly sink?: (path: string, bytes: Uint8Array) => void | Promise<void>;
|
|
69
|
+
}
|
|
70
|
+
export interface FinalizedPackageProduct {
|
|
71
|
+
readonly pack: PackageDocument;
|
|
72
|
+
readonly packageUrl: string;
|
|
73
|
+
readonly digest: string;
|
|
74
|
+
readonly receipts: readonly CookReceipt[];
|
|
75
|
+
readonly diagnostics: readonly unknown[];
|
|
76
|
+
readonly sourceRevision: string;
|
|
77
|
+
readonly sourceKey?: string;
|
|
78
|
+
}
|
|
79
|
+
interface PackageTransportSource {
|
|
80
|
+
readonly assets: readonly PackageProduct['assets'][number][];
|
|
81
|
+
readonly receipts?: readonly CookReceipt[];
|
|
82
|
+
readonly diagnostics?: readonly unknown[];
|
|
83
|
+
readonly sourceKey?: string;
|
|
84
|
+
}
|
|
85
|
+
export interface PackageFinalizerError {
|
|
86
|
+
readonly code: 'duplicate-guid' | 'missing-source-revision' | 'missing-receipt' | 'invalid-artifact';
|
|
87
|
+
readonly expected: string;
|
|
88
|
+
readonly hint: string;
|
|
89
|
+
readonly detail: {
|
|
90
|
+
readonly guid?: string;
|
|
91
|
+
readonly key?: string;
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
export type PackageFinalizeResult = {
|
|
95
|
+
readonly ok: true;
|
|
96
|
+
readonly value: FinalizedPackageProduct;
|
|
97
|
+
} | {
|
|
98
|
+
readonly ok: false;
|
|
99
|
+
readonly error: PackageFinalizerError;
|
|
100
|
+
};
|
|
101
|
+
/** Finalize a complete producer product without owning producer-specific policy. */
|
|
102
|
+
export declare function finalizePackageProduct(product: PackageProduct, policy: PackageFinalizePolicy): Promise<PackageFinalizeResult>;
|
|
103
|
+
/** Finalize a product-shaped source with the deterministic transport revision. */
|
|
104
|
+
export declare function finalizePackageTransportSource(input: PackageTransportSource, policy: PackageFinalizePolicy): Promise<{
|
|
105
|
+
pack: PackageDocument;
|
|
106
|
+
packageUrl: string;
|
|
107
|
+
artifacts: {
|
|
108
|
+
bytes: Uint8Array<ArrayBufferLike>;
|
|
109
|
+
assetCodec?: AssetCodec;
|
|
110
|
+
guid: string;
|
|
111
|
+
localKey: string;
|
|
112
|
+
path: string;
|
|
113
|
+
mediaType: string;
|
|
114
|
+
}[];
|
|
115
|
+
digest: string;
|
|
116
|
+
sourceRevision: string;
|
|
117
|
+
semantic: string;
|
|
118
|
+
}>;
|
|
119
|
+
export declare function packageTransportRevision(value: unknown): string;
|
|
120
|
+
export {};
|
|
121
|
+
//# sourceMappingURL=package-finalizer.d.ts.map
|