mirror of
https://github.com/cloudflare/vinext.git
synced 2026-09-14 19:04:59 +08:00
vinext@0.1.7
4 Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
0ecd05dfe1 |
test(app-router): remove live network from ISR revalidation fixtures (#1756)
* test(app-router): remove live network from ISR revalidation fixtures The App Router ISR revalidation fixtures fetch httpbin.org only to collect next.tags before rendering request ids. That makes CI depend on external network latency even though the tests never assert the remote response body. Use a deterministic data URL for the tagged fetch in the parent and nested fixtures so tag collection still exercises the ISR invalidation path without waiting on an external service. * test: remove incidental external fetches A layout observation unit test and a dynamic-error fixture used external URLs even though their contracts only need fetch observation or no-store policy detection. Those URLs can start unnecessary network work when the test behavior does not depend on a remote response. Use deterministic data URLs so the same fetch branches and cache options are exercised without relying on external network availability. |
||
|
|
b8cecf8b46 |
fix(skip): centralize final static-layout skip rejection (#1722)
* feat(skip): reject runtime-unsafe layouts from static transport skip The enable slice omits a static layout from RSC transport once it passes the cache cross-check. The cross-check proves artifact and payload identity, but it does not, on its own, assert that the layout's render was free of per-request behaviour. A layout that reads params, calls a request API, sets a finite revalidate, opens a cacheLife scope, reads unstable_cache, or issues cache-tagged or dynamic fetches must never be reused across a sibling-route navigation. This adds an explicit observation gate at the skip decision point. Before a static layout is omitted, its per-layout observation is checked: an incomplete probe, observed param keys, structural param scope, request APIs, finite revalidate, cacheLife, unstable_cache, cache tags, cacheable fetches, or dynamic fetches each reject the entry with a specific SKIP_LAYOUT_* code and fall the layout back to render-and-send. Scope note: the enable base already classifies most runtime-unsafe layouts as dynamic (so they never enter the static skip set) and the cache cross-check rejects payload-divergent reuse. This gate is a deliberate last line of defence at the omission boundary, with diagnostic rejection codes. The tests assert the end-to-end contract (unsafe layouts are never omitted); they pass on the enable base alone, so this PR is kept as a draft pending rebase once the enable PR merges. * fix(skip): centralize final static-layout skip rejection * refactor(skip): table-drive static layout rejection rules * docs(skip): clarify !observation guards missing-tracker, not missing observation |
||
|
|
49d2f0966d |
feat(skip): plumb client reuse manifests through the app request path (#1717)
* feat(skip): plumb client reuse manifests through the app request path The browser never sends a client reuse manifest and the server has no path to receive one, so the static-layout skip planner (#1674) and observation foundations (#1672) have nothing to act on. Skip transport stays fully off, but the transport wiring those slices depend on is missing. Add a bounded browser producer that derives a client reuse manifest from the visible AppElements state and attaches it as X-Vinext-Client-Reuse-Manifest only on soft navigations. Parse the header into a render/send-by-default disposition and thread the parsed result through the RSC handler, the generated app entry, page dispatch, and the render lifecycle. Wire the per-layout observation tracker and segment-config classification into dispatch so the enable-transport slice can read them. No RSC payload is omitted and no response cache-control changes: the parsed manifest is carried but never authorizes a skip yet. Unproven and absent- manifest requests render and send exactly as before. Slice 3 of 4 from #1437; enable-transport follows. Refs #726 * fix(skip): address review feedback on manifest plumbing - app-page-execution: defer layout debug classification until after the observation override so the emitted reason agrees with the final flag; emit a runtime-probe reason when an observation flips a build-time static layout to dynamic. - app-page-dispatch: document classifyLayoutSegmentConfigFromModule as the request-time counterpart to the build-time classifier, noting the intentional revalidate===false divergence and the dev-mode classification. - app-render-dependency: mark registerAppElementRenderDependencies as write-only until the enable slice's consumer lands. * docs(skip): clarify layout classification comments per review - app-page-dispatch: correct the classifyLayoutSegmentConfigFromModule docblock — the build-time classifier already normalizes revalidate=false to Infinity, so both agree on that case; the real difference is scope (request-time classifies dev-mode layouts not captured at build time). - app-page-dispatch: document the merge precedence in createEffectiveLayoutClassifications — module segment config is authoritative over the build-time map. * test(skip): pin observation override flipping build-time static layout to dynamic Adds a parity assertion for the request-time behavioral change the review flagged: isLayoutObservationDynamic downgrades a build-time `static` layout to `d` (and emits a runtime-probe reason) when the observed render is unsafe for static reuse, while a safe observation keeps it static. Complements the existing children-subtree probe assertion in app-page-route-wiring.test.ts. * fix(skip): fold probe-scoped dynamic usage into layout observation The probe rewrite runs layout probes inside runWithIsolatedProbeDependencies, which opens a child unified scope and resets dynamicUsageDetected. A markDynamicUsage() during the probe that leaves no other observable trace (e.g. "use cache: private") was therefore discarded on scope exit, so the parent's consumeDynamicUsage() in the Layer-3 path returned false and the layout could classify static where the pre-PR direct probe classified dynamic. recordProbeDependencies now peeks the child scope's dynamic-usage flag before it is reset and folds it into the per-layout observation (dynamicUsageObserved), which isAppLayoutObservationUnsafeForStaticReuse treats as unsafe. This keeps the observation signal a superset of the markDynamicUsage() triggers the Layer-3 dynamicDetected path covered, so the two signals can no longer diverge into a false-static. Adds peekDynamicUsage() (non-resetting read) and a regression test. * perf(skip): defer client reuse manifest production to the cache-miss path The manifest header is excluded from VINEXT_RSC_VARY_HEADER, so it never affects the cache-busting URL or the visited-response cache lookup. Producing it up front ran createClientReuseManifestHeaderFromVisibleAppState (iterates visible layout ids, builds a cache-variant proof per static layout, then binary-searches the byte budget) on every soft navigate — including ones that resolve entirely from the visited-response cache or a prefetch and never send a request, where the work is pure waste. Move production to just before the network fetch, gated on the same `navigationKind === "navigate"` condition and computed from the captured nav-start router state, so the emitted header is unchanged on the request path while the cache-hit/prefetch-hit hot paths skip it entirely. --------- Co-authored-by: James <james@eli.cx> |
||
|
|
a35e43cef3 |
feat(skip): add layout safety observation foundations (#1672)
* feat(skip): add layout safety observation foundations Introduce the per-layout observation primitives that later slices need to decide whether retained static layouts can be reused. This slice adds the machinery only; nothing reads the observations yet, so behavior is unchanged. - app-layout-param-observation: per-layout completeness, param keys, structural scope, finite revalidate, request API, cacheLife, cache tag, cacheable fetch, dynamic fetch, and unstable_cache observation tracker. - app-page-probe: bounded React server subtree probe that walks layout-returned children including memo, forwardRef, and React.lazy wrappers, enforces depth and node limits, and refuses single-use iterable children. - shims: thenable params observer for sync, await, destructure, and enumeration. Cache, fetch, and unified request context record unstable_cache, cacheable fetch, and request-scoped observation state. - app-page-params: structural segment-param scope so empty optional catch-all targets are not treated as param-free downstream. No skip transport behavior. Later slices wire the planner, manifest, and encoder onto these observations. * fix(skip): address review feedback on observation foundations - Add currentFetchSoftTags reset in runWithIsolatedProbeDependencies to prevent soft tag leak from parent scope (matches runWithFetchCache isolation) - Replace unnecessary JSON.stringify in cacheLife check with direct null comparison - Add comment documenting intentional omission of markProbeComplete on probe errors (keeps completeness as unknown, safe fallback to render-and-send) * fix(skip): record cacheable-fetch observation synchronously before first await The patched fetch deferred observation and tag collection past await buildFetchCacheKey(). If a layout probe started a cacheable fetch and returned synchronously without awaiting it, the probe snapshot would see cacheableFetchCount: 0 -- a false proof that could let the skip planner reuse a stale retained layout. Move observation and tag recording before the first await in the cacheable branch. If key generation later fails and the fetch falls back to dynamic, recording both is conservative (false unsafe costs performance, not correctness). Add an integration test that exercises the real patched fetch shim without mocks to verify synchronous observation capture. * fix(test): remove polluting fetch restore and catch background promise Do not restore globalThis.fetch in test cleanup. ensureFetchPatch sets a global _PATCH_KEY guard; restoring fetch without clearing that marker makes later calls skip reinstallation silently. Catch the background fetch promise inside the probe so the patched fetch continuation (cache lookup, network fetch) cannot produce an unhandled rejection after the synchronous assertions complete. * chore: empty commit to trigger CI * chore: empty commit to trigger CI |