You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Regression (#1759): store-action bindings treated as local shadows; main fails its own object-literal-methods test #1762
#1759 ("a receiver-less JS/TS call never binds to a method") makes isLocallyBoundJsName treat a Zustand-style store binding as a local rebinding, so bare calls to store actions stop resolving. This regresses the repo's own test suite: main at cd4e65b fails one of its own tests.
Reproduction
Clean checkout of main at cd4e65b, npm ci, full build, then:
npx vitest run __tests__/object-literal-methods.test.ts
FAIL __tests__/object-literal-methods.test.ts > object-literal method resolution (end-to-end)
> resolves callers of store actions across files (destructured + chained getState())
AssertionError: expected [] to include 'loginFlow'
__tests__/ui-steps-api.test.ts fails the same way (2 tests):
buildSteps > walks a screen through its handler, the bridge, the event, the store and the request
screen regions > a screen names every step's region: the screen body for its own code, inherited down the walk
Cause
isLocallyBoundJsName carves out require(...) / import(...) on the right-hand side, because an imported name is a node elsewhere rather than a shadow. A store binding is the same situation and is not carved out:
const{ fetchUser }=useStore.getState();// action lives in the store fileconstsetZipUri=useStore((s)=>s.setZipUri);
Both name the store's own action, so the bare call that follows means that action. matchStoreAccessorChain already recognises the accessor shapes; isLocallyBoundJsName now discards those candidates before it runs.
Fix
Extend the existing carve-out to the two accessor shapes plus the selector read. PR follows.
The selector pattern is deliberately narrow — one named parameter and a plain member read of the same name — so a genuine local binding such as const now = options.now || (() => Date.now()) still shadows. That case is covered by bare-call-no-method, which stays green.
Verification
Full suite on the same machine, same build, pristine vs patched:
pristine cd4e65b
with the fix
Test files
13 failed
7 failed
Unique failing tests
29
24
object-literal-methods and both ui-steps-api tests go green, and no test that passed before fails after. The remaining failures are pre-existing on this machine (Windows, native kernel not built) and byte-identical across both runs.
Closing as a duplicate of #1760, which diagnoses the same regression and covers the same two store shapes. I filed this without checking the open PR list first — apologies for the noise.
Leaving the independent verification here in case a second platform is useful, since #1760's measurements are from wasm arm and these are Windows x64:
Clean checkout of main at cd4e65b, full build, full suite — pristine vs a patch of the same shape:
pristine cd4e65b
patched
Test files
13 failed
7 failed
Unique failing tests
29
24
The three that go green are exactly the ones #1760 names — object-literal-methods.test.ts > resolves callers of store actions across files (destructured + chained getState()) (expected [] to include 'loginFlow') and the two in ui-steps-api.test.ts. No test that passed before failed after. The remaining 24 are pre-existing on this machine (native kernel not built) and identical across both runs.
One difference worth flagging for #1760: I kept const now = options.now || (() => Date.now()) shadowing, by requiring the selector shape to be a single named parameter plus a member read ((?!\s*\()), so bare-call-no-method stayed green unmodified. #1760 instead reclassifies that case as a re-binding and drops it from the fixture. That is a deliberate call in #1760 and the reasoning there is sound — noting it only so the choice is explicit.
Thanks for the Windows x64 confirmation. One correction on the flagged difference: that was #1760's first commit. Its second commit (a29d112) went the same way you did — const now = options.now || … is a local binding again and the bare-call-no-method fixture is back unmodified. The exemption now covers only destructuring and a selector arrow that reads the same member off its own parameter ((s) => s.name), so () => Date.now() still shadows. Measured on a Kotlin + JS + Go app: the wide version put 24 now() calls onto a Kotlin private val now; the narrow one puts them nowhere.
Activity
bompus commentedon Sep 8, 2026
Closing as a duplicate of #1760, which diagnoses the same regression and covers the same two store shapes. I filed this without checking the open PR list first — apologies for the noise.
Leaving the independent verification here in case a second platform is useful, since #1760's measurements are from wasm arm and these are Windows x64:
Clean checkout of
mainatcd4e65b, full build, full suite — pristine vs a patch of the same shape:cd4e65bThe three that go green are exactly the ones #1760 names —
object-literal-methods.test.ts > resolves callers of store actions across files (destructured + chained getState())(expected [] to include 'loginFlow') and the two inui-steps-api.test.ts. No test that passed before failed after. The remaining 24 are pre-existing on this machine (native kernel not built) and identical across both runs.One difference worth flagging for #1760: I kept
const now = options.now || (() => Date.now())shadowing, by requiring the selector shape to be a single named parameter plus a member read ((?!\s*\()), sobare-call-no-methodstayed green unmodified. #1760 instead reclassifies that case as a re-binding and drops it from the fixture. That is a deliberate call in #1760 and the reasoning there is sound — noting it only so the choice is explicit.danusha2345 commentedon Sep 8, 2026
Thanks for the Windows x64 confirmation. One correction on the flagged difference: that was #1760's first commit. Its second commit (a29d112) went the same way you did —
const now = options.now || …is a local binding again and thebare-call-no-methodfixture is back unmodified. The exemption now covers only destructuring and a selector arrow that reads the same member off its own parameter ((s) => s.name), so() => Date.now()still shadows. Measured on a Kotlin + JS + Go app: the wide version put 24now()calls onto a Kotlinprivate val now; the narrow one puts them nowhere.