Files
rea/tests/acceptance/applications/javascriptWorkerUrlCli.test.ts
T
Avocado 3a4d49406a fix(javascript): preserve Electron runtime entrypoint paths (#2742)
* fix(javascript): preserve Electron runtime entrypoint paths

* fix(javascript): resolve runtime entrypoints before modules

* fix(javascript): preserve Electron runtime path semantics

* fix(javascript): resolve Electron renderers exactly

* fix(javascript): retain runtime package aliases

* fix(javascript): retain missing renderer URL paths

* fix(javascript): resolve Electron URL and utility entries correctly

* fix(javascript): reject suffix-only worker URLs

* fix(javascript): respect Electron URL and utility path semantics

* fix(javascript): separate Node runtime entrypoints from source imports

* fix(comparison): recheck historical calls-only changes (#2223)

* fix(comparison): preserve incomplete function call evidence

Keep missing, unknown, partial and unresolved call relationships unknown in function comparisons and reconstruction claims. Aggregate IDA caller and callee coverage without overwriting uncertainty.

Validate with 144 focused tests, typecheck/lint, docs checks and test discovery. Real providers were not verified; the full headless IDA session suite requires unavailable Windows native controls.

* fix(comparison): recheck historical calls-only changes

---------

Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
(cherry picked from commit 688defca0f)

* fix(ci): isolate Linux Hopper process fixture inventory

(cherry picked from commit 8cac51e121)

* fix(android): make DEX descriptor pattern portable

(cherry picked from commit 1335bcddc5)

* fix: repair the static-check failures on the default branch (#2895)

* fix: repair the static-check failures on the default branch

The default branch fails its own required `Static checks` job three ways, so
every pull request reports that job as failed regardless of its own change.

- `npm run format:check` reports `src/domain/functionComparison.test.ts` and
  `src/ida/IdaOperationRunner.ts`.
- `npm run lint` reports a duplicate `import { join } from "node:path"` in
  `tests/boundary/mcp/ghidraInventoryMcp.test.ts` (lines 4 and 17).
- `npm run typecheck` reports `format: "devin"` missing from the client format
  union in `src/application/SupportedClients.ts`, and an unchecked index in
  `tests/boundary/cli/clientProfileLifecycle.test.ts`, whose pair array is
  inferred as `string[][]`.

Apply the pinned formatter, drop the duplicate import, list the `"devin"` format
that its own definition already uses, and mark the pair array `as const`.

Validation: `npm run check:fast` and `npm run format:check` pass.

* fix(setup): keep Devin on the JSON configuration dialect

---------

Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
(cherry picked from commit df3d96da80)

---------

Co-authored-by: morluto <morluto@users.noreply.github.com>
Co-authored-by: morluto <76467478+morluto@users.noreply.github.com>
Co-authored-by: Lucien <164857476+dakjdakd@users.noreply.github.com>
Co-authored-by: 吴佳浩Alben <35291372+XiaoBinGan@users.noreply.github.com>
Co-authored-by: 吴佳浩 <wujiahao@supremind.com>
2026-10-14 06:39:17 +08:00

633 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));',
"empty-worker-url.mjs": 'new Worker("");',
"query-only-worker-url.mjs": 'new Worker("?cache=1");',
"fragment-only-worker-url.mjs": 'new Worker("#worker");',
"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",
"empty-worker-url.mjs": "",
"query-only-worker-url.mjs": "?cache=1",
"fragment-only-worker-url.mjs": "#worker",
"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"],
},
]),
);
},
);