Conversation
🦋 Changeset detectedLatest commit: b4a52f6 The changes in this PR will be included in the next version bump. This PR includes changesets to release 6 packages
Not sure what this means? Click here to learn what changesets are. Click here if you're a maintainer who wants to add another changeset to this PR |
Polling moves out of the async signal engine: usePoll(signal, expires) invalidates the signal on an interval that starts on document idle and resets whenever a new result lands. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Polling now lives in usePoll (@qwik.dev/utils). Drops the expires/poll/ interval options and accessors, the NO_POLL flag, the expires slot in the AsyncSignal wire format, and the SSR eager-resume hack for polling signals (the usePoll visible task resumes on qidle instead). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
allowStale:false made every invalidation clear the value; clear() makes that a one-shot, caller-controlled operation and drops the CLEAR_ON_INVALIDATE flag and the loader allowStale plumbing. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The browser HTTP cache becomes the single freshness authority for loader data. cacheControl accepts any cacheControl() value or a function of the request event (null return skips the header); default is no-cache. SSG writes per-loader files only for cacheControl: 'immutable' loaders. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…erender on unchanged data Every navigation invalidates every loader signal; the browser HTTP cache (driven by each loader's cacheControl) is the freshness authority. The TTL fetch cache becomes a per-navigation dedupe map shared between hover prefetch and the nav's own fetches. Loader responses keep their raw text so an unchanged payload skips deserialization and returns the previous object, firing no effects. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…utils Also refresh the core-notes agent guidance and add the utils package to the monorepo map. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
cacheControl:'immutable' data cannot change until a rebuild, so the per-navigation invalidation sweep skips those signals; their tracked URL inputs still invalidate them when the path or search changes. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Skip immutable loaders in the eager nav kick-off and in link data prefetch: if the SSR'd page never reads them client-side, their JSON is never downloaded. First read fetches once per deploy (immutable browser caching covers every fetch after that). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@qwik.dev/core
@qwik.dev/router
eslint-plugin-qwik
create-qwik
@qwik.dev/optimizer
@qwik.dev/devtools
commit: |
built with Refined Cloudflare Pages Action⚡ Cloudflare Pages Deployment
|
GitHub strips embedded svg
|
@copilot resolve the merge conflicts in this pull request |
# Conflicts: # packages/qwik-router/src/runtime/src/link-prefetch.ts # packages/qwik-router/src/runtime/src/router-outlet-component.tsx Co-authored-by: wmertens <54934+wmertens@users.noreply.github.com>
Merge conflicts resolved in f3d5ea2. The two conflicts were in |
There was a problem hiding this comment.
Pull request overview
This PR refactors “polling/expiry” behavior out of @qwik.dev/core computed signals into a new @qwik.dev/utils package, and reworks Qwik Router loader freshness to rely on browser HTTP caching (Cache-Control/ETag) with a new cacheControl loader option (including an 'immutable' SSG-friendly mode).
Changes:
- Introduces
@qwik.dev/utils(tied to core) and addsusePoll(signal, ms)for client-only polling on document idle. - Updates computed signals: removes
expires/poll/interval(+allowStale) and addsComputedSignal.clear()to explicitly drop the value and recompute. - Updates router loaders: adds
cacheControl, refetches loaders on every SPA navigation (skipping immutable ones), and avoids rerenders when loader response text is unchanged.
Reviewed changes
Copilot reviewed 65 out of 68 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
| tsconfig.json | Includes packages/qwik-utils/src in TS project paths. |
| scripts/util.ts | Adds --qwikutils boolean CLI option. |
| scripts/qwik-utils.ts | Adds build script for the new qwik-utils package. |
| scripts/ci-restore-artifacts.ts | Restores CI artifacts for packages/qwik-utils/lib. |
| scripts/build.ts | Wires buildQwikUtils() into the main build pipeline. |
| README.md | Replaces inline sponsor SVG with a referenced asset file. |
| pnpm-lock.yaml | Adds lockfile entries for packages/qwik-utils. |
| packages/qwik/src/core/tests/use-computed.spec.tsx | Removes SSR polling resume test now that core polling is removed. |
| packages/qwik/src/core/ssr/ssr-render-jsx.ts | Removes SSR eager-resume tracking for polling computeds. |
| packages/qwik/src/core/shared/serdes/serialize.ts | Removes async-signal expires serialization field. |
| packages/qwik/src/core/shared/serdes/serdes.unit.ts | Updates serdes tests/snapshots after removing expires. |
| packages/qwik/src/core/shared/serdes/inflate.ts | Adjusts async-signal inflation tuple layout (no expires). |
| packages/qwik/src/core/reactive-primitives/utils.ts | Removes internal _isSignalNotInvalid helper. |
| packages/qwik/src/core/reactive-primitives/types.ts | Removes expires/poll/interval/allowStale from ComputedOptions; removes related flags. |
| packages/qwik/src/core/reactive-primitives/signal.public.ts | Adds ComputedSignal.clear(); removes expires/poll/interval properties. |
| packages/qwik/src/core/reactive-primitives/impl/signal.unit.tsx | Updates type tests (removes interval). |
| packages/qwik/src/core/reactive-primitives/impl/computed-signal-impl.ts | Removes polling/expiry engine; adds clear() behavior; updates messaging. |
| packages/qwik/src/core/reactive-primitives/impl/async-signal.unit.tsx | Removes polling/allowStale tests; adds clear() behavior test coverage. |
| packages/qwik/src/core/reactive-primitives/impl/async-signal-impl.ts | Updates docs comment to remove polling mention. |
| packages/qwik/src/core/qwik.core.api.md | Updates public API docs for removed fields + new clear(). |
| packages/qwik/src/core/internal.ts | Stops exporting removed _isSignalNotInvalid. |
| packages/qwik-utils/vite.config.ts | Adds Vite lib build config for @qwik.dev/utils. |
| packages/qwik-utils/tsconfig.json | Adds tsconfig extending repo defaults, sets jsxImportSource. |
| packages/qwik-utils/src/use-poll.unit.tsx | Adds unit tests for usePoll in SSR + DOM render modes. |
| packages/qwik-utils/src/use-poll.ts | Implements usePoll() via useVisibleTask$ + interval invalidation. |
| packages/qwik-utils/src/index.qwik.ts | Exports usePoll from the package entrypoint. |
| packages/qwik-utils/package.json | Adds new published package manifest for @qwik.dev/utils. |
| packages/qwik-router/src/ssg/worker-thread.ts | Switches SSG per-loader file writing to cacheControl: 'immutable'. |
| packages/qwik-router/src/runtime/src/use-endpoint.unit.ts | Updates loader-fetch expectations and adds cache-clear refetch test. |
| packages/qwik-router/src/runtime/src/types.ts | Replaces expires/poll/allowStale loader options with cacheControl. |
| packages/qwik-router/src/runtime/src/route-loaders.unit.ts | Updates loader option tests; adds immutable registration test. |
| packages/qwik-router/src/runtime/src/route-loaders.ts | Refactors loader fetch caching to per-navigation dedupe + raw-text identity optimization; adds immutable tracking. |
| packages/qwik-router/src/runtime/src/qwik-router.runtime.api.md | Updates runtime API docs imports (adds CacheControl). |
| packages/qwik-router/src/runtime/src/qwik-router-component.tsx | Invalidates loaders on every navigation (skipping immutable); clears per-nav fetch cache after kickoff. |
| packages/qwik-router/src/runtime/src/prefetch-route.unit.ts | Updates prefetch tests to new immutable behavior and raw fetch result shape. |
| packages/qwik-router/src/runtime/src/prefetch-route.ts | Prefetches loader data without relying on in-memory freshness; skips immutable loaders. |
| packages/qwik-router/src/runtime/src/link-prefetch.unit.ts | Updates link prefetch tests for new prefetchRoute signature. |
| packages/qwik-router/src/runtime/src/link-prefetch.ts | Removes loaderState param plumbed into link prefetching. |
| packages/qwik-router/src/runtime/src/link-component.unit.tsx | Updates Link prefetch tests for new prefetchRoute signature. |
| packages/qwik-router/src/runtime/src/link-component.tsx | Removes RouteStateContext dependency for prefetch data path. |
| packages/qwik-router/src/middleware/request-handler/resolve-request-handlers-core.ts | Updates comment for SSG loader internals tracking (__cacheControl). |
| packages/qwik-router/src/middleware/request-handler/handlers/loader-request-event.unit.ts | Updates loader test fixtures after removing __expires/__poll/__allowStale. |
| packages/qwik-router/src/middleware/request-handler/handlers/loader-handler.unit.ts | Adds coverage for new cacheControl behavior and precedence rules. |
| packages/qwik-router/src/middleware/request-handler/handlers/loader-handler.ts | Implements cacheControl option resolution and header precedence rules. |
| packages/docs/src/routes/docs/upgrade/index.mdx | Updates upgrade docs to point polling to usePoll() in utils. |
| packages/docs/src/routes/docs/integrations/prisma/index.mdx | Moves zod$ import from router to utils. |
| packages/docs/src/routes/docs/integrations/drizzle/index.mdx | Moves zod$ import from router to utils. |
| packages/docs/src/routes/docs/(qwikrouter)/route-loader/index.mdx | Updates loader docs for cacheControl, nav refetch behavior, and usePoll. |
| packages/docs/src/routes/docs/(qwikrouter)/guides/qwik-nutshell/index.mdx | Moves zod$ import from router to utils. |
| packages/docs/src/routes/docs/(qwikrouter)/action/index.mdx | Moves zod$ import from router to utils across examples. |
| packages/docs/src/routes/docs/(qwik)/core/state/index.mdx | Updates computed-signal docs for usePoll and clear(); removes expires/poll. |
| packages/docs/src/routes/api/qwik/index.mdx | Adds ComputedSignal.clear() API docs; removes old computed options/props docs. |
| packages/docs/src/routes/api/qwik/api.json | Regenerates Qwik API JSON for clear() and removed fields. |
| packages/docs/src/routes/api/qwik-router/index.mdx | Updates Router API docs for loader options text. |
| packages/docs/src/routes/api/qwik-router/api.json | Regenerates Router API JSON for loader options text. |
| package.json | Adds --qwikutils to build.full. |
| CLAUDE.md | Updates repo map to include @qwik.dev/utils. |
| AGENTS.md | Updates repo map to include @qwik.dev/utils. |
| .ruler/skills/qwik-core-development/references/core-notes.md | Updates core guidance to reflect removal of polling/expiry and new usePoll/clear(). |
| .ruler/AGENTS.md | Updates repo map to include @qwik.dev/utils. |
| .github/workflows/ci.yml | Adds build/cache/artifact wiring for qwik-utils. |
| .github/assets/kunaico.svg | Adds sponsor SVG asset referenced by README. |
| .changeset/utils-package-usepoll.md | Changeset for new utils package and usePoll. |
| .changeset/router-nav-invalidation.md | Changeset for nav refetch + no-rerender-on-unchanged behavior. |
| .changeset/router-loader-cache-control.md | Changeset for breaking loader caching option migration to cacheControl. |
| .changeset/core-remove-expires-poll.md | Changeset for breaking removal of computed polling fields. |
| .changeset/core-clear-signal.md | Changeset for breaking allowStale removal and new clear(). |
| .changeset/config.json | Adds @qwik.dev/utils to the fixed version group. |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
Suppressed comments (1)
packages/qwik-router/src/runtime/src/route-loaders.ts:276
- Same as above: cap
navFetchCacheon the non-abortable path as well so it can’t grow without bound within a single navigation cycle.
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
|
@copilot fix the unit and e2e test failures |
The nav-wide loader invalidation iterated every key in the loader state, including the `__qwik_route_loader_value__*` resume entries, which are not signals. Every SPA navigation threw, breaking client-side routing. Also update the link prefetch expectation for the dropped loaderState argument, refresh the SSG state snapshot, and make usePoll generic with a 5ms minimum poll interval. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01B9ATgshxvKbASTKzb2uG9F
…d2pt feat(utils): new @qwik.dev/utils package with usePoll hook
The current ComputedSignal API with
pollandexpirestakes quite a lot of core code while it's fairly rare/doesn't cover all use cases.Furthermore, there was a disconnect between the
expiresoption in routeLoaders and thecache-controlheaders that might have been set by middleware.This PR does a few things:
@qwik.dev/utilspackage, which contains non-core code that can still access internals and which is tied to the exact core versionusePollhook which can be used to poll a computed signal once it hits the client.clear()to computed signals, which clears their value, which will cause Suspense boundaries to show while recalculatingcacheControloption to routeLoaders. Set to"immutable"to make it be generated during SSG.poll,expires, andallowStalefrom ComputedSignal and thus also routeLoaderszod$into utils