[GH-ISSUE #2647] fix(javascript): empty ESM import names become dynamic in re-export provenance #946

Closed
opened 2026-10-15 18:29:48 +00:00 by bsakaguchi · 0 comments
Owner

Originally created by @coygeek on GitHub (Oct 13, 2026).
Original GitHub issue: https://github.com/morluto/rea/issues/2647

Summary

Static JavaScript application analysis changes an exact empty-string ESM import name to [dynamic] when reporting the origin of a local re-export. The original import edge preserves the name, but the export-origin edge reports a different imported member while still marking the module relationship resolved. This makes origin metadata unreliable for this admitted source pattern.

Runtime or environment

Verified source revision: a2d8b94dd5. Source tests ran with Node.js 24.18.0 and npm 11.16.0. Verification used the shared production application service and a prepared compiled static source-analysis worker. The worker entry point and its built import-origin logic were inspected and matched the relevant source logic. No built CLI process, stdio MCP session or published package runtime was tested. No native analysis provider is required.

Current-upstream source recheck at commit 3ae826ceba found the empty-name binding producer, semantic worker entry point and local re-export origin projection unchanged. Reviewed source-map, file-admission and Evidence metadata changes do not alter this fixture's path. The recheck did not build or execute a worker at this revision; the compiled-worker observations above remain scoped to the verified baseline.

Steps to reproduce

From the source checkout with its pinned dependencies installed and compiled worker artifacts prepared with npm run build:cached, place the following observation test at src/application/javascript/import-name-observation.test.ts. It analyzes two inert source files through the same service used by the public CLI/MCP workflow. Executed verification used these source strings and this service in a temporary focused test; the example below uses the repository's temporary-directory cleanup helper.

import { writeFile } from "node:fs/promises";
import { join } from "node:path";
import { expect, it } from "vitest";
import { createTestTempDirectory } from "../../../tests/fixtures/temporaryDirectory.js";
import { analyzeJavaScriptApplication } from "../../../tests/support/javascriptApplicationScope.js";

it("prints the imported origin for a local re-export", async () => {
  const directory = await createTestTempDirectory("rea-import-name-");
  await writeFile(join(directory, "module.mjs"),
    'const value = 1; export { value as "" };');
  await writeFile(join(directory, "app.mjs"),
    'import { "" as selected } from "./module.mjs"; export { selected };');
  const response = await analyzeJavaScriptApplication({
    input_path: directory,
    format: "directory",
  });
  if (!response.ok) throw response.error;
  const result = response.value.normalized_result as any;
  expect(result.statistics.parse_failures).toBe(0);
  const rows = result.graph.edges
    .filter((edge: any) => edge.relation === "imports"
      && edge.properties.module_link_kind
      && edge.properties.specifier === "./module.mjs")
    .map((edge: any) => ({
      module_link_kind: edge.properties.module_link_kind,
      exported_name: edge.properties.exported_name,
      imported_name: edge.properties.imported_name,
      imported_path: edge.properties.imported_path,
      resolution_status: edge.properties.resolution_status,
    }));
  console.log(JSON.stringify(rows));
});

Run the single test and inspect its printed rows. The test observes behavior; a passing test does not mean the defect is repaired.

npm run test:local -- src/application/javascript/import-name-observation.test.ts

Remove the observation test after use. Both source files also pass Node --check, which parses without executing them. The observed two-file reproduction contains 107 bytes of source. Removing the nonempty-name controls retained the failure. An import statement alone reproduces the producer mismatch; the local re-export exposes it in the application result.

Expected behavior

The import name is the exact string "". Binding provenance must retain importedPath: [""], and the local re-export's module-origin edge must retain imported_name: "" and imported_path: [""]. This promises exact identity of an explicit syntax name, not a runtime value or proof of execution. The original import edge already retains the expected identity.

Actual behavior

The shared application service succeeds and returns two parsed JavaScript files with zero parse failures. The relevant edges, with unrelated fields omitted, are:

[
  {
    "module_link_kind": "import",
    "exported_name": null,
    "imported_name": "",
    "imported_path": [""],
    "resolution_status": "resolved"
  },
  {
    "module_link_kind": "export",
    "exported_name": "selected",
    "imported_name": "[dynamic]",
    "imported_path": ["[dynamic]"],
    "resolution_status": "resolved"
  }
]

Independent producer verification also returns status: "module" with importedPath: ["[dynamic]"] for the imported binding, while its module link retains importedName: "". String-name controls "a" and "0" preserve their exact names in both binding provenance and derived application edges. The explicit re-export control export { "" as selected } from "./module.mjs"; preserves the empty imported path.

The producer and application control cases were observed twice; the reduced case and explicit re-export control were observed once each. The final focused run passed three observation tests. These bounded results establish the incorrect origin metadata without measuring production incidence. The module target resolves correctly; this is not a missing dependency or module-resolution failure.

Evidence

  • Expected source: src/domain/javascript/javascriptSemanticIr.ts:8-20 defines exact module origins for imports and aliases, and AGENTS.md:29 requires preserved source identity. docs/javascript-artifact-reconstruction.md:1-58 documents the supported static directory workflow. src/domain/javascript/javascriptSemanticProjection.ts:299-312 preserves the empty string in the admitted import link. Both minimal .mjs files passed independent Node syntax checks without execution.
  • Failure source: The observed source-service and producer outputs above. src/domain/javascript/javascriptSemanticAnalysis.ts:994-1020 replaces the empty import name when creating its direct binding origin. src/domain/javascript/javascriptSemanticValues.ts:816-821 publishes that origin. src/application/javascript/JavaScriptModuleRelationships.ts:351-381 follows the local exported binding and projects its imported path into the application edge.
  • Evidence provenance: observed
  • Local verification: reproduced
  • Reproduction completeness: complete

The executed shared application-service run accepted both .mjs files and returned the correct original import edge alongside the incorrect local re-export origin edge. It used the prepared compiled static source-analysis worker. Necessary service and binding-retention locators are src/application/javascript/JavaScriptApplicationService.ts:37-113 and src/domain/javascript/javascriptModuleSemanticIr.ts:17-34. The CLI service locator is src/cli/javascriptApplicationAnalysis.ts:11-25; CLI and MCP adapter processes were not separately exercised.

Restoration check

Analyze the same two-file input without changing its source bytes. Both the original import edge and the local export-origin edge must identify the imported member with "" and [""]. The imported binding's producer provenance must agree. Keep the module path resolved, preserve the original edges and source evidence, retain exact "a" and "0" names, and keep the explicit empty-name re-export control correct. Passing must preserve an exact known name rather than omit the origin or reclassify it as unknown.

Additional context

The closed empty-key consumer audit describes environment-key and method-name incidents and lists module-provenance consumers for inspection. Its current body and comments were checked. It does not provide this empty ESM-import/local-export reproduction or evidence that this contract was repaired. This report states the separately verified current failure and does not claim a regression.

For the tested case, a caller can manually cross-check the re-export against the original import edge, which preserves the correct name. That provides exact origin identification without changing artifact bytes, although it does not correct the export-origin edge. Rewriting the input as an explicit re-export changes its source and provenance, so that positive control is not an artifact-preserving repair.

Triage assessment

  • Impact level: P2
  • Assessment status: supported
  • Impact basis: A supported static application analysis returns incorrect exact imported-member metadata for an empty-string import followed by a local re-export. The module resolves and the workflow succeeds; no wider outage or production frequency was established.
  • Workaround status: available
  • Workaround basis: The observed original import edge retains imported_name: "" and imported_path: [""], allowing manual cross-checking of the derived edge without changing the artifact. This identifies the member but leaves the stored export-origin metadata incorrect.
Originally created by @coygeek on GitHub (Oct 13, 2026). Original GitHub issue: https://github.com/morluto/rea/issues/2647 ## Summary Static JavaScript application analysis changes an exact empty-string ESM import name to `[dynamic]` when reporting the origin of a local re-export. The original import edge preserves the name, but the export-origin edge reports a different imported member while still marking the module relationship resolved. This makes origin metadata unreliable for this admitted source pattern. ## Runtime or environment Verified source revision: a2d8b94dd50dab8d966239dd63ab84718aa8b4b4. Source tests ran with Node.js 24.18.0 and npm 11.16.0. Verification used the shared production application service and a prepared compiled static source-analysis worker. The worker entry point and its built import-origin logic were inspected and matched the relevant source logic. No built CLI process, stdio MCP session or published package runtime was tested. No native analysis provider is required. Current-upstream source recheck at commit 3ae826ceba6f25b08bce50bcb51001209bc16b57 found the empty-name binding producer, semantic worker entry point and local re-export origin projection unchanged. Reviewed source-map, file-admission and Evidence metadata changes do not alter this fixture's path. The recheck did not build or execute a worker at this revision; the compiled-worker observations above remain scoped to the verified baseline. ## Steps to reproduce From the source checkout with its pinned dependencies installed and compiled worker artifacts prepared with `npm run build:cached`, place the following observation test at `src/application/javascript/import-name-observation.test.ts`. It analyzes two inert source files through the same service used by the public CLI/MCP workflow. Executed verification used these source strings and this service in a temporary focused test; the example below uses the repository's temporary-directory cleanup helper. ```ts import { writeFile } from "node:fs/promises"; import { join } from "node:path"; import { expect, it } from "vitest"; import { createTestTempDirectory } from "../../../tests/fixtures/temporaryDirectory.js"; import { analyzeJavaScriptApplication } from "../../../tests/support/javascriptApplicationScope.js"; it("prints the imported origin for a local re-export", async () => { const directory = await createTestTempDirectory("rea-import-name-"); await writeFile(join(directory, "module.mjs"), 'const value = 1; export { value as "" };'); await writeFile(join(directory, "app.mjs"), 'import { "" as selected } from "./module.mjs"; export { selected };'); const response = await analyzeJavaScriptApplication({ input_path: directory, format: "directory", }); if (!response.ok) throw response.error; const result = response.value.normalized_result as any; expect(result.statistics.parse_failures).toBe(0); const rows = result.graph.edges .filter((edge: any) => edge.relation === "imports" && edge.properties.module_link_kind && edge.properties.specifier === "./module.mjs") .map((edge: any) => ({ module_link_kind: edge.properties.module_link_kind, exported_name: edge.properties.exported_name, imported_name: edge.properties.imported_name, imported_path: edge.properties.imported_path, resolution_status: edge.properties.resolution_status, })); console.log(JSON.stringify(rows)); }); ``` Run the single test and inspect its printed rows. The test observes behavior; a passing test does not mean the defect is repaired. ```sh npm run test:local -- src/application/javascript/import-name-observation.test.ts ``` Remove the observation test after use. Both source files also pass Node `--check`, which parses without executing them. The observed two-file reproduction contains 107 bytes of source. Removing the nonempty-name controls retained the failure. An import statement alone reproduces the producer mismatch; the local re-export exposes it in the application result. ## Expected behavior The import name is the exact string `""`. Binding provenance must retain `importedPath: [""]`, and the local re-export's module-origin edge must retain `imported_name: ""` and `imported_path: [""]`. This promises exact identity of an explicit syntax name, not a runtime value or proof of execution. The original import edge already retains the expected identity. ## Actual behavior The shared application service succeeds and returns two parsed JavaScript files with zero parse failures. The relevant edges, with unrelated fields omitted, are: ```json [ { "module_link_kind": "import", "exported_name": null, "imported_name": "", "imported_path": [""], "resolution_status": "resolved" }, { "module_link_kind": "export", "exported_name": "selected", "imported_name": "[dynamic]", "imported_path": ["[dynamic]"], "resolution_status": "resolved" } ] ``` Independent producer verification also returns `status: "module"` with `importedPath: ["[dynamic]"]` for the imported binding, while its module link retains `importedName: ""`. String-name controls `"a"` and `"0"` preserve their exact names in both binding provenance and derived application edges. The explicit re-export control `export { "" as selected } from "./module.mjs";` preserves the empty imported path. The producer and application control cases were observed twice; the reduced case and explicit re-export control were observed once each. The final focused run passed three observation tests. These bounded results establish the incorrect origin metadata without measuring production incidence. The module target resolves correctly; this is not a missing dependency or module-resolution failure. ## Evidence - Expected source: `src/domain/javascript/javascriptSemanticIr.ts:8-20` defines exact module origins for imports and aliases, and `AGENTS.md:29` requires preserved source identity. `docs/javascript-artifact-reconstruction.md:1-58` documents the supported static directory workflow. `src/domain/javascript/javascriptSemanticProjection.ts:299-312` preserves the empty string in the admitted import link. Both minimal `.mjs` files passed independent Node syntax checks without execution. - Failure source: The observed source-service and producer outputs above. `src/domain/javascript/javascriptSemanticAnalysis.ts:994-1020` replaces the empty import name when creating its direct binding origin. `src/domain/javascript/javascriptSemanticValues.ts:816-821` publishes that origin. `src/application/javascript/JavaScriptModuleRelationships.ts:351-381` follows the local exported binding and projects its imported path into the application edge. - Evidence provenance: observed - Local verification: reproduced - Reproduction completeness: complete The executed shared application-service run accepted both `.mjs` files and returned the correct original import edge alongside the incorrect local re-export origin edge. It used the prepared compiled static source-analysis worker. Necessary service and binding-retention locators are `src/application/javascript/JavaScriptApplicationService.ts:37-113` and `src/domain/javascript/javascriptModuleSemanticIr.ts:17-34`. The CLI service locator is `src/cli/javascriptApplicationAnalysis.ts:11-25`; CLI and MCP adapter processes were not separately exercised. ## Restoration check Analyze the same two-file input without changing its source bytes. Both the original import edge and the local export-origin edge must identify the imported member with `""` and `[""]`. The imported binding's producer provenance must agree. Keep the module path resolved, preserve the original edges and source evidence, retain exact `"a"` and `"0"` names, and keep the explicit empty-name re-export control correct. Passing must preserve an exact known name rather than omit the origin or reclassify it as unknown. ## Additional context The closed [empty-key consumer audit](https://github.com/morluto/rea/issues/783) describes environment-key and method-name incidents and lists module-provenance consumers for inspection. Its current body and comments were checked. It does not provide this empty ESM-import/local-export reproduction or evidence that this contract was repaired. This report states the separately verified current failure and does not claim a regression. For the tested case, a caller can manually cross-check the re-export against the original import edge, which preserves the correct name. That provides exact origin identification without changing artifact bytes, although it does not correct the export-origin edge. Rewriting the input as an explicit re-export changes its source and provenance, so that positive control is not an artifact-preserving repair. ## Triage assessment - Impact level: P2 - Assessment status: supported - Impact basis: A supported static application analysis returns incorrect exact imported-member metadata for an empty-string import followed by a local re-export. The module resolves and the workflow succeeds; no wider outage or production frequency was established. - Workaround status: available - Workaround basis: The observed original import edge retains `imported_name: ""` and `imported_path: [""]`, allowing manual cross-checking of the derived edge without changing the artifact. This identifies the member but leaves the stored export-origin metadata incorrect.
bsakaguchi 2026-10-15 18:29:48 +00:00
  • closed this issue
  • added the
    bug
    label
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
morluto/rea#946
No description provided.