arcane-os 0.5.7 → 0.5.9
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/CHANGELOG.md +25 -0
- package/README.md +39 -27
- package/browser-runtime/ai/browser-speech-artifacts.mjs +23 -1683
- package/browser-runtime/ai/browser-speech-providers.mjs +353 -143
- package/browser-runtime/ai/browser-wasm-llm-provider.mjs +32 -324
- package/browser-runtime/ai/speech-worker-client.mjs +51 -14
- package/browser-runtime/ai/speech-worker-runtime.mjs +45 -1072
- package/browser-runtime/dependencies/strong-type/package.json +22 -23
- package/browser-runtime/event-manager.mjs +27 -41
- package/package.json +2 -3
- package/runtime/arcane/components/chat.html +1 -1
- package/runtime/arcane/components/speech.html +8 -38
- package/runtime/arcane/components/voice-transcription.html +1 -1
- package/runtime/arcane/css/theme.css +1 -1
- package/runtime/arcane/entities/Chat.js +12 -7
- package/runtime/arcane/modules/AI.js +569 -402
- package/runtime/arcane/modules/AIPreferenceTuple.js +1 -1
- package/runtime/arcane/modules/AIProviderRuntime.js +129 -68
- package/runtime/arcane/modules/AIRuntimeState.js +2 -18
- package/runtime/arcane/modules/BrowserTestSuite.js +2 -5
- package/runtime/arcane/modules/CalculatorEngine.js +0 -4
- package/runtime/arcane/modules/ChatRecords.js +169 -1
- package/runtime/arcane/modules/CommunicationHub.js +0 -19
- package/runtime/arcane/modules/ConfiguredAIChatSession.js +28 -25
- package/runtime/arcane/modules/DBOPFSDocumentLibrary.js +4 -5
- package/runtime/arcane/modules/DocumentLexicalSearch.js +1 -1
- package/runtime/arcane/modules/Errors.js +6 -19
- package/runtime/arcane/modules/Mail.js +1 -2
- package/runtime/arcane/modules/MailTransport.mjs +1 -3
- package/runtime/arcane/modules/OllamaModelIdentifier.js +1 -1
- package/runtime/arcane/modules/PersistentAIChatSession.js +5 -5
- package/runtime/arcane/modules/ScreenCapture.js +2 -4
- package/runtime/arcane/modules/SpeechPlayback.js +0 -27
- package/runtime/arcane/modules/WaitForComponent.js +0 -1
- package/src/app-descriptor.mjs +1 -1
- package/src/doctor.mjs +1 -3
- package/src/event-manager.mjs +27 -41
- package/src/import-map.mjs +1 -1
- package/src/index.mjs +2 -9
- package/src/installed-sdk-runtime.mjs +1 -11
- package/src/mail-api.mjs +0 -1
- package/src/native-provider-loader.mjs +0 -9
- package/src/scaffold.mjs +9 -21
- package/src/toolchain.mjs +5 -28
- package/src/workspace-runtime.mjs +0 -4
- package/src/workspace.mjs +2 -23
|
@@ -13,11 +13,7 @@ const MODEL_AUTHORITY_PROTOCOL = "arcane-ai-model-authority/1";
|
|
|
13
13
|
const ARTIFACT_GRAPH_KIND = "browser-speech-authenticated-artifact-graph";
|
|
14
14
|
const ARTIFACT_GRAPH_MODULE_KIND =
|
|
15
15
|
"browser-speech-authenticated-artifact-graph";
|
|
16
|
-
const ARTIFACT_GRAPH_GUARDS = "__arcaneBrowserSpeechArtifactGraphGuardsV1";
|
|
17
16
|
const ARTIFACT_MODULE_ROUTER = "__arcaneBrowserSpeechModuleRouterV1";
|
|
18
|
-
const MUTABLE_PATH_PATTERN = /\/(?:resolve\/)?(?:main|master|latest)(?:\/|$)/iu;
|
|
19
|
-
const ARTIFACT_GRAPH_MUTABLE_SOURCE_PATTERN =
|
|
20
|
-
/\/(?:refs\/heads\/(?:main|master)|resolve\/(?:main|master)|(?:main|master|latest))(?:\/|$)|@(?:latest|next)(?:\/|$)/iu;
|
|
21
17
|
const AUTHORITIES = new WeakSet();
|
|
22
18
|
const AUTHORITY_METADATA = new WeakMap();
|
|
23
19
|
const ARTIFACT_GRAPHS = new WeakSet();
|
|
@@ -30,7 +26,7 @@ const PLATFORM_CREATE_OBJECT_URL = typeof globalThis.URL?.createObjectURL === "f
|
|
|
30
26
|
const PLATFORM_REVOKE_OBJECT_URL = typeof globalThis.URL?.revokeObjectURL === "function"
|
|
31
27
|
? globalThis.URL.revokeObjectURL.bind(globalThis.URL)
|
|
32
28
|
: null;
|
|
33
|
-
const
|
|
29
|
+
const ARTIFACT_ERROR_REASONS = completeValue({
|
|
34
30
|
ARCANE_AI_REQUEST_ABORTED: "browser-speech-artifact-preparation-cancelled",
|
|
35
31
|
ARCANE_AI_STORAGE_BUSY: "browser-speech-artifact-dbopfs-write-lock-unavailable",
|
|
36
32
|
ARCANE_AI_STORAGE_UNAVAILABLE: "browser-speech-artifact-dbopfs-table-unavailable",
|
|
@@ -67,16 +63,8 @@ const ARTIFACT_GRAPH_ONNX_NAMESPACES = new Set([
|
|
|
67
63
|
"kokoro-env-wasm-paths",
|
|
68
64
|
"transformers-env-backends-onnx-wasm",
|
|
69
65
|
]);
|
|
70
|
-
const ARTIFACT_GRAPH_EDGE_POLICIES = new Set([
|
|
71
|
-
"artifact-targets-admitted",
|
|
72
|
-
"inactive-runtime-branch-rejected",
|
|
73
|
-
]);
|
|
74
|
-
const ARTIFACT_GRAPH_IMPORT_MATCHES = new Set([
|
|
75
|
-
"exact-runtime-specifier",
|
|
76
|
-
"materialized-module-url",
|
|
77
|
-
]);
|
|
78
66
|
|
|
79
|
-
function speechError(code, message, cause, reason =
|
|
67
|
+
function speechError(code, message, cause, reason = ARTIFACT_ERROR_REASONS[code]) {
|
|
80
68
|
const error = cause === undefined
|
|
81
69
|
? new Error(message)
|
|
82
70
|
: new Error(message, { cause });
|
|
@@ -155,29 +143,6 @@ function ordinarySourceUrl(value, label) {
|
|
|
155
143
|
return result.href;
|
|
156
144
|
}
|
|
157
145
|
|
|
158
|
-
function immutableUrl(value, label) {
|
|
159
|
-
let result;
|
|
160
|
-
try {
|
|
161
|
-
result = new URL(value, globalThis.location?.href);
|
|
162
|
-
} catch {
|
|
163
|
-
throw new TypeError(`${label} must be an absolute or same-origin URL.`);
|
|
164
|
-
}
|
|
165
|
-
const sameOrigin = globalThis.location?.origin
|
|
166
|
-
&& result.origin === globalThis.location.origin;
|
|
167
|
-
if (
|
|
168
|
-
(result.protocol !== "https:" && !sameOrigin)
|
|
169
|
-
|| result.username
|
|
170
|
-
|| result.password
|
|
171
|
-
|| result.hash
|
|
172
|
-
|| MUTABLE_PATH_PATTERN.test(result.pathname)
|
|
173
|
-
) {
|
|
174
|
-
throw new TypeError(
|
|
175
|
-
`${label} must be immutable HTTPS or a same-origin immutable URL without credentials or fragments.`,
|
|
176
|
-
);
|
|
177
|
-
}
|
|
178
|
-
return result.href;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
146
|
function canonicalArtifactPath(
|
|
182
147
|
value,
|
|
183
148
|
label,
|
|
@@ -302,137 +267,11 @@ function graphRuntimeRequestUrl(value, label) {
|
|
|
302
267
|
return result.href;
|
|
303
268
|
}
|
|
304
269
|
|
|
305
|
-
function graphRedirectFinalOrigin(value, label) {
|
|
306
|
-
const text = artifactGraphText(
|
|
307
|
-
value,
|
|
308
|
-
label,
|
|
309
|
-
"artifact-graph-source-redirect-final-origin-text-required",
|
|
310
|
-
);
|
|
311
|
-
if (text !== value) {
|
|
312
|
-
throw artifactGraphTypeError(
|
|
313
|
-
"artifact-graph-source-redirect-final-origin-whitespace-rejected",
|
|
314
|
-
`${label} must not contain surrounding whitespace.`,
|
|
315
|
-
);
|
|
316
|
-
}
|
|
317
|
-
let result;
|
|
318
|
-
try {
|
|
319
|
-
result = new URL(text);
|
|
320
|
-
} catch (error) {
|
|
321
|
-
throw artifactGraphTypeError(
|
|
322
|
-
"artifact-graph-source-redirect-final-origin-not-absolute",
|
|
323
|
-
`${label} must be an absolute origin.`,
|
|
324
|
-
error,
|
|
325
|
-
);
|
|
326
|
-
}
|
|
327
|
-
if (result.username || result.password) {
|
|
328
|
-
throw artifactGraphTypeError(
|
|
329
|
-
"artifact-graph-source-redirect-final-origin-credentials-rejected",
|
|
330
|
-
`${label} must not contain credentials.`,
|
|
331
|
-
);
|
|
332
|
-
}
|
|
333
|
-
if (result.pathname !== "/") {
|
|
334
|
-
throw artifactGraphTypeError(
|
|
335
|
-
"artifact-graph-source-redirect-final-origin-path-rejected",
|
|
336
|
-
`${label} must not contain a path.`,
|
|
337
|
-
);
|
|
338
|
-
}
|
|
339
|
-
if (result.search) {
|
|
340
|
-
throw artifactGraphTypeError(
|
|
341
|
-
"artifact-graph-source-redirect-final-origin-query-rejected",
|
|
342
|
-
`${label} must not contain a query.`,
|
|
343
|
-
);
|
|
344
|
-
}
|
|
345
|
-
if (result.hash) {
|
|
346
|
-
throw artifactGraphTypeError(
|
|
347
|
-
"artifact-graph-source-redirect-final-origin-fragment-rejected",
|
|
348
|
-
`${label} must not contain a fragment.`,
|
|
349
|
-
);
|
|
350
|
-
}
|
|
351
|
-
return result.origin;
|
|
352
|
-
}
|
|
353
|
-
|
|
354
|
-
function normalizeGraphRedirectFinalOrigins(value, path) {
|
|
355
|
-
if (value === undefined) return completeValue([]);
|
|
356
|
-
if (!Array.isArray(value)) {
|
|
357
|
-
throw artifactGraphTypeError(
|
|
358
|
-
"artifact-graph-source-redirect-final-origins-not-array",
|
|
359
|
-
`Artifact graph file ${path} redirectFinalOrigins must be an array.`,
|
|
360
|
-
);
|
|
361
|
-
}
|
|
362
|
-
if (value.length < 1) {
|
|
363
|
-
throw artifactGraphTypeError(
|
|
364
|
-
"artifact-graph-source-redirect-final-origin-inventory-empty",
|
|
365
|
-
`Artifact graph file ${path} redirectFinalOrigins must contain at least one final origin when supplied.`,
|
|
366
|
-
);
|
|
367
|
-
}
|
|
368
|
-
const origins = value.map((origin, index) => graphRedirectFinalOrigin(
|
|
369
|
-
origin,
|
|
370
|
-
`Artifact graph file ${path} redirectFinalOrigins[${String(index)}]`,
|
|
371
|
-
));
|
|
372
|
-
const unique = new Set(origins);
|
|
373
|
-
if (unique.size !== origins.length) {
|
|
374
|
-
throw artifactGraphTypeError(
|
|
375
|
-
"artifact-graph-source-redirect-final-origin-duplicate",
|
|
376
|
-
`Artifact graph file ${path} redirectFinalOrigins must be unique after canonicalization.`,
|
|
377
|
-
);
|
|
378
|
-
}
|
|
379
|
-
return completeValue([...origins].sort(lexicalCompare));
|
|
380
|
-
}
|
|
381
|
-
|
|
382
|
-
function graphImmutableUrl(value, label, revision) {
|
|
383
|
-
let url;
|
|
384
|
-
try {
|
|
385
|
-
url = immutableUrl(
|
|
386
|
-
artifactGraphText(
|
|
387
|
-
value,
|
|
388
|
-
label,
|
|
389
|
-
"artifact-graph-source-url-missing",
|
|
390
|
-
),
|
|
391
|
-
label,
|
|
392
|
-
);
|
|
393
|
-
} catch (error) {
|
|
394
|
-
if (ARTIFACT_ERRORS.has(error)) throw error;
|
|
395
|
-
throw artifactGraphTypeError(
|
|
396
|
-
"artifact-graph-source-url-mutable",
|
|
397
|
-
`${label} must identify an immutable HTTPS or same-origin source authority.`,
|
|
398
|
-
error,
|
|
399
|
-
);
|
|
400
|
-
}
|
|
401
|
-
if (ARTIFACT_GRAPH_MUTABLE_SOURCE_PATTERN.test(new URL(url).pathname)) {
|
|
402
|
-
throw artifactGraphTypeError(
|
|
403
|
-
"artifact-graph-source-url-mutable",
|
|
404
|
-
`${label} names a mutable branch, channel, or release alias.`,
|
|
405
|
-
);
|
|
406
|
-
}
|
|
407
|
-
if (!url.toLowerCase().includes(revision.toLowerCase())) {
|
|
408
|
-
throw artifactGraphTypeError(
|
|
409
|
-
"artifact-graph-source-revision-unbound",
|
|
410
|
-
`${label} must contain the file revision.`,
|
|
411
|
-
);
|
|
412
|
-
}
|
|
413
|
-
return url;
|
|
414
|
-
}
|
|
415
|
-
|
|
416
270
|
function lexicalCompare(left, right) {
|
|
417
271
|
return left < right ? -1 : left > right ? 1 : 0;
|
|
418
272
|
}
|
|
419
273
|
|
|
420
|
-
|
|
421
|
-
// normalizers only. Ordinary graph creation treats edges and transforms as
|
|
422
|
-
// inert metadata and never calls these helpers. Do not enable them, including
|
|
423
|
-
// for secure mode, without an explicit review with the user.
|
|
424
|
-
function compatibilityRecordKey(value) {
|
|
425
|
-
if (Array.isArray(value)) {
|
|
426
|
-
return `[${value.map(compatibilityRecordKey).join(",")}]`;
|
|
427
|
-
}
|
|
428
|
-
if (value && typeof value === "object") {
|
|
429
|
-
return `{${Object.keys(value).sort().map((key) =>
|
|
430
|
-
`${JSON.stringify(key)}:${compatibilityRecordKey(value[key])}`).join(",")}}`;
|
|
431
|
-
}
|
|
432
|
-
return JSON.stringify(value);
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
function normalizeFile(value, kind, index, revision, secure) {
|
|
274
|
+
function normalizeFile(value, kind, index) {
|
|
436
275
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
437
276
|
throw new TypeError(`${kind} file ${String(index)} must be an object.`);
|
|
438
277
|
}
|
|
@@ -444,14 +283,7 @@ function normalizeFile(value, kind, index, revision, secure) {
|
|
|
444
283
|
) {
|
|
445
284
|
throw new TypeError(`${kind} file path must be a normalized relative path.`);
|
|
446
285
|
}
|
|
447
|
-
const url =
|
|
448
|
-
? immutableUrl(value.url, `${kind} file url`)
|
|
449
|
-
: ordinarySourceUrl(value.url, `${kind} file url`);
|
|
450
|
-
if (secure && !url.toLowerCase().includes(revision.toLowerCase())) {
|
|
451
|
-
throw new TypeError(
|
|
452
|
-
`${kind} file URL must contain its caller-supplied revision.`,
|
|
453
|
-
);
|
|
454
|
-
}
|
|
286
|
+
const url = ordinarySourceUrl(value.url, `${kind} file url`);
|
|
455
287
|
return completeValue({
|
|
456
288
|
kind,
|
|
457
289
|
index,
|
|
@@ -465,9 +297,8 @@ function normalizeFile(value, kind, index, revision, secure) {
|
|
|
465
297
|
});
|
|
466
298
|
}
|
|
467
299
|
|
|
468
|
-
function uniqueFiles(files, label, kind,
|
|
300
|
+
function uniqueFiles(files, label, kind, {
|
|
469
301
|
allowEmpty = false,
|
|
470
|
-
secure = false,
|
|
471
302
|
} = {}) {
|
|
472
303
|
if (!Array.isArray(files) || (!allowEmpty && files.length < 1)) {
|
|
473
304
|
throw new TypeError(
|
|
@@ -479,7 +310,7 @@ function uniqueFiles(files, label, kind, revision, {
|
|
|
479
310
|
const paths = new Set();
|
|
480
311
|
const urls = new Set();
|
|
481
312
|
return completeValue(files.map((value, index) => {
|
|
482
|
-
const file = normalizeFile(value, kind, index
|
|
313
|
+
const file = normalizeFile(value, kind, index);
|
|
483
314
|
if (paths.has(file.path) || urls.has(file.url)) {
|
|
484
315
|
throw new TypeError(`${label} file paths and URLs must be unique.`);
|
|
485
316
|
}
|
|
@@ -498,7 +329,7 @@ function publicFile(file) {
|
|
|
498
329
|
return completeValue(result);
|
|
499
330
|
}
|
|
500
331
|
|
|
501
|
-
function normalizeArtifactGraphFile(value, index
|
|
332
|
+
function normalizeArtifactGraphFile(value, index) {
|
|
502
333
|
if (!value || typeof value !== "object" || Array.isArray(value)) {
|
|
503
334
|
throw artifactGraphTypeError(
|
|
504
335
|
"artifact-graph-file-descriptor-not-object",
|
|
@@ -526,19 +357,9 @@ function normalizeArtifactGraphFile(value, index, secure = false) {
|
|
|
526
357
|
"artifact-graph-file-revision-missing",
|
|
527
358
|
"artifact-graph-file-revision-length-exceeded",
|
|
528
359
|
);
|
|
529
|
-
const sourceUrl =
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
`Artifact graph file ${path} sourceUrl`,
|
|
533
|
-
revision,
|
|
534
|
-
)
|
|
535
|
-
: ordinarySourceUrl(
|
|
536
|
-
value.sourceUrl ?? value.url,
|
|
537
|
-
`Artifact graph file ${path} sourceUrl`,
|
|
538
|
-
);
|
|
539
|
-
const redirectFinalOrigins = normalizeGraphRedirectFinalOrigins(
|
|
540
|
-
value.redirectFinalOrigins,
|
|
541
|
-
path,
|
|
360
|
+
const sourceUrl = ordinarySourceUrl(
|
|
361
|
+
value.sourceUrl ?? value.url,
|
|
362
|
+
`Artifact graph file ${path} sourceUrl`,
|
|
542
363
|
);
|
|
543
364
|
// Legal metadata belongs to the selected upstream distribution. Preserve a
|
|
544
365
|
// caller-supplied value as inert metadata, but never require or interpret it
|
|
@@ -604,7 +425,6 @@ function normalizeArtifactGraphFile(value, index, secure = false) {
|
|
|
604
425
|
mediaType,
|
|
605
426
|
sourceMediaType,
|
|
606
427
|
runtimeRequestUrls: completeValue(normalizedRequestUrls),
|
|
607
|
-
redirectFinalOrigins,
|
|
608
428
|
});
|
|
609
429
|
}
|
|
610
430
|
|
|
@@ -620,36 +440,9 @@ function publicArtifactGraphFile(file) {
|
|
|
620
440
|
? {}
|
|
621
441
|
: { sourceMediaType: file.sourceMediaType }),
|
|
622
442
|
runtimeRequestUrls: file.runtimeRequestUrls,
|
|
623
|
-
...(file.redirectFinalOrigins.length < 1
|
|
624
|
-
? {}
|
|
625
|
-
: { redirectFinalOrigins: file.redirectFinalOrigins }),
|
|
626
443
|
});
|
|
627
444
|
}
|
|
628
445
|
|
|
629
|
-
function normalizeGraphOccurrence(value, label) {
|
|
630
|
-
return graphPositiveInteger(
|
|
631
|
-
value,
|
|
632
|
-
`${label} occurrence`,
|
|
633
|
-
"artifact-graph-edge-occurrence-positive-safe-integer-required",
|
|
634
|
-
);
|
|
635
|
-
}
|
|
636
|
-
|
|
637
|
-
function normalizeGraphModulePath(value, label, filesByPath) {
|
|
638
|
-
const path = canonicalArtifactPath(
|
|
639
|
-
value,
|
|
640
|
-
`${label} modulePath`,
|
|
641
|
-
"artifact-graph-edge-module-path-missing",
|
|
642
|
-
"artifact-graph-edge-module-path-noncanonical",
|
|
643
|
-
);
|
|
644
|
-
if (!ARTIFACT_GRAPH_JAVASCRIPT_KINDS.has(filesByPath.get(path)?.kind)) {
|
|
645
|
-
throw artifactGraphTypeError(
|
|
646
|
-
"artifact-graph-edge-module-path-not-runtime-javascript",
|
|
647
|
-
`${label} modulePath must name one declared runtime JavaScript file.`,
|
|
648
|
-
);
|
|
649
|
-
}
|
|
650
|
-
return path;
|
|
651
|
-
}
|
|
652
|
-
|
|
653
446
|
function normalizeGraphTargetPath(value, label, filesByPath, javascript = false) {
|
|
654
447
|
const path = canonicalArtifactPath(
|
|
655
448
|
value,
|
|
@@ -667,380 +460,6 @@ function normalizeGraphTargetPath(value, label, filesByPath, javascript = false)
|
|
|
667
460
|
return path;
|
|
668
461
|
}
|
|
669
462
|
|
|
670
|
-
function normalizeGraphPolicy(value, label) {
|
|
671
|
-
const policy = value ?? "artifact-targets-admitted";
|
|
672
|
-
if (!ARTIFACT_GRAPH_EDGE_POLICIES.has(policy)) {
|
|
673
|
-
throw artifactGraphTypeError(
|
|
674
|
-
"artifact-graph-edge-policy-not-admitted",
|
|
675
|
-
`${label} edgePolicy must identify an admitted artifact target or an inactive rejected runtime branch.`,
|
|
676
|
-
);
|
|
677
|
-
}
|
|
678
|
-
return policy;
|
|
679
|
-
}
|
|
680
|
-
|
|
681
|
-
function normalizeGraphTargets(value, label, filesByPath, {
|
|
682
|
-
javascript = false,
|
|
683
|
-
worker = false,
|
|
684
|
-
} = {}) {
|
|
685
|
-
if (!Array.isArray(value)) {
|
|
686
|
-
throw artifactGraphTypeError(
|
|
687
|
-
"artifact-graph-edge-targets-not-array",
|
|
688
|
-
`${label} targets must be an array.`,
|
|
689
|
-
);
|
|
690
|
-
}
|
|
691
|
-
const allowedMatches = worker
|
|
692
|
-
? new Set([...ARTIFACT_GRAPH_IMPORT_MATCHES, "self-module-url"])
|
|
693
|
-
: ARTIFACT_GRAPH_IMPORT_MATCHES;
|
|
694
|
-
const targets = value.map((target, index) => {
|
|
695
|
-
if (!target || typeof target !== "object" || Array.isArray(target)) {
|
|
696
|
-
throw artifactGraphTypeError(
|
|
697
|
-
"artifact-graph-edge-target-not-object",
|
|
698
|
-
`${label} target ${String(index)} must be an object.`,
|
|
699
|
-
);
|
|
700
|
-
}
|
|
701
|
-
const match = artifactGraphText(
|
|
702
|
-
target.match,
|
|
703
|
-
`${label} target ${String(index)} match`,
|
|
704
|
-
"artifact-graph-edge-target-match-missing",
|
|
705
|
-
);
|
|
706
|
-
if (!allowedMatches.has(match)) {
|
|
707
|
-
throw artifactGraphTypeError(
|
|
708
|
-
"artifact-graph-edge-target-match-not-admitted",
|
|
709
|
-
`${label} target ${String(index)} match is not admitted.`,
|
|
710
|
-
);
|
|
711
|
-
}
|
|
712
|
-
const targetPath = normalizeGraphTargetPath(
|
|
713
|
-
target.targetPath,
|
|
714
|
-
`${label} target ${String(index)}`,
|
|
715
|
-
filesByPath,
|
|
716
|
-
javascript,
|
|
717
|
-
);
|
|
718
|
-
const exactSpecifier = match === "exact-runtime-specifier"
|
|
719
|
-
? artifactGraphText(
|
|
720
|
-
target.exactSpecifier ?? target.specifier,
|
|
721
|
-
`${label} target ${String(index)} exactSpecifier`,
|
|
722
|
-
"artifact-graph-edge-target-specifier-missing",
|
|
723
|
-
)
|
|
724
|
-
: null;
|
|
725
|
-
return completeValue({ match, targetPath, exactSpecifier });
|
|
726
|
-
});
|
|
727
|
-
targets.sort((left, right) =>
|
|
728
|
-
lexicalCompare(compatibilityRecordKey(left), compatibilityRecordKey(right)));
|
|
729
|
-
const identities = new Set(targets.map(compatibilityRecordKey));
|
|
730
|
-
if (identities.size !== targets.length) {
|
|
731
|
-
throw artifactGraphTypeError(
|
|
732
|
-
"artifact-graph-edge-target-duplicate",
|
|
733
|
-
`${label} targets must be unique.`,
|
|
734
|
-
);
|
|
735
|
-
}
|
|
736
|
-
return completeValue(targets);
|
|
737
|
-
}
|
|
738
|
-
|
|
739
|
-
function normalizeArtifactGraphEdges(value, filesByPath, negativeRuntimeRequestUrls) {
|
|
740
|
-
const edges = value ?? {};
|
|
741
|
-
if (!edges || typeof edges !== "object" || Array.isArray(edges)) {
|
|
742
|
-
throw artifactGraphTypeError(
|
|
743
|
-
"artifact-graph-edges-not-object",
|
|
744
|
-
"Artifact graph edges must be an object.",
|
|
745
|
-
);
|
|
746
|
-
}
|
|
747
|
-
const allowedEdgeNames = new Set([
|
|
748
|
-
"cacheOpens",
|
|
749
|
-
"dynamicImports",
|
|
750
|
-
"fetches",
|
|
751
|
-
"moduleWorkers",
|
|
752
|
-
"staticImports",
|
|
753
|
-
]);
|
|
754
|
-
if (Reflect.ownKeys(edges).some((name) =>
|
|
755
|
-
typeof name !== "string" || !allowedEdgeNames.has(name))) {
|
|
756
|
-
throw artifactGraphTypeError(
|
|
757
|
-
"artifact-graph-edge-kind-not-admitted",
|
|
758
|
-
"Artifact graph edges contain an edge kind that is not admitted.",
|
|
759
|
-
);
|
|
760
|
-
}
|
|
761
|
-
|
|
762
|
-
function normalizeArray(name, normalize) {
|
|
763
|
-
const values = edges[name] ?? [];
|
|
764
|
-
if (!Array.isArray(values)) {
|
|
765
|
-
throw artifactGraphTypeError(
|
|
766
|
-
"artifact-graph-edge-list-not-array",
|
|
767
|
-
`Artifact graph ${name} must be an array.`,
|
|
768
|
-
);
|
|
769
|
-
}
|
|
770
|
-
const normalized = values.map((edge, index) => {
|
|
771
|
-
if (!edge || typeof edge !== "object" || Array.isArray(edge)) {
|
|
772
|
-
throw artifactGraphTypeError(
|
|
773
|
-
"artifact-graph-edge-not-object",
|
|
774
|
-
`Artifact graph ${name}[${String(index)}] must be an object.`,
|
|
775
|
-
);
|
|
776
|
-
}
|
|
777
|
-
const label = `Artifact graph ${name}[${String(index)}]`;
|
|
778
|
-
const modulePath = normalizeGraphModulePath(edge.modulePath, label, filesByPath);
|
|
779
|
-
const occurrence = normalizeGraphOccurrence(edge.occurrence, label);
|
|
780
|
-
return normalize(edge, label, modulePath, occurrence);
|
|
781
|
-
});
|
|
782
|
-
normalized.sort((left, right) =>
|
|
783
|
-
lexicalCompare(compatibilityRecordKey(left), compatibilityRecordKey(right)));
|
|
784
|
-
const occurrences = new Set();
|
|
785
|
-
for (const edge of normalized) {
|
|
786
|
-
const key = `${edge.modulePath}\u0000${String(edge.occurrence)}`;
|
|
787
|
-
if (occurrences.has(key)) {
|
|
788
|
-
throw artifactGraphTypeError(
|
|
789
|
-
"artifact-graph-edge-occurrence-duplicate",
|
|
790
|
-
`Artifact graph ${name} contains a duplicate module occurrence.`,
|
|
791
|
-
);
|
|
792
|
-
}
|
|
793
|
-
occurrences.add(key);
|
|
794
|
-
}
|
|
795
|
-
return completeValue(normalized);
|
|
796
|
-
}
|
|
797
|
-
|
|
798
|
-
const staticImports = normalizeArray(
|
|
799
|
-
"staticImports",
|
|
800
|
-
(edge, label, modulePath, occurrence) => completeValue({
|
|
801
|
-
modulePath,
|
|
802
|
-
occurrence,
|
|
803
|
-
specifier: artifactGraphText(
|
|
804
|
-
edge.specifier,
|
|
805
|
-
`${label} specifier`,
|
|
806
|
-
"artifact-graph-static-import-specifier-missing",
|
|
807
|
-
),
|
|
808
|
-
targetPath: normalizeGraphTargetPath(
|
|
809
|
-
edge.targetPath,
|
|
810
|
-
label,
|
|
811
|
-
filesByPath,
|
|
812
|
-
true,
|
|
813
|
-
),
|
|
814
|
-
}),
|
|
815
|
-
);
|
|
816
|
-
const dynamicImports = normalizeArray(
|
|
817
|
-
"dynamicImports",
|
|
818
|
-
(edge, label, modulePath, occurrence) => {
|
|
819
|
-
const edgePolicy = normalizeGraphPolicy(edge.edgePolicy, label);
|
|
820
|
-
const targets = normalizeGraphTargets(edge.targets ?? [], label, filesByPath, {
|
|
821
|
-
javascript: true,
|
|
822
|
-
});
|
|
823
|
-
if (
|
|
824
|
-
(edgePolicy === "artifact-targets-admitted") !== (targets.length > 0)
|
|
825
|
-
) {
|
|
826
|
-
throw artifactGraphTypeError(
|
|
827
|
-
"artifact-graph-dynamic-import-policy-target-mismatch",
|
|
828
|
-
`${label} must have targets exactly when its edgePolicy admits artifact targets.`,
|
|
829
|
-
);
|
|
830
|
-
}
|
|
831
|
-
return completeValue({ modulePath, occurrence, edgePolicy, targets });
|
|
832
|
-
},
|
|
833
|
-
);
|
|
834
|
-
const moduleWorkers = normalizeArray(
|
|
835
|
-
"moduleWorkers",
|
|
836
|
-
(edge, label, modulePath, occurrence) => {
|
|
837
|
-
const edgePolicy = normalizeGraphPolicy(edge.edgePolicy, label);
|
|
838
|
-
const targets = normalizeGraphTargets(edge.targets ?? [], label, filesByPath, {
|
|
839
|
-
javascript: true,
|
|
840
|
-
worker: true,
|
|
841
|
-
});
|
|
842
|
-
if (
|
|
843
|
-
(edgePolicy === "artifact-targets-admitted") !== (targets.length > 0)
|
|
844
|
-
) {
|
|
845
|
-
throw artifactGraphTypeError(
|
|
846
|
-
"artifact-graph-module-worker-policy-target-mismatch",
|
|
847
|
-
`${label} must have targets exactly when its edgePolicy admits artifact targets.`,
|
|
848
|
-
);
|
|
849
|
-
}
|
|
850
|
-
if (targets.some((target) =>
|
|
851
|
-
target.match === "self-module-url" && target.targetPath !== modulePath)) {
|
|
852
|
-
throw artifactGraphTypeError(
|
|
853
|
-
"artifact-graph-module-worker-self-target-mismatch",
|
|
854
|
-
`${label} self-module-url target must equal modulePath.`,
|
|
855
|
-
);
|
|
856
|
-
}
|
|
857
|
-
return completeValue({ modulePath, occurrence, edgePolicy, targets });
|
|
858
|
-
},
|
|
859
|
-
);
|
|
860
|
-
const fetches = normalizeArray(
|
|
861
|
-
"fetches",
|
|
862
|
-
(edge, label, modulePath, occurrence) => {
|
|
863
|
-
const edgePolicy = normalizeGraphPolicy(edge.edgePolicy, label);
|
|
864
|
-
const methods = edge.methods ?? ["GET"];
|
|
865
|
-
if (
|
|
866
|
-
!Array.isArray(methods)
|
|
867
|
-
|| methods.length !== 1
|
|
868
|
-
|| methods[0] !== "GET"
|
|
869
|
-
) {
|
|
870
|
-
throw artifactGraphTypeError(
|
|
871
|
-
"artifact-graph-fetch-method-not-get",
|
|
872
|
-
`${label} methods must be exactly ["GET"].`,
|
|
873
|
-
);
|
|
874
|
-
}
|
|
875
|
-
const targetPaths = edge.targetPaths ?? [];
|
|
876
|
-
if (!Array.isArray(targetPaths)) {
|
|
877
|
-
throw artifactGraphTypeError(
|
|
878
|
-
"artifact-graph-fetch-targets-not-array",
|
|
879
|
-
`${label} targetPaths must be an array.`,
|
|
880
|
-
);
|
|
881
|
-
}
|
|
882
|
-
const normalizedTargetPaths = [...new Set(targetPaths.map((path) =>
|
|
883
|
-
normalizeGraphTargetPath(path, label, filesByPath)))].sort();
|
|
884
|
-
if (normalizedTargetPaths.length !== targetPaths.length) {
|
|
885
|
-
throw artifactGraphTypeError(
|
|
886
|
-
"artifact-graph-fetch-target-duplicate",
|
|
887
|
-
`${label} targetPaths must be unique.`,
|
|
888
|
-
);
|
|
889
|
-
}
|
|
890
|
-
if (normalizedTargetPaths.some((path) =>
|
|
891
|
-
ARTIFACT_GRAPH_JAVASCRIPT_KINDS.has(filesByPath.get(path)?.kind))) {
|
|
892
|
-
throw artifactGraphTypeError(
|
|
893
|
-
"artifact-graph-fetch-javascript-target-rejected",
|
|
894
|
-
`${label} must not expose authenticated JavaScript bytes through a fetch edge.`,
|
|
895
|
-
);
|
|
896
|
-
}
|
|
897
|
-
const negativeUrls = edge.negativeRuntimeRequestUrls ?? [];
|
|
898
|
-
if (!Array.isArray(negativeUrls)) {
|
|
899
|
-
throw artifactGraphTypeError(
|
|
900
|
-
"artifact-graph-fetch-negative-routes-not-array",
|
|
901
|
-
`${label} negativeRuntimeRequestUrls must be an array.`,
|
|
902
|
-
);
|
|
903
|
-
}
|
|
904
|
-
const normalizedNegativeUrls = [...new Set(negativeUrls.map((url, index) =>
|
|
905
|
-
graphRuntimeRequestUrl(url, `${label} negativeRuntimeRequestUrls[${String(index)}]`)))].sort();
|
|
906
|
-
if (
|
|
907
|
-
normalizedNegativeUrls.length !== negativeUrls.length
|
|
908
|
-
|| normalizedNegativeUrls.some((url) => !negativeRuntimeRequestUrls.has(url))
|
|
909
|
-
) {
|
|
910
|
-
throw artifactGraphTypeError(
|
|
911
|
-
"artifact-graph-fetch-negative-route-undeclared",
|
|
912
|
-
`${label} negative runtime request routes must be unique graph-level declarations.`,
|
|
913
|
-
);
|
|
914
|
-
}
|
|
915
|
-
if (
|
|
916
|
-
edgePolicy === "artifact-targets-admitted"
|
|
917
|
-
&& normalizedTargetPaths.length === 0
|
|
918
|
-
&& normalizedNegativeUrls.length === 0
|
|
919
|
-
) {
|
|
920
|
-
throw artifactGraphTypeError(
|
|
921
|
-
"artifact-graph-fetch-targets-incomplete",
|
|
922
|
-
`${label} must admit at least one artifact or authenticated negative route.`,
|
|
923
|
-
);
|
|
924
|
-
}
|
|
925
|
-
if (
|
|
926
|
-
edgePolicy === "inactive-runtime-branch-rejected"
|
|
927
|
-
&& (normalizedTargetPaths.length > 0 || normalizedNegativeUrls.length > 0)
|
|
928
|
-
) {
|
|
929
|
-
throw artifactGraphTypeError(
|
|
930
|
-
"artifact-graph-fetch-policy-target-mismatch",
|
|
931
|
-
`${label} rejected inactive branch must not name fetch targets.`,
|
|
932
|
-
);
|
|
933
|
-
}
|
|
934
|
-
return completeValue({
|
|
935
|
-
modulePath,
|
|
936
|
-
occurrence,
|
|
937
|
-
edgePolicy,
|
|
938
|
-
methods: completeValue(["GET"]),
|
|
939
|
-
targetPaths: completeValue(normalizedTargetPaths),
|
|
940
|
-
negativeRuntimeRequestUrls: completeValue(normalizedNegativeUrls),
|
|
941
|
-
allowMaterializedUrls: edge.allowMaterializedUrls === true,
|
|
942
|
-
});
|
|
943
|
-
},
|
|
944
|
-
);
|
|
945
|
-
const cacheOpens = normalizeArray(
|
|
946
|
-
"cacheOpens",
|
|
947
|
-
(edge, label, modulePath, occurrence) => {
|
|
948
|
-
const edgePolicy = normalizeGraphPolicy(edge.edgePolicy, label);
|
|
949
|
-
const cacheName = artifactGraphText(
|
|
950
|
-
edge.cacheName,
|
|
951
|
-
`${label} cacheName`,
|
|
952
|
-
"artifact-graph-cache-name-missing",
|
|
953
|
-
);
|
|
954
|
-
const targetPaths = edge.targetPaths ?? [];
|
|
955
|
-
if (!Array.isArray(targetPaths)) {
|
|
956
|
-
throw artifactGraphTypeError(
|
|
957
|
-
"artifact-graph-cache-targets-not-array",
|
|
958
|
-
`${label} targetPaths must be an array.`,
|
|
959
|
-
);
|
|
960
|
-
}
|
|
961
|
-
const normalizedTargetPaths = [...new Set(targetPaths.map((path) =>
|
|
962
|
-
normalizeGraphTargetPath(path, label, filesByPath)))].sort();
|
|
963
|
-
if (normalizedTargetPaths.length !== targetPaths.length) {
|
|
964
|
-
throw artifactGraphTypeError(
|
|
965
|
-
"artifact-graph-cache-target-duplicate",
|
|
966
|
-
`${label} targetPaths must be unique.`,
|
|
967
|
-
);
|
|
968
|
-
}
|
|
969
|
-
if (normalizedTargetPaths.some((path) =>
|
|
970
|
-
ARTIFACT_GRAPH_JAVASCRIPT_KINDS.has(filesByPath.get(path)?.kind))) {
|
|
971
|
-
throw artifactGraphTypeError(
|
|
972
|
-
"artifact-graph-cache-javascript-target-rejected",
|
|
973
|
-
`${label} must not expose authenticated JavaScript bytes through a cache edge.`,
|
|
974
|
-
);
|
|
975
|
-
}
|
|
976
|
-
if (
|
|
977
|
-
(edgePolicy === "artifact-targets-admitted") !== (normalizedTargetPaths.length > 0)
|
|
978
|
-
) {
|
|
979
|
-
throw artifactGraphTypeError(
|
|
980
|
-
"artifact-graph-cache-policy-target-mismatch",
|
|
981
|
-
`${label} must have targets exactly when its edgePolicy admits authenticated cache reads.`,
|
|
982
|
-
);
|
|
983
|
-
}
|
|
984
|
-
return completeValue({
|
|
985
|
-
modulePath,
|
|
986
|
-
occurrence,
|
|
987
|
-
edgePolicy,
|
|
988
|
-
cacheName,
|
|
989
|
-
targetPaths: completeValue(normalizedTargetPaths),
|
|
990
|
-
});
|
|
991
|
-
},
|
|
992
|
-
);
|
|
993
|
-
return completeValue({
|
|
994
|
-
staticImports,
|
|
995
|
-
dynamicImports,
|
|
996
|
-
moduleWorkers,
|
|
997
|
-
fetches,
|
|
998
|
-
cacheOpens,
|
|
999
|
-
});
|
|
1000
|
-
}
|
|
1001
|
-
|
|
1002
|
-
function normalizeArtifactGraphTransforms(value, filesByPath) {
|
|
1003
|
-
const transforms = value ?? [];
|
|
1004
|
-
if (!Array.isArray(transforms)) {
|
|
1005
|
-
throw artifactGraphTypeError(
|
|
1006
|
-
"artifact-graph-transforms-not-array",
|
|
1007
|
-
"Artifact graph transforms must be an array.",
|
|
1008
|
-
);
|
|
1009
|
-
}
|
|
1010
|
-
const normalized = transforms.map((transform, index) => {
|
|
1011
|
-
if (!transform || typeof transform !== "object" || Array.isArray(transform)) {
|
|
1012
|
-
throw artifactGraphTypeError(
|
|
1013
|
-
"artifact-graph-transform-not-object",
|
|
1014
|
-
`Artifact graph transform ${String(index)} must be an object.`,
|
|
1015
|
-
);
|
|
1016
|
-
}
|
|
1017
|
-
const label = `Artifact graph transform ${String(index)}`;
|
|
1018
|
-
if (!["function-return-this-to-global-this", "typed-array-constructor"].includes(
|
|
1019
|
-
transform.kind,
|
|
1020
|
-
)) {
|
|
1021
|
-
throw artifactGraphTypeError(
|
|
1022
|
-
"artifact-graph-transform-kind-not-admitted",
|
|
1023
|
-
`${label} kind is not admitted.`,
|
|
1024
|
-
);
|
|
1025
|
-
}
|
|
1026
|
-
return completeValue({
|
|
1027
|
-
kind: transform.kind,
|
|
1028
|
-
modulePath: normalizeGraphModulePath(transform.modulePath, label, filesByPath),
|
|
1029
|
-
occurrence: normalizeGraphOccurrence(transform.occurrence, label),
|
|
1030
|
-
});
|
|
1031
|
-
});
|
|
1032
|
-
normalized.sort((left, right) =>
|
|
1033
|
-
lexicalCompare(compatibilityRecordKey(left), compatibilityRecordKey(right)));
|
|
1034
|
-
const identities = new Set(normalized.map(compatibilityRecordKey));
|
|
1035
|
-
if (identities.size !== normalized.length) {
|
|
1036
|
-
throw artifactGraphTypeError(
|
|
1037
|
-
"artifact-graph-transform-occurrence-duplicate",
|
|
1038
|
-
"Artifact graph transform occurrences must be unique.",
|
|
1039
|
-
);
|
|
1040
|
-
}
|
|
1041
|
-
return completeValue(normalized);
|
|
1042
|
-
}
|
|
1043
|
-
|
|
1044
463
|
function normalizeArtifactGraphVoices(value, defaultVoice, filesByPath) {
|
|
1045
464
|
if (!Array.isArray(value) || value.length < 1) {
|
|
1046
465
|
throw artifactGraphTypeError(
|
|
@@ -1098,8 +517,6 @@ function artifactGraphProjection({
|
|
|
1098
517
|
model,
|
|
1099
518
|
runtime,
|
|
1100
519
|
files,
|
|
1101
|
-
edges,
|
|
1102
|
-
transforms,
|
|
1103
520
|
}) {
|
|
1104
521
|
return completeValue({
|
|
1105
522
|
protocol: BROWSER_SPEECH_ARTIFACT_GRAPH_PROTOCOL,
|
|
@@ -1109,8 +526,6 @@ function artifactGraphProjection({
|
|
|
1109
526
|
model,
|
|
1110
527
|
runtime,
|
|
1111
528
|
files: completeValue(files.map((file) => publicArtifactGraphFile(file))),
|
|
1112
|
-
edges,
|
|
1113
|
-
transforms,
|
|
1114
529
|
});
|
|
1115
530
|
}
|
|
1116
531
|
|
|
@@ -1127,8 +542,6 @@ export function createBrowserSpeechArtifactGraph({
|
|
|
1127
542
|
model,
|
|
1128
543
|
runtime,
|
|
1129
544
|
files,
|
|
1130
|
-
edges,
|
|
1131
|
-
transforms,
|
|
1132
545
|
} = {}) {
|
|
1133
546
|
if (kind !== ARTIFACT_GRAPH_KIND) {
|
|
1134
547
|
throw artifactGraphTypeError(
|
|
@@ -1173,7 +586,7 @@ export function createBrowserSpeechArtifactGraph({
|
|
|
1173
586
|
);
|
|
1174
587
|
}
|
|
1175
588
|
const normalizedFiles = files.map((file, index) =>
|
|
1176
|
-
normalizeArtifactGraphFile(file, index
|
|
589
|
+
normalizeArtifactGraphFile(file, index));
|
|
1177
590
|
normalizedFiles.sort((left, right) => lexicalCompare(left.path, right.path));
|
|
1178
591
|
const filesByPath = new Map();
|
|
1179
592
|
const lowercasePaths = new Set();
|
|
@@ -1318,11 +731,6 @@ export function createBrowserSpeechArtifactGraph({
|
|
|
1318
731
|
);
|
|
1319
732
|
}
|
|
1320
733
|
|
|
1321
|
-
// Negative-route admission belonged to the former authenticated graph. The
|
|
1322
|
-
// ordinary materializer now routes known files and lets every unknown URL
|
|
1323
|
-
// continue through the browser's native operation.
|
|
1324
|
-
const normalizedNegativeRoutes = completeValue([]);
|
|
1325
|
-
|
|
1326
734
|
const modelId = artifactGraphIdentifier(
|
|
1327
735
|
model.id,
|
|
1328
736
|
"Browser speech artifact graph model id",
|
|
@@ -1378,33 +786,6 @@ export function createBrowserSpeechArtifactGraph({
|
|
|
1378
786
|
? normalizeArtifactGraphVoices(model.voices, defaultVoice, filesByPath)
|
|
1379
787
|
: completeValue([]);
|
|
1380
788
|
|
|
1381
|
-
// Edge and transform declarations are retained only as inert compatibility
|
|
1382
|
-
// metadata. Ordinary execution discovers routing from the complete file
|
|
1383
|
-
// inventory and never admits or rejects runtime operations through them.
|
|
1384
|
-
const edgeRecord = edges && typeof edges === "object" && !Array.isArray(edges)
|
|
1385
|
-
? edges
|
|
1386
|
-
: {};
|
|
1387
|
-
const normalizedEdges = completeValue({
|
|
1388
|
-
staticImports: completeValue(Array.isArray(edgeRecord.staticImports)
|
|
1389
|
-
? edgeRecord.staticImports.map((edge) => completeValue({ ...edge }))
|
|
1390
|
-
: []),
|
|
1391
|
-
dynamicImports: completeValue(Array.isArray(edgeRecord.dynamicImports)
|
|
1392
|
-
? edgeRecord.dynamicImports.map((edge) => completeValue({ ...edge }))
|
|
1393
|
-
: []),
|
|
1394
|
-
moduleWorkers: completeValue(Array.isArray(edgeRecord.moduleWorkers)
|
|
1395
|
-
? edgeRecord.moduleWorkers.map((edge) => completeValue({ ...edge }))
|
|
1396
|
-
: []),
|
|
1397
|
-
fetches: completeValue(Array.isArray(edgeRecord.fetches)
|
|
1398
|
-
? edgeRecord.fetches.map((edge) => completeValue({ ...edge }))
|
|
1399
|
-
: []),
|
|
1400
|
-
cacheOpens: completeValue(Array.isArray(edgeRecord.cacheOpens)
|
|
1401
|
-
? edgeRecord.cacheOpens.map((edge) => completeValue({ ...edge }))
|
|
1402
|
-
: []),
|
|
1403
|
-
});
|
|
1404
|
-
const normalizedTransforms = completeValue(Array.isArray(transforms)
|
|
1405
|
-
? transforms.map((transform) => completeValue({ ...transform }))
|
|
1406
|
-
: []);
|
|
1407
|
-
|
|
1408
789
|
const runtimeFiles = completeValue(normalizedFiles.filter((file) =>
|
|
1409
790
|
file.kind.startsWith("runtime-")));
|
|
1410
791
|
const modelFiles = completeValue(normalizedFiles.filter((file) =>
|
|
@@ -1448,7 +829,6 @@ export function createBrowserSpeechArtifactGraph({
|
|
|
1448
829
|
wasmPath,
|
|
1449
830
|
...(numThreads === null ? {} : { numThreads }),
|
|
1450
831
|
}),
|
|
1451
|
-
negativeRuntimeRequestUrls: completeValue(normalizedNegativeRoutes),
|
|
1452
832
|
});
|
|
1453
833
|
const normalizedModel = completeValue({
|
|
1454
834
|
id: modelId,
|
|
@@ -1466,8 +846,6 @@ export function createBrowserSpeechArtifactGraph({
|
|
|
1466
846
|
model: normalizedModel,
|
|
1467
847
|
runtime: normalizedRuntime,
|
|
1468
848
|
files: normalizedFiles,
|
|
1469
|
-
edges: normalizedEdges,
|
|
1470
|
-
transforms: normalizedTransforms,
|
|
1471
849
|
});
|
|
1472
850
|
const graph = completeValue({
|
|
1473
851
|
...projection,
|
|
@@ -1482,8 +860,6 @@ export function createBrowserSpeechArtifactGraph({
|
|
|
1482
860
|
modelFiles,
|
|
1483
861
|
model: normalizedModel,
|
|
1484
862
|
runtime: normalizedRuntime,
|
|
1485
|
-
edges: normalizedEdges,
|
|
1486
|
-
transforms: normalizedTransforms,
|
|
1487
863
|
...(normalizedSecurity ? { security: normalizedSecurity } : {}),
|
|
1488
864
|
}));
|
|
1489
865
|
return graph;
|
|
@@ -1524,11 +900,7 @@ export function createBrowserSpeechAuthority({
|
|
|
1524
900
|
model.files ?? [],
|
|
1525
901
|
"Browser speech model",
|
|
1526
902
|
"model",
|
|
1527
|
-
|
|
1528
|
-
{
|
|
1529
|
-
allowEmpty: true,
|
|
1530
|
-
secure: false,
|
|
1531
|
-
},
|
|
903
|
+
{ allowEmpty: true },
|
|
1532
904
|
);
|
|
1533
905
|
const runtimeAdapter = requiredText(runtime.adapter, "Browser speech runtime adapter");
|
|
1534
906
|
const expectedAdapter = role === "stt"
|
|
@@ -1543,8 +915,6 @@ export function createBrowserSpeechAuthority({
|
|
|
1543
915
|
runtime.files,
|
|
1544
916
|
"Browser speech runtime",
|
|
1545
917
|
"runtime",
|
|
1546
|
-
runtimeRevision,
|
|
1547
|
-
{ secure: false },
|
|
1548
918
|
);
|
|
1549
919
|
const wasmPaths = runtime.wasmPaths === undefined
|
|
1550
920
|
? null
|
|
@@ -1708,7 +1078,6 @@ function storageNames(authority, files) {
|
|
|
1708
1078
|
return completeValue({
|
|
1709
1079
|
key: prefix,
|
|
1710
1080
|
selection: `${prefix}.selection.json`,
|
|
1711
|
-
legacyManifest: `${prefix}.complete.json`,
|
|
1712
1081
|
files: completeValue(files.map((_, index) =>
|
|
1713
1082
|
`${prefix}.${String(index).padStart(4, "0")}.artifact`)),
|
|
1714
1083
|
});
|
|
@@ -1745,41 +1114,12 @@ async function* byteChunks(body, signal) {
|
|
|
1745
1114
|
);
|
|
1746
1115
|
}
|
|
1747
1116
|
|
|
1748
|
-
|
|
1749
|
-
|
|
1750
|
-
// which the Worker replaces with its admitted object-URL map before import.
|
|
1751
|
-
// Executable strings, child execution contexts, script loaders, and alternate
|
|
1752
|
-
// network transports are outside the grammar. Literal escape sequences are
|
|
1753
|
-
// decoded before the same capability tokens are evaluated.
|
|
1754
|
-
const CLOSED_MODULE_OUT_OF_GRAMMAR_IDENTIFIERS = new Set([
|
|
1755
|
-
"AsyncFunction",
|
|
1756
|
-
"AsyncGeneratorFunction",
|
|
1757
|
-
"EventSource",
|
|
1758
|
-
"Function",
|
|
1759
|
-
"GeneratorFunction",
|
|
1760
|
-
"RTCPeerConnection",
|
|
1761
|
-
"SharedWorker",
|
|
1762
|
-
"WebSocket",
|
|
1763
|
-
"WebTransport",
|
|
1764
|
-
"Worker",
|
|
1765
|
-
"XMLHttpRequest",
|
|
1766
|
-
"eval",
|
|
1767
|
-
"importScripts",
|
|
1768
|
-
]);
|
|
1769
|
-
const CLOSED_MODULE_OUT_OF_GRAMMAR_LITERAL =
|
|
1770
|
-
/(?:^|[^A-Za-z0-9_$])(?:AsyncFunction|AsyncGeneratorFunction|EventSource|Function|GeneratorFunction|RTCPeerConnection|SharedWorker|WebSocket|WebTransport|Worker|XMLHttpRequest|constructor|eval|importScripts)(?:$|[^A-Za-z0-9_$])|(?:^|[^A-Za-z0-9_$])import\s*\(/u;
|
|
1771
|
-
|
|
1772
|
-
function assertSelfContainedModuleSource(source, label) {
|
|
1117
|
+
function tokenizeArtifactGraphModule(source, modulePath) {
|
|
1118
|
+
const tokens = [];
|
|
1773
1119
|
let index = 0;
|
|
1774
|
-
|
|
1775
|
-
|
|
1776
|
-
|
|
1777
|
-
|
|
1778
|
-
function fail() {
|
|
1779
|
-
throw speechError(
|
|
1780
|
-
"ARCANE_AI_RUNTIME_MODULE_GRAPH_UNDECLARED",
|
|
1781
|
-
`${label} must be one self-contained JavaScript module without imports or re-exports.`,
|
|
1782
|
-
);
|
|
1120
|
+
|
|
1121
|
+
function fail(reason, message) {
|
|
1122
|
+
throw artifactGraphError(reason, `${modulePath}: ${message}`);
|
|
1783
1123
|
}
|
|
1784
1124
|
|
|
1785
1125
|
function identifierStart(character) {
|
|
@@ -1790,316 +1130,10 @@ function assertSelfContainedModuleSource(source, label) {
|
|
|
1790
1130
|
return /[A-Za-z0-9_$]/u.test(character ?? "");
|
|
1791
1131
|
}
|
|
1792
1132
|
|
|
1793
|
-
function assertLiteral(value) {
|
|
1794
|
-
if (CLOSED_MODULE_OUT_OF_GRAMMAR_LITERAL.test(value)) fail();
|
|
1795
|
-
}
|
|
1796
|
-
|
|
1797
|
-
function assertComputedLiteral(value) {
|
|
1798
|
-
if (value.includes("constructor") || /import\s*\(/u.test(value)) fail();
|
|
1799
|
-
for (const identifier of CLOSED_MODULE_OUT_OF_GRAMMAR_IDENTIFIERS) {
|
|
1800
|
-
if (value.includes(identifier)) fail();
|
|
1801
|
-
}
|
|
1802
|
-
}
|
|
1803
|
-
|
|
1804
|
-
function recordLiteral(start, end, value) {
|
|
1805
|
-
assertLiteral(value);
|
|
1806
|
-
literalFragments.push(completeValue({
|
|
1807
|
-
start,
|
|
1808
|
-
end,
|
|
1809
|
-
value,
|
|
1810
|
-
templateIds: completeValue([...templateStack]),
|
|
1811
|
-
}));
|
|
1812
|
-
}
|
|
1813
|
-
|
|
1814
|
-
function stripJoinerTrivia(value) {
|
|
1815
|
-
return value
|
|
1816
|
-
.replace(/\/\*[\s\S]*?\*\//gu, "")
|
|
1817
|
-
.replace(/\/\/[^\r\n]*(?:\r?\n|$)/gu, "")
|
|
1818
|
-
.replace(/\s+/gu, "");
|
|
1819
|
-
}
|
|
1820
|
-
|
|
1821
|
-
function sharesTemplate(left, right) {
|
|
1822
|
-
return left.templateIds.some((id) => right.templateIds.includes(id));
|
|
1823
|
-
}
|
|
1824
|
-
|
|
1825
|
-
function staticallyJoins(left, right) {
|
|
1826
|
-
const separator = stripJoinerTrivia(source.slice(left.end, right.start));
|
|
1827
|
-
const usesConcat = separator.includes(".concat");
|
|
1828
|
-
const withoutConcat = separator.replace(/\.concat/gu, "");
|
|
1829
|
-
const usesPlus = withoutConcat.includes("+");
|
|
1830
|
-
const usesTemplate = sharesTemplate(left, right)
|
|
1831
|
-
&& (withoutConcat.includes("${") || withoutConcat.includes("}"));
|
|
1832
|
-
if (!usesConcat && !usesPlus && !usesTemplate) return false;
|
|
1833
|
-
return (usesTemplate ? /^[+()${}]*$/u : /^[+()]*$/u).test(withoutConcat);
|
|
1834
|
-
}
|
|
1835
|
-
|
|
1836
|
-
function assertStaticLiteralChains() {
|
|
1837
|
-
let chain = [];
|
|
1838
|
-
function flushChain() {
|
|
1839
|
-
if (chain.length > 1) {
|
|
1840
|
-
assertComputedLiteral(chain.map((fragment) => fragment.value).join(""));
|
|
1841
|
-
}
|
|
1842
|
-
chain = [];
|
|
1843
|
-
}
|
|
1844
|
-
for (const fragment of literalFragments) {
|
|
1845
|
-
const previous = chain[chain.length - 1];
|
|
1846
|
-
if (previous && staticallyJoins(previous, fragment)) {
|
|
1847
|
-
chain.push(fragment);
|
|
1848
|
-
continue;
|
|
1849
|
-
}
|
|
1850
|
-
flushChain();
|
|
1851
|
-
chain.push(fragment);
|
|
1852
|
-
}
|
|
1853
|
-
flushChain();
|
|
1854
|
-
}
|
|
1855
|
-
|
|
1856
1133
|
function readEscape() {
|
|
1857
|
-
if (index >= source.length)
|
|
1858
|
-
|
|
1859
|
-
|
|
1860
|
-
if (character === "x") {
|
|
1861
|
-
const hex = source.slice(index, index + 2);
|
|
1862
|
-
if (!/^[a-f0-9]{2}$/iu.test(hex)) fail();
|
|
1863
|
-
index += 2;
|
|
1864
|
-
return String.fromCodePoint(Number.parseInt(hex, 16));
|
|
1865
|
-
}
|
|
1866
|
-
if (character === "u") {
|
|
1867
|
-
if (source[index] === "{") {
|
|
1868
|
-
const end = source.indexOf("}", index + 1);
|
|
1869
|
-
if (end < 0) fail();
|
|
1870
|
-
const hex = source.slice(index + 1, end);
|
|
1871
|
-
if (!/^[a-f0-9]{1,6}$/iu.test(hex)) fail();
|
|
1872
|
-
const codePoint = Number.parseInt(hex, 16);
|
|
1873
|
-
if (codePoint > 0x10ffff) fail();
|
|
1874
|
-
index = end + 1;
|
|
1875
|
-
return String.fromCodePoint(codePoint);
|
|
1876
|
-
}
|
|
1877
|
-
const hex = source.slice(index, index + 4);
|
|
1878
|
-
if (!/^[a-f0-9]{4}$/iu.test(hex)) fail();
|
|
1879
|
-
index += 4;
|
|
1880
|
-
return String.fromCodePoint(Number.parseInt(hex, 16));
|
|
1881
|
-
}
|
|
1882
|
-
if (character === "\n") return "";
|
|
1883
|
-
if (character === "\r") {
|
|
1884
|
-
if (source[index] === "\n") index += 1;
|
|
1885
|
-
return "";
|
|
1886
|
-
}
|
|
1887
|
-
return completeValue({
|
|
1888
|
-
"0": "\0",
|
|
1889
|
-
b: "\b",
|
|
1890
|
-
f: "\f",
|
|
1891
|
-
n: "\n",
|
|
1892
|
-
r: "\r",
|
|
1893
|
-
t: "\t",
|
|
1894
|
-
v: "\v",
|
|
1895
|
-
})[character] ?? character;
|
|
1896
|
-
}
|
|
1897
|
-
|
|
1898
|
-
function readQuoted(quote) {
|
|
1899
|
-
const start = index;
|
|
1900
|
-
let value = "";
|
|
1901
|
-
index += 1;
|
|
1902
|
-
while (index < source.length) {
|
|
1903
|
-
const character = source[index];
|
|
1904
|
-
index += 1;
|
|
1905
|
-
if (character === "\\") {
|
|
1906
|
-
value += readEscape();
|
|
1907
|
-
continue;
|
|
1908
|
-
}
|
|
1909
|
-
if (character === quote) {
|
|
1910
|
-
recordLiteral(start, index, value);
|
|
1911
|
-
return;
|
|
1912
|
-
}
|
|
1913
|
-
if (character === "\n" || character === "\r") fail();
|
|
1914
|
-
value += character;
|
|
1915
|
-
}
|
|
1916
|
-
fail();
|
|
1917
|
-
}
|
|
1918
|
-
|
|
1919
|
-
function skipRegex() {
|
|
1920
|
-
index += 1;
|
|
1921
|
-
let inClass = false;
|
|
1922
|
-
while (index < source.length) {
|
|
1923
|
-
const character = source[index];
|
|
1924
|
-
index += 1;
|
|
1925
|
-
if (character === "\\") {
|
|
1926
|
-
index += 1;
|
|
1927
|
-
continue;
|
|
1928
|
-
}
|
|
1929
|
-
if (character === "[") inClass = true;
|
|
1930
|
-
else if (character === "]") inClass = false;
|
|
1931
|
-
else if (character === "/" && !inClass) {
|
|
1932
|
-
while (/[A-Za-z]/u.test(source[index] ?? "")) index += 1;
|
|
1933
|
-
return;
|
|
1934
|
-
} else if (character === "\n" || character === "\r") {
|
|
1935
|
-
fail();
|
|
1936
|
-
}
|
|
1937
|
-
}
|
|
1938
|
-
fail();
|
|
1939
|
-
}
|
|
1940
|
-
|
|
1941
|
-
function canStartRegex(lastToken) {
|
|
1942
|
-
return lastToken === null
|
|
1943
|
-
|| [
|
|
1944
|
-
"(", "[", "{", "=", ":", ",", ";", "!", "?",
|
|
1945
|
-
"&&", "||", "=>", "return", "case", "throw",
|
|
1946
|
-
].includes(lastToken);
|
|
1947
|
-
}
|
|
1948
|
-
|
|
1949
|
-
function readTemplate() {
|
|
1950
|
-
const templateId = nextTemplateId;
|
|
1951
|
-
nextTemplateId += 1;
|
|
1952
|
-
templateStack.push(templateId);
|
|
1953
|
-
let fragmentStart = index;
|
|
1954
|
-
let value = "";
|
|
1955
|
-
index += 1;
|
|
1956
|
-
while (index < source.length) {
|
|
1957
|
-
const character = source[index];
|
|
1958
|
-
index += 1;
|
|
1959
|
-
if (character === "\\") {
|
|
1960
|
-
value += readEscape();
|
|
1961
|
-
continue;
|
|
1962
|
-
}
|
|
1963
|
-
if (character === "`") {
|
|
1964
|
-
recordLiteral(fragmentStart, index, value);
|
|
1965
|
-
templateStack.pop();
|
|
1966
|
-
return;
|
|
1967
|
-
}
|
|
1968
|
-
if (character === "$" && source[index] === "{") {
|
|
1969
|
-
recordLiteral(fragmentStart, index - 1, value);
|
|
1970
|
-
value = "";
|
|
1971
|
-
index += 1;
|
|
1972
|
-
scanCode(true);
|
|
1973
|
-
fragmentStart = index;
|
|
1974
|
-
continue;
|
|
1975
|
-
}
|
|
1976
|
-
value += character;
|
|
1977
|
-
}
|
|
1978
|
-
fail();
|
|
1979
|
-
}
|
|
1980
|
-
|
|
1981
|
-
function scanCode(stopAtTemplateBrace = false) {
|
|
1982
|
-
let nestedBraces = 0;
|
|
1983
|
-
let lastToken = null;
|
|
1984
|
-
while (index < source.length) {
|
|
1985
|
-
const character = source[index];
|
|
1986
|
-
const next = source[index + 1];
|
|
1987
|
-
if (/\s/u.test(character)) {
|
|
1988
|
-
index += 1;
|
|
1989
|
-
continue;
|
|
1990
|
-
}
|
|
1991
|
-
if (character === "/" && next === "/") {
|
|
1992
|
-
index += 2;
|
|
1993
|
-
while (index < source.length && source[index] !== "\n") index += 1;
|
|
1994
|
-
continue;
|
|
1995
|
-
}
|
|
1996
|
-
if (character === "/" && next === "*") {
|
|
1997
|
-
const end = source.indexOf("*/", index + 2);
|
|
1998
|
-
if (end < 0) fail();
|
|
1999
|
-
index = end + 2;
|
|
2000
|
-
continue;
|
|
2001
|
-
}
|
|
2002
|
-
if (character === "'" || character === '"') {
|
|
2003
|
-
if (lastToken === "from") fail();
|
|
2004
|
-
readQuoted(character);
|
|
2005
|
-
lastToken = "literal";
|
|
2006
|
-
continue;
|
|
2007
|
-
}
|
|
2008
|
-
if (character === "`") {
|
|
2009
|
-
if (lastToken === "from") fail();
|
|
2010
|
-
readTemplate();
|
|
2011
|
-
lastToken = "literal";
|
|
2012
|
-
continue;
|
|
2013
|
-
}
|
|
2014
|
-
if (character === "/" && canStartRegex(lastToken)) {
|
|
2015
|
-
skipRegex();
|
|
2016
|
-
lastToken = "literal";
|
|
2017
|
-
continue;
|
|
2018
|
-
}
|
|
2019
|
-
if (identifierStart(character)) {
|
|
2020
|
-
const start = index;
|
|
2021
|
-
index += 1;
|
|
2022
|
-
while (identifierPart(source[index])) index += 1;
|
|
2023
|
-
const word = source.slice(start, index);
|
|
2024
|
-
if (CLOSED_MODULE_OUT_OF_GRAMMAR_IDENTIFIERS.has(word)) fail();
|
|
2025
|
-
if (word === "constructor" && lastToken === ".") fail();
|
|
2026
|
-
if (word === "import") {
|
|
2027
|
-
while (/\s/u.test(source[index] ?? "")) index += 1;
|
|
2028
|
-
if (source[index] === "." && source.slice(index + 1, index + 5) === "meta") {
|
|
2029
|
-
index += 5;
|
|
2030
|
-
lastToken = "import.meta";
|
|
2031
|
-
continue;
|
|
2032
|
-
}
|
|
2033
|
-
fail();
|
|
2034
|
-
}
|
|
2035
|
-
lastToken = word;
|
|
2036
|
-
continue;
|
|
2037
|
-
}
|
|
2038
|
-
if (character === "\\") fail();
|
|
2039
|
-
if (stopAtTemplateBrace && character === "}") {
|
|
2040
|
-
if (nestedBraces === 0) {
|
|
2041
|
-
index += 1;
|
|
2042
|
-
return;
|
|
2043
|
-
}
|
|
2044
|
-
nestedBraces -= 1;
|
|
2045
|
-
} else if (stopAtTemplateBrace && character === "{") {
|
|
2046
|
-
nestedBraces += 1;
|
|
2047
|
-
}
|
|
2048
|
-
const twoCharacters = `${character}${next ?? ""}`;
|
|
2049
|
-
if (["&&", "||", "=>"].includes(twoCharacters)) {
|
|
2050
|
-
lastToken = twoCharacters;
|
|
2051
|
-
index += 2;
|
|
2052
|
-
} else {
|
|
2053
|
-
lastToken = character;
|
|
2054
|
-
index += 1;
|
|
2055
|
-
}
|
|
2056
|
-
}
|
|
2057
|
-
if (stopAtTemplateBrace) fail();
|
|
2058
|
-
}
|
|
2059
|
-
|
|
2060
|
-
scanCode();
|
|
2061
|
-
assertStaticLiteralChains();
|
|
2062
|
-
}
|
|
2063
|
-
|
|
2064
|
-
// Dormant hardening retained for future review only. Ordinary speech artifact
|
|
2065
|
-
// preparation never calls this closed-module inspection, and it must not be
|
|
2066
|
-
// enabled for secure mode without an explicit review with the user.
|
|
2067
|
-
async function assertSelfContainedRuntime(admitted, metadata) {
|
|
2068
|
-
const javascriptFiles = admitted.files.filter(({ descriptor }) =>
|
|
2069
|
-
descriptor.kind === "runtime" && descriptor.mediaType === "text/javascript");
|
|
2070
|
-
if (
|
|
2071
|
-
javascriptFiles.length !== 1
|
|
2072
|
-
|| javascriptFiles[0].descriptor.path !== metadata.runtime.entry
|
|
2073
|
-
) {
|
|
2074
|
-
throw speechError(
|
|
2075
|
-
"ARCANE_AI_RUNTIME_MODULE_GRAPH_UNDECLARED",
|
|
2076
|
-
"Browser speech requires exactly one admitted self-contained runtime module.",
|
|
2077
|
-
);
|
|
2078
|
-
}
|
|
2079
|
-
const [{ descriptor, file }] = javascriptFiles;
|
|
2080
|
-
void file;
|
|
2081
|
-
}
|
|
2082
|
-
|
|
2083
|
-
function tokenizeArtifactGraphModule(source, modulePath) {
|
|
2084
|
-
const tokens = [];
|
|
2085
|
-
let index = 0;
|
|
2086
|
-
|
|
2087
|
-
function fail(reason, message) {
|
|
2088
|
-
throw artifactGraphError(reason, `${modulePath}: ${message}`);
|
|
2089
|
-
}
|
|
2090
|
-
|
|
2091
|
-
function identifierStart(character) {
|
|
2092
|
-
return /[A-Za-z_$]/u.test(character ?? "");
|
|
2093
|
-
}
|
|
2094
|
-
|
|
2095
|
-
function identifierPart(character) {
|
|
2096
|
-
return /[A-Za-z0-9_$]/u.test(character ?? "");
|
|
2097
|
-
}
|
|
2098
|
-
|
|
2099
|
-
function readEscape() {
|
|
2100
|
-
if (index >= source.length) {
|
|
2101
|
-
fail("artifact-graph-javascript-escape-unterminated", "unterminated escape sequence.");
|
|
2102
|
-
}
|
|
1134
|
+
if (index >= source.length) {
|
|
1135
|
+
fail("artifact-graph-javascript-escape-unterminated", "unterminated escape sequence.");
|
|
1136
|
+
}
|
|
2103
1137
|
const character = source[index];
|
|
2104
1138
|
index += 1;
|
|
2105
1139
|
if (character === "x") {
|
|
@@ -2304,694 +1338,6 @@ function tokenizeArtifactGraphModule(source, modulePath) {
|
|
|
2304
1338
|
return completeValue(tokens);
|
|
2305
1339
|
}
|
|
2306
1340
|
|
|
2307
|
-
function artifactGraphDeclarationsByModule(values) {
|
|
2308
|
-
const result = new Map();
|
|
2309
|
-
for (const value of values) {
|
|
2310
|
-
const entries = result.get(value.modulePath) ?? [];
|
|
2311
|
-
entries.push(value);
|
|
2312
|
-
result.set(value.modulePath, entries);
|
|
2313
|
-
}
|
|
2314
|
-
for (const entries of result.values()) {
|
|
2315
|
-
entries.sort((left, right) => left.occurrence - right.occurrence);
|
|
2316
|
-
}
|
|
2317
|
-
return result;
|
|
2318
|
-
}
|
|
2319
|
-
|
|
2320
|
-
function assertArtifactGraphOccurrences(observed, declared, modulePath, subject) {
|
|
2321
|
-
if (observed.length !== declared.length) {
|
|
2322
|
-
throw artifactGraphError(
|
|
2323
|
-
observed.length > declared.length
|
|
2324
|
-
? "artifact-graph-runtime-edge-undeclared"
|
|
2325
|
-
: "artifact-graph-runtime-edge-declaration-unmatched",
|
|
2326
|
-
`${modulePath} exposes ${String(observed.length)} ${subject} occurrence(s), but the graph declares ${String(declared.length)}.`,
|
|
2327
|
-
);
|
|
2328
|
-
}
|
|
2329
|
-
for (let index = 0; index < declared.length; index += 1) {
|
|
2330
|
-
if (declared[index].occurrence !== index + 1) {
|
|
2331
|
-
throw artifactGraphError(
|
|
2332
|
-
"artifact-graph-runtime-edge-occurrence-noncanonical",
|
|
2333
|
-
`${modulePath} ${subject} declarations must use contiguous one-based occurrence values.`,
|
|
2334
|
-
);
|
|
2335
|
-
}
|
|
2336
|
-
}
|
|
2337
|
-
}
|
|
2338
|
-
|
|
2339
|
-
function inspectArtifactGraphModuleSource(source, modulePath, metadata, {
|
|
2340
|
-
strict = false,
|
|
2341
|
-
} = {}) {
|
|
2342
|
-
const tokens = tokenizeArtifactGraphModule(source, modulePath);
|
|
2343
|
-
const staticImports = [];
|
|
2344
|
-
const dynamicImports = [];
|
|
2345
|
-
const fetches = [];
|
|
2346
|
-
const moduleWorkers = [];
|
|
2347
|
-
const cacheOpens = [];
|
|
2348
|
-
const returnThisTransforms = [];
|
|
2349
|
-
const typedArrayConstructors = [];
|
|
2350
|
-
const warnings = new Set();
|
|
2351
|
-
const forbiddenDynamicCode = new Set([
|
|
2352
|
-
"AsyncFunction",
|
|
2353
|
-
"AsyncGeneratorFunction",
|
|
2354
|
-
"BroadcastChannel",
|
|
2355
|
-
"GeneratorFunction",
|
|
2356
|
-
"RTCPeerConnection",
|
|
2357
|
-
"ShadowRealm",
|
|
2358
|
-
"eval",
|
|
2359
|
-
"importScripts",
|
|
2360
|
-
"WebSocketStream",
|
|
2361
|
-
]);
|
|
2362
|
-
|
|
2363
|
-
function next(index, offset = 1) {
|
|
2364
|
-
return tokens[index + offset] ?? null;
|
|
2365
|
-
}
|
|
2366
|
-
|
|
2367
|
-
function previous(index, offset = 1) {
|
|
2368
|
-
return tokens[index - offset] ?? null;
|
|
2369
|
-
}
|
|
2370
|
-
|
|
2371
|
-
function recordGuardCall(target, index, kind) {
|
|
2372
|
-
const token = tokens[index];
|
|
2373
|
-
const opening = next(index);
|
|
2374
|
-
if (opening?.value !== "(") {
|
|
2375
|
-
throw artifactGraphError(
|
|
2376
|
-
`artifact-graph-runtime-${kind.toLowerCase()}-direct-call-required`,
|
|
2377
|
-
`${modulePath} references ${kind} outside an admitted direct call boundary.`,
|
|
2378
|
-
);
|
|
2379
|
-
}
|
|
2380
|
-
let start = token.start;
|
|
2381
|
-
if (previous(index)?.value === ".") {
|
|
2382
|
-
if (!["globalThis", "self"].includes(previous(index, 2)?.value)) {
|
|
2383
|
-
throw artifactGraphError(
|
|
2384
|
-
`artifact-graph-runtime-${kind.toLowerCase()}-receiver-not-global`,
|
|
2385
|
-
`${modulePath} calls ${kind} through a non-global receiver.`,
|
|
2386
|
-
);
|
|
2387
|
-
}
|
|
2388
|
-
start = previous(index, 2).start;
|
|
2389
|
-
}
|
|
2390
|
-
target.push(completeValue({ start, end: opening.end }));
|
|
2391
|
-
}
|
|
2392
|
-
|
|
2393
|
-
function typedArrayConstructor(index) {
|
|
2394
|
-
const opening = next(index);
|
|
2395
|
-
if (opening?.value !== "(") return null;
|
|
2396
|
-
const property = previous(index, 2);
|
|
2397
|
-
if (property?.type !== "identifier") return null;
|
|
2398
|
-
if (previous(index, 3)?.value === "new") {
|
|
2399
|
-
return completeValue({
|
|
2400
|
-
start: property.start,
|
|
2401
|
-
end: tokens[index].end,
|
|
2402
|
-
receiver: source.slice(property.start, property.end),
|
|
2403
|
-
});
|
|
2404
|
-
}
|
|
2405
|
-
if (
|
|
2406
|
-
previous(index, 3)?.value === "."
|
|
2407
|
-
&& previous(index, 4)?.value === "]"
|
|
2408
|
-
&& previous(index, 5)?.value === "0"
|
|
2409
|
-
&& previous(index, 6)?.value === "["
|
|
2410
|
-
&& previous(index, 7)?.type === "identifier"
|
|
2411
|
-
&& previous(index, 8)?.value === "new"
|
|
2412
|
-
) {
|
|
2413
|
-
return completeValue({
|
|
2414
|
-
start: previous(index, 7).start,
|
|
2415
|
-
end: tokens[index].end,
|
|
2416
|
-
receiver: source.slice(previous(index, 7).start, property.end),
|
|
2417
|
-
});
|
|
2418
|
-
}
|
|
2419
|
-
return null;
|
|
2420
|
-
}
|
|
2421
|
-
|
|
2422
|
-
for (let index = 0; index < tokens.length; index += 1) {
|
|
2423
|
-
const token = tokens[index];
|
|
2424
|
-
if (
|
|
2425
|
-
(token.type === "identifier" || token.type === "string")
|
|
2426
|
-
&& token.value === ARTIFACT_GRAPH_GUARDS
|
|
2427
|
-
) {
|
|
2428
|
-
throw artifactGraphError(
|
|
2429
|
-
"artifact-graph-runtime-guard-reference-reserved",
|
|
2430
|
-
`${modulePath} references the SDK-owned artifact graph runtime guard name.`,
|
|
2431
|
-
);
|
|
2432
|
-
}
|
|
2433
|
-
if (
|
|
2434
|
-
token.type === "string"
|
|
2435
|
-
&& [
|
|
2436
|
-
"Function",
|
|
2437
|
-
"BroadcastChannel",
|
|
2438
|
-
"RTCPeerConnection",
|
|
2439
|
-
"ShadowRealm",
|
|
2440
|
-
"SharedWorker",
|
|
2441
|
-
"Worker",
|
|
2442
|
-
"WebSocketStream",
|
|
2443
|
-
"XMLHttpRequest",
|
|
2444
|
-
"constructor",
|
|
2445
|
-
"eval",
|
|
2446
|
-
"fetch",
|
|
2447
|
-
"importScripts",
|
|
2448
|
-
].includes(token.value)
|
|
2449
|
-
&& previous(index)?.value === "["
|
|
2450
|
-
&& next(index)?.value === "]"
|
|
2451
|
-
) {
|
|
2452
|
-
if (strict) {
|
|
2453
|
-
throw artifactGraphError(
|
|
2454
|
-
"artifact-graph-runtime-computed-dynamic-code-undeclared",
|
|
2455
|
-
`${modulePath} contains computed access to dynamic code capability ${token.value}.`,
|
|
2456
|
-
);
|
|
2457
|
-
}
|
|
2458
|
-
warnings.add(token.value);
|
|
2459
|
-
continue;
|
|
2460
|
-
}
|
|
2461
|
-
if (token.type !== "identifier") continue;
|
|
2462
|
-
if (forbiddenDynamicCode.has(token.value)) {
|
|
2463
|
-
if (strict) {
|
|
2464
|
-
throw artifactGraphError(
|
|
2465
|
-
"artifact-graph-runtime-dynamic-code-undeclared",
|
|
2466
|
-
`${modulePath} contains dynamic code capability ${token.value}, which is not admitted.`,
|
|
2467
|
-
);
|
|
2468
|
-
}
|
|
2469
|
-
warnings.add(token.value);
|
|
2470
|
-
continue;
|
|
2471
|
-
}
|
|
2472
|
-
if (token.value === "Function") {
|
|
2473
|
-
const sequence = [
|
|
2474
|
-
next(index)?.value,
|
|
2475
|
-
next(index, 2)?.value,
|
|
2476
|
-
next(index, 3)?.value,
|
|
2477
|
-
next(index, 4)?.value,
|
|
2478
|
-
next(index, 5)?.value,
|
|
2479
|
-
];
|
|
2480
|
-
if (
|
|
2481
|
-
[".", "?."].includes(previous(index)?.value)
|
|
2482
|
-
|| previous(index)?.value === "new"
|
|
2483
|
-
|| sequence[0] !== "("
|
|
2484
|
-
|| next(index, 2)?.type !== "string"
|
|
2485
|
-
|| sequence[1].trim() !== "return this"
|
|
2486
|
-
|| sequence[2] !== ")"
|
|
2487
|
-
|| sequence[3] !== "("
|
|
2488
|
-
|| sequence[4] !== ")"
|
|
2489
|
-
) {
|
|
2490
|
-
if (strict) {
|
|
2491
|
-
throw artifactGraphError(
|
|
2492
|
-
"artifact-graph-runtime-dynamic-code-undeclared",
|
|
2493
|
-
`${modulePath} contains a Function constructor outside the sole supported global-object transform.`,
|
|
2494
|
-
);
|
|
2495
|
-
}
|
|
2496
|
-
warnings.add("Function");
|
|
2497
|
-
continue;
|
|
2498
|
-
}
|
|
2499
|
-
returnThisTransforms.push(completeValue({
|
|
2500
|
-
start: token.start,
|
|
2501
|
-
end: next(index, 5).end,
|
|
2502
|
-
}));
|
|
2503
|
-
index += 5;
|
|
2504
|
-
continue;
|
|
2505
|
-
}
|
|
2506
|
-
if (
|
|
2507
|
-
token.value === "caches"
|
|
2508
|
-
&& next(index)?.value === "."
|
|
2509
|
-
&& next(index, 2)?.value === "open"
|
|
2510
|
-
&& next(index, 3)?.value === "("
|
|
2511
|
-
) {
|
|
2512
|
-
let start = token.start;
|
|
2513
|
-
if (previous(index)?.value === ".") {
|
|
2514
|
-
if (!["globalThis", "self"].includes(previous(index, 2)?.value)) {
|
|
2515
|
-
throw artifactGraphError(
|
|
2516
|
-
"artifact-graph-runtime-cache-open-receiver-not-global",
|
|
2517
|
-
`${modulePath} calls CacheStorage.open through a non-global receiver.`,
|
|
2518
|
-
);
|
|
2519
|
-
}
|
|
2520
|
-
start = previous(index, 2).start;
|
|
2521
|
-
}
|
|
2522
|
-
cacheOpens.push(completeValue({ start, end: next(index, 3).end }));
|
|
2523
|
-
continue;
|
|
2524
|
-
}
|
|
2525
|
-
if (token.value === "caches") {
|
|
2526
|
-
if (previous(index)?.value === "typeof") continue;
|
|
2527
|
-
throw artifactGraphError(
|
|
2528
|
-
"artifact-graph-runtime-cache-open-direct-call-required",
|
|
2529
|
-
`${modulePath} references CacheStorage outside an admitted direct open call.`,
|
|
2530
|
-
);
|
|
2531
|
-
}
|
|
2532
|
-
if (
|
|
2533
|
-
token.value === "constructor"
|
|
2534
|
-
&& [".", "?."].includes(previous(index)?.value)
|
|
2535
|
-
&& next(index)?.value === "("
|
|
2536
|
-
) {
|
|
2537
|
-
const observed = previous(index)?.value === "." ? typedArrayConstructor(index) : null;
|
|
2538
|
-
if (!observed) {
|
|
2539
|
-
throw artifactGraphError(
|
|
2540
|
-
"artifact-graph-runtime-constructor-dynamic-code-undeclared",
|
|
2541
|
-
`${modulePath} calls a constructor property outside the declared typed-array constructor transform.`,
|
|
2542
|
-
);
|
|
2543
|
-
}
|
|
2544
|
-
typedArrayConstructors.push(observed);
|
|
2545
|
-
continue;
|
|
2546
|
-
}
|
|
2547
|
-
if (token.value === "import") {
|
|
2548
|
-
if (next(index)?.value === "." && next(index, 2)?.value === "meta") {
|
|
2549
|
-
index += 2;
|
|
2550
|
-
continue;
|
|
2551
|
-
}
|
|
2552
|
-
if (next(index)?.value === "(") {
|
|
2553
|
-
dynamicImports.push(completeValue({ start: token.start, end: next(index).end }));
|
|
2554
|
-
continue;
|
|
2555
|
-
}
|
|
2556
|
-
let specifier = next(index)?.type === "string" ? next(index) : null;
|
|
2557
|
-
if (!specifier) {
|
|
2558
|
-
for (let cursor = index + 1; cursor < tokens.length; cursor += 1) {
|
|
2559
|
-
if (tokens[cursor].value === ";") break;
|
|
2560
|
-
if (tokens[cursor].value === "from" && next(cursor)?.type === "string") {
|
|
2561
|
-
specifier = next(cursor);
|
|
2562
|
-
break;
|
|
2563
|
-
}
|
|
2564
|
-
}
|
|
2565
|
-
}
|
|
2566
|
-
if (!specifier) {
|
|
2567
|
-
throw artifactGraphError(
|
|
2568
|
-
"artifact-graph-static-import-specifier-unresolved",
|
|
2569
|
-
`${modulePath} contains a static import without one literal specifier.`,
|
|
2570
|
-
);
|
|
2571
|
-
}
|
|
2572
|
-
staticImports.push(completeValue({
|
|
2573
|
-
start: specifier.start,
|
|
2574
|
-
end: specifier.end,
|
|
2575
|
-
specifier: specifier.value,
|
|
2576
|
-
}));
|
|
2577
|
-
continue;
|
|
2578
|
-
}
|
|
2579
|
-
if (token.value === "export") {
|
|
2580
|
-
if (!["*", "{"].includes(next(index)?.value)) continue;
|
|
2581
|
-
let specifier = null;
|
|
2582
|
-
for (let cursor = index + 1; cursor < tokens.length; cursor += 1) {
|
|
2583
|
-
if (tokens[cursor].value === ";") break;
|
|
2584
|
-
if (tokens[cursor].value === "from" && next(cursor)?.type === "string") {
|
|
2585
|
-
specifier = next(cursor);
|
|
2586
|
-
break;
|
|
2587
|
-
}
|
|
2588
|
-
if (["export", "import"].includes(tokens[cursor].value)) break;
|
|
2589
|
-
}
|
|
2590
|
-
if (specifier) {
|
|
2591
|
-
staticImports.push(completeValue({
|
|
2592
|
-
start: specifier.start,
|
|
2593
|
-
end: specifier.end,
|
|
2594
|
-
specifier: specifier.value,
|
|
2595
|
-
}));
|
|
2596
|
-
}
|
|
2597
|
-
continue;
|
|
2598
|
-
}
|
|
2599
|
-
if (token.value === "fetch") {
|
|
2600
|
-
recordGuardCall(fetches, index, "fetch");
|
|
2601
|
-
continue;
|
|
2602
|
-
}
|
|
2603
|
-
if (token.value === "Worker") {
|
|
2604
|
-
const opening = next(index);
|
|
2605
|
-
if (opening?.value !== "(") {
|
|
2606
|
-
if (strict) {
|
|
2607
|
-
throw artifactGraphError(
|
|
2608
|
-
"artifact-graph-runtime-worker-constructor-call-required",
|
|
2609
|
-
`${modulePath} references Worker outside an admitted constructor boundary.`,
|
|
2610
|
-
);
|
|
2611
|
-
}
|
|
2612
|
-
warnings.add("Worker");
|
|
2613
|
-
continue;
|
|
2614
|
-
}
|
|
2615
|
-
let start = token.start;
|
|
2616
|
-
if (previous(index)?.value === "new") {
|
|
2617
|
-
start = previous(index).start;
|
|
2618
|
-
} else if (previous(index)?.value === ".") {
|
|
2619
|
-
if (!["globalThis", "self"].includes(previous(index, 2)?.value)) {
|
|
2620
|
-
if (strict) {
|
|
2621
|
-
throw artifactGraphError(
|
|
2622
|
-
"artifact-graph-runtime-worker-receiver-not-global",
|
|
2623
|
-
`${modulePath} constructs Worker through a non-global receiver.`,
|
|
2624
|
-
);
|
|
2625
|
-
}
|
|
2626
|
-
warnings.add("Worker");
|
|
2627
|
-
continue;
|
|
2628
|
-
}
|
|
2629
|
-
start = previous(index, 2).start;
|
|
2630
|
-
if (previous(index, 3)?.value === "new") start = previous(index, 3).start;
|
|
2631
|
-
}
|
|
2632
|
-
moduleWorkers.push(completeValue({ start, end: opening.end }));
|
|
2633
|
-
}
|
|
2634
|
-
}
|
|
2635
|
-
|
|
2636
|
-
const moduleTransforms = artifactGraphDeclarationsByModule(metadata.transforms)
|
|
2637
|
-
.get(modulePath) ?? [];
|
|
2638
|
-
const declarations = {
|
|
2639
|
-
staticImports: artifactGraphDeclarationsByModule(metadata.edges.staticImports)
|
|
2640
|
-
.get(modulePath) ?? [],
|
|
2641
|
-
dynamicImports: artifactGraphDeclarationsByModule(metadata.edges.dynamicImports)
|
|
2642
|
-
.get(modulePath) ?? [],
|
|
2643
|
-
fetches: artifactGraphDeclarationsByModule(metadata.edges.fetches)
|
|
2644
|
-
.get(modulePath) ?? [],
|
|
2645
|
-
moduleWorkers: artifactGraphDeclarationsByModule(metadata.edges.moduleWorkers)
|
|
2646
|
-
.get(modulePath) ?? [],
|
|
2647
|
-
cacheOpens: artifactGraphDeclarationsByModule(metadata.edges.cacheOpens)
|
|
2648
|
-
.get(modulePath) ?? [],
|
|
2649
|
-
returnThisTransforms: moduleTransforms.filter((transform) =>
|
|
2650
|
-
transform.kind === "function-return-this-to-global-this"),
|
|
2651
|
-
typedArrayConstructors: moduleTransforms.filter((transform) =>
|
|
2652
|
-
transform.kind === "typed-array-constructor"),
|
|
2653
|
-
};
|
|
2654
|
-
assertArtifactGraphOccurrences(
|
|
2655
|
-
staticImports,
|
|
2656
|
-
declarations.staticImports,
|
|
2657
|
-
modulePath,
|
|
2658
|
-
"static import or re-export",
|
|
2659
|
-
);
|
|
2660
|
-
assertArtifactGraphOccurrences(
|
|
2661
|
-
dynamicImports,
|
|
2662
|
-
declarations.dynamicImports,
|
|
2663
|
-
modulePath,
|
|
2664
|
-
"dynamic import",
|
|
2665
|
-
);
|
|
2666
|
-
assertArtifactGraphOccurrences(fetches, declarations.fetches, modulePath, "fetch");
|
|
2667
|
-
assertArtifactGraphOccurrences(
|
|
2668
|
-
moduleWorkers,
|
|
2669
|
-
declarations.moduleWorkers,
|
|
2670
|
-
modulePath,
|
|
2671
|
-
"module Worker",
|
|
2672
|
-
);
|
|
2673
|
-
assertArtifactGraphOccurrences(
|
|
2674
|
-
cacheOpens,
|
|
2675
|
-
declarations.cacheOpens,
|
|
2676
|
-
modulePath,
|
|
2677
|
-
"CacheStorage open",
|
|
2678
|
-
);
|
|
2679
|
-
assertArtifactGraphOccurrences(
|
|
2680
|
-
returnThisTransforms,
|
|
2681
|
-
declarations.returnThisTransforms,
|
|
2682
|
-
modulePath,
|
|
2683
|
-
"Function return-this transform",
|
|
2684
|
-
);
|
|
2685
|
-
assertArtifactGraphOccurrences(
|
|
2686
|
-
typedArrayConstructors,
|
|
2687
|
-
declarations.typedArrayConstructors,
|
|
2688
|
-
modulePath,
|
|
2689
|
-
"typed-array constructor transform",
|
|
2690
|
-
);
|
|
2691
|
-
for (let index = 0; index < staticImports.length; index += 1) {
|
|
2692
|
-
if (staticImports[index].specifier !== declarations.staticImports[index].specifier) {
|
|
2693
|
-
throw artifactGraphError(
|
|
2694
|
-
"artifact-graph-static-import-specifier-mismatch",
|
|
2695
|
-
`${modulePath} static import occurrence ${String(index + 1)} does not match its declared specifier.`,
|
|
2696
|
-
);
|
|
2697
|
-
}
|
|
2698
|
-
}
|
|
2699
|
-
return completeValue({
|
|
2700
|
-
source,
|
|
2701
|
-
staticImports: completeValue(staticImports),
|
|
2702
|
-
dynamicImports: completeValue(dynamicImports),
|
|
2703
|
-
fetches: completeValue(fetches),
|
|
2704
|
-
moduleWorkers: completeValue(moduleWorkers),
|
|
2705
|
-
cacheOpens: completeValue(cacheOpens),
|
|
2706
|
-
returnThisTransforms: completeValue(returnThisTransforms),
|
|
2707
|
-
typedArrayConstructors: completeValue(typedArrayConstructors),
|
|
2708
|
-
warnings: completeValue([...warnings].sort()),
|
|
2709
|
-
declarations,
|
|
2710
|
-
});
|
|
2711
|
-
}
|
|
2712
|
-
|
|
2713
|
-
function assertArtifactGraphStaticImportClosure(metadata) {
|
|
2714
|
-
const dependencies = new Map(metadata.runtimeFiles
|
|
2715
|
-
.filter((file) => ARTIFACT_GRAPH_JAVASCRIPT_KINDS.has(file.kind))
|
|
2716
|
-
.map((file) => [file.path, []]));
|
|
2717
|
-
for (const edge of metadata.edges.staticImports) {
|
|
2718
|
-
dependencies.get(edge.modulePath).push(edge.targetPath);
|
|
2719
|
-
}
|
|
2720
|
-
const visiting = new Set();
|
|
2721
|
-
const visited = new Set();
|
|
2722
|
-
const order = [];
|
|
2723
|
-
function visit(path) {
|
|
2724
|
-
if (visiting.has(path)) {
|
|
2725
|
-
throw artifactGraphError(
|
|
2726
|
-
"artifact-graph-runtime-static-import-cycle",
|
|
2727
|
-
`Artifact graph static imports contain a cycle at ${path}.`,
|
|
2728
|
-
);
|
|
2729
|
-
}
|
|
2730
|
-
if (visited.has(path)) return;
|
|
2731
|
-
visiting.add(path);
|
|
2732
|
-
for (const dependency of dependencies.get(path) ?? []) visit(dependency);
|
|
2733
|
-
visiting.delete(path);
|
|
2734
|
-
visited.add(path);
|
|
2735
|
-
order.push(path);
|
|
2736
|
-
}
|
|
2737
|
-
for (const path of [...dependencies.keys()].sort()) visit(path);
|
|
2738
|
-
return completeValue(order);
|
|
2739
|
-
}
|
|
2740
|
-
|
|
2741
|
-
// Dormant hardening retained for future review only. Ordinary materialization
|
|
2742
|
-
// uses the permissive router below; these declaration, capability, and closed
|
|
2743
|
-
// inspection controls must not be enabled without explicit user review.
|
|
2744
|
-
async function inspectArtifactGraphRuntime(admitted, metadata, signal) {
|
|
2745
|
-
const admittedByPath = new Map(admitted.files.map((entry) => [entry.descriptor.path, entry]));
|
|
2746
|
-
const plans = new Map();
|
|
2747
|
-
for (const descriptor of metadata.runtimeFiles) {
|
|
2748
|
-
throwIfAborted(signal);
|
|
2749
|
-
if (!ARTIFACT_GRAPH_JAVASCRIPT_KINDS.has(descriptor.kind)) continue;
|
|
2750
|
-
const file = admittedByPath.get(descriptor.path)?.file;
|
|
2751
|
-
if (!file) {
|
|
2752
|
-
throw artifactGraphError(
|
|
2753
|
-
"artifact-graph-runtime-javascript-file-missing",
|
|
2754
|
-
`Artifact graph runtime JavaScript file ${descriptor.path} is unavailable.`,
|
|
2755
|
-
);
|
|
2756
|
-
}
|
|
2757
|
-
let source;
|
|
2758
|
-
try {
|
|
2759
|
-
const bytes = new Uint8Array(await file.arrayBuffer());
|
|
2760
|
-
source = new TextDecoder("utf-8", { fatal: true }).decode(bytes);
|
|
2761
|
-
} catch (error) {
|
|
2762
|
-
throw artifactGraphError(
|
|
2763
|
-
"artifact-graph-runtime-javascript-utf8-decode-rejected",
|
|
2764
|
-
`Artifact graph runtime JavaScript file ${descriptor.path} is not valid UTF-8.`,
|
|
2765
|
-
error,
|
|
2766
|
-
);
|
|
2767
|
-
}
|
|
2768
|
-
plans.set(
|
|
2769
|
-
descriptor.path,
|
|
2770
|
-
inspectArtifactGraphModuleSource(source, descriptor.path, metadata, {
|
|
2771
|
-
strict: false,
|
|
2772
|
-
}),
|
|
2773
|
-
);
|
|
2774
|
-
}
|
|
2775
|
-
const order = assertArtifactGraphStaticImportClosure(metadata);
|
|
2776
|
-
const warnings = [...new Set([...plans.values()].flatMap((plan) => plan.warnings))]
|
|
2777
|
-
.sort();
|
|
2778
|
-
throwIfAborted(signal);
|
|
2779
|
-
return completeValue({ plans, order, warnings: completeValue(warnings) });
|
|
2780
|
-
}
|
|
2781
|
-
|
|
2782
|
-
function assertArtifactGraphRuntimeInspection(inspection) {
|
|
2783
|
-
if (
|
|
2784
|
-
!inspection
|
|
2785
|
-
|| !(inspection.plans instanceof Map)
|
|
2786
|
-
|| !Array.isArray(inspection.order)
|
|
2787
|
-
|| !Array.isArray(inspection.warnings)
|
|
2788
|
-
) {
|
|
2789
|
-
throw artifactGraphError(
|
|
2790
|
-
"artifact-graph-runtime-inspection-missing",
|
|
2791
|
-
"Artifact graph runtime inspection is required before executable materialization.",
|
|
2792
|
-
);
|
|
2793
|
-
}
|
|
2794
|
-
return inspection;
|
|
2795
|
-
}
|
|
2796
|
-
|
|
2797
|
-
function applyArtifactGraphModuleTransforms(plan, materializedByPath, guardCapability) {
|
|
2798
|
-
const replacements = [];
|
|
2799
|
-
for (let index = 0; index < plan.staticImports.length; index += 1) {
|
|
2800
|
-
const observed = plan.staticImports[index];
|
|
2801
|
-
const declared = plan.declarations.staticImports[index];
|
|
2802
|
-
const targetUrl = materializedByPath.get(declared.targetPath)?.moduleUrl;
|
|
2803
|
-
if (!targetUrl) {
|
|
2804
|
-
throw artifactGraphError(
|
|
2805
|
-
"artifact-graph-static-import-target-unmaterialized",
|
|
2806
|
-
`Static import target ${declared.targetPath} was not materialized before ${declared.modulePath}.`,
|
|
2807
|
-
);
|
|
2808
|
-
}
|
|
2809
|
-
replacements.push({
|
|
2810
|
-
start: observed.start,
|
|
2811
|
-
end: observed.end,
|
|
2812
|
-
value: JSON.stringify(targetUrl),
|
|
2813
|
-
});
|
|
2814
|
-
}
|
|
2815
|
-
for (let index = 0; index < plan.dynamicImports.length; index += 1) {
|
|
2816
|
-
const observed = plan.dynamicImports[index];
|
|
2817
|
-
const declared = plan.declarations.dynamicImports[index];
|
|
2818
|
-
replacements.push({
|
|
2819
|
-
start: observed.start,
|
|
2820
|
-
end: observed.end,
|
|
2821
|
-
value: `globalThis.${ARTIFACT_GRAPH_GUARDS}.dynamicImport(${JSON.stringify(guardCapability)},${JSON.stringify(declared.modulePath)},${String(declared.occurrence)},`,
|
|
2822
|
-
});
|
|
2823
|
-
}
|
|
2824
|
-
for (let index = 0; index < plan.fetches.length; index += 1) {
|
|
2825
|
-
const observed = plan.fetches[index];
|
|
2826
|
-
const declared = plan.declarations.fetches[index];
|
|
2827
|
-
replacements.push({
|
|
2828
|
-
start: observed.start,
|
|
2829
|
-
end: observed.end,
|
|
2830
|
-
value: `globalThis.${ARTIFACT_GRAPH_GUARDS}.fetch(${JSON.stringify(guardCapability)},${JSON.stringify(declared.modulePath)},${String(declared.occurrence)},`,
|
|
2831
|
-
});
|
|
2832
|
-
}
|
|
2833
|
-
for (let index = 0; index < plan.moduleWorkers.length; index += 1) {
|
|
2834
|
-
const observed = plan.moduleWorkers[index];
|
|
2835
|
-
const declared = plan.declarations.moduleWorkers[index];
|
|
2836
|
-
replacements.push({
|
|
2837
|
-
start: observed.start,
|
|
2838
|
-
end: observed.end,
|
|
2839
|
-
value: `globalThis.${ARTIFACT_GRAPH_GUARDS}.createWorker(${JSON.stringify(guardCapability)},${JSON.stringify(declared.modulePath)},${String(declared.occurrence)},`,
|
|
2840
|
-
});
|
|
2841
|
-
}
|
|
2842
|
-
for (let index = 0; index < plan.cacheOpens.length; index += 1) {
|
|
2843
|
-
const observed = plan.cacheOpens[index];
|
|
2844
|
-
const declared = plan.declarations.cacheOpens[index];
|
|
2845
|
-
replacements.push({
|
|
2846
|
-
start: observed.start,
|
|
2847
|
-
end: observed.end,
|
|
2848
|
-
value: `globalThis.${ARTIFACT_GRAPH_GUARDS}.openCache(${JSON.stringify(guardCapability)},${JSON.stringify(declared.modulePath)},${String(declared.occurrence)},`,
|
|
2849
|
-
});
|
|
2850
|
-
}
|
|
2851
|
-
for (const observed of plan.returnThisTransforms) {
|
|
2852
|
-
replacements.push({ start: observed.start, end: observed.end, value: "globalThis" });
|
|
2853
|
-
}
|
|
2854
|
-
for (let index = 0; index < plan.typedArrayConstructors.length; index += 1) {
|
|
2855
|
-
const observed = plan.typedArrayConstructors[index];
|
|
2856
|
-
const declared = plan.declarations.typedArrayConstructors[index];
|
|
2857
|
-
replacements.push({
|
|
2858
|
-
start: observed.start,
|
|
2859
|
-
end: observed.end,
|
|
2860
|
-
value: `(globalThis.${ARTIFACT_GRAPH_GUARDS}.typedArrayConstructor(${JSON.stringify(guardCapability)},${JSON.stringify(declared.modulePath)},${String(declared.occurrence)},${observed.receiver}))`,
|
|
2861
|
-
});
|
|
2862
|
-
}
|
|
2863
|
-
replacements.sort((left, right) => right.start - left.start);
|
|
2864
|
-
let source = plan.source;
|
|
2865
|
-
let previousStart = source.length;
|
|
2866
|
-
for (const replacement of replacements) {
|
|
2867
|
-
if (replacement.end > previousStart) {
|
|
2868
|
-
throw artifactGraphError(
|
|
2869
|
-
"artifact-graph-module-transform-overlap",
|
|
2870
|
-
"Artifact graph module transforms overlap and cannot be applied deterministically.",
|
|
2871
|
-
);
|
|
2872
|
-
}
|
|
2873
|
-
source = `${source.slice(0, replacement.start)}${replacement.value}${source.slice(replacement.end)}`;
|
|
2874
|
-
previousStart = replacement.start;
|
|
2875
|
-
}
|
|
2876
|
-
return source;
|
|
2877
|
-
}
|
|
2878
|
-
|
|
2879
|
-
function artifactGraphGuardCapability() {
|
|
2880
|
-
return "ordinary";
|
|
2881
|
-
}
|
|
2882
|
-
|
|
2883
|
-
async function createArtifactGraphObjectUrls(admitted, metadata, inspection) {
|
|
2884
|
-
if (
|
|
2885
|
-
typeof PLATFORM_CREATE_OBJECT_URL !== "function"
|
|
2886
|
-
|| typeof PLATFORM_REVOKE_OBJECT_URL !== "function"
|
|
2887
|
-
) {
|
|
2888
|
-
throw artifactGraphError(
|
|
2889
|
-
"artifact-graph-object-url-platform-unavailable",
|
|
2890
|
-
"Artifact graph materialization requires native Blob URL creation and revocation.",
|
|
2891
|
-
);
|
|
2892
|
-
}
|
|
2893
|
-
const admittedByPath = new Map(admitted.files.map((entry) => [entry.descriptor.path, entry]));
|
|
2894
|
-
const materializedByPath = new Map();
|
|
2895
|
-
const created = [];
|
|
2896
|
-
const createdIdentities = new Set();
|
|
2897
|
-
const guardCapability = artifactGraphGuardCapability();
|
|
2898
|
-
const runtimeInspection = assertArtifactGraphRuntimeInspection(inspection);
|
|
2899
|
-
async function materialize(descriptor, body) {
|
|
2900
|
-
const blob = body instanceof Blob && body.type === descriptor.mediaType
|
|
2901
|
-
? body
|
|
2902
|
-
: new Blob([body], { type: descriptor.mediaType });
|
|
2903
|
-
const moduleUrl = PLATFORM_CREATE_OBJECT_URL(blob);
|
|
2904
|
-
if (typeof moduleUrl !== "string" || !moduleUrl.startsWith("blob:")) {
|
|
2905
|
-
throw artifactGraphError(
|
|
2906
|
-
"artifact-graph-object-url-scheme-not-blob",
|
|
2907
|
-
`Materialized artifact graph file ${descriptor.path} did not produce a Blob URL.`,
|
|
2908
|
-
);
|
|
2909
|
-
}
|
|
2910
|
-
if (createdIdentities.has(moduleUrl)) {
|
|
2911
|
-
throw artifactGraphError(
|
|
2912
|
-
"artifact-graph-object-url-identity-ambiguous",
|
|
2913
|
-
`Materialized artifact graph file ${descriptor.path} reused another file's Blob URL.`,
|
|
2914
|
-
);
|
|
2915
|
-
}
|
|
2916
|
-
createdIdentities.add(moduleUrl);
|
|
2917
|
-
created.push(moduleUrl);
|
|
2918
|
-
materializedByPath.set(descriptor.path, completeValue({
|
|
2919
|
-
kind: descriptor.kind,
|
|
2920
|
-
path: descriptor.path,
|
|
2921
|
-
sourceUrl: descriptor.sourceUrl,
|
|
2922
|
-
revision: descriptor.revision,
|
|
2923
|
-
moduleUrl,
|
|
2924
|
-
mediaType: descriptor.mediaType,
|
|
2925
|
-
...(descriptor.sourceMediaType === descriptor.mediaType
|
|
2926
|
-
? {}
|
|
2927
|
-
: { sourceMediaType: descriptor.sourceMediaType }),
|
|
2928
|
-
runtimeRequestUrls: descriptor.runtimeRequestUrls,
|
|
2929
|
-
...(descriptor.redirectFinalOrigins.length < 1
|
|
2930
|
-
? {}
|
|
2931
|
-
: { redirectFinalOrigins: descriptor.redirectFinalOrigins }),
|
|
2932
|
-
}));
|
|
2933
|
-
}
|
|
2934
|
-
try {
|
|
2935
|
-
for (const descriptor of metadata.files) {
|
|
2936
|
-
if (ARTIFACT_GRAPH_JAVASCRIPT_KINDS.has(descriptor.kind)) continue;
|
|
2937
|
-
const admittedFile = admittedByPath.get(descriptor.path)?.file;
|
|
2938
|
-
if (!admittedFile) {
|
|
2939
|
-
throw artifactGraphError(
|
|
2940
|
-
"artifact-graph-materialized-source-file-missing",
|
|
2941
|
-
`Artifact graph file ${descriptor.path} is unavailable for materialization.`,
|
|
2942
|
-
);
|
|
2943
|
-
}
|
|
2944
|
-
await materialize(descriptor, admittedFile);
|
|
2945
|
-
}
|
|
2946
|
-
for (const path of runtimeInspection.order) {
|
|
2947
|
-
const descriptor = metadata.filesByPath.get(path);
|
|
2948
|
-
const plan = runtimeInspection.plans.get(path);
|
|
2949
|
-
if (!descriptor || !plan) {
|
|
2950
|
-
throw artifactGraphError(
|
|
2951
|
-
"artifact-graph-runtime-inspection-incomplete",
|
|
2952
|
-
`Artifact graph runtime inspection is incomplete for ${path}.`,
|
|
2953
|
-
);
|
|
2954
|
-
}
|
|
2955
|
-
await materialize(
|
|
2956
|
-
descriptor,
|
|
2957
|
-
applyArtifactGraphModuleTransforms(plan, materializedByPath, guardCapability),
|
|
2958
|
-
);
|
|
2959
|
-
}
|
|
2960
|
-
const files = metadata.files.map((descriptor) => {
|
|
2961
|
-
const materialized = materializedByPath.get(descriptor.path);
|
|
2962
|
-
if (!materialized) {
|
|
2963
|
-
throw artifactGraphError(
|
|
2964
|
-
"artifact-graph-materialization-incomplete",
|
|
2965
|
-
`Artifact graph file ${descriptor.path} was not materialized.`,
|
|
2966
|
-
);
|
|
2967
|
-
}
|
|
2968
|
-
return materialized;
|
|
2969
|
-
});
|
|
2970
|
-
return completeValue({
|
|
2971
|
-
guardCapability,
|
|
2972
|
-
files: completeValue(files),
|
|
2973
|
-
release() {
|
|
2974
|
-
for (const url of created.splice(0).reverse()) {
|
|
2975
|
-
try {
|
|
2976
|
-
PLATFORM_REVOKE_OBJECT_URL(url);
|
|
2977
|
-
} catch {
|
|
2978
|
-
// Object URL revocation follows worker termination and is best effort.
|
|
2979
|
-
}
|
|
2980
|
-
}
|
|
2981
|
-
},
|
|
2982
|
-
});
|
|
2983
|
-
} catch (error) {
|
|
2984
|
-
for (const url of created.splice(0).reverse()) {
|
|
2985
|
-
try {
|
|
2986
|
-
PLATFORM_REVOKE_OBJECT_URL(url);
|
|
2987
|
-
} catch {
|
|
2988
|
-
// Preserve the graph inspection or materialization error.
|
|
2989
|
-
}
|
|
2990
|
-
}
|
|
2991
|
-
throw error;
|
|
2992
|
-
}
|
|
2993
|
-
}
|
|
2994
|
-
|
|
2995
1341
|
function ordinaryArtifactUrl(value, base) {
|
|
2996
1342
|
try {
|
|
2997
1343
|
return new URL(value, base).href;
|
|
@@ -3382,9 +1728,6 @@ async function createOrdinaryArtifactObjectUrls(
|
|
|
3382
1728
|
? {}
|
|
3383
1729
|
: { sourceMediaType: descriptor.sourceMediaType }),
|
|
3384
1730
|
runtimeRequestUrls: descriptor.runtimeRequestUrls,
|
|
3385
|
-
...(descriptor.redirectFinalOrigins.length < 1
|
|
3386
|
-
? {}
|
|
3387
|
-
: { redirectFinalOrigins: descriptor.redirectFinalOrigins }),
|
|
3388
1731
|
}));
|
|
3389
1732
|
}
|
|
3390
1733
|
try {
|
|
@@ -3591,18 +1934,15 @@ export function createDbopfsSpeechArtifactStore({
|
|
|
3591
1934
|
const metadata = artifactMetadata(authority);
|
|
3592
1935
|
const names = storageNames(authority, metadata.files);
|
|
3593
1936
|
const priorSelection = await readJsonFile(names.selection);
|
|
3594
|
-
const
|
|
3595
|
-
|
|
3596
|
-
|
|
3597
|
-
Array.isArray(legacySelection?.files) ? legacySelection.files.length : 0,
|
|
3598
|
-
);
|
|
1937
|
+
const priorCount = Array.isArray(priorSelection?.files)
|
|
1938
|
+
? priorSelection.files.length
|
|
1939
|
+
: 0;
|
|
3599
1940
|
const fileNames = Array.from(
|
|
3600
1941
|
{ length: Math.max(names.files.length, priorCount) },
|
|
3601
1942
|
(_, index) => `${names.key}.${String(index).padStart(4, "0")}.artifact`,
|
|
3602
1943
|
);
|
|
3603
1944
|
const results = await Promise.all([
|
|
3604
1945
|
removeEntry(names.selection),
|
|
3605
|
-
removeEntry(names.legacyManifest),
|
|
3606
1946
|
...fileNames.map(removeEntry),
|
|
3607
1947
|
]);
|
|
3608
1948
|
return results.some(Boolean);
|