Skip to content

fix(networking): resolve fetch lazily instead of at module scope - #353

Draft
Huxpro wants to merge 2 commits into
mainfrom
claude/todo-implementation-sey0il
Draft

fix(networking): resolve fetch lazily instead of at module scope#353
Huxpro wants to merge 2 commits into
mainfrom
claude/todo-implementation-sey0il

Conversation

@Huxpro

@Huxpro Huxpro commented Aug 1, 2026

Copy link
Copy Markdown
Owner

What

Two changes to examples/networking/src/App.vue, the site of the repo's TODO: Remove once lynx-stack shims 'fetch' on the 'lynx' global.

1. Resolve fetch lazily instead of at module scope

The example bound the workaround eagerly:

const _fetch: typeof fetch = globalThis.fetch ?? fetch

That form has two problems:

  • IFR-unsafe. The IFR main-thread context has no fetch at all, so a module-scope reference crashes bundle evaluation there. This hazard is already documented — and already solved — in examples/hackernews-css/src/api.ts and examples/hackernews-tailwind/src/api.ts. The networking example never picked up the fix. (enableIFR defaults to false and this example doesn't set it, so nothing is broken today; this makes the example correct if IFR is turned on.)
  • Poor failure mode. When neither binding resolves, the eager form silently stores undefined, and the failure surfaces as _fetch is not a function at whichever call site runs first, rather than where fetch was found to be missing.

Replaced with the lazy getFetch() resolver already used by the hackernews examples — same shape, verbatim — and updated the three call sites.

2. Correct the attribution in the comment

The original comment blamed the bundler:

lynx-stack web-platform's RuntimeWrapperWebpackPlugin shadows fetch with an undefined parameter

That misplaces the defect. The shadowing is deliberate — fetch sits in RuntimeWrapperWebpackPlugin's default BOM inject list right next to window, document, navigator, localStorage — and the generated banner ships an explicit fallback:

tt.define("${moduleId}", function (require, module, exports, ${injectStr}) {
  lynx = lynx || {};
  ...
  fetch = fetch || lynx.fetch;

Both sides of that fallback are the host's to supply. On the web platform neither is: nothing lands in the injected slot and lynx.fetch is unset, so the binding resolves to undefined. globalThis.fetch is untouched and still reaches the browser's real implementation, which is why the workaround works.

So this is a web-platform gap against its own documented contract — the Lynx docs state that fetch is a global (not lynx.fetch) and that "Web Platform supports Fetch API using Browser's Fetch implementation, it follows the same rules as the web" — not a bundler defect. The comment and the TODO now point there.

Verification

  • RuntimeWrapperWebpackPlugin inject list and banner: read from source, quoted above.
  • The documented web-platform fetch contract: from the Lynx fetch API docs.
  • Not verified: the web-core / web-platform code that actually builds lynxCoreInject was not read, so which package drops the injection — and whether current versions still do — is inference, not confirmed. The workaround is left in place regardless.

Testing

Workspace dependencies aren't installed in this environment, so the example's vue-tsc typecheck could not be run. The extracted <script setup> block parses clean (node --experimental-strip-types --check), and change 1 is a like-for-like port of a pattern already shipping in two other examples. Change 2 is comments only.

Notes on the other TODOs

The repo has exactly three first-party TODO comments; the other two are not actionable here:

  • packages/vue-lynx/main-thread/src/shims.d.ts:28 — asks for a fix in @lynx-js/type-element-api (a different repo). Worth flagging: applying its recommended ElementRef | ElementRef[] union to the local override would be a regression, since the narrow array-only declaration is what stops callers from passing a bare element and crashing on the web PAPI.
  • packages/vue-lynx/runtime/src/node-ops.ts:51 — blocked on a Lynx engine fix for numeric flex through __SetInlineStyles; not verifiable without the engine.

Follow-up worth considering (not in this PR): examples/ai-chat carries the same eager const _fetch = globalThis.fetch ?? fetch in three separate files (src/lib/api.ts, src/lib/backend-mode.ts, src/lib/stream.ts), each with the same misattributing comment.

🤖 Generated with Claude Code

https://claude.ai/code/session_01NCNxvDyNN8HAmGtw2E5vuo

The `fetch` workaround in the networking example bound `globalThis.fetch
?? fetch` eagerly at module scope. That has two problems:

- The IFR main-thread context has no `fetch` at all, so a module-scope
  reference crashes bundle evaluation there — the same hazard already
  documented in `examples/hackernews-*/src/api.ts`.
- When neither binding resolves, the eager form silently stores
  `undefined` and surfaces as "_fetch is not a function" at the call
  site rather than at the point of failure.

Switch to the lazy `getFetch()` resolver already used by the hackernews
examples, which reads the binding at request time and throws a clear
error when no `fetch` is available. The underlying lynx-stack workaround
(and its TODO) is unchanged.

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

vercel Bot commented Aug 1, 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 8:52pm

Request Review

The comment blamed RuntimeWrapperWebpackPlugin for shadowing `fetch`
with an undefined parameter. The shadowing is deliberate: `fetch` is in
the plugin's default BOM inject list next to `window`/`document`/
`navigator`, and the generated banner emits `fetch = fetch || lynx.fetch`
as a fallback.

Both sides of that fallback are the host's to supply. On the web
platform neither is — nothing lands in the injected slot and `lynx.fetch`
is unset — so the binding resolves to `undefined`. That is a web-platform
gap against its own documented contract ("Web Platform supports Fetch API
using Browser's Fetch implementation"), not a bundler defect.

Retarget the comment and the TODO accordingly. No behaviour change.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NCNxvDyNN8HAmGtw2E5vuo
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.

2 participants