Skip to content

fix(runtime,main-thread): backport branch-independent fixes from vapor - #359

Draft
Huxpro wants to merge 1 commit into
mainfrom
huxcc/intelligent-pasteur-m9skmr
Draft

fix(runtime,main-thread): backport branch-independent fixes from vapor#359
Huxpro wants to merge 1 commit into
mainfrom
huxcc/intelligent-pasteur-m9skmr

Conversation

@Huxpro

@Huxpro Huxpro commented Aug 6, 2026

Copy link
Copy Markdown
Owner

Follow-up to #358. Ports the parts of the vapor line that aren't vapor-specific — each is a fix main wants on its own terms, and porting them shrinks the eventual vapormain merge surface in shared files.

Ported

worklet-apply.ts — the background thread's MainThreadRef init value was being dropped. INIT_MT_REF is an 'always' op during hydration precisely because the background value is authoritative, but applyInitMtRef skipped whenever the main-thread first-screen render had already registered the same _wvid. Now it mutates the existing cell (worklet-runtime hands these out by reference) instead of skipping. resetWorkletState() was an empty stub; it now clears the external ref registry, and IFR teardown calls it for the same reason it clears list state — the replay re-applies SET_MT_REF/INIT_MT_REF.

ifr.ts — list platform metadata and the no-throw boundary. A diverging item-key / estimated-main-axis-size-px / … reaches native through update-list-info at insert time, so patching our JS map cannot repair the row native was already told about; that now forces a rebuild rather than an in-place patch (this was the open question I flagged in #357). A throwing hydration patch falls back to the background replay instead of escaping vuePatchUpdate into Lepus.

flush.ts — main-thread ops could be posted as background ops. On the IFR main thread with no ops sink installed, doFlush fell through to callLepusMethod('vuePatchUpdate'), re-entering the executor as if the batch had come from the background thread and desyncing the hydration stream. It now drops the batch with a dev warning. Also: typeof lynx guard before use (bare AMD identifier throws ReferenceError in plain node realms), and the ack fallback is only armed where setTimeout exists.

cross-thread.tsrunOnMainThread no longer stamps _execId into the background exec map when the module evaluates in the IFR main-thread realm; that map is meaningless there and the entry is retained for the life of the page.

worklet-loader.ts — use the shared hasMainThreadDirective rather than an inline substring check, so the two thread bundles cannot disagree about which files carry worklets. No behavior change on main today (the helper is the same substring check); it removes a call-site divergence and picks up vapor's AST-based version for free when that lands.

Deliberately not ported

  • Page.ts scope handling and transition-shared.ts — composable scope classes and the ShadowElement.iduid rename, both vapor infrastructure.
  • The vueIfrHydrationComplete handshake, completeIfrInitialRender, ifrInert / isIfrEnabled — need the vapor background entry and plugin pipeline.
  • __VUE_LYNX_FLUSH_HOOK__ observability — benchmark tooling, not a fix.
  • entry-main.ts's SystemInfo init — vapor flips the precedence between an engine-provided global and lynx.SystemInfo; the two behaviors differ and neither is obviously right, so I left main's alone.

Testing

  • pnpm --filter vue-lynx-testing-library test — 238 passed (4 new).
  • New mt-worklet-refs.test.ts (2 of 3 fail without the worklet-apply change) and a list platform-metadata rebuild case in ifr-list.test.ts (fails without the ifr.ts change).
  • pnpm --filter vue-lynx build clean; biome clean over the touched sources.
  • The flush.ts and cross-thread.ts guards are not covered by new tests — both are small guards on paths that need a real IFR bundle to exercise.

Generated by Claude Code

Ports the parts of the `vapor` line that are not vapor-specific, both because
they are fixes `main` wants on its own terms and because it shrinks the
eventual vapor -> main merge surface in shared files.

- worklet-apply: INIT_MT_REF is an 'always' op during hydration, so the BG
  initial value is authoritative — it was being dropped whenever the MT
  first-screen render had already registered the same _wvid. Mutate the
  existing cell (worklet-runtime hands these out by reference) instead of
  skipping. resetWorkletState() was an empty stub; it now clears the external
  ref registry, and IFR teardown calls it for the same reason it clears list
  state — the replay re-applies SET_MT_REF / INIT_MT_REF.
- ifr: a diverging list platform attribute (item-key, estimated sizes, …)
  reaches native through update-list-info at insert time, so patching the JS
  map cannot repair the row native already knows about — treat it as a
  rebuild. A throwing hydration patch now falls back to the background replay
  instead of escaping vuePatchUpdate into Lepus.
- flush: on the IFR main thread a missing ops sink fell through to
  callLepusMethod, re-entering vuePatchUpdate as if the batch came from the
  background thread; drop it with a dev warning instead. Guard `lynx` with
  typeof (bare AMD identifier) and skip arming the ack fallback in realms
  without setTimeout.
- cross-thread: runOnMainThread no longer stamps _execId into the background
  exec map when the module evaluates in the IFR main-thread realm.
- worklet-loader: use the shared hasMainThreadDirective so the two thread
  bundles cannot disagree about which files carry worklets.

Not ported: scope-class handling (Page.ts), ShadowElement id -> uid, the
hydration-complete handshake, and the flush observability hook — all tied to
vapor infrastructure.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01CuaHSwJh1PvJHUcwrH95pi
@vercel

vercel Bot commented Aug 6, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
vue-lynx Ready Ready Preview Aug 6, 2026 5:16pm

Request Review

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