Skip to content

[APPS] fix(apps): wrong connection IDs from mispaired module specifiers - #469

Draft
sdkennedy2 wants to merge 1 commit into
masterfrom
sdkennedy2/apps-static-dep-pairing
Draft

[APPS] fix(apps): wrong connection IDs from mispaired module specifiers#469
sdkennedy2 wants to merge 1 commit into
masterfrom
sdkennedy2/apps-static-dep-pairing

Conversation

@sdkennedy2

@sdkennedy2 sdkennedy2 commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #468 — review that first; this PR targets its branch, so the diff here is only the pairing fix.

Motivation

collectStaticModuleDependencies pairs the bundler's resolved dependency IDs against the AST's static specifiers by index. But bundlers report one entry per unique dependency, so a module that imports the same specifier twice yields more specifiers than IDs and the pairing slips by one from there on.

Reproduced against a real Rollup build — 6 specifiers, 5 resolved IDs, 3 mispaired and 1 dropped:

specifier paired to should be
./dup (2nd) reexport.js (collapses into the entry above)
./reexport star.js reexport.js
./star zlast.js star.js
./zlast dropped zlast.js

Why it matters: these pairings map an action-catalog call back to its module and feed importsByVariable / exportsByName / starExports. A wrong pairing can attribute a connectionId to the wrong file or miss it. Since allowedConnectionIds is an allowlist, a miss breaks the function at runtime — and the build still exits 0, so it fails silently.

Live today on Vite 6/7 with plain Rollup. Unrelated to any bundler compatibility issue; it only shares a code path with #468.

Changes

Deduplicate the specifiers by source string, preserving first-occurrence order, so both sequences stay in step.

function getStaticModuleSources(ast: Program): string[] {
    const sources = new Set<string>();
    for (const node of ast.body) {
        // ...import / export-from declarations
        sources.add(node.source.value);
    }
    return [...sources];
}

Known gap, documented rather than hidden. The two bundlers disagree on what "unique" means — verified against real builds, not assumed:

One file imported via ./dup.js and ./dup Entries reported
Rollup 4 (Vite 7) 2 — dedups by specifier string
Rolldown 1.1.5 (Vite 8) 1 — dedups by resolved module ID

They agree unless one module imports the same file through two different specifiers. In that case this fix is correct under Rollup and still mispairs under Rolldown. Closing that needs pairing by path correspondence instead of position — a larger change, so it's called out in the code comment and in the test that encodes the Rollup-specific expectation.

I also tried failing closed on a specifier/ID count mismatch. It broke 18 existing tests, because legitimate callers pass partial dependency lists — a build error there would be worse than the narrow remaining gap.

QA Instructions

No manual QA needed. Four unit cases cover the pairing (three fail without the fix), one asserts importsByVariable resolves each binding to the right module, and a new end-to-end test drives connection-ID collection through a real vite.build() asserting the exact collected IDs — it passes on master unchanged, so it characterises existing behaviour before the fix moves it.

Blast Radius

Only the Apps backend-function build path — how collected module records map imports to resolved modules, which feeds allowedConnectionIds.

This changes behaviour for existing Vite 6/7 users, which is the point: allowlists that were silently wrong become correct. Any app whose backend graph imports one specifier twice could previously have had a missing or misattributed connection ID. No dependency or public API changes.

Risk: moderate — it touches the allowlist path, so a mistake has runtime consequences. Mitigated by the tests above plus the end-to-end assertion of exact IDs.

Documentation

🤖 Generated with Claude Code

@sdkennedy2

Copy link
Copy Markdown
Collaborator Author

@codex review
@cursor review

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Another round soon, please!

Reviewed commit: 8db13595fc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

…specifier

`collectStaticModuleDependencies` zipped the resolved dependency IDs the
bundler supplies against every static specifier found in the AST. Bundlers
report one entry per *unique* dependency, so a module importing the same
specifier twice yields more specifiers than resolved IDs and the pairing
slips by one from that point on — attributing imports to the wrong
dependency and dropping the last one entirely.

Verified against a real Rollup build. Six specifiers, five resolved IDs,
three mispaired:

    ./dup      -> reexport.js   (should be dup.js)
    ./reexport -> star.js       (should be reexport.js)
    ./star     -> zlast.js      (should be star.js)
    ./zlast    -> dropped

These pairings map an action-catalog call back to the module it came from
and feed importsByVariable / exportsByName / starExports, so a wrong
pairing can attribute a connectionId to the wrong file or miss it. Since
allowedConnectionIds is an allowlist, a miss is a functional break — and
the build still succeeds, so it fails silently. Present today on Vite 6/7.

Deduplicate specifiers by source string, preserving first-occurrence order.

Known gap, documented rather than fixed: Rollup dedups its resolved-ID list
by specifier string while Rolldown dedups by resolved module ID, both
verified against real builds. They agree unless one module imports the same
file through two different specifiers, which still mispairs under Rolldown.
Removing that dependence needs pairing by path correspondence instead of by
position, which is a larger change than this fix.

Also adds an end-to-end test driving connection ID collection through a real
vite.build() and asserting the exact collected IDs; it passes on master
unchanged, so it characterises existing behaviour.

Co-Authored-By: Claude <noreply@anthropic.com>
@sdkennedy2
sdkennedy2 force-pushed the sdkennedy2/apps-static-dep-pairing branch from 8db1359 to cb0af18 Compare July 28, 2026 17:11
@sdkennedy2 sdkennedy2 changed the title [APPS] Pair static module specifiers with resolved IDs by unique specifier [APPS] fix(apps): wrong connection IDs from mispaired module specifiers Jul 28, 2026
@sdkennedy2

Copy link
Copy Markdown
Collaborator Author

@codex review

Head is now cb0af18e (last reviewed 8db13595); it was restacked after the parent branch changed.

@chatgpt-codex-connector

Copy link
Copy Markdown

Codex Review: Didn't find any major issues. Can't wait for the next one!

Reviewed commit: cb0af18efc

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Base automatically changed from sdkennedy2/apps-parse-backend-module-source to master July 29, 2026 13:40
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant