@opengeni/artifact-tool 0.2.2
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 +190 -0
- package/README.md +104 -0
- package/dist/bounded-zip.d.ts +28 -0
- package/dist/chunk-4BSENMAE.js +39 -0
- package/dist/chunk-4BSENMAE.js.map +1 -0
- package/dist/chunk-4FRFNNBM.js +276 -0
- package/dist/chunk-4FRFNNBM.js.map +1 -0
- package/dist/chunk-5XIA6KHZ.js +49 -0
- package/dist/chunk-5XIA6KHZ.js.map +1 -0
- package/dist/chunk-66CT2ZRP.js +2229 -0
- package/dist/chunk-66CT2ZRP.js.map +1 -0
- package/dist/chunk-72SYDU4O.js +319 -0
- package/dist/chunk-72SYDU4O.js.map +1 -0
- package/dist/chunk-B2ZJRZDA.js +279 -0
- package/dist/chunk-B2ZJRZDA.js.map +1 -0
- package/dist/chunk-DCTLTNPW.js +2458 -0
- package/dist/chunk-DCTLTNPW.js.map +1 -0
- package/dist/chunk-FGZM4FQ6.js +473 -0
- package/dist/chunk-FGZM4FQ6.js.map +1 -0
- package/dist/chunk-HTJBLBDE.js +591 -0
- package/dist/chunk-HTJBLBDE.js.map +1 -0
- package/dist/chunk-IYFXZQ34.js +5306 -0
- package/dist/chunk-IYFXZQ34.js.map +1 -0
- package/dist/chunk-J6N7IR44.js +172 -0
- package/dist/chunk-J6N7IR44.js.map +1 -0
- package/dist/chunk-N2IHNWBS.js +1002 -0
- package/dist/chunk-N2IHNWBS.js.map +1 -0
- package/dist/chunk-NAG44K67.js +549 -0
- package/dist/chunk-NAG44K67.js.map +1 -0
- package/dist/chunk-O24KTEGP.js +33 -0
- package/dist/chunk-O24KTEGP.js.map +1 -0
- package/dist/chunk-PI2AZVGK.js +2074 -0
- package/dist/chunk-PI2AZVGK.js.map +1 -0
- package/dist/chunk-Q2FFHEY7.js +40 -0
- package/dist/chunk-Q2FFHEY7.js.map +1 -0
- package/dist/chunk-RBGQADXA.js +57 -0
- package/dist/chunk-RBGQADXA.js.map +1 -0
- package/dist/chunk-U5G2BKM2.js +613 -0
- package/dist/chunk-U5G2BKM2.js.map +1 -0
- package/dist/chunk-VOJHMSM3.js +39 -0
- package/dist/chunk-VOJHMSM3.js.map +1 -0
- package/dist/chunk-VUNV25KB.js +16 -0
- package/dist/chunk-VUNV25KB.js.map +1 -0
- package/dist/chunk-XIFUNVBO.js +101 -0
- package/dist/chunk-XIFUNVBO.js.map +1 -0
- package/dist/chunk-ZCJYRJGB.js +489 -0
- package/dist/chunk-ZCJYRJGB.js.map +1 -0
- package/dist/chunk-ZZXPEE4S.js +2260 -0
- package/dist/chunk-ZZXPEE4S.js.map +1 -0
- package/dist/document-docx-api.d.ts +49 -0
- package/dist/document-docx-codec.d.ts +9 -0
- package/dist/document-docx-codec.js +4765 -0
- package/dist/document-docx-codec.js.map +1 -0
- package/dist/document-docx-import.d.ts +261 -0
- package/dist/document-docx-packer.d.ts +18 -0
- package/dist/document-docx-state.d.ts +12 -0
- package/dist/document-render.d.ts +3 -0
- package/dist/document-render.js +492 -0
- package/dist/document-render.js.map +1 -0
- package/dist/document.d.ts +432 -0
- package/dist/document.js +37 -0
- package/dist/document.js.map +1 -0
- package/dist/errors.d.ts +23 -0
- package/dist/file-blob.d.ts +11 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +186 -0
- package/dist/index.js.map +1 -0
- package/dist/inspection.d.ts +12 -0
- package/dist/kernel.d.ts +101 -0
- package/dist/lib-GQLPACFK.js +9746 -0
- package/dist/lib-GQLPACFK.js.map +1 -0
- package/dist/materializer-cli-entry.d.ts +2 -0
- package/dist/materializer-cli-entry.js +29 -0
- package/dist/materializer-cli-entry.js.map +1 -0
- package/dist/materializer-cli.d.ts +24 -0
- package/dist/materializer-cli.js +24 -0
- package/dist/materializer-cli.js.map +1 -0
- package/dist/native-raster.d.ts +10 -0
- package/dist/native.d.ts +129 -0
- package/dist/native.js +31 -0
- package/dist/native.js.map +1 -0
- package/dist/office-import.d.ts +48 -0
- package/dist/office-import.js +154 -0
- package/dist/office-import.js.map +1 -0
- package/dist/presentation-pptx-api.d.ts +60 -0
- package/dist/presentation-pptx-import.d.ts +5 -0
- package/dist/presentation-pptx-state-digest-765CZZED.js +10 -0
- package/dist/presentation-pptx-state-digest-765CZZED.js.map +1 -0
- package/dist/presentation-pptx-state-digest.d.ts +3 -0
- package/dist/presentation-pptx-state.d.ts +4 -0
- package/dist/presentation-pptx-xml.d.ts +29 -0
- package/dist/presentation-pptx.d.ts +5 -0
- package/dist/presentation-pptx.js +2365 -0
- package/dist/presentation-pptx.js.map +1 -0
- package/dist/presentation-render.d.ts +29 -0
- package/dist/presentation-render.js +375 -0
- package/dist/presentation-render.js.map +1 -0
- package/dist/presentation.d.ts +565 -0
- package/dist/presentation.js +71 -0
- package/dist/presentation.js.map +1 -0
- package/dist/production-composite.d.ts +68 -0
- package/dist/production-document.d.ts +2 -0
- package/dist/production-document.js +176 -0
- package/dist/production-document.js.map +1 -0
- package/dist/production-native-codecs.d.ts +94 -0
- package/dist/production-presentation.d.ts +2 -0
- package/dist/production-presentation.js +176 -0
- package/dist/production-presentation.js.map +1 -0
- package/dist/production-sha256.d.ts +2 -0
- package/dist/production-spreadsheet-native.d.ts +12 -0
- package/dist/production-spreadsheet.d.ts +2 -0
- package/dist/production-spreadsheet.js +176 -0
- package/dist/production-spreadsheet.js.map +1 -0
- package/dist/production.d.ts +96 -0
- package/dist/raster-image.d.ts +34 -0
- package/dist/reference.d.ts +17 -0
- package/dist/reference.js +574 -0
- package/dist/reference.js.map +1 -0
- package/dist/runtime-cli-entry.d.ts +2 -0
- package/dist/runtime-cli-entry.js +21 -0
- package/dist/runtime-cli-entry.js.map +1 -0
- package/dist/runtime-cli.d.ts +76 -0
- package/dist/runtime-cli.js +26 -0
- package/dist/runtime-cli.js.map +1 -0
- package/dist/runtime-development.d.ts +73 -0
- package/dist/runtime-development.js +30 -0
- package/dist/runtime-development.js.map +1 -0
- package/dist/runtime-receipt.d.ts +19 -0
- package/dist/runtime.d.ts +355 -0
- package/dist/runtime.js +42 -0
- package/dist/runtime.js.map +1 -0
- package/dist/snapshot.d.ts +26 -0
- package/dist/snapshot.js +61 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/spreadsheet-address.d.ts +18 -0
- package/dist/spreadsheet-file.d.ts +17 -0
- package/dist/spreadsheet-formula.d.ts +67 -0
- package/dist/spreadsheet-image.d.ts +17 -0
- package/dist/spreadsheet-render.d.ts +5 -0
- package/dist/spreadsheet-render.js +752 -0
- package/dist/spreadsheet-render.js.map +1 -0
- package/dist/spreadsheet-types.d.ts +155 -0
- package/dist/spreadsheet-xlsx-api.d.ts +88 -0
- package/dist/spreadsheet-xlsx-codec.d.ts +16 -0
- package/dist/spreadsheet-xlsx-codec.js +16 -0
- package/dist/spreadsheet-xlsx-codec.js.map +1 -0
- package/dist/spreadsheet-xlsx-state.d.ts +16 -0
- package/dist/spreadsheet.d.ts +724 -0
- package/dist/spreadsheet.js +67 -0
- package/dist/spreadsheet.js.map +1 -0
- package/package.json +129 -0
- package/src/bounded-zip.ts +450 -0
- package/src/document-docx-api.ts +64 -0
- package/src/document-docx-codec.ts +2253 -0
- package/src/document-docx-import.ts +3814 -0
- package/src/document-docx-packer.ts +72 -0
- package/src/document-docx-state.ts +59 -0
- package/src/document-render.ts +615 -0
- package/src/document.ts +2800 -0
- package/src/errors.ts +45 -0
- package/src/file-blob.ts +43 -0
- package/src/index.ts +4 -0
- package/src/inspection.ts +50 -0
- package/src/kernel.ts +525 -0
- package/src/materializer-cli-entry.ts +11 -0
- package/src/materializer-cli.ts +743 -0
- package/src/native-raster.ts +141 -0
- package/src/native.ts +390 -0
- package/src/office-import.ts +243 -0
- package/src/presentation-pptx-api.ts +107 -0
- package/src/presentation-pptx-import.ts +2065 -0
- package/src/presentation-pptx-state-digest.ts +46 -0
- package/src/presentation-pptx-state.ts +18 -0
- package/src/presentation-pptx-xml.ts +473 -0
- package/src/presentation-pptx.ts +447 -0
- package/src/presentation-render.ts +532 -0
- package/src/presentation.ts +2599 -0
- package/src/production-composite.ts +805 -0
- package/src/production-document.ts +2 -0
- package/src/production-native-codecs.ts +1141 -0
- package/src/production-presentation.ts +2 -0
- package/src/production-sha256.ts +150 -0
- package/src/production-spreadsheet-native.ts +686 -0
- package/src/production-spreadsheet.ts +2 -0
- package/src/production.ts +1022 -0
- package/src/raster-image.ts +586 -0
- package/src/reference.ts +17 -0
- package/src/runtime-cli-entry.ts +13 -0
- package/src/runtime-cli.ts +670 -0
- package/src/runtime-development.ts +689 -0
- package/src/runtime-receipt.ts +169 -0
- package/src/runtime.ts +1463 -0
- package/src/snapshot.ts +88 -0
- package/src/spreadsheet-address.ts +90 -0
- package/src/spreadsheet-file.ts +71 -0
- package/src/spreadsheet-formula.ts +1212 -0
- package/src/spreadsheet-image.ts +242 -0
- package/src/spreadsheet-render.ts +985 -0
- package/src/spreadsheet-types.ts +171 -0
- package/src/spreadsheet-xlsx-api.ts +123 -0
- package/src/spreadsheet-xlsx-codec.ts +3063 -0
- package/src/spreadsheet-xlsx-state.ts +191 -0
- package/src/spreadsheet.ts +5195 -0
|
@@ -0,0 +1,689 @@
|
|
|
1
|
+
import { createHash } from "node:crypto";
|
|
2
|
+
import { open, realpath, stat } from "node:fs/promises";
|
|
3
|
+
import { isAbsolute, relative, resolve } from "node:path";
|
|
4
|
+
import { pathToFileURL } from "node:url";
|
|
5
|
+
|
|
6
|
+
import {
|
|
7
|
+
canonicalArtifactKernelBuildReceiptBytes,
|
|
8
|
+
validateArtifactKernelBuildReceipt,
|
|
9
|
+
type ArtifactKernelBuildReceipt,
|
|
10
|
+
} from "./runtime-receipt";
|
|
11
|
+
import {
|
|
12
|
+
ARTIFACT_RUNTIME_ENVIRONMENT,
|
|
13
|
+
ArtifactRuntimeError,
|
|
14
|
+
loadArtifactKernelRuntime,
|
|
15
|
+
validateArtifactKernelPackageManifest,
|
|
16
|
+
type ArtifactKernelPackageManifest,
|
|
17
|
+
type ArtifactKernelRuntime,
|
|
18
|
+
type ArtifactRuntimeKernelDependencies,
|
|
19
|
+
type NativeArtifactRuntimeTarget,
|
|
20
|
+
} from "./runtime";
|
|
21
|
+
import {
|
|
22
|
+
doctorVerifiedArtifactRuntime,
|
|
23
|
+
loadVerifiedArtifactKernelRuntime,
|
|
24
|
+
probeVerifiedArtifactSkillFacade,
|
|
25
|
+
resolveCurrentArtifactRuntimeTarget,
|
|
26
|
+
runArtifactRuntimeCli,
|
|
27
|
+
type VerifiedArtifactRuntimeLocation,
|
|
28
|
+
} from "./runtime-cli";
|
|
29
|
+
|
|
30
|
+
export const DEVELOPMENT_ARTIFACT_RUNTIME_ENVIRONMENT = {
|
|
31
|
+
manifest: "OPENGENI_ARTIFACT_DEVELOPMENT_RUNTIME_MANIFEST",
|
|
32
|
+
} as const;
|
|
33
|
+
|
|
34
|
+
const MAX_MANIFEST_BYTES = 256 * 1024;
|
|
35
|
+
const MAX_IDENTITY_BYTES = 64 * 1024;
|
|
36
|
+
const MAX_RECEIPT_BYTES = 256 * 1024;
|
|
37
|
+
const MAX_JAVASCRIPT_BYTES = 32 * 1024 * 1024;
|
|
38
|
+
const MAX_KERNEL_ASSET_BYTES = 512 * 1024 * 1024;
|
|
39
|
+
const MAX_MATERIALIZER_EXECUTABLE_BYTES = 512 * 1024 * 1024;
|
|
40
|
+
|
|
41
|
+
type RuntimeEnvironment = Readonly<Record<string, string | undefined>>;
|
|
42
|
+
type FileDescriptor = Readonly<{
|
|
43
|
+
path: string;
|
|
44
|
+
bytes: number;
|
|
45
|
+
sha256: `sha256:${string}`;
|
|
46
|
+
}>;
|
|
47
|
+
|
|
48
|
+
export type DevelopmentArtifactRuntimeManifest = Readonly<{
|
|
49
|
+
schemaVersion: 1;
|
|
50
|
+
mode: "development-current-host";
|
|
51
|
+
target: NativeArtifactRuntimeTarget;
|
|
52
|
+
sourceFingerprint: `sha256:${string}`;
|
|
53
|
+
artifactTool: Readonly<{
|
|
54
|
+
packageName: "@opengeni/artifact-tool";
|
|
55
|
+
packageVersion: string;
|
|
56
|
+
}>;
|
|
57
|
+
artifactToolIdentity: FileDescriptor;
|
|
58
|
+
receipt: FileDescriptor;
|
|
59
|
+
skillFacadeEntrypoint: FileDescriptor;
|
|
60
|
+
materializerExecutable: FileDescriptor;
|
|
61
|
+
kernelPackageRoot: string;
|
|
62
|
+
kernel: ArtifactKernelPackageManifest;
|
|
63
|
+
}>;
|
|
64
|
+
|
|
65
|
+
export type VerifiedDevelopmentArtifactRuntimeLocation = Readonly<{
|
|
66
|
+
schemaVersion: 1;
|
|
67
|
+
mode: "development-current-host";
|
|
68
|
+
target: NativeArtifactRuntimeTarget;
|
|
69
|
+
manifestPath: string;
|
|
70
|
+
receiptPath: string;
|
|
71
|
+
artifactTool: DevelopmentArtifactRuntimeManifest["artifactTool"];
|
|
72
|
+
skillFacadeEntrypoint: string;
|
|
73
|
+
materializerExecutable: string;
|
|
74
|
+
kernel: Readonly<{
|
|
75
|
+
packageName: string;
|
|
76
|
+
packageVersion: string;
|
|
77
|
+
entrypoint: string;
|
|
78
|
+
asset: string;
|
|
79
|
+
supportFiles: readonly string[];
|
|
80
|
+
buildIdentity: string;
|
|
81
|
+
}>;
|
|
82
|
+
}>;
|
|
83
|
+
|
|
84
|
+
export type ConfiguredArtifactRuntimeLocation =
|
|
85
|
+
| VerifiedArtifactRuntimeLocation
|
|
86
|
+
| VerifiedDevelopmentArtifactRuntimeLocation;
|
|
87
|
+
|
|
88
|
+
export type VerifiedConfiguredArtifactKernelRuntime = Readonly<{
|
|
89
|
+
location: ConfiguredArtifactRuntimeLocation;
|
|
90
|
+
runtime: ArtifactKernelRuntime;
|
|
91
|
+
}>;
|
|
92
|
+
|
|
93
|
+
/** True when any production/development runtime authority was explicitly supplied. */
|
|
94
|
+
export function isArtifactRuntimeConfigured(
|
|
95
|
+
environment: RuntimeEnvironment = process.env,
|
|
96
|
+
): boolean {
|
|
97
|
+
return [
|
|
98
|
+
ARTIFACT_RUNTIME_ENVIRONMENT.manifest,
|
|
99
|
+
ARTIFACT_RUNTIME_ENVIRONMENT.toolEntrypoint,
|
|
100
|
+
DEVELOPMENT_ARTIFACT_RUNTIME_ENVIRONMENT.manifest,
|
|
101
|
+
].some((name) => typeof environment[name] === "string");
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
export function validateDevelopmentArtifactRuntimeManifest(
|
|
105
|
+
value: unknown,
|
|
106
|
+
expectedTarget?: NativeArtifactRuntimeTarget,
|
|
107
|
+
): DevelopmentArtifactRuntimeManifest {
|
|
108
|
+
const record = exactRecord(
|
|
109
|
+
value,
|
|
110
|
+
[
|
|
111
|
+
"schemaVersion",
|
|
112
|
+
"mode",
|
|
113
|
+
"target",
|
|
114
|
+
"sourceFingerprint",
|
|
115
|
+
"artifactTool",
|
|
116
|
+
"artifactToolIdentity",
|
|
117
|
+
"receipt",
|
|
118
|
+
"skillFacadeEntrypoint",
|
|
119
|
+
"materializerExecutable",
|
|
120
|
+
"kernelPackageRoot",
|
|
121
|
+
"kernel",
|
|
122
|
+
],
|
|
123
|
+
"development runtime manifest",
|
|
124
|
+
);
|
|
125
|
+
if (record.schemaVersion !== 1 || record.mode !== "development-current-host") {
|
|
126
|
+
invalid("development runtime schema/mode is invalid");
|
|
127
|
+
}
|
|
128
|
+
if (record.target === "wasm-web") invalid("development server runtime must be native");
|
|
129
|
+
const kernel = validateArtifactKernelPackageManifest(record.kernel);
|
|
130
|
+
if (kernel.kind !== "native" || kernel.target !== record.target) {
|
|
131
|
+
invalid("development runtime target differs from its native kernel");
|
|
132
|
+
}
|
|
133
|
+
if (expectedTarget && kernel.target !== expectedTarget) {
|
|
134
|
+
invalid(`development runtime target ${kernel.target} does not match ${expectedTarget}`);
|
|
135
|
+
}
|
|
136
|
+
const artifactTool = exactRecord(
|
|
137
|
+
record.artifactTool,
|
|
138
|
+
["packageName", "packageVersion"],
|
|
139
|
+
"development artifact-tool identity",
|
|
140
|
+
);
|
|
141
|
+
if (
|
|
142
|
+
artifactTool.packageName !== "@opengeni/artifact-tool" ||
|
|
143
|
+
artifactTool.packageVersion !== kernel.artifactToolVersion
|
|
144
|
+
) {
|
|
145
|
+
invalid("development artifact-tool identity differs from its kernel");
|
|
146
|
+
}
|
|
147
|
+
return Object.freeze({
|
|
148
|
+
schemaVersion: 1,
|
|
149
|
+
mode: "development-current-host",
|
|
150
|
+
target: kernel.target as NativeArtifactRuntimeTarget,
|
|
151
|
+
sourceFingerprint: digest(record.sourceFingerprint, "development source fingerprint"),
|
|
152
|
+
artifactTool: Object.freeze({
|
|
153
|
+
packageName: "@opengeni/artifact-tool",
|
|
154
|
+
packageVersion: kernel.artifactToolVersion,
|
|
155
|
+
}),
|
|
156
|
+
artifactToolIdentity: fileDescriptor(
|
|
157
|
+
record.artifactToolIdentity,
|
|
158
|
+
"development artifact-tool identity file",
|
|
159
|
+
),
|
|
160
|
+
receipt: fileDescriptor(record.receipt, "development build receipt"),
|
|
161
|
+
skillFacadeEntrypoint: fileDescriptor(record.skillFacadeEntrypoint, "development skill facade"),
|
|
162
|
+
materializerExecutable: fileDescriptor(
|
|
163
|
+
record.materializerExecutable,
|
|
164
|
+
"development materializer executable",
|
|
165
|
+
),
|
|
166
|
+
kernelPackageRoot: safeRelativePath(record.kernelPackageRoot, "development kernel root"),
|
|
167
|
+
kernel,
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
export function canonicalDevelopmentArtifactRuntimeManifestBytes(value: unknown): Uint8Array {
|
|
172
|
+
const manifest = validateDevelopmentArtifactRuntimeManifest(value);
|
|
173
|
+
return new TextEncoder().encode(`${JSON.stringify(manifest, null, 2)}\n`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
export function canonicalDevelopmentArtifactToolIdentityBytes(
|
|
177
|
+
artifactTool: DevelopmentArtifactRuntimeManifest["artifactTool"],
|
|
178
|
+
): Uint8Array {
|
|
179
|
+
if (
|
|
180
|
+
artifactTool.packageName !== "@opengeni/artifact-tool" ||
|
|
181
|
+
!stableVersion(artifactTool.packageVersion)
|
|
182
|
+
) {
|
|
183
|
+
invalid("development artifact-tool identity is invalid");
|
|
184
|
+
}
|
|
185
|
+
return new TextEncoder().encode(`${JSON.stringify(artifactTool, null, 2)}\n`);
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
export async function locateVerifiedDevelopmentArtifactRuntime(
|
|
189
|
+
options: {
|
|
190
|
+
environment?: RuntimeEnvironment;
|
|
191
|
+
expectedTarget?: NativeArtifactRuntimeTarget;
|
|
192
|
+
} = {},
|
|
193
|
+
): Promise<VerifiedDevelopmentArtifactRuntimeLocation> {
|
|
194
|
+
const environment = options.environment ?? process.env;
|
|
195
|
+
assertDevelopmentEnvironment(environment);
|
|
196
|
+
const expectedTarget = options.expectedTarget ?? currentNativeTarget();
|
|
197
|
+
const manifestInput = requiredPath(
|
|
198
|
+
environment,
|
|
199
|
+
DEVELOPMENT_ARTIFACT_RUNTIME_ENVIRONMENT.manifest,
|
|
200
|
+
);
|
|
201
|
+
const facadeInput = requiredPath(environment, ARTIFACT_RUNTIME_ENVIRONMENT.toolEntrypoint);
|
|
202
|
+
const manifestPath = await canonicalFile(manifestInput, "development runtime manifest");
|
|
203
|
+
const manifestBytes = await readBoundedFile(
|
|
204
|
+
manifestPath,
|
|
205
|
+
MAX_MANIFEST_BYTES,
|
|
206
|
+
"development runtime manifest",
|
|
207
|
+
);
|
|
208
|
+
const manifest = validateDevelopmentArtifactRuntimeManifest(
|
|
209
|
+
parseJson(manifestBytes, "development runtime manifest"),
|
|
210
|
+
expectedTarget,
|
|
211
|
+
);
|
|
212
|
+
if (!sameBytes(manifestBytes, canonicalDevelopmentArtifactRuntimeManifestBytes(manifest))) {
|
|
213
|
+
integrity("Development runtime manifest is not canonical");
|
|
214
|
+
}
|
|
215
|
+
const root = await realpath(resolve(manifestPath, ".."));
|
|
216
|
+
const identityPath = await confinedFile(
|
|
217
|
+
root,
|
|
218
|
+
manifest.artifactToolIdentity.path,
|
|
219
|
+
"artifact-tool identity",
|
|
220
|
+
);
|
|
221
|
+
const receiptPath = await confinedFile(root, manifest.receipt.path, "kernel build receipt");
|
|
222
|
+
const skillFacadeEntrypoint = await confinedFile(
|
|
223
|
+
root,
|
|
224
|
+
manifest.skillFacadeEntrypoint.path,
|
|
225
|
+
"development skill facade",
|
|
226
|
+
);
|
|
227
|
+
const materializerExecutable = await confinedFile(
|
|
228
|
+
root,
|
|
229
|
+
manifest.materializerExecutable.path,
|
|
230
|
+
"development materializer executable",
|
|
231
|
+
);
|
|
232
|
+
const configuredFacade = await canonicalFile(facadeInput, "configured development skill facade");
|
|
233
|
+
if (configuredFacade !== skillFacadeEntrypoint) {
|
|
234
|
+
integrity(
|
|
235
|
+
`${ARTIFACT_RUNTIME_ENVIRONMENT.toolEntrypoint} differs from the development manifest`,
|
|
236
|
+
);
|
|
237
|
+
}
|
|
238
|
+
const kernelRoot = await confinedDirectory(
|
|
239
|
+
root,
|
|
240
|
+
manifest.kernelPackageRoot,
|
|
241
|
+
"kernel package root",
|
|
242
|
+
);
|
|
243
|
+
const kernelEntrypoint = await confinedFile(
|
|
244
|
+
kernelRoot,
|
|
245
|
+
manifest.kernel.entrypoint.path,
|
|
246
|
+
"kernel entrypoint",
|
|
247
|
+
);
|
|
248
|
+
const kernelAsset = await confinedFile(kernelRoot, manifest.kernel.asset.path, "kernel asset");
|
|
249
|
+
const kernelSupportFiles = await Promise.all(
|
|
250
|
+
manifest.kernel.supportFiles.map((file, index) =>
|
|
251
|
+
confinedFile(kernelRoot, file.path, `kernel support file ${index}`),
|
|
252
|
+
),
|
|
253
|
+
);
|
|
254
|
+
|
|
255
|
+
const identityBytes = await readAndVerify(
|
|
256
|
+
identityPath,
|
|
257
|
+
manifest.artifactToolIdentity,
|
|
258
|
+
MAX_IDENTITY_BYTES,
|
|
259
|
+
"artifact-tool identity",
|
|
260
|
+
);
|
|
261
|
+
if (
|
|
262
|
+
!sameBytes(identityBytes, canonicalDevelopmentArtifactToolIdentityBytes(manifest.artifactTool))
|
|
263
|
+
) {
|
|
264
|
+
integrity("Development artifact-tool identity bytes differ from the manifest");
|
|
265
|
+
}
|
|
266
|
+
const receiptBytes = await readAndVerify(
|
|
267
|
+
receiptPath,
|
|
268
|
+
manifest.receipt,
|
|
269
|
+
MAX_RECEIPT_BYTES,
|
|
270
|
+
"kernel build receipt",
|
|
271
|
+
);
|
|
272
|
+
const receipt = validateArtifactKernelBuildReceipt(
|
|
273
|
+
parseJson(receiptBytes, "kernel build receipt"),
|
|
274
|
+
manifest.target,
|
|
275
|
+
);
|
|
276
|
+
if (!sameBytes(receiptBytes, canonicalArtifactKernelBuildReceiptBytes(receipt))) {
|
|
277
|
+
integrity("Development kernel receipt is not canonical");
|
|
278
|
+
}
|
|
279
|
+
assertReceiptMatchesKernel(receipt, manifest.kernel);
|
|
280
|
+
await verifyFile(
|
|
281
|
+
skillFacadeEntrypoint,
|
|
282
|
+
manifest.skillFacadeEntrypoint,
|
|
283
|
+
MAX_JAVASCRIPT_BYTES,
|
|
284
|
+
"development skill facade",
|
|
285
|
+
);
|
|
286
|
+
await verifyFile(
|
|
287
|
+
materializerExecutable,
|
|
288
|
+
manifest.materializerExecutable,
|
|
289
|
+
MAX_MATERIALIZER_EXECUTABLE_BYTES,
|
|
290
|
+
"development materializer executable",
|
|
291
|
+
);
|
|
292
|
+
await verifyFile(
|
|
293
|
+
kernelEntrypoint,
|
|
294
|
+
manifest.kernel.entrypoint,
|
|
295
|
+
MAX_JAVASCRIPT_BYTES,
|
|
296
|
+
"kernel entrypoint",
|
|
297
|
+
);
|
|
298
|
+
await verifyFile(kernelAsset, manifest.kernel.asset, MAX_KERNEL_ASSET_BYTES, "kernel asset");
|
|
299
|
+
for (const [index, path] of kernelSupportFiles.entries()) {
|
|
300
|
+
await verifyFile(
|
|
301
|
+
path,
|
|
302
|
+
manifest.kernel.supportFiles[index]!,
|
|
303
|
+
MAX_JAVASCRIPT_BYTES,
|
|
304
|
+
`kernel support file ${index}`,
|
|
305
|
+
);
|
|
306
|
+
}
|
|
307
|
+
return Object.freeze({
|
|
308
|
+
schemaVersion: 1,
|
|
309
|
+
mode: "development-current-host",
|
|
310
|
+
target: manifest.target,
|
|
311
|
+
manifestPath,
|
|
312
|
+
receiptPath,
|
|
313
|
+
artifactTool: Object.freeze({ ...manifest.artifactTool }),
|
|
314
|
+
skillFacadeEntrypoint,
|
|
315
|
+
materializerExecutable,
|
|
316
|
+
kernel: Object.freeze({
|
|
317
|
+
packageName: manifest.kernel.packageName,
|
|
318
|
+
packageVersion: manifest.kernel.packageVersion,
|
|
319
|
+
entrypoint: kernelEntrypoint,
|
|
320
|
+
asset: kernelAsset,
|
|
321
|
+
supportFiles: Object.freeze(kernelSupportFiles),
|
|
322
|
+
buildIdentity: manifest.kernel.buildIdentity,
|
|
323
|
+
}),
|
|
324
|
+
});
|
|
325
|
+
}
|
|
326
|
+
|
|
327
|
+
export async function doctorVerifiedDevelopmentArtifactRuntime(
|
|
328
|
+
options: {
|
|
329
|
+
environment?: RuntimeEnvironment;
|
|
330
|
+
expectedTarget?: NativeArtifactRuntimeTarget;
|
|
331
|
+
importer?: (specifier: string) => unknown | Promise<unknown>;
|
|
332
|
+
probe?: (module: Record<string, unknown>) => void | Promise<void>;
|
|
333
|
+
} = {},
|
|
334
|
+
): Promise<VerifiedDevelopmentArtifactRuntimeLocation> {
|
|
335
|
+
const location = await locateVerifiedDevelopmentArtifactRuntime(options);
|
|
336
|
+
let imported: unknown;
|
|
337
|
+
try {
|
|
338
|
+
imported = await (options.importer ?? defaultImporter)(
|
|
339
|
+
pathToFileURL(location.skillFacadeEntrypoint).href,
|
|
340
|
+
);
|
|
341
|
+
} catch (cause) {
|
|
342
|
+
throw new ArtifactRuntimeError(
|
|
343
|
+
"ARTIFACT_RUNTIME_UNAVAILABLE",
|
|
344
|
+
"Verified development skill facade could not be evaluated",
|
|
345
|
+
{ cause },
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
if (!isRecord(imported)) incompatible("development skill facade returned an invalid module");
|
|
349
|
+
if (options.probe) await options.probe(imported);
|
|
350
|
+
else probeVerifiedArtifactSkillFacade(imported, location);
|
|
351
|
+
return location;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
export async function loadVerifiedDevelopmentArtifactKernelRuntime(
|
|
355
|
+
options: {
|
|
356
|
+
environment?: RuntimeEnvironment;
|
|
357
|
+
expectedTarget?: NativeArtifactRuntimeTarget;
|
|
358
|
+
} = {},
|
|
359
|
+
): Promise<VerifiedConfiguredArtifactKernelRuntime> {
|
|
360
|
+
const environment = options.environment ?? process.env;
|
|
361
|
+
const expectedTarget = options.expectedTarget ?? currentNativeTarget();
|
|
362
|
+
const before = await locateVerifiedDevelopmentArtifactRuntime({ environment, expectedTarget });
|
|
363
|
+
const manifest = validateDevelopmentArtifactRuntimeManifest(
|
|
364
|
+
parseJson(
|
|
365
|
+
await readBoundedFile(
|
|
366
|
+
before.manifestPath,
|
|
367
|
+
MAX_MANIFEST_BYTES,
|
|
368
|
+
"development runtime manifest",
|
|
369
|
+
),
|
|
370
|
+
"development runtime manifest",
|
|
371
|
+
),
|
|
372
|
+
expectedTarget,
|
|
373
|
+
);
|
|
374
|
+
const kernelRoot = pathToFileURL(
|
|
375
|
+
`${resolve(before.manifestPath, "..", manifest.kernelPackageRoot)}/`,
|
|
376
|
+
);
|
|
377
|
+
const dependencies: ArtifactRuntimeKernelDependencies = {
|
|
378
|
+
target: {
|
|
379
|
+
target: manifest.target,
|
|
380
|
+
kind: "native",
|
|
381
|
+
packageName: manifest.kernel.packageName,
|
|
382
|
+
platform: manifest.target.startsWith("darwin-")
|
|
383
|
+
? "darwin"
|
|
384
|
+
: manifest.target.startsWith("linux-")
|
|
385
|
+
? "linux"
|
|
386
|
+
: "win32",
|
|
387
|
+
arch: manifest.target.includes("arm64") ? "arm64" : "x64",
|
|
388
|
+
...(manifest.target.includes("-gnu")
|
|
389
|
+
? { libc: "gnu" as const }
|
|
390
|
+
: manifest.target.includes("-musl")
|
|
391
|
+
? { libc: "musl" as const }
|
|
392
|
+
: {}),
|
|
393
|
+
},
|
|
394
|
+
kernelEntrypoint: new URL(manifest.kernel.entrypoint.path, kernelRoot),
|
|
395
|
+
manifest: {
|
|
396
|
+
kernel: manifest.kernel,
|
|
397
|
+
},
|
|
398
|
+
};
|
|
399
|
+
const runtime = await loadArtifactKernelRuntime(dependencies);
|
|
400
|
+
const after = await locateVerifiedDevelopmentArtifactRuntime({ environment, expectedTarget });
|
|
401
|
+
if (
|
|
402
|
+
!sameJson(before, after) ||
|
|
403
|
+
runtime.kind !== "native" ||
|
|
404
|
+
runtime.target !== after.target ||
|
|
405
|
+
runtime.buildIdentity !== after.kernel.buildIdentity
|
|
406
|
+
) {
|
|
407
|
+
integrity("Loaded development kernel differs from its verified installation");
|
|
408
|
+
}
|
|
409
|
+
return Object.freeze({ location: after, runtime });
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
export async function doctorConfiguredArtifactRuntime(
|
|
413
|
+
environment: RuntimeEnvironment = process.env,
|
|
414
|
+
): Promise<ConfiguredArtifactRuntimeLocation> {
|
|
415
|
+
return usesDevelopmentRuntime(environment)
|
|
416
|
+
? await doctorVerifiedDevelopmentArtifactRuntime({ environment })
|
|
417
|
+
: await doctorVerifiedArtifactRuntime({ environment });
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
export async function loadConfiguredArtifactKernelRuntime(
|
|
421
|
+
environment: RuntimeEnvironment = process.env,
|
|
422
|
+
): Promise<VerifiedConfiguredArtifactKernelRuntime> {
|
|
423
|
+
return usesDevelopmentRuntime(environment)
|
|
424
|
+
? await loadVerifiedDevelopmentArtifactKernelRuntime({ environment })
|
|
425
|
+
: await loadVerifiedArtifactKernelRuntime({ environment });
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
export async function runConfiguredArtifactRuntimeCli(
|
|
429
|
+
args: readonly string[],
|
|
430
|
+
environment: RuntimeEnvironment = process.env,
|
|
431
|
+
): Promise<string> {
|
|
432
|
+
if (!usesDevelopmentRuntime(environment)) return await runArtifactRuntimeCli(args, environment);
|
|
433
|
+
const [command, format, ...rest] = args;
|
|
434
|
+
if ((command !== "locate" && command !== "doctor") || format !== "--json" || rest.length > 0) {
|
|
435
|
+
invalid("Usage: opengeni-artifact-runtime <locate|doctor> --json");
|
|
436
|
+
}
|
|
437
|
+
const location =
|
|
438
|
+
command === "doctor"
|
|
439
|
+
? await doctorVerifiedDevelopmentArtifactRuntime({ environment })
|
|
440
|
+
: await locateVerifiedDevelopmentArtifactRuntime({ environment });
|
|
441
|
+
return `${JSON.stringify(location)}\n`;
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
function usesDevelopmentRuntime(environment: RuntimeEnvironment): boolean {
|
|
445
|
+
return Boolean(environment[DEVELOPMENT_ARTIFACT_RUNTIME_ENVIRONMENT.manifest]);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
function assertDevelopmentEnvironment(environment: RuntimeEnvironment): void {
|
|
449
|
+
if (environment.NODE_ENV === "production") {
|
|
450
|
+
throw new ArtifactRuntimeError(
|
|
451
|
+
"ARTIFACT_RUNTIME_UNAVAILABLE",
|
|
452
|
+
"Development artifact runtime is forbidden when NODE_ENV=production",
|
|
453
|
+
);
|
|
454
|
+
}
|
|
455
|
+
if (environment[ARTIFACT_RUNTIME_ENVIRONMENT.manifest]) {
|
|
456
|
+
invalid("Production and development artifact runtime manifests cannot both be configured");
|
|
457
|
+
}
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
function currentNativeTarget(): NativeArtifactRuntimeTarget {
|
|
461
|
+
const target = resolveCurrentArtifactRuntimeTarget();
|
|
462
|
+
if (target === "wasm-web") {
|
|
463
|
+
throw new ArtifactRuntimeError(
|
|
464
|
+
"ARTIFACT_RUNTIME_UNSUPPORTED_TARGET",
|
|
465
|
+
"Development server runtime requires a native host",
|
|
466
|
+
);
|
|
467
|
+
}
|
|
468
|
+
return target;
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
function assertReceiptMatchesKernel(
|
|
472
|
+
receipt: ArtifactKernelBuildReceipt,
|
|
473
|
+
kernel: ArtifactKernelPackageManifest,
|
|
474
|
+
): void {
|
|
475
|
+
const runtimeFiles = [kernel.asset, ...kernel.supportFiles].sort((left, right) =>
|
|
476
|
+
left.path < right.path ? -1 : left.path > right.path ? 1 : 0,
|
|
477
|
+
);
|
|
478
|
+
if (
|
|
479
|
+
receipt.buildIdentity !== kernel.buildIdentity ||
|
|
480
|
+
!sameJson(receipt.runtimeFiles, runtimeFiles)
|
|
481
|
+
) {
|
|
482
|
+
integrity("Development build receipt differs from its kernel manifest");
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
async function confinedFile(root: string, path: string, name: string): Promise<string> {
|
|
487
|
+
return await confined(root, path, name, "file");
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
async function confinedDirectory(root: string, path: string, name: string): Promise<string> {
|
|
491
|
+
return await confined(root, path, name, "directory");
|
|
492
|
+
}
|
|
493
|
+
|
|
494
|
+
async function confined(
|
|
495
|
+
root: string,
|
|
496
|
+
path: string,
|
|
497
|
+
name: string,
|
|
498
|
+
kind: "file" | "directory",
|
|
499
|
+
): Promise<string> {
|
|
500
|
+
const candidate = await realpath(resolve(root, path)).catch((cause) => {
|
|
501
|
+
throw new ArtifactRuntimeError("ARTIFACT_RUNTIME_UNAVAILABLE", `${name} is unavailable`, {
|
|
502
|
+
cause,
|
|
503
|
+
});
|
|
504
|
+
});
|
|
505
|
+
const fromRoot = relative(root, candidate);
|
|
506
|
+
if (
|
|
507
|
+
fromRoot === ".." ||
|
|
508
|
+
fromRoot.startsWith(`..${process.platform === "win32" ? "\\" : "/"}`) ||
|
|
509
|
+
isAbsolute(fromRoot)
|
|
510
|
+
) {
|
|
511
|
+
invalid(`${name} resolves outside the development installation`);
|
|
512
|
+
}
|
|
513
|
+
const metadata = await stat(candidate);
|
|
514
|
+
if (
|
|
515
|
+
(kind === "file" && !metadata.isFile()) ||
|
|
516
|
+
(kind === "directory" && !metadata.isDirectory())
|
|
517
|
+
) {
|
|
518
|
+
throw new ArtifactRuntimeError("ARTIFACT_RUNTIME_UNAVAILABLE", `${name} is not a ${kind}`);
|
|
519
|
+
}
|
|
520
|
+
return candidate;
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
async function canonicalFile(input: string, name: string): Promise<string> {
|
|
524
|
+
if (!isAbsolute(input)) invalid(`${name} path must be absolute`);
|
|
525
|
+
try {
|
|
526
|
+
const path = await realpath(input);
|
|
527
|
+
if (!(await stat(path)).isFile()) throw new Error("not a file");
|
|
528
|
+
return path;
|
|
529
|
+
} catch (cause) {
|
|
530
|
+
throw new ArtifactRuntimeError("ARTIFACT_RUNTIME_UNAVAILABLE", `${name} is unavailable`, {
|
|
531
|
+
cause,
|
|
532
|
+
});
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
async function readAndVerify(
|
|
537
|
+
path: string,
|
|
538
|
+
expected: FileDescriptor,
|
|
539
|
+
maximumBytes: number,
|
|
540
|
+
name: string,
|
|
541
|
+
): Promise<Uint8Array> {
|
|
542
|
+
const bytes = await readBoundedFile(path, maximumBytes, name);
|
|
543
|
+
if (bytes.byteLength !== expected.bytes || sha256(bytes) !== expected.sha256) {
|
|
544
|
+
integrity(`${name} differs from the development manifest`);
|
|
545
|
+
}
|
|
546
|
+
return bytes;
|
|
547
|
+
}
|
|
548
|
+
|
|
549
|
+
async function verifyFile(
|
|
550
|
+
path: string,
|
|
551
|
+
expected: FileDescriptor,
|
|
552
|
+
maximumBytes: number,
|
|
553
|
+
name: string,
|
|
554
|
+
): Promise<void> {
|
|
555
|
+
await readAndVerify(path, expected, maximumBytes, name);
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
async function readBoundedFile(
|
|
559
|
+
path: string,
|
|
560
|
+
maximumBytes: number,
|
|
561
|
+
name: string,
|
|
562
|
+
): Promise<Uint8Array> {
|
|
563
|
+
let handle;
|
|
564
|
+
try {
|
|
565
|
+
handle = await open(path, "r");
|
|
566
|
+
const before = await handle.stat();
|
|
567
|
+
if (!before.isFile() || before.size <= 0 || before.size > maximumBytes) {
|
|
568
|
+
integrity(`${name} size is invalid`);
|
|
569
|
+
}
|
|
570
|
+
const bytes = new Uint8Array(await handle.readFile());
|
|
571
|
+
const after = await handle.stat();
|
|
572
|
+
if (after.size !== before.size || bytes.byteLength !== before.size) {
|
|
573
|
+
integrity(`${name} changed while reading`);
|
|
574
|
+
}
|
|
575
|
+
return bytes;
|
|
576
|
+
} catch (cause) {
|
|
577
|
+
if (cause instanceof ArtifactRuntimeError) throw cause;
|
|
578
|
+
throw new ArtifactRuntimeError("ARTIFACT_RUNTIME_UNAVAILABLE", `Could not read ${name}`, {
|
|
579
|
+
cause,
|
|
580
|
+
});
|
|
581
|
+
} finally {
|
|
582
|
+
await handle?.close();
|
|
583
|
+
}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
function fileDescriptor(value: unknown, name: string): FileDescriptor {
|
|
587
|
+
const record = exactRecord(value, ["path", "bytes", "sha256"], name);
|
|
588
|
+
if (!Number.isSafeInteger(record.bytes) || (record.bytes as number) <= 0) {
|
|
589
|
+
invalid(`${name} byte size is invalid`);
|
|
590
|
+
}
|
|
591
|
+
return Object.freeze({
|
|
592
|
+
path: safeRelativePath(record.path, `${name} path`),
|
|
593
|
+
bytes: record.bytes as number,
|
|
594
|
+
sha256: digest(record.sha256, `${name} digest`),
|
|
595
|
+
});
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
function safeRelativePath(value: unknown, name: string): string {
|
|
599
|
+
if (
|
|
600
|
+
typeof value !== "string" ||
|
|
601
|
+
value.length === 0 ||
|
|
602
|
+
value.length > 1024 ||
|
|
603
|
+
value.startsWith("/") ||
|
|
604
|
+
value.startsWith("\\") ||
|
|
605
|
+
value.includes("\\") ||
|
|
606
|
+
value.split("/").some((part) => part === "" || part === "." || part === "..") ||
|
|
607
|
+
/[%?#\0]/u.test(value)
|
|
608
|
+
) {
|
|
609
|
+
invalid(`${name} must be a normalized relative path`);
|
|
610
|
+
}
|
|
611
|
+
return value;
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
function digest(value: unknown, name: string): `sha256:${string}` {
|
|
615
|
+
if (typeof value !== "string" || !/^sha256:[0-9a-f]{64}$/u.test(value)) {
|
|
616
|
+
invalid(`${name} is invalid`);
|
|
617
|
+
}
|
|
618
|
+
return value as `sha256:${string}`;
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
function stableVersion(value: unknown): value is string {
|
|
622
|
+
return (
|
|
623
|
+
typeof value === "string" && /^(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)$/u.test(value)
|
|
624
|
+
);
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
function requiredPath(environment: RuntimeEnvironment, name: string): string {
|
|
628
|
+
const value = environment[name];
|
|
629
|
+
if (!value) {
|
|
630
|
+
throw new ArtifactRuntimeError("ARTIFACT_RUNTIME_UNAVAILABLE", `${name} is required`);
|
|
631
|
+
}
|
|
632
|
+
return value;
|
|
633
|
+
}
|
|
634
|
+
|
|
635
|
+
function parseJson(bytes: Uint8Array, name: string): unknown {
|
|
636
|
+
try {
|
|
637
|
+
return JSON.parse(new TextDecoder("utf-8", { fatal: true }).decode(bytes));
|
|
638
|
+
} catch (cause) {
|
|
639
|
+
throw new ArtifactRuntimeError(
|
|
640
|
+
"ARTIFACT_RUNTIME_MANIFEST_INVALID",
|
|
641
|
+
`${name} is not strict UTF-8 JSON`,
|
|
642
|
+
{ cause },
|
|
643
|
+
);
|
|
644
|
+
}
|
|
645
|
+
}
|
|
646
|
+
|
|
647
|
+
function exactRecord(
|
|
648
|
+
value: unknown,
|
|
649
|
+
keys: readonly string[],
|
|
650
|
+
name: string,
|
|
651
|
+
): Record<string, unknown> {
|
|
652
|
+
if (!isRecord(value)) invalid(`${name} is invalid`);
|
|
653
|
+
if (JSON.stringify(Object.keys(value).sort()) !== JSON.stringify([...keys].sort())) {
|
|
654
|
+
invalid(`${name} has unexpected fields`);
|
|
655
|
+
}
|
|
656
|
+
return value;
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
660
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
function sha256(value: Uint8Array): `sha256:${string}` {
|
|
664
|
+
return `sha256:${createHash("sha256").update(value).digest("hex")}`;
|
|
665
|
+
}
|
|
666
|
+
|
|
667
|
+
function sameJson(left: unknown, right: unknown): boolean {
|
|
668
|
+
return JSON.stringify(left) === JSON.stringify(right);
|
|
669
|
+
}
|
|
670
|
+
|
|
671
|
+
function sameBytes(left: Uint8Array, right: Uint8Array): boolean {
|
|
672
|
+
return left.byteLength === right.byteLength && left.every((byte, index) => byte === right[index]);
|
|
673
|
+
}
|
|
674
|
+
|
|
675
|
+
function defaultImporter(specifier: string): Promise<unknown> {
|
|
676
|
+
return import(/* @vite-ignore */ specifier);
|
|
677
|
+
}
|
|
678
|
+
|
|
679
|
+
function invalid(message: string): never {
|
|
680
|
+
throw new ArtifactRuntimeError("ARTIFACT_RUNTIME_MANIFEST_INVALID", message);
|
|
681
|
+
}
|
|
682
|
+
|
|
683
|
+
function integrity(message: string): never {
|
|
684
|
+
throw new ArtifactRuntimeError("ARTIFACT_RUNTIME_INTEGRITY", message);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
function incompatible(message: string): never {
|
|
688
|
+
throw new ArtifactRuntimeError("ARTIFACT_RUNTIME_INCOMPATIBLE", message);
|
|
689
|
+
}
|