Files
rea/tests/acceptance/applications/javascriptWorkerUrlCli.test.ts
Yann Robertoandbaron 653067d799 fix(javascript): resolve module-relative Worker URL references (#2104)
* fix(javascript): resolve module-relative Worker URL references

* fix(javascript): validate Worker URL paths independently of suffixes

* fix(javascript): preserve directory-form Worker URL paths

* fix(javascript): unwrap type assertions in Worker URL references

* fix(javascript): validate Worker bindings before URL resolution

* fix(javascript): leave drive-letter Worker URLs unresolved

Use WHATWG file URL parsing before pathname decoding to detect drive-letter
forms whose resource identity depends on the unmodeled source URL scheme.
Preserve encoded and ordinary relative pipe paths, and cover false matches
through the compiled CLI and resolver boundary tests.

* fix(javascript): preserve Worker URL identity boundaries

Recognize known Worker bindings before filtering callee spellings so ESM
aliases and transparent constructor wrappers reach URL analysis. Keep
root-relative module URLs unknown without a modeled runtime base root.

Index direct source-local global writes with the shared mutation collector
before promoting URL and browser Worker constructors. Preserve lexical
shadowing and unrelated property identities, and exercise all three review
regressions through the compiled CLI.

* fix(javascript): invalidate mutated import.meta URL bases

Track source-local import.meta writes alongside ambient global writes so
Worker URL expressions keep an unknown base after a possible URL mutation.
Tag the identity roots independently and cover direct mutation and unrelated
property cases through the compiled CLI.

* fix(javascript): close Worker URL identity and pathname gaps

Resolve ambient object identities before appending written properties, and
retain possible source-local aliases across rebinding. Reuse intrinsic
invocation handling for aliased and bound mutators, array spreads, and
transparent TypeScript wrappers while preserving Electron write semantics.

Keep malformed-percent nested Worker URL pathnames unknown because the base
scheme is unavailable and Node rejects these file URLs. Preserve tolerant
HTML URL handling, valid escapes, and query/fragment evidence.

Cover the new cases and unrelated writes through the compiled CLI with
existing-file decoys. All 591 targeted tests and npm run check pass.

* fix(javascript): bound alias expansion and classify Worker paths

Join ambient identity projections instead of enumerating alias histories.
Memoize repeated value and provenance queries while preserving capture,
depth, and cycle-dependent uncertainty.

Keep Node Worker string arguments unresolved without filesystem or cwd
context, and admit qualified global URL constructors through identity checks.

Validate three 40-level alias branch shapes through the compiled CLI,
925 source tests, 25 CLI/boundary tests, and the full npm run check gate.

* fix(server): preserve partial Evidence target-content provenance

Pass the exact named contract returnsTargetContent flag through the shared partial-Evidence error delivery path introduced by the shortlist integration.

Exercise successful and failed recording through in-memory MCP for marked and unmarked contracts, preserving source payload and original failures. Observe both shared-handler emission and wire delivery so the SDK error-marker fallback cannot hide omissions. Suppressing the flag fails exactly the two marked cases.

Validation: check:fast, check, 736 semantic tests, 29 MCP transport/error tests and six compiled static Worker CLI journeys pass. No live target/provider execution or hosted publication.

* fix(javascript): structurally share semantic dependency proofs

---------

Co-authored-by: baron <richstyles@gmail.com>
2026-10-13 07:24:45 +09:00

627 lines
25 KiB
TypeScript

import { expect } from "vitest";
import type { JavaScriptApplicationGraph } from "../../../src/domain/javascript/javascriptApplicationGraph.js";
import { createTestTempDirectory } from "../../fixtures/temporaryDirectory.js";
import { cliTest } from "../../support/cli/cliFixture.js";
import { analyzeJavaScriptDirectory } from "../../support/cli/javascriptApplication.js";
import { writeFixtureFiles } from "../../support/javascriptApplicationFixture.js";
const workerReferences = (graph: JavaScriptApplicationGraph) =>
graph.nodes
.filter(({ kind }) => kind === "worker")
.flatMap((node) =>
node.observations.map(({ properties, evidence }) => ({
source:
evidence.location.available &&
evidence.location.value.kind === "source-range"
? evidence.location.value.source
: null,
declared_specifier: properties.declared_specifier,
expression: properties.expression,
resolved_path: properties.resolved_path,
mapped_paths: graph.edges
.filter(
({ relation, source_node_id }) =>
relation === "maps_to" && source_node_id === node.node_id,
)
.map(({ properties: edge }) => edge.resolved_path),
})),
);
cliTest(
"promotes Worker URLs only for known constructor bindings through the CLI",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-worker-identity-");
const identityCases = [
...["globalThis", "window", "self"].map((namespace) => ({
prefix: "",
constructor: `new ${namespace}.Worker`,
suffix: "",
resolves: true,
})),
...["worker_threads", "node:worker_threads"].flatMap((specifier) => [
{
prefix: `import { Worker } from "${specifier}";`,
constructor: "new Worker",
suffix: "",
resolves: true,
},
{
prefix: `import * as threads from "${specifier}";`,
constructor: "new threads.Worker",
suffix: "",
resolves: true,
},
]),
{
prefix: 'import { Worker as ThreadWorker } from "node:worker_threads";',
constructor: "new ThreadWorker",
suffix: "",
resolves: true,
},
{
prefix: 'import { Worker } from "node:worker_threads";',
constructor: "new (Worker as typeof Worker)",
suffix: "",
resolves: true,
},
{
prefix: 'import * as threads from "node:worker_threads";',
constructor: "new ((threads as typeof threads).Worker!)",
suffix: "",
resolves: true,
},
{
prefix:
'import { Worker } from "node:worker_threads"; function start(globalThis) { globalThis.URL = Fake;',
constructor: "new Worker",
suffix: "}",
resolves: true,
},
{
prefix: 'globalThis.unrelated = Fake; URL.extra = "metadata";',
constructor: "new Worker",
suffix: "",
resolves: true,
},
...[
["class Worker {}", "new Worker", ""],
["globalThis.Worker = Fake;", "new Worker", ""],
["globalThis.Worker = Fake;", "new window.Worker", ""],
["globalThis = replacement;", "new globalThis.Worker", ""],
["function start(Worker) {", "new Worker", "}"],
['import { Worker } from "./custom.js";', "new Worker", ""],
["const custom = { Worker: class {} };", "new custom.Worker", ""],
["function start(window) {", "new window.Worker", "}"],
["function start(globalThis) {", "new globalThis.Worker", "}"],
[
'import type { Worker } from "node:worker_threads";',
"new Worker",
"",
],
[
'import { type Worker } from "node:worker_threads";',
"new Worker",
"",
],
[
'import type * as threads from "node:worker_threads";',
"new threads.Worker",
"",
],
[
'import threads from "node:worker_threads"; threads.Worker = Fake;',
"new threads.Worker",
"",
],
[
'const threads = require("node:worker_threads"); threads.Worker = Fake;',
"new threads.Worker",
"",
],
[
'import threads = require("node:worker_threads"); threads.Worker = Fake;',
"new threads.Worker",
"",
],
[
'import { Worker } from "node:worker_threads"; Worker = Fake;',
"new Worker",
"",
],
["", "Worker", ""],
].map(([prefix, constructor, suffix]) => ({
prefix,
constructor,
suffix,
resolves: false,
})),
].map(({ prefix, constructor, suffix, resolves }, index) => ({
source: `identity-${index}.ts`,
prefix,
expression: `${constructor}(new URL("./worker.js", import.meta.url))`,
suffix,
resolves,
}));
await writeFixtureFiles(root, {
...Object.fromEntries(
identityCases.map(({ source, prefix, expression, suffix }) => [
source,
`${prefix}\n${expression};${suffix}`,
]),
),
"worker.js": "self.postMessage('existing worker file');",
"custom.js": "export class Worker {}",
"custom-alias.mjs":
'import { Worker as ThreadWorker } from "./custom.js"; new ThreadWorker(new URL("./worker.js", import.meta.url));',
});
const result = await analyzeJavaScriptDirectory("CLI", cli, root);
expect(result.statistics.parse_failures).toBe(0);
const references = workerReferences(result.graph);
expect(references).toHaveLength(identityCases.length);
expect(references.some(({ source }) => source === "custom-alias.mjs")).toBe(
false,
);
for (const { source, expression, resolves } of identityCases)
expect(references).toContainEqual({
source,
declared_specifier: resolves ? "./worker.js" : null,
expression: resolves ? null : expression,
resolved_path: resolves ? "worker.js" : null,
mapped_paths: resolves ? ["worker.js"] : [],
});
},
);
cliTest(
"resolves module-relative Worker URL constructors through the compiled CLI",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-worker-module-url-");
const cases = [
{
source: "nested/relative.mjs",
expression: 'new Worker(new URL("./worker.js", import.meta.url));',
specifier: "./worker.js",
resolved: "nested/worker.js",
},
...[
'import.meta.unrelated = "metadata";',
'globalThis["import.meta"] = { url: "file:///other/entry.mjs" };',
'const meta = import.meta; meta.unrelated = "metadata";',
'const ambient = globalThis; ambient.unrelated = "metadata";',
'const copy = { ...import.meta }; Object.defineProperty(copy, "url", { value: "file:///other/entry.mjs" });',
"const copy = { ...globalThis }; copy.URL = replacement;",
"function update(globalThis) { const ambient = globalThis; ambient.URL = replacement; } update({});",
"const define = Object.defineProperty.bind(Object);",
'Object.defineProperty(...[import.meta, "unrelated", { value: "metadata" }]);',
'let meta = import.meta; meta = {}; let base = import.meta.url; base = "file:///other/entry.mjs";',
].map((mutation, index) => ({
source: `nested/unrelated-meta-${index}.mjs`,
expression: `import { Worker } from "node:worker_threads"; ${mutation} new Worker(new URL("./worker.js", import.meta.url));`,
specifier: "./worker.js",
resolved: "nested/worker.js",
})),
{
source: "nested/parent.mjs",
expression:
'new Worker(new URL("../parent-worker.js", import.meta.url));',
specifier: "../parent-worker.js",
resolved: "parent-worker.js",
},
{
source: "nested/dot-segments.mjs",
expression:
'new Worker(new URL("./child/.././worker.js", import.meta.url));',
specifier: "./child/.././worker.js",
resolved: "nested/worker.js",
},
{
source: "nested/template.mjs",
expression:
"new Worker(new URL(`./template-worker.js`, import.meta.url));",
specifier: "./template-worker.js",
resolved: "nested/template-worker.js",
},
...[
'new URL("./worker.js", import.meta.url) as URL',
'<URL>new URL("./worker.js", import.meta.url)',
'new URL("./worker.js", import.meta.url)!',
'new URL("./worker.js", import.meta.url) satisfies URL',
'((new URL("./worker.js", import.meta.url) as URL)!) satisfies URL',
'new (URL as typeof URL)("./worker.js", import.meta.url)',
'new URL("./worker.js" as string, import.meta.url as string)',
'new URL("./worker.js", (import.meta as ImportMeta).url)',
'new globalThis.URL("./worker.js", import.meta.url)',
'new window.URL("./worker.js", import.meta.url)',
'new self.URL("./worker.js", import.meta.url)',
'new global.URL("./worker.js", import.meta.url)',
'new ((globalThis as typeof globalThis)["URL"]!)("./worker.js", import.meta.url)',
].map((urlExpression, index) => ({
source: `nested/typed-${index}.ts`,
expression: `new Worker(${urlExpression});`,
specifier: "./worker.js",
resolved: "nested/worker.js",
})),
{
source: "nested/space.mjs",
expression:
'new Worker(new URL("./space%20worker.js?worker=1#entry", import.meta.url));',
specifier: "./space%20worker.js?worker=1#entry",
resolved: "nested/space worker.js",
},
{
source: "nested/percent.mjs",
expression:
'new Worker(new URL("./percent%2520worker.js", import.meta.url));',
specifier: "./percent%2520worker.js",
resolved: "nested/percent%20worker.js",
},
{
source: "nested/literal-percent.mjs",
expression:
'new Worker(new URL("./percent%25worker.js", import.meta.url));',
specifier: "./percent%25worker.js",
resolved: "nested/percent%worker.js",
},
...["?value=", "#value="].map((suffix, index) => {
const specifier = `./worker.js${suffix}%2F%2E%5C%00%FF%GG%2%\\\0`;
return {
source: `nested/suffix-${index}.mjs`,
expression: `new Worker(new URL(${JSON.stringify(specifier)}, import.meta.url));`,
specifier,
resolved: "nested/worker.js",
};
}),
{
source: "bundled/entry.mjs",
expression:
'self.webpackChunkdemo.push([[1], {1: function(module, exports, require) { new Worker(new URL("./worker.js", import.meta.url)); }, "./worker.js": function(module) { module.exports = 42; }}]);',
specifier: "./worker.js",
resolved: "bundled/worker.js",
},
{
source: "nested/literal.mjs",
expression: 'new Worker("./literal-worker.js");',
specifier: "./literal-worker.js",
resolved: "nested/literal-worker.js",
},
];
await writeFixtureFiles(root, {
...Object.fromEntries(
cases.flatMap(({ source, expression, resolved }) => [
[source, expression],
[resolved, `self.postMessage(${JSON.stringify(resolved)});`],
]),
),
"nested/percent worker.js": "self.postMessage('double-decoding decoy');",
});
const result = await analyzeJavaScriptDirectory("CLI", cli, root);
expect(result.statistics.parse_failures).toBe(0);
expect(workerReferences(result.graph)).toEqual(
expect.arrayContaining(
cases.map(({ source, specifier, resolved }) => ({
source,
declared_specifier: specifier,
expression: null,
resolved_path: resolved,
mapped_paths: [resolved],
})),
),
);
expect(workerReferences(result.graph)).toHaveLength(cases.length);
expect(result.graph.nodes).toContainEqual(
expect.objectContaining({
kind: "javascript-module",
observations: expect.arrayContaining([
expect.objectContaining({
properties: expect.objectContaining({ module_key: "./worker.js" }),
}),
]),
}),
);
},
);
cliTest(
"keeps unsupported Worker URL expressions and missing exact files unresolved through the CLI",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-worker-url-unknown-");
const malformedPercentPaths = [
["./worker%.js", "worker%.js"],
["./worker%2.js", "worker%2.js"],
["./worker%GG.js", "worker%GG.js"],
["./space%20worker%GG.js", "space worker%GG.js"],
];
const expressions = {
"dynamic-path.mjs": "new Worker(new URL(path, import.meta.url));",
"absolute.mjs":
'import { Worker } from "node:worker_threads"; new Worker(new URL("/worker.js", import.meta.url));',
"typed-apply-meta.ts":
'import { Worker } from "node:worker_threads"; Object.defineProperty.apply(Object, ([import.meta, "url", { value: "file:///other.mjs" }] as const)); new Worker(new URL("./worker.js", import.meta.url));',
"overwritten-qualified-url.mjs":
'globalThis.URL = replacement; new Worker(new globalThis.URL("./worker.js", import.meta.url));',
"shadowed-qualified-url.mjs":
'function start(window) { new Worker(new window.URL("./worker.js", import.meta.url)); }',
...Object.fromEntries(
["globalThis.URL", "URL", 'globalThis["URL"]'].map((target, index) => [
`overwritten-url-${index}.mjs`,
`import { Worker } from "node:worker_threads"; const RealURL = URL; ${target} = class { constructor() { return new RealURL("./other.js", import.meta.url); } }; new Worker(new URL("./worker.js", import.meta.url));`,
]),
),
...Object.fromEntries(
[
"delete globalThis.URL;",
"({ URL: globalThis.URL } = replacements);",
"globalThis[key] = replacement;",
'Object.defineProperty(globalThis, "URL", { value: replacement });',
'Reflect.set(globalThis, "URL", replacement);',
"globalThis.globalThis.URL = replacement;",
"globalThis.global.URL = replacement;",
"const ambient = globalThis; ambient.URL = replacement;",
"const { globalThis: ambient } = globalThis; ambient.URL = replacement;",
].map((mutation, index) => [
`mutated-url-${index}.mjs`,
`import { Worker } from "node:worker_threads"; ${mutation} new Worker(new URL("./worker.js", import.meta.url));`,
]),
),
...Object.fromEntries(
[
'import.meta.url = "file:///other/entry.mjs";',
"delete import.meta.url;",
'Object.defineProperty(import.meta, "url", { value: "file:///other/entry.mjs" });',
"import.meta[key] = replacement;",
'const meta = import.meta; const alias = meta; alias.url = "file:///other/entry.mjs";',
'let meta = import.meta; meta.url = "file:///other/entry.mjs"; meta = {};',
'const define = Object.defineProperty; define(import.meta, "url", { value: "file:///other/entry.mjs" });',
'const set = Reflect.set.bind(Reflect); set(import.meta, "url", "file:///other/entry.mjs");',
'Object.defineProperty.bind(Object, import.meta, "url")({ value: "file:///other/entry.mjs" });',
'Object.assign(...[import.meta, { url: "file:///other/entry.mjs" }]);',
'Object.defineProperty(...[import.meta, "url", { value: "file:///other/entry.mjs" }]);',
].map((mutation, index) => [
`mutated-import-meta-${index}.mjs`,
`import { Worker } from "node:worker_threads"; ${mutation} new Worker(new URL("./worker.js", import.meta.url));`,
]),
),
...Object.fromEntries(
malformedPercentPaths.map(([specifier], index) => [
`malformed-percent-${index}.mjs`,
`import { Worker } from "node:worker_threads"; new Worker(new URL(${JSON.stringify(specifier)}, import.meta.url));`,
]),
),
"dynamic-base.mjs": 'new Worker(new URL("./worker.js", base));',
"shadowed.mjs":
'function start(URL) { return new Worker(new URL("./worker.js", import.meta.url)); }',
"typed-shadowed.ts":
'function start(URL: typeof globalThis.URL) { return new Worker((new (URL as typeof globalThis.URL)("./worker.js", import.meta.url) as URL)!); }',
"typed-dynamic-path.ts":
"new Worker((new URL(path as string, import.meta.url) as URL)!);",
"typed-dynamic-base.ts":
'new Worker((new URL("./worker.js", base as string) as URL)!);',
"call.mjs": 'new Worker(URL("./worker.js", import.meta.url));',
"custom.mjs":
'new Worker(new custom.URL("./worker.js", import.meta.url));',
"extension.mjs":
'new Worker(new URL("./extensionless", import.meta.url));',
"directory.mjs": 'new Worker(new URL("./directory", import.meta.url));',
"directory-dot.mjs":
'new Worker(new URL("./worker.js/.", import.meta.url));',
"directory-parent.mjs":
'new Worker(new URL("./worker.js/child/..", import.meta.url));',
"empty-segment.mjs":
'new Worker(new URL("./child//../worker.js", import.meta.url));',
"package.mjs":
'new Worker(new URL("./package-worker", import.meta.url));',
"external.mjs":
'new Worker(new URL("https://example.test/worker.js", import.meta.url));',
"empty.mjs": 'new Worker(new URL("", import.meta.url));',
"query.mjs": 'new Worker(new URL("?worker=1", import.meta.url));',
"whitespace.mjs":
'new Worker(new URL(" ./worker.js ", import.meta.url));',
"control.mjs": 'new Worker(new URL("./worker\\t.js", import.meta.url));',
"leading-control.mjs":
'new Worker(new URL("\\u0001./worker.js", import.meta.url));',
};
const knownSpecifiers: Readonly<Record<string, string>> = {
"extension.mjs": "./extensionless",
"directory.mjs": "./directory",
"directory-dot.mjs": "./worker.js/.",
"directory-parent.mjs": "./worker.js/child/..",
"empty-segment.mjs": "./child//../worker.js",
"package.mjs": "./package-worker",
"external.mjs": "https://example.test/worker.js",
};
await writeFixtureFiles(root, {
...expressions,
...Object.fromEntries(
malformedPercentPaths.map(([, decoy]) => [
decoy,
"self.postMessage('malformed percent pathname decoy');",
]),
),
"worker.js": "self.postMessage('existing decoy');",
"other.js": "self.postMessage('custom URL result decoy');",
"extensionless.js": "self.postMessage('extension decoy');",
"directory/index.js": "self.postMessage('index decoy');",
"package-worker/package.json": JSON.stringify({ main: "selected.js" }),
"package-worker/selected.js": "self.postMessage('package main decoy');",
});
const result = await analyzeJavaScriptDirectory("CLI", cli, root);
expect(result.statistics.parse_failures).toBe(0);
const references = workerReferences(result.graph);
expect(references).toHaveLength(Object.keys(expressions).length);
for (const [source, expression] of Object.entries(expressions)) {
const specifier = knownSpecifiers[source] ?? null;
expect(references).toContainEqual(
expect.objectContaining({
source,
declared_specifier: specifier,
expression:
specifier === null
? expression.slice(
expression.indexOf("new Worker("),
expression.lastIndexOf(";"),
)
: null,
resolved_path: null,
mapped_paths: [],
}),
);
}
},
);
cliTest(
"bounds repeated ambient alias branches through the compiled CLI",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-worker-alias-branches-");
const aliases = [
"const alias0 = globalThis;",
...Array.from(
{ length: 40 },
(_, index) =>
`const alias${index + 1} = flag${index} ? alias${index} : alias${index};`,
),
"alias40.URL = replacement;",
].join("\n");
const splitAliases = [
"const alias0 = globalThis;",
...Array.from(
{ length: 40 },
(_, index) =>
`const left${index} = alias${index}; const right${index} = alias${index}; const alias${index + 1} = flag${index} ? left${index} : right${index};`,
),
"alias40.URL = replacement;",
].join("\n");
const memberAliases = [
"const alias0 = globalThis;",
...Array.from(
{ length: 40 },
(_, index) =>
`const alias${index + 1} = flag${index} ? alias${index}.globalThis : alias${index}.window;`,
),
"alias40.URL = replacement;",
].join("\n");
const expression = 'new Worker(new URL("./worker.js", import.meta.url))';
await writeFixtureFiles(root, {
"entry.mjs": `import { Worker } from "node:worker_threads";\n${aliases}\n${expression};`,
"split.mjs": `import { Worker } from "node:worker_threads";\n${splitAliases}\n${expression};`,
"member.mjs": `import { Worker } from "node:worker_threads";\n${memberAliases}\n${expression};`,
"worker.js": "self.postMessage('existing alias-mutation decoy');",
});
const result = await analyzeJavaScriptDirectory(
"CLI",
{ run: (invocation) => cli.run({ ...invocation, timeoutMs: 20_000 }) },
root,
);
expect(result.statistics.parse_failures).toBe(0);
const references = workerReferences(result.graph);
expect(references).toHaveLength(3);
expect(references).toEqual(
expect.arrayContaining(
["entry.mjs", "split.mjs", "member.mjs"].map((source) => ({
source,
declared_specifier: null,
expression,
resolved_path: null,
mapped_paths: [],
})),
),
);
},
);
cliTest(
"keeps Node Worker filesystem strings unresolved through the compiled CLI",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-node-worker-filesystem-");
const cases = [
{
prefix: 'import { Worker } from "node:worker_threads";',
expression: 'new Worker("./worker.js")',
},
{
prefix: 'import { Worker as ThreadWorker } from "worker_threads";',
expression: 'new ThreadWorker("/worker.js")',
},
{
prefix: 'import * as threads from "node:worker_threads";',
expression: 'new threads.Worker("./worker.js")',
},
{
prefix: 'import { Worker } from "node:worker_threads";',
expression: 'new (Worker as typeof Worker)("/worker.js")',
},
{
prefix: 'import * as threads from "node:worker_threads";',
expression: 'new ((threads as typeof threads).Worker!)("./worker.js")',
},
].map((fixture, index) => ({
...fixture,
source: `nested/node-literal-${index}.ts`,
}));
await writeFixtureFiles(root, {
...Object.fromEntries(
cases.map(({ prefix, expression, source }) => [
source,
`${prefix}\n${expression};`,
]),
),
"worker.js": "self.postMessage('filesystem-root decoy');",
"nested/worker.js": "self.postMessage('source-sibling decoy');",
});
const result = await analyzeJavaScriptDirectory("CLI", cli, root);
expect(result.statistics.parse_failures).toBe(0);
const references = workerReferences(result.graph);
expect(references).toHaveLength(cases.length);
for (const { source, expression } of cases)
expect(references).toContainEqual({
source,
declared_specifier: null,
expression,
resolved_path: null,
mapped_paths: [],
});
},
);
// Windows cannot materialize the pipe-named directory needed for this decoy.
cliTest.skipIf(process.platform === "win32")(
"keeps file-URL drive letters distinct from encoded POSIX paths through the compiled CLI",
async ({ cli }) => {
const root = await createTestTempDirectory("rea-worker-url-drive-");
await writeFixtureFiles(root, {
"dist/drive.mjs": 'new Worker(new URL("c|/worker.js", import.meta.url));',
"dist/encoded.mjs":
'new Worker(new URL("c%7C/worker.js", import.meta.url));',
"dist/c|/worker.js": "self.postMessage('existing relative-path decoy');",
});
const result = await analyzeJavaScriptDirectory("CLI", cli, root);
expect(result.statistics.parse_failures).toBe(0);
const references = workerReferences(result.graph);
expect(references).toHaveLength(2);
expect(references).toEqual(
expect.arrayContaining([
{
source: "dist/drive.mjs",
declared_specifier: "c|/worker.js",
expression: null,
resolved_path: null,
mapped_paths: [],
},
{
source: "dist/encoded.mjs",
declared_specifier: "c%7C/worker.js",
expression: null,
resolved_path: "dist/c|/worker.js",
mapped_paths: ["dist/c|/worker.js"],
},
]),
);
},
);