vinext 0.1.4 → 0.1.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (126) hide show
  1. package/dist/build/css-url-assets.d.ts +1 -1
  2. package/dist/build/css-url-assets.js +9 -7
  3. package/dist/build/prerender.js +2 -1
  4. package/dist/cache/cache-adapters-virtual.js +1 -1
  5. package/dist/cloudflare/src/cache/kv-data-adapter.runtime.d.ts +1 -1
  6. package/dist/entries/app-rsc-entry.js +24 -20
  7. package/dist/entries/pages-server-entry.js +2 -1
  8. package/dist/index.js +187 -146
  9. package/dist/plugins/css-data-url.js +30 -26
  10. package/dist/plugins/extensionless-dynamic-import.js +27 -24
  11. package/dist/plugins/import-meta-url.js +21 -15
  12. package/dist/plugins/instrumentation-client.js +1 -1
  13. package/dist/plugins/middleware-server-only.js +7 -6
  14. package/dist/plugins/og-assets.js +48 -46
  15. package/dist/plugins/optimize-imports.js +9 -3
  16. package/dist/plugins/remove-console.d.ts +7 -1
  17. package/dist/plugins/remove-console.js +4 -1
  18. package/dist/plugins/require-context.js +21 -20
  19. package/dist/plugins/strip-server-exports.d.ts +7 -1
  20. package/dist/plugins/strip-server-exports.js +4 -1
  21. package/dist/server/app-bfcache-identity.d.ts +26 -0
  22. package/dist/server/app-bfcache-identity.js +127 -0
  23. package/dist/server/app-browser-entry.js +14 -11
  24. package/dist/server/app-browser-navigation-controller.js +1 -1
  25. package/dist/server/app-browser-state.d.ts +2 -21
  26. package/dist/server/app-browser-state.js +4 -128
  27. package/dist/server/app-browser-stream.js +1 -1
  28. package/dist/server/app-browser-visible-commit.js +3 -2
  29. package/dist/server/app-fallback-renderer.d.ts +1 -1
  30. package/dist/server/app-layout-param-observation.d.ts +1 -1
  31. package/dist/server/app-layout-param-observation.js +1 -1
  32. package/dist/server/app-middleware.js +2 -1
  33. package/dist/server/app-page-boundary-render.d.ts +1 -1
  34. package/dist/server/app-page-boundary.js +1 -1
  35. package/dist/server/app-page-cache-finalizer.d.ts +62 -0
  36. package/dist/server/app-page-cache-finalizer.js +122 -0
  37. package/dist/server/app-page-cache-render.d.ts +2 -2
  38. package/dist/server/app-page-cache-render.js +1 -1
  39. package/dist/server/app-page-cache.d.ts +2 -53
  40. package/dist/server/app-page-cache.js +5 -131
  41. package/dist/server/app-page-dispatch.d.ts +2 -2
  42. package/dist/server/app-page-dispatch.js +10 -8
  43. package/dist/server/app-page-probe.js +3 -2
  44. package/dist/server/app-page-render-observation.js +2 -2
  45. package/dist/server/app-page-render.d.ts +3 -3
  46. package/dist/server/app-page-render.js +3 -2
  47. package/dist/server/app-page-stream.d.ts +2 -9
  48. package/dist/server/app-page-stream.js +1 -35
  49. package/dist/server/app-request-context.d.ts +1 -2
  50. package/dist/server/app-request-context.js +2 -1
  51. package/dist/server/app-route-handler-dispatch.js +3 -2
  52. package/dist/server/app-route-handler-execution.d.ts +1 -1
  53. package/dist/server/app-route-handler-execution.js +1 -1
  54. package/dist/server/app-route-handler-response.d.ts +1 -1
  55. package/dist/server/app-router-entry.js +2 -1
  56. package/dist/server/app-rsc-handler.js +22 -16
  57. package/dist/server/app-rsc-response-finalizer.js +1 -1
  58. package/dist/server/app-server-action-execution.d.ts +1 -1
  59. package/dist/server/app-server-action-execution.js +5 -4
  60. package/dist/server/app-ssr-entry.d.ts +1 -1
  61. package/dist/server/app-ssr-entry.js +11 -9
  62. package/dist/server/app-ssr-router-instance.d.ts +6 -0
  63. package/dist/server/app-ssr-router-instance.js +24 -0
  64. package/dist/server/app-ssr-stream.js +1 -1
  65. package/dist/server/artifact-compatibility.js +1 -1
  66. package/dist/server/client-reuse-manifest.js +1 -1
  67. package/dist/server/defer-until-stream-consumed.d.ts +7 -0
  68. package/dist/server/defer-until-stream-consumed.js +34 -0
  69. package/dist/server/dev-server.js +1 -1
  70. package/dist/server/instrumentation.js +1 -1
  71. package/dist/server/isr-cache.d.ts +1 -1
  72. package/dist/server/isr-cache.js +1 -1
  73. package/dist/server/isr-decision.d.ts +1 -1
  74. package/dist/server/middleware-matcher.js +8 -6
  75. package/dist/server/middleware-runtime.js +2 -2
  76. package/dist/server/open-redirect.d.ts +12 -0
  77. package/dist/server/open-redirect.js +21 -0
  78. package/dist/server/pages-page-data.d.ts +1 -1
  79. package/dist/server/pages-page-response.d.ts +1 -1
  80. package/dist/server/pages-page-response.js +2 -2
  81. package/dist/server/prod-server.js +2 -1
  82. package/dist/server/request-pipeline.d.ts +1 -24
  83. package/dist/server/request-pipeline.js +1 -33
  84. package/dist/server/seed-cache.d.ts +1 -1
  85. package/dist/shims/cache-handler.d.ts +106 -0
  86. package/dist/shims/cache-handler.js +176 -0
  87. package/dist/shims/cache-request-state.d.ts +47 -0
  88. package/dist/shims/cache-request-state.js +126 -0
  89. package/dist/shims/cache-runtime.d.ts +2 -2
  90. package/dist/shims/cache-runtime.js +3 -14
  91. package/dist/shims/cache.d.ts +3 -231
  92. package/dist/shims/cache.js +17 -383
  93. package/dist/shims/cdn-cache.d.ts +1 -1
  94. package/dist/shims/cdn-cache.js +1 -1
  95. package/dist/shims/error-boundary-navigation.d.ts +7 -0
  96. package/dist/shims/error-boundary-navigation.js +44 -0
  97. package/dist/shims/error-boundary.js +10 -8
  98. package/dist/shims/error.js +2 -1
  99. package/dist/shims/fetch-cache.js +1 -1
  100. package/dist/shims/form.js +1 -1
  101. package/dist/shims/image.js +67 -9
  102. package/dist/shims/internal/app-page-props-cache-key.d.ts +5 -0
  103. package/dist/shims/internal/app-page-props-cache-key.js +16 -0
  104. package/dist/shims/internal/navigation-untracked.js +2 -1
  105. package/dist/shims/layout-segment-context.d.ts +1 -1
  106. package/dist/shims/layout-segment-context.js +2 -1
  107. package/dist/shims/link.js +2 -2
  108. package/dist/shims/navigation-context-state.d.ts +40 -0
  109. package/dist/shims/navigation-context-state.js +116 -0
  110. package/dist/shims/navigation-errors.d.ts +55 -0
  111. package/dist/shims/navigation-errors.js +110 -0
  112. package/dist/shims/navigation-server.d.ts +3 -0
  113. package/dist/shims/navigation-server.js +3 -0
  114. package/dist/shims/navigation-state.d.ts +1 -2
  115. package/dist/shims/navigation-state.js +2 -1
  116. package/dist/shims/navigation.d.ts +3 -291
  117. package/dist/shims/navigation.js +14 -445
  118. package/dist/shims/navigation.react-server.d.ts +2 -2
  119. package/dist/shims/navigation.react-server.js +3 -1
  120. package/dist/shims/request-state-types.d.ts +3 -3
  121. package/dist/shims/script.js +1 -1
  122. package/dist/shims/slot.js +3 -1
  123. package/dist/shims/unified-request-context.d.ts +2 -2
  124. package/dist/utils/virtual-module.d.ts +5 -0
  125. package/dist/utils/virtual-module.js +0 -0
  126. package/package.json +5 -1
@@ -1,144 +1,20 @@
1
- import { countConsumedPathnameSegments, isInvisibleSegment, normalizePathnameForRouteMatch, splitPathSegments } from "../routing/utils.js";
1
+ import { normalizePathnameForRouteMatch } from "../routing/utils.js";
2
2
  import { stripBasePath } from "../utils/base-path.js";
3
3
  import { NEXT_ACTION_HEADER, RSC_ACTION_HEADER, VINEXT_INTERCEPTION_CONTEXT_HEADER, VINEXT_MOUNTED_SLOTS_HEADER } from "./headers.js";
4
4
  import { normalizePath } from "./normalize-path.js";
5
5
  import { AppElementsWire } from "./app-elements-wire.js";
6
6
  import { getMountedSlotIds, getMountedSlotIdsHeader } from "./app-elements.js";
7
- import "./app-bfcache-id.js";
8
- import { createHistoryStateWithNavigationMetadata, createHistoryStateWithPreviousNextUrl, isBfcacheSegmentId, isHistoryStateBfcacheVersionCurrent, readHistoryStateBfcacheIds, readHistoryStateBfcacheVersion, readHistoryStatePreviousNextUrl, readHistoryStateTraversalIndex, resolveHistoryTraversalIntent } from "./app-history-state.js";
9
7
  import { createRscRequestHeaders } from "./app-rsc-cache-busting.js";
8
+ import { createCacheEntryReuseProof } from "./cache-proof.js";
9
+ import { createHistoryStateWithNavigationMetadata, createHistoryStateWithPreviousNextUrl, isHistoryStateBfcacheVersionCurrent, readHistoryStateBfcacheIds, readHistoryStateBfcacheVersion, readHistoryStatePreviousNextUrl, readHistoryStateTraversalIndex, resolveHistoryTraversalIntent } from "./app-history-state.js";
10
+ import { createBfcacheSegmentStateKeyMap, createInitialBfcacheIdMap, createNextBfcacheIdMap, preserveBfcacheIdsForMergedElements } from "./app-bfcache-identity.js";
10
11
  import { NavigationTraceReasonCodes, createNavigationLifecycleTraceFields, createNavigationTrace } from "./navigation-trace.js";
11
12
  import { verifyOperationTokenForCommit } from "./operation-token.js";
12
13
  import { navigationPlanner, resolveDefaultOrUnmatchedSlotPersistenceForLayouts } from "./navigation-planner.js";
13
14
  import { createSnapshotPathAndSearch } from "../shims/navigation.js";
14
- import { createCacheEntryReuseProof } from "./cache-proof.js";
15
15
  //#region src/server/app-browser-state.ts
16
16
  const FRESH_APP_NAVIGATION_PAYLOAD_ORIGIN = { origin: "fresh" };
17
17
  const VISITED_CACHE_APP_NAVIGATION_PAYLOAD_ORIGIN = { origin: "visited-cache" };
18
- let nextBfcacheId = 0;
19
- function rememberBfcacheId(value) {
20
- const match = /^_b_(\d+)_$/.exec(value);
21
- if (!match) return;
22
- nextBfcacheId = Math.max(nextBfcacheId, Number(match[1]));
23
- }
24
- function mintBfcacheId() {
25
- nextBfcacheId += 1;
26
- return `_b_${nextBfcacheId}_`;
27
- }
28
- function getVisibleTreePathSegments(treePath) {
29
- return splitPathSegments(treePath).filter((segment) => !isInvisibleSegment(segment));
30
- }
31
- function getTreePathIdentityPrefix(pathname, treePath) {
32
- const pathnameSegments = splitPathSegments(pathname);
33
- const consumedPathnameSegments = countConsumedPathnameSegments(getVisibleTreePathSegments(treePath), pathnameSegments.length);
34
- if (consumedPathnameSegments === 0) return "/";
35
- return `/${pathnameSegments.slice(0, consumedPathnameSegments).join("/")}`;
36
- }
37
- function readAppElementsMetadata(elements) {
38
- let metadata;
39
- try {
40
- metadata = AppElementsWire.readMetadata(elements);
41
- } catch {
42
- return null;
43
- }
44
- const slotBindingsBySlotId = /* @__PURE__ */ new Map();
45
- for (const binding of metadata.slotBindings) slotBindingsBySlotId.set(binding.slotId, binding);
46
- return {
47
- metadata,
48
- slotBindingsBySlotId
49
- };
50
- }
51
- function createActiveSlotIdentity(id, parsed) {
52
- const activeSlotBinding = parsed?.slotBindingsBySlotId.get(id);
53
- if (activeSlotBinding?.activeRouteId != null) return `${id}@${activeSlotBinding.activeRouteId}`;
54
- const interception = parsed?.metadata.interception;
55
- if (interception?.slotId !== id) return null;
56
- return `${id}@${interception.targetRouteId}`;
57
- }
58
- /**
59
- * Legacy bridge for deriving a bfcache segment identity from AppElements wire
60
- * keys. Keep wire-key parsing contained here until Vinext has a route-manifest
61
- * semantic authority equivalent to Next.js CacheNode/segment-cache state.
62
- */
63
- function createBfcacheSegmentIdentity(id, options) {
64
- const parsed = AppElementsWire.parseElementKey(id);
65
- if (!parsed) return null;
66
- if (parsed.kind === "page") return `${id}@${options.pathname}`;
67
- if (parsed.kind === "slot") {
68
- const activeSlotIdentity = createActiveSlotIdentity(id, options.metadata);
69
- if (activeSlotIdentity !== null) return activeSlotIdentity;
70
- return `${id}@${getTreePathIdentityPrefix(options.pathname, parsed.treePath)}`;
71
- }
72
- if (parsed.kind === "layout" || parsed.kind === "template") return `${id}@${getTreePathIdentityPrefix(options.pathname, parsed.treePath)}`;
73
- return null;
74
- }
75
- function collectBfcacheSegmentIds(elements, parsed) {
76
- const ids = new Set(Object.keys(elements));
77
- const metadata = parsed === void 0 ? readAppElementsMetadata(elements) : parsed;
78
- for (const layoutId of metadata?.metadata.layoutIds ?? []) ids.add(layoutId);
79
- return Array.from(ids).filter(isBfcacheSegmentId);
80
- }
81
- function createInitialBfcacheIdMap(elements) {
82
- const ids = {};
83
- for (const id of collectBfcacheSegmentIds(elements)) ids[id] = "0";
84
- return ids;
85
- }
86
- function normalizeBfcachePathname(pathname) {
87
- const normalized = normalizePath(normalizePathnameForRouteMatch(pathname));
88
- return normalized.length > 1 ? normalized.replace(/\/$/, "") : normalized;
89
- }
90
- function createBfcacheSegmentStateKeyMap(options) {
91
- const metadata = readAppElementsMetadata(options.elements);
92
- const normalizedPathname = normalizeBfcachePathname(options.pathname);
93
- const stateKeys = {};
94
- for (const id of collectBfcacheSegmentIds(options.elements, metadata)) {
95
- const stateKey = createBfcacheSegmentIdentity(id, {
96
- metadata,
97
- pathname: normalizedPathname
98
- });
99
- if (stateKey !== null) stateKeys[id] = stateKey;
100
- }
101
- return stateKeys;
102
- }
103
- function createNextBfcacheIdMap(options) {
104
- const current = options.reuseCurrent === false ? {} : options.current;
105
- for (const value of Object.values(current)) rememberBfcacheId(value);
106
- for (const value of Object.values(options.restored ?? {})) rememberBfcacheId(value);
107
- const currentMetadata = readAppElementsMetadata(options.currentElements);
108
- const nextMetadata = readAppElementsMetadata(options.elements);
109
- const currentPathname = normalizeBfcachePathname(options.currentPathname);
110
- const nextPathname = normalizeBfcachePathname(options.nextPathname);
111
- const ids = {};
112
- for (const id of collectBfcacheSegmentIds(options.elements, nextMetadata)) {
113
- const currentValue = createBfcacheSegmentIdentity(id, {
114
- metadata: currentMetadata,
115
- pathname: currentPathname
116
- }) === createBfcacheSegmentIdentity(id, {
117
- metadata: nextMetadata,
118
- pathname: nextPathname
119
- }) ? current[id] : void 0;
120
- const value = options.restored?.[id] ?? currentValue ?? mintBfcacheId();
121
- ids[id] = value;
122
- rememberBfcacheId(value);
123
- }
124
- return ids;
125
- }
126
- function preserveBfcacheIdsForMergedElements(options) {
127
- const ids = {};
128
- for (const id of collectBfcacheSegmentIds(options.elements)) {
129
- const nextValue = options.next[id];
130
- if (nextValue !== void 0) {
131
- ids[id] = nextValue;
132
- continue;
133
- }
134
- const previousValue = options.previous[id];
135
- if (previousValue !== void 0) {
136
- ids[id] = previousValue;
137
- rememberBfcacheId(previousValue);
138
- }
139
- }
140
- return ids;
141
- }
142
18
  function createOperationRecord(options) {
143
19
  return {
144
20
  id: options.id,
@@ -1,5 +1,5 @@
1
- import { ensureNavigationRuntimeRscBootstrap, getNavigationRuntime } from "../client/navigation-runtime.js";
2
1
  import { decodeRscEmbeddedChunk } from "./app-rsc-embedded-chunks.js";
2
+ import { ensureNavigationRuntimeRscBootstrap, getNavigationRuntime } from "../client/navigation-runtime.js";
3
3
  //#region src/server/app-browser-stream.ts
4
4
  function getVinextBrowserGlobal() {
5
5
  return globalThis;
@@ -1,8 +1,9 @@
1
1
  import { normalizeAppElementsSlotBindings } from "./app-elements-wire.js";
2
2
  import "./app-elements.js";
3
- import { NavigationTraceReasonCodes, NavigationTraceTransactionCodes, createNavigationTrace, prependNavigationTraceEntry } from "./navigation-trace.js";
4
3
  import { mergeElements } from "../shims/slot.js";
5
- import { createPendingNavigationCommit, preserveBfcacheIdsForMergedElements, resolvePendingNavigationCommitDispositionDecision } from "./app-browser-state.js";
4
+ import { preserveBfcacheIdsForMergedElements } from "./app-bfcache-identity.js";
5
+ import { NavigationTraceReasonCodes, NavigationTraceTransactionCodes, createNavigationTrace, prependNavigationTraceEntry } from "./navigation-trace.js";
6
+ import { createPendingNavigationCommit, resolvePendingNavigationCommitDispositionDecision } from "./app-browser-state.js";
6
7
  //#region src/server/app-browser-visible-commit.ts
7
8
  const approvedVisibleCommitBrand = Symbol("ApprovedVisibleCommit");
8
9
  function applyApprovedVisibleCommit(state, commit) {
@@ -1,6 +1,6 @@
1
1
  import { MetadataFileRoute } from "./metadata-routes.js";
2
2
  import { AppElements } from "./app-elements-wire.js";
3
- import { NavigationContext } from "../shims/navigation.js";
3
+ import { NavigationContext } from "../shims/navigation-context-state.js";
4
4
  import { AppPageParams } from "./app-page-boundary.js";
5
5
  import { AppPageFontPreload } from "./app-page-execution.js";
6
6
  import { AppPageMiddlewareContext } from "./app-page-response.js";
@@ -1,5 +1,5 @@
1
+ import { UnstableCacheObservation } from "../shims/cache-request-state.js";
1
2
  import { RenderRequestApiKind } from "./cache-proof.js";
2
- import { UnstableCacheObservation } from "../shims/cache.js";
3
3
  import { ClientReuseManifestRejectionCode, ClientReuseManifestTraceFields } from "./client-reuse-manifest.js";
4
4
  import { ThenableParamsObserver } from "../shims/thenable-params.js";
5
5
 
@@ -1,6 +1,6 @@
1
1
  import { isInsideUnifiedScope, runWithUnifiedStateMutation } from "../shims/unified-request-context.js";
2
2
  import { peekDynamicUsage, peekRenderRequestApiUsage } from "../shims/headers.js";
3
- import { _peekRequestScopedCacheLife, _peekUnstableCacheObservations } from "../shims/cache.js";
3
+ import { _peekRequestScopedCacheLife, _peekUnstableCacheObservations } from "../shims/cache-request-state.js";
4
4
  import { getCollectedFetchTags, peekCacheableFetchObservations, peekDynamicFetchObservations } from "../shims/fetch-cache.js";
5
5
  import { isPromiseLike } from "../utils/promise.js";
6
6
  //#region src/server/app-layout-param-observation.ts
@@ -5,8 +5,9 @@ import { internalServerErrorResponse } from "./http-error-responses.js";
5
5
  import { cloneRequestWithHeaders, processMiddlewareHeaders } from "./request-pipeline.js";
6
6
  import { executeMiddleware } from "./middleware-runtime.js";
7
7
  import { applyMiddlewareRequestHeaders, setHeadersContext } from "../shims/headers.js";
8
- import { setNavigationContext } from "../shims/navigation.js";
8
+ import { setNavigationContext } from "../shims/navigation-context-state.js";
9
9
  import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
10
+ import "../shims/navigation.js";
10
11
  //#region src/server/app-middleware.ts
11
12
  const FLIGHT_HEADER_SET = new Set(FLIGHT_HEADERS);
12
13
  function isForwardedMiddlewareContext(value) {
@@ -1,6 +1,6 @@
1
1
  import { MetadataFileRoute } from "./metadata-routes.js";
2
2
  import { AppElements } from "./app-elements-wire.js";
3
- import { NavigationContext } from "../shims/navigation.js";
3
+ import { NavigationContext } from "../shims/navigation-context-state.js";
4
4
  import { AppPageParams } from "./app-page-boundary.js";
5
5
  import { AppPageFontPreload } from "./app-page-execution.js";
6
6
  import { AppPageMiddlewareContext } from "./app-page-response.js";
@@ -1,6 +1,6 @@
1
1
  import { runWithFetchDedupe } from "../shims/fetch-cache.js";
2
- import { VINEXT_RSC_CONTENT_TYPE, VINEXT_RSC_VARY_HEADER, applyRscCompatibilityIdHeader } from "./app-rsc-cache-busting.js";
3
2
  import { resolveAppPageSegmentParams } from "./app-page-params.js";
3
+ import { VINEXT_RSC_CONTENT_TYPE, VINEXT_RSC_VARY_HEADER, applyRscCompatibilityIdHeader } from "./app-rsc-cache-busting.js";
4
4
  import { mergeMiddlewareResponseHeaders } from "./middleware-response-headers.js";
5
5
  import { applyEdgeRuntimeHeader } from "./app-page-response.js";
6
6
  //#region src/server/app-page-boundary.ts
@@ -0,0 +1,62 @@
1
+ import { RenderObservation } from "./cache-proof.js";
2
+ import { CachedAppPageValue } from "../shims/cache-handler.js";
3
+ import { AppRscRenderMode } from "./app-rsc-render-mode.js";
4
+ import { AppPageRenderObservationState } from "./app-page-render-observation.js";
5
+
6
+ //#region src/server/app-page-cache-finalizer.d.ts
7
+ type AppPageDebugLogger = (event: string, detail: string) => void;
8
+ type AppPageCacheSetter = (key: string, data: CachedAppPageValue, revalidateSeconds: number, tags: string[], expireSeconds?: number) => Promise<void>;
9
+ type AppPageRscCacheKeyBuilder = (pathname: string, mountedSlotsHeader?: string | null, renderMode?: AppRscRenderMode, interceptionContext?: string | null) => string;
10
+ type AppPageRequestCacheLife = {
11
+ revalidate?: number;
12
+ expire?: number;
13
+ };
14
+ type BuildAppPageCacheRenderObservation = (input: {
15
+ cacheTags: readonly string[];
16
+ state: AppPageRenderObservationState;
17
+ }) => RenderObservation;
18
+ type FinalizeAppPageHtmlCacheResponseOptions = {
19
+ capturedDynamicUsageBeforeContextCleanup?: () => boolean;
20
+ capturedRscDataPromise: Promise<ArrayBuffer> | null;
21
+ cleanPathname: string;
22
+ consumeDynamicUsage: () => boolean;
23
+ consumeRenderObservationState?: () => AppPageRenderObservationState;
24
+ createHtmlRenderObservation?: BuildAppPageCacheRenderObservation;
25
+ createRscRenderObservation?: BuildAppPageCacheRenderObservation;
26
+ getPageTags: () => string[];
27
+ getRequestCacheLife?: () => AppPageRequestCacheLife | null;
28
+ isrDebug?: AppPageDebugLogger;
29
+ isrHtmlKey: (pathname: string) => string;
30
+ isrRscKey: AppPageRscCacheKeyBuilder;
31
+ isrSet: AppPageCacheSetter;
32
+ interceptionContext?: string | null;
33
+ preserveClientResponseHeaders?: boolean;
34
+ expireSeconds?: number;
35
+ revalidateSeconds: number | null;
36
+ waitUntil?: (promise: Promise<void>) => void;
37
+ };
38
+ type ScheduleAppPageRscCacheWriteOptions = {
39
+ capturedRscDataPromise: Promise<ArrayBuffer> | null;
40
+ cleanPathname: string;
41
+ consumeDynamicUsage: () => boolean;
42
+ consumeRenderObservationState?: () => AppPageRenderObservationState;
43
+ createRscRenderObservation?: BuildAppPageCacheRenderObservation;
44
+ dynamicUsedDuringBuild: boolean;
45
+ getPageTags: () => string[];
46
+ getRequestCacheLife?: () => AppPageRequestCacheLife | null;
47
+ isrDebug?: AppPageDebugLogger;
48
+ isrRscKey: AppPageRscCacheKeyBuilder;
49
+ isrSet: AppPageCacheSetter;
50
+ interceptionContext?: string | null;
51
+ mountedSlotsHeader?: string | null;
52
+ renderMode?: AppRscRenderMode;
53
+ preserveClientResponseHeaders?: boolean;
54
+ expireSeconds?: number;
55
+ revalidateSeconds: number | null;
56
+ waitUntil?: (promise: Promise<void>) => void;
57
+ };
58
+ declare function finalizeAppPageHtmlCacheResponse(response: Response, options: FinalizeAppPageHtmlCacheResponseOptions): Response;
59
+ declare function finalizeAppPageRscCacheResponse(response: Response, options: ScheduleAppPageRscCacheWriteOptions): Response;
60
+ declare function scheduleAppPageRscCacheWrite(options: ScheduleAppPageRscCacheWriteOptions): boolean;
61
+ //#endregion
62
+ export { finalizeAppPageHtmlCacheResponse, finalizeAppPageRscCacheResponse, scheduleAppPageRscCacheWrite };
@@ -0,0 +1,122 @@
1
+ import { setCacheStateHeaders } from "./cache-headers.js";
2
+ import { applyCdnResponseHeaders } from "./cache-control.js";
3
+ import { buildAppPageCacheValue } from "./isr-cache.js";
4
+ import { readStreamAsText } from "../utils/text-stream.js";
5
+ import { createEmptyAppPageRenderObservationState } from "./app-page-render-observation.js";
6
+ //#region src/server/app-page-cache-finalizer.ts
7
+ function applyPendingDynamicCdnHeaders(headers, tags) {
8
+ applyCdnResponseHeaders(headers, {
9
+ cacheControl: headers.get("Cache-Control") ?? "",
10
+ pendingDynamicCheck: true,
11
+ tags
12
+ });
13
+ setCacheStateHeaders(headers, "MISS");
14
+ }
15
+ function resolveAppPageCacheWritePolicy(options) {
16
+ let revalidateSeconds = options.revalidateSeconds;
17
+ let expireSeconds = options.expireSeconds;
18
+ const requestCacheLife = options.requestCacheLife;
19
+ if (requestCacheLife?.revalidate !== void 0) revalidateSeconds = revalidateSeconds === null ? requestCacheLife.revalidate : Math.min(revalidateSeconds, requestCacheLife.revalidate);
20
+ if (requestCacheLife?.expire !== void 0) expireSeconds = requestCacheLife.expire;
21
+ if (revalidateSeconds === null || Number.isNaN(revalidateSeconds) || revalidateSeconds <= 0) return null;
22
+ return {
23
+ expireSeconds,
24
+ revalidateSeconds
25
+ };
26
+ }
27
+ function finalizeAppPageHtmlCacheResponse(response, options) {
28
+ if (!response.body) return response;
29
+ const [streamForClient, streamForCache] = response.body.tee();
30
+ const htmlKey = options.isrHtmlKey(options.cleanPathname);
31
+ const rscKey = options.isrRscKey(options.cleanPathname, null, void 0, options.interceptionContext);
32
+ const clientHeaders = new Headers(response.headers);
33
+ if (options.preserveClientResponseHeaders !== true) applyPendingDynamicCdnHeaders(clientHeaders, options.getPageTags());
34
+ const cachePromise = (async () => {
35
+ try {
36
+ const cachedHtml = await readStreamAsText(streamForCache);
37
+ if (options.capturedDynamicUsageBeforeContextCleanup?.() === true || options.consumeDynamicUsage()) {
38
+ options.isrDebug?.("HTML cache write skipped (dynamic usage during render)", htmlKey);
39
+ return;
40
+ }
41
+ const cachePolicy = resolveAppPageCacheWritePolicy({
42
+ expireSeconds: options.expireSeconds,
43
+ requestCacheLife: options.getRequestCacheLife?.(),
44
+ revalidateSeconds: options.revalidateSeconds
45
+ });
46
+ if (!cachePolicy) {
47
+ options.isrDebug?.("HTML cache write skipped (no cache policy)", htmlKey);
48
+ return;
49
+ }
50
+ const pageTags = options.getPageTags();
51
+ const observationState = options.consumeRenderObservationState?.() ?? createEmptyAppPageRenderObservationState();
52
+ const htmlRenderObservation = options.createHtmlRenderObservation?.({
53
+ cacheTags: pageTags,
54
+ state: observationState
55
+ });
56
+ const rscRenderObservation = options.createRscRenderObservation?.({
57
+ cacheTags: pageTags,
58
+ state: observationState
59
+ });
60
+ const writes = [options.isrSet(htmlKey, buildAppPageCacheValue(cachedHtml, void 0, 200, htmlRenderObservation), cachePolicy.revalidateSeconds, pageTags, cachePolicy.expireSeconds)];
61
+ if (options.capturedRscDataPromise) writes.push(options.capturedRscDataPromise.then((rscData) => options.isrSet(rscKey, buildAppPageCacheValue("", rscData, 200, rscRenderObservation), cachePolicy.revalidateSeconds, pageTags, cachePolicy.expireSeconds)));
62
+ await Promise.all(writes);
63
+ options.isrDebug?.("HTML cache written", htmlKey);
64
+ } catch (cacheError) {
65
+ console.error("[vinext] ISR cache write error:", cacheError);
66
+ }
67
+ })();
68
+ options.waitUntil?.(cachePromise);
69
+ return new Response(streamForClient, {
70
+ status: response.status,
71
+ statusText: response.statusText,
72
+ headers: clientHeaders
73
+ });
74
+ }
75
+ function finalizeAppPageRscCacheResponse(response, options) {
76
+ if (!scheduleAppPageRscCacheWrite(options)) return response;
77
+ if (options.preserveClientResponseHeaders === true) return response;
78
+ const clientHeaders = new Headers(response.headers);
79
+ applyPendingDynamicCdnHeaders(clientHeaders, options.getPageTags());
80
+ return new Response(response.body, {
81
+ status: response.status,
82
+ statusText: response.statusText,
83
+ headers: clientHeaders
84
+ });
85
+ }
86
+ function scheduleAppPageRscCacheWrite(options) {
87
+ const capturedRscDataPromise = options.capturedRscDataPromise;
88
+ if (!capturedRscDataPromise || options.dynamicUsedDuringBuild) return false;
89
+ const rscKey = options.isrRscKey(options.cleanPathname, options.mountedSlotsHeader, options.renderMode, options.interceptionContext);
90
+ const cachePromise = (async () => {
91
+ try {
92
+ const rscData = await capturedRscDataPromise;
93
+ if (options.consumeDynamicUsage()) {
94
+ options.isrDebug?.("RSC cache write skipped (dynamic usage during render)", rscKey);
95
+ return;
96
+ }
97
+ const cachePolicy = resolveAppPageCacheWritePolicy({
98
+ expireSeconds: options.expireSeconds,
99
+ requestCacheLife: options.getRequestCacheLife?.(),
100
+ revalidateSeconds: options.revalidateSeconds
101
+ });
102
+ if (!cachePolicy) {
103
+ options.isrDebug?.("RSC cache write skipped (no cache policy)", rscKey);
104
+ return;
105
+ }
106
+ const pageTags = options.getPageTags();
107
+ const observationState = options.consumeRenderObservationState?.() ?? createEmptyAppPageRenderObservationState();
108
+ const rscRenderObservation = options.createRscRenderObservation?.({
109
+ cacheTags: pageTags,
110
+ state: observationState
111
+ });
112
+ await options.isrSet(rscKey, buildAppPageCacheValue("", rscData, 200, rscRenderObservation), cachePolicy.revalidateSeconds, pageTags, cachePolicy.expireSeconds);
113
+ options.isrDebug?.("RSC cache written", rscKey);
114
+ } catch (cacheError) {
115
+ console.error("[vinext] ISR RSC cache write error:", cacheError);
116
+ }
117
+ })();
118
+ options.waitUntil?.(cachePromise);
119
+ return true;
120
+ }
121
+ //#endregion
122
+ export { finalizeAppPageHtmlCacheResponse, finalizeAppPageRscCacheResponse, scheduleAppPageRscCacheWrite };
@@ -1,5 +1,5 @@
1
- import { CacheControlMetadata } from "../shims/cache.js";
2
- import { NavigationContext } from "../shims/navigation.js";
1
+ import { CacheControlMetadata } from "../shims/cache-handler.js";
2
+ import { NavigationContext } from "../shims/navigation-context-state.js";
3
3
  import { AppPageFontPreload } from "./app-page-execution.js";
4
4
  import { RootParams } from "../shims/root-params.js";
5
5
  import { AppPageSsrHandler } from "./app-page-stream.js";
@@ -1,5 +1,5 @@
1
1
  import { consumeDynamicUsage, consumeInvalidDynamicUsageError } from "../shims/headers.js";
2
- import { _consumeRequestScopedCacheLife } from "../shims/cache.js";
2
+ import { _consumeRequestScopedCacheLife } from "../shims/cache-request-state.js";
3
3
  import { getCollectedFetchTags } from "../shims/fetch-cache.js";
4
4
  import { readStreamAsText } from "../utils/text-stream.js";
5
5
  import { teeAppPageRscStreamForCapture } from "./app-page-execution.js";
@@ -1,7 +1,7 @@
1
1
  import { RenderObservation } from "./cache-proof.js";
2
- import { CacheControlMetadata, CachedAppPageValue } from "../shims/cache.js";
3
- import { AppPageRenderObservationState } from "./app-page-render-observation.js";
2
+ import { CacheControlMetadata, CachedAppPageValue } from "../shims/cache-handler.js";
4
3
  import { AppRscRenderMode } from "./app-rsc-render-mode.js";
4
+ import { finalizeAppPageHtmlCacheResponse, finalizeAppPageRscCacheResponse, scheduleAppPageRscCacheWrite } from "./app-page-cache-finalizer.js";
5
5
  import { ISRCacheEntry } from "./isr-cache.js";
6
6
 
7
7
  //#region src/server/app-page-cache.d.ts
@@ -10,10 +10,6 @@ type AppPageCacheGetter = (key: string) => Promise<ISRCacheEntry | null>;
10
10
  type AppPageCacheSetter = (key: string, data: CachedAppPageValue, revalidateSeconds: number, tags: string[], expireSeconds?: number) => Promise<void>;
11
11
  type AppPageBackgroundRegenerator = (key: string, renderFn: () => Promise<void>) => void;
12
12
  type AppPageRscCacheKeyBuilder = (pathname: string, mountedSlotsHeader?: string | null, renderMode?: AppRscRenderMode, interceptionContext?: string | null) => string;
13
- type AppPageRequestCacheLife = {
14
- revalidate?: number;
15
- expire?: number;
16
- };
17
13
  type AppPageCacheOutcomeMetric = Readonly<{
18
14
  artifact: "html" | "rsc";
19
15
  /**
@@ -25,10 +21,6 @@ type AppPageCacheOutcomeMetric = Readonly<{
25
21
  reason: "empty-entry" | "no-entry" | "non-app-page-entry" | "query-variant-unproven" | "read-error" | "served" | "stale-empty-entry";
26
22
  }>;
27
23
  type AppPageCacheOutcomeRecorder = (metric: AppPageCacheOutcomeMetric) => void;
28
- type BuildAppPageCacheRenderObservation = (input: {
29
- cacheTags: readonly string[];
30
- state: AppPageRenderObservationState;
31
- }) => RenderObservation;
32
24
  type AppPageCacheRenderResult = {
33
25
  cacheControl?: CacheControlMetadata;
34
26
  html: string;
@@ -83,52 +75,9 @@ type ReadAppPageFallbackShellCacheResponseOptions = {
83
75
  revalidateSeconds: number;
84
76
  rewriteHtml: (html: string) => string;
85
77
  };
86
- type FinalizeAppPageHtmlCacheResponseOptions = {
87
- capturedDynamicUsageBeforeContextCleanup?: () => boolean;
88
- capturedRscDataPromise: Promise<ArrayBuffer> | null;
89
- cleanPathname: string;
90
- consumeDynamicUsage: () => boolean;
91
- consumeRenderObservationState?: () => AppPageRenderObservationState;
92
- createHtmlRenderObservation?: BuildAppPageCacheRenderObservation;
93
- createRscRenderObservation?: BuildAppPageCacheRenderObservation;
94
- getPageTags: () => string[];
95
- getRequestCacheLife?: () => AppPageRequestCacheLife | null;
96
- isrDebug?: AppPageDebugLogger;
97
- isrHtmlKey: (pathname: string) => string;
98
- isrRscKey: AppPageRscCacheKeyBuilder;
99
- isrSet: AppPageCacheSetter;
100
- interceptionContext?: string | null;
101
- preserveClientResponseHeaders?: boolean;
102
- expireSeconds?: number;
103
- revalidateSeconds: number | null;
104
- waitUntil?: (promise: Promise<void>) => void;
105
- };
106
- type ScheduleAppPageRscCacheWriteOptions = {
107
- capturedRscDataPromise: Promise<ArrayBuffer> | null;
108
- cleanPathname: string;
109
- consumeDynamicUsage: () => boolean;
110
- consumeRenderObservationState?: () => AppPageRenderObservationState;
111
- createRscRenderObservation?: BuildAppPageCacheRenderObservation;
112
- dynamicUsedDuringBuild: boolean;
113
- getPageTags: () => string[];
114
- getRequestCacheLife?: () => AppPageRequestCacheLife | null;
115
- isrDebug?: AppPageDebugLogger;
116
- isrRscKey: AppPageRscCacheKeyBuilder;
117
- isrSet: AppPageCacheSetter;
118
- interceptionContext?: string | null;
119
- mountedSlotsHeader?: string | null;
120
- renderMode?: AppRscRenderMode;
121
- preserveClientResponseHeaders?: boolean;
122
- expireSeconds?: number;
123
- revalidateSeconds: number | null;
124
- waitUntil?: (promise: Promise<void>) => void;
125
- };
126
78
  declare function buildAppPageCacheTags(pathname: string, extraTags: readonly string[]): string[];
127
79
  declare function buildAppPageCachedResponse(cachedValue: CachedAppPageValue, options: BuildAppPageCachedResponseOptions): Response | null;
128
80
  declare function readAppPageCacheResponse(options: ReadAppPageCacheResponseOptions): Promise<Response | null>;
129
81
  declare function readAppPageFallbackShellCacheResponse(options: ReadAppPageFallbackShellCacheResponseOptions): Promise<Response | null>;
130
- declare function finalizeAppPageHtmlCacheResponse(response: Response, options: FinalizeAppPageHtmlCacheResponseOptions): Response;
131
- declare function finalizeAppPageRscCacheResponse(response: Response, options: ScheduleAppPageRscCacheWriteOptions): Response;
132
- declare function scheduleAppPageRscCacheWrite(options: ScheduleAppPageRscCacheWriteOptions): boolean;
133
82
  //#endregion
134
83
  export { AppPageCacheOutcomeMetric, buildAppPageCacheTags, buildAppPageCachedResponse, finalizeAppPageHtmlCacheResponse, finalizeAppPageRscCacheResponse, readAppPageCacheResponse, readAppPageFallbackShellCacheResponse, scheduleAppPageRscCacheWrite };