[PR #1862] [MERGED] feat(javascript): resolve exact package import aliases #4858

Closed
opened 2026-10-31 00:56:06 +00:00 by bsakaguchi · 0 comments
Owner

📋 Pull Request Information

Original PR: https://github.com/morluto/rea/pull/1862
Author: @mvanhorn
Created: 10/11/2026
Status: ✅ Merged
Merged: 10/11/2026
Merged by: @morluto

Base: main ← Head: fix/package-imports-aliases


📝 Commits (7)

  • 6bed757 feat(javascript): resolve exact package import aliases
  • 208c404 fix(javascript): keep nested import aliases in CLI graphs
  • 5492780 fix(javascript): resolve literal package import keys before path admission
  • 5603a4d fix(javascript): preserve import keys and invalid-target fallback
  • cfc07f6 fix(javascript): preserve unmatched package target arrays
  • 2f97b2a fix(javascript): retain package target refusal evidence
  • e4ccc23 test(browser): match real verifier startup budget

📊 Changes

10 files changed (+1838 additions, -83 deletions)

View changed files

📝 .agents/skills/reverse-engineer-anything/references/javascript-applications.md (+4 -1)
📝 docs/javascript-artifact-reconstruction.md (+20 -0)
📝 src/application/javascript/JavaScriptArtifactPathResolution.test.ts (+368 -0)
📝 src/application/javascript/JavaScriptArtifactPathResolution.ts (+238 -80)
➕ src/domain/javascript/javascriptPackageImportAliases.test.ts (+147 -0)
➕ src/domain/javascript/javascriptPackageImportAliases.ts (+98 -0)
➕ tests/acceptance/applications/packageImportAliasesCli.test.ts (+251 -0)
📝 tests/boundary/browser/cdpSvgXlinkHref.test.ts (+9 -2)
📝 tests/boundary/javascript/packageConditionalExports.test.ts (+49 -0)
➕ tests/boundary/javascript/packageImportAliases.test.ts (+654 -0)

📄 Description

Summary

Static JavaScript application analysis now resolves exact package-private #alias specifiers against the importer's enclosing package.json imports map. Inventoried local files become resolved application-graph edges instead of external unknowns, and missing, blocked, or out-of-scope aliases keep an unresolved edge with a precise reason.

Fixes #1838

Demo

demo

The sample is a generated local application, not third-party code.

Problem and expected behavior

A package that declares {"imports":{"#config":"./config.cjs"}} and requires #config leaves a real dependency out of the static application graph. On 0b4ce836, that specifier is resolved_path: null, resolution_status: "external", with the limitation "The bare specifier is a Node builtin, package import map, or invalid package name." Node's resolver selects config.cjs.

The graph should resolve exact #alias keys through the nearest enclosing same-container package.json, stopping at a node_modules directory so a nested package cannot inherit an ancestor's map. Selected targets are package-local ./ files with no extension or index fallback. A missing key, explicit null, or out-of-scope alias reports that reason and does not fall through to files, indexes, or another package's map.

Change and scope

Domain lookup classifies # specifiers and exact imports keys. Application path resolution walks the importer's enclosing package scope, then reuses the existing condition, array, and null target selection. Shared CLI and MCP adapters already thread those resolver outcomes into analyze-javascript-application / analyze_javascript_application graphs; no new tool is added.

  • Exact #alias keys resolve relative to the owning package root.
  • Nested node_modules packages use their own imports map.
  • ESM #config is treated as an import alias, not a URL fragment.
  • Wildcard keys (#internal/*), recursive # targets, and external-package import targets remain unresolved with that limitation.

Out of scope: wildcard expansion, recursive alias chasing, external-package targets, TypeScript paths, browser import maps, package installation, network access, and target execution.

Contract and boundary impact

  • Semantic owner and earliest changed stage: domain src/domain/javascript/javascriptPackageImportAliases.ts, then application JavaScriptArtifactPathResolution, before graph relationship construction.
  • CLI and MCP/tool-catalog contract: none. Existing analyze_javascript_application / analyze-javascript-application schemas, names, and adapters are unchanged; resolution outcomes flow through the current resolution_status / resolved_path / limitations fields.
  • Provider, bridge, target-format, or platform compatibility: static JavaScript artifacts only; no Hopper, Ghidra, IDA, or browser provider changes.
  • Evidence, artifact, provenance, or reconstruction contract: original specifier, source path, artifact digest, source range, and container identity are preserved. Unsupported alias forms stay explicit unknowns rather than guessed files.
  • Process execution, authorization, cleanup, or containment impact: none. Resolution reads the inventoried file map only; artifact-root confinement remains enforced.
  • Generated metadata (docs/public/product-catalog.json), package, or installation impact (CI generates and validates ignored catalogs and skills/; edit authored instructions in .agents/skills/reverse-engineer-anything/ and never commit build outputs): authored skill reference javascript-applications.md and docs/javascript-artifact-reconstruction.md updated. Catalog is regenerated by the repo build and is not committed.

Evidence and regression coverage

  • Tests added or updated: domain specifier/lookup/target classification; application enclosing-scope, conditions, arrays, exact-file, missing-key, explicit-null, wildcard, recursive, external-package, reserved #, and foreign-container refusals; Node import/require oracle comparisons including nested packages without an imports field; compiled CLI and stdio MCP graphs on an owned fixture.
  • Base reproduction or other evidence: on 0b4ce836, #config against the fixture above is external with the builtin/import-map limitation. After this change the same specifier is resolved_path: "config.cjs", resolution_status: "resolved".
  • User-visible CLI/MCP output (if applicable): generated local fixture. Root #config → config.cjs (resolved); nested node_modules/fixture #config → node_modules/fixture/local.cjs (resolved); #missing → resolved_path: null, resolution_status: "external", limitation "Package metadata package.json does not define package import alias #missing; it declares #config."
  • Remaining proof gaps: wildcard, recursive, and external-package import targets are intentionally unresolved. Real Hopper/Ghidra and browser lanes do not apply. This host did not rerun a Windows package.

For evidence-bearing changes:

  • Observed, derived, and inferred claims remain distinguishable.
  • Artifact identity, source provenance, and failed attempts remain preserved.
  • Unsupported, incomplete, unavailable, or uncertain outcomes remain visible.

Validation performed

  • npm ci — pass (377 packages added, 378 audited, 0 vulnerabilities)
  • npm run test:focused -- src/domain/javascript/javascriptPackageImportAliases.test.ts src/application/javascript/JavaScriptArtifactPathResolution.test.ts tests/boundary/javascript/packageImportAliases.test.ts tests/acceptance/applications/packageImportAliasesCli.test.ts — pass (4 files, 111 tests; CLI 1255 ms, stdio MCP 3863 ms)
  • npm run check:fast — pass
  • npm run check — pass (typecheck, lint, format:check, knip, metadata:check; 6/6 turbo tasks; architecture guards across 1138 source files; lint 753 warnings, 0 errors)
  • npm run docs:check — pass
  • npm run verify:test-discovery — pass (“Discovered all 839 test files exactly once.”)

Compatibility, safety, and release

  • Breaking changes or migration steps: none. Additive resolution inside the existing application-graph contract. # specifiers that previously stayed external now resolve when an exact enclosing imports key selects an inventoried ./ file; unsupported forms remain unresolved.
  • Real Hopper/Ghidra, browser, or OS coverage: not run. This change is static JavaScript path resolution. CLI/MCP and Node resolver comparisons ran on Linux with Node 24.18.0.
  • Package or release metadata impact: none. No new tool, skill version, or package identity change.
  • Security, privacy, process, or containment review: caller-selected inputs and source evidence remain intact. Same-container package-scope checks and artifact-root confinement are preserved. No network, installation, or target execution is introduced.

Review checklist

  • This PR addresses a concrete problem or an agreed enhancement.
  • The PR has one focused outcome and the title follows type(scope): outcome.
  • Related issue is linked, or the reason for not linking one is stated above.
  • Tests cover changed observable behavior and meaningful failure paths.
  • Owning docs, contracts, and generated metadata are updated where needed.
  • User-visible CLI/MCP changes include representative output.
  • I checked the final diff for secrets, unrelated cleanup, and unsupported claims.

🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.

## 📋 Pull Request Information **Original PR:** https://github.com/morluto/rea/pull/1862 **Author:** [@mvanhorn](https://github.com/mvanhorn) **Created:** 10/11/2026 **Status:** ✅ Merged **Merged:** 10/11/2026 **Merged by:** [@morluto](https://github.com/morluto) **Base:** `main` ← **Head:** `fix/package-imports-aliases` --- ### 📝 Commits (7) - [`6bed757`](https://github.com/morluto/rea/commit/6bed757006f2128dbe82a77ba01ca480180cd242) feat(javascript): resolve exact package import aliases - [`208c404`](https://github.com/morluto/rea/commit/208c40463c772a891dc42b29325c1e347537889a) fix(javascript): keep nested import aliases in CLI graphs - [`5492780`](https://github.com/morluto/rea/commit/5492780737ee7b445b203f1f2dfcf86b7bd1af04) fix(javascript): resolve literal package import keys before path admission - [`5603a4d`](https://github.com/morluto/rea/commit/5603a4d84273bd2ba38d56c73da4bc993432ae90) fix(javascript): preserve import keys and invalid-target fallback - [`cfc07f6`](https://github.com/morluto/rea/commit/cfc07f6cc5c0adea77505812df0d1780128263c6) fix(javascript): preserve unmatched package target arrays - [`2f97b2a`](https://github.com/morluto/rea/commit/2f97b2a757ac34ccf49ce7ee1c3f670d1dde9880) fix(javascript): retain package target refusal evidence - [`e4ccc23`](https://github.com/morluto/rea/commit/e4ccc234029f9abc7e31cc61cd534e835c72bc1d) test(browser): match real verifier startup budget ### 📊 Changes **10 files changed** (+1838 additions, -83 deletions) <details> <summary>View changed files</summary> 📝 `.agents/skills/reverse-engineer-anything/references/javascript-applications.md` (+4 -1) 📝 `docs/javascript-artifact-reconstruction.md` (+20 -0) 📝 `src/application/javascript/JavaScriptArtifactPathResolution.test.ts` (+368 -0) 📝 `src/application/javascript/JavaScriptArtifactPathResolution.ts` (+238 -80) ➕ `src/domain/javascript/javascriptPackageImportAliases.test.ts` (+147 -0) ➕ `src/domain/javascript/javascriptPackageImportAliases.ts` (+98 -0) ➕ `tests/acceptance/applications/packageImportAliasesCli.test.ts` (+251 -0) 📝 `tests/boundary/browser/cdpSvgXlinkHref.test.ts` (+9 -2) 📝 `tests/boundary/javascript/packageConditionalExports.test.ts` (+49 -0) ➕ `tests/boundary/javascript/packageImportAliases.test.ts` (+654 -0) </details> ### 📄 Description ## Summary Static JavaScript application analysis now resolves exact package-private `#alias` specifiers against the importer's enclosing `package.json` `imports` map. Inventoried local files become resolved application-graph edges instead of external unknowns, and missing, blocked, or out-of-scope aliases keep an unresolved edge with a precise reason. Fixes #1838 ## Demo ![demo](https://raw.githubusercontent.com/mvanhorn/pr-demo-assets/main/rea/package-imports-aliases/demo.gif) <video src="https://raw.githubusercontent.com/mvanhorn/pr-demo-assets/main/rea/package-imports-aliases/demo.mp4" controls width="720"></video> The sample is a generated local application, not third-party code. ## Problem and expected behavior A package that declares `{"imports":{"#config":"./config.cjs"}}` and requires `#config` leaves a real dependency out of the static application graph. On `0b4ce836`, that specifier is `resolved_path: null`, `resolution_status: "external"`, with the limitation "The bare specifier is a Node builtin, package import map, or invalid package name." Node's resolver selects `config.cjs`. The graph should resolve exact `#alias` keys through the nearest enclosing same-container `package.json`, stopping at a `node_modules` directory so a nested package cannot inherit an ancestor's map. Selected targets are package-local `./` files with no extension or index fallback. A missing key, explicit null, or out-of-scope alias reports that reason and does not fall through to files, indexes, or another package's map. ## Change and scope Domain lookup classifies `#` specifiers and exact `imports` keys. Application path resolution walks the importer's enclosing package scope, then reuses the existing condition, array, and null target selection. Shared CLI and MCP adapters already thread those resolver outcomes into `analyze-javascript-application` / `analyze_javascript_application` graphs; no new tool is added. - Exact `#alias` keys resolve relative to the owning package root. - Nested `node_modules` packages use their own `imports` map. - ESM `#config` is treated as an import alias, not a URL fragment. - Wildcard keys (`#internal/*`), recursive `#` targets, and external-package import targets remain unresolved with that limitation. Out of scope: wildcard expansion, recursive alias chasing, external-package targets, TypeScript `paths`, browser import maps, package installation, network access, and target execution. ## Contract and boundary impact - Semantic owner and earliest changed stage: domain `src/domain/javascript/javascriptPackageImportAliases.ts`, then application `JavaScriptArtifactPathResolution`, before graph relationship construction. - CLI and MCP/tool-catalog contract: none. Existing `analyze_javascript_application` / `analyze-javascript-application` schemas, names, and adapters are unchanged; resolution outcomes flow through the current `resolution_status` / `resolved_path` / limitations fields. - Provider, bridge, target-format, or platform compatibility: static JavaScript artifacts only; no Hopper, Ghidra, IDA, or browser provider changes. - Evidence, artifact, provenance, or reconstruction contract: original specifier, source path, artifact digest, source range, and container identity are preserved. Unsupported alias forms stay explicit unknowns rather than guessed files. - Process execution, authorization, cleanup, or containment impact: none. Resolution reads the inventoried file map only; artifact-root confinement remains enforced. - Generated metadata (`docs/public/product-catalog.json`), package, or installation impact (CI generates and validates ignored catalogs and `skills/`; edit authored instructions in `.agents/skills/reverse-engineer-anything/` and never commit build outputs): authored skill reference `javascript-applications.md` and `docs/javascript-artifact-reconstruction.md` updated. Catalog is regenerated by the repo build and is not committed. ## Evidence and regression coverage - Tests added or updated: domain specifier/lookup/target classification; application enclosing-scope, conditions, arrays, exact-file, missing-key, explicit-null, wildcard, recursive, external-package, reserved `#`, and foreign-container refusals; Node import/require oracle comparisons including nested packages without an `imports` field; compiled CLI and stdio MCP graphs on an owned fixture. - Base reproduction or other evidence: on `0b4ce836`, `#config` against the fixture above is external with the builtin/import-map limitation. After this change the same specifier is `resolved_path: "config.cjs"`, `resolution_status: "resolved"`. - User-visible CLI/MCP output (if applicable): generated local fixture. Root `#config` → `config.cjs` (`resolved`); nested `node_modules/fixture` `#config` → `node_modules/fixture/local.cjs` (`resolved`); `#missing` → `resolved_path: null`, `resolution_status: "external"`, limitation "Package metadata package.json does not define package import alias #missing; it declares #config." - Remaining proof gaps: wildcard, recursive, and external-package import targets are intentionally unresolved. Real Hopper/Ghidra and browser lanes do not apply. This host did not rerun a Windows package. For evidence-bearing changes: - [x] Observed, derived, and inferred claims remain distinguishable. - [x] Artifact identity, source provenance, and failed attempts remain preserved. - [x] Unsupported, incomplete, unavailable, or uncertain outcomes remain visible. ## Validation performed - `npm ci` — pass (377 packages added, 378 audited, 0 vulnerabilities) - `npm run test:focused -- src/domain/javascript/javascriptPackageImportAliases.test.ts src/application/javascript/JavaScriptArtifactPathResolution.test.ts tests/boundary/javascript/packageImportAliases.test.ts tests/acceptance/applications/packageImportAliasesCli.test.ts` — pass (4 files, 111 tests; CLI 1255 ms, stdio MCP 3863 ms) - `npm run check:fast` — pass - `npm run check` — pass (typecheck, lint, format:check, knip, metadata:check; 6/6 turbo tasks; architecture guards across 1138 source files; lint 753 warnings, 0 errors) - `npm run docs:check` — pass - `npm run verify:test-discovery` — pass (“Discovered all 839 test files exactly once.”) ## Compatibility, safety, and release - Breaking changes or migration steps: none. Additive resolution inside the existing application-graph contract. `#` specifiers that previously stayed external now resolve when an exact enclosing `imports` key selects an inventoried `./` file; unsupported forms remain unresolved. - Real Hopper/Ghidra, browser, or OS coverage: not run. This change is static JavaScript path resolution. CLI/MCP and Node resolver comparisons ran on Linux with Node 24.18.0. - Package or release metadata impact: none. No new tool, skill version, or package identity change. - Security, privacy, process, or containment review: caller-selected inputs and source evidence remain intact. Same-container package-scope checks and artifact-root confinement are preserved. No network, installation, or target execution is introduced. ## Review checklist - [x] This PR addresses a concrete problem or an agreed enhancement. - [x] The PR has one focused outcome and the title follows `type(scope): outcome`. - [x] Related issue is linked, or the reason for not linking one is stated above. - [x] Tests cover changed observable behavior and meaningful failure paths. - [x] Owning docs, contracts, and generated metadata are updated where needed. - [x] User-visible CLI/MCP changes include representative output. - [x] I checked the final diff for secrets, unrelated cleanup, and unsupported claims. --- <sub>🔄 This issue represents a GitHub Pull Request. It cannot be merged through Gitea due to API limitations.</sub>
bsakaguchi 2026-10-31 00:56:06 +00:00
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#4858
No description provided.