vinext 0.0.26 → 0.0.28

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 (227) hide show
  1. package/README.md +89 -85
  2. package/dist/build/static-export.d.ts +1 -1
  3. package/dist/build/static-export.d.ts.map +1 -1
  4. package/dist/build/static-export.js +5 -9
  5. package/dist/build/static-export.js.map +1 -1
  6. package/dist/check.d.ts.map +1 -1
  7. package/dist/check.js +152 -48
  8. package/dist/check.js.map +1 -1
  9. package/dist/cli.js +10 -11
  10. package/dist/cli.js.map +1 -1
  11. package/dist/cloudflare/kv-cache-handler.d.ts +43 -1
  12. package/dist/cloudflare/kv-cache-handler.d.ts.map +1 -1
  13. package/dist/cloudflare/kv-cache-handler.js +135 -44
  14. package/dist/cloudflare/kv-cache-handler.js.map +1 -1
  15. package/dist/cloudflare/tpr.d.ts.map +1 -1
  16. package/dist/cloudflare/tpr.js +15 -4
  17. package/dist/cloudflare/tpr.js.map +1 -1
  18. package/dist/config/config-matchers.d.ts +28 -0
  19. package/dist/config/config-matchers.d.ts.map +1 -1
  20. package/dist/config/config-matchers.js +353 -79
  21. package/dist/config/config-matchers.js.map +1 -1
  22. package/dist/config/dotenv.d.ts.map +1 -1
  23. package/dist/config/dotenv.js +1 -6
  24. package/dist/config/dotenv.js.map +1 -1
  25. package/dist/config/next-config.d.ts +7 -0
  26. package/dist/config/next-config.d.ts.map +1 -1
  27. package/dist/config/next-config.js +44 -19
  28. package/dist/config/next-config.js.map +1 -1
  29. package/dist/deploy.d.ts +1 -1
  30. package/dist/deploy.d.ts.map +1 -1
  31. package/dist/deploy.js +81 -48
  32. package/dist/deploy.js.map +1 -1
  33. package/dist/entries/app-rsc-entry.d.ts +3 -1
  34. package/dist/entries/app-rsc-entry.d.ts.map +1 -1
  35. package/dist/entries/app-rsc-entry.js +584 -113
  36. package/dist/entries/app-rsc-entry.js.map +1 -1
  37. package/dist/entries/pages-client-entry.d.ts.map +1 -1
  38. package/dist/entries/pages-client-entry.js +5 -3
  39. package/dist/entries/pages-client-entry.js.map +1 -1
  40. package/dist/entries/pages-server-entry.d.ts.map +1 -1
  41. package/dist/entries/pages-server-entry.js +100 -32
  42. package/dist/entries/pages-server-entry.js.map +1 -1
  43. package/dist/index.d.ts +24 -8
  44. package/dist/index.d.ts.map +1 -1
  45. package/dist/index.js +327 -154
  46. package/dist/index.js.map +1 -1
  47. package/dist/init.d.ts.map +1 -1
  48. package/dist/init.js +6 -5
  49. package/dist/init.js.map +1 -1
  50. package/dist/plugins/client-reference-dedup.d.ts +19 -0
  51. package/dist/plugins/client-reference-dedup.d.ts.map +1 -0
  52. package/dist/plugins/client-reference-dedup.js +96 -0
  53. package/dist/plugins/client-reference-dedup.js.map +1 -0
  54. package/dist/routing/app-router.d.ts +2 -0
  55. package/dist/routing/app-router.d.ts.map +1 -1
  56. package/dist/routing/app-router.js +70 -107
  57. package/dist/routing/app-router.js.map +1 -1
  58. package/dist/routing/file-matcher.d.ts.map +1 -1
  59. package/dist/routing/file-matcher.js.map +1 -1
  60. package/dist/routing/pages-router.d.ts +3 -1
  61. package/dist/routing/pages-router.d.ts.map +1 -1
  62. package/dist/routing/pages-router.js +33 -18
  63. package/dist/routing/pages-router.js.map +1 -1
  64. package/dist/routing/route-validation.d.ts +8 -0
  65. package/dist/routing/route-validation.d.ts.map +1 -0
  66. package/dist/routing/route-validation.js +124 -0
  67. package/dist/routing/route-validation.js.map +1 -0
  68. package/dist/routing/utils.d.ts.map +1 -1
  69. package/dist/routing/utils.js.map +1 -1
  70. package/dist/server/api-handler.d.ts.map +1 -1
  71. package/dist/server/api-handler.js +31 -9
  72. package/dist/server/api-handler.js.map +1 -1
  73. package/dist/server/app-router-entry.d.ts +3 -2
  74. package/dist/server/app-router-entry.d.ts.map +1 -1
  75. package/dist/server/app-router-entry.js +8 -4
  76. package/dist/server/app-router-entry.js.map +1 -1
  77. package/dist/server/dev-module-runner.d.ts.map +1 -1
  78. package/dist/server/dev-module-runner.js +1 -1
  79. package/dist/server/dev-module-runner.js.map +1 -1
  80. package/dist/server/dev-origin-check.d.ts.map +1 -1
  81. package/dist/server/dev-origin-check.js.map +1 -1
  82. package/dist/server/dev-server.d.ts.map +1 -1
  83. package/dist/server/dev-server.js +39 -21
  84. package/dist/server/dev-server.js.map +1 -1
  85. package/dist/server/image-optimization.d.ts.map +1 -1
  86. package/dist/server/image-optimization.js.map +1 -1
  87. package/dist/server/instrumentation.js +1 -1
  88. package/dist/server/instrumentation.js.map +1 -1
  89. package/dist/server/isr-cache.d.ts +5 -1
  90. package/dist/server/isr-cache.d.ts.map +1 -1
  91. package/dist/server/isr-cache.js +13 -3
  92. package/dist/server/isr-cache.js.map +1 -1
  93. package/dist/server/metadata-routes.d.ts +8 -2
  94. package/dist/server/metadata-routes.d.ts.map +1 -1
  95. package/dist/server/metadata-routes.js +78 -45
  96. package/dist/server/metadata-routes.js.map +1 -1
  97. package/dist/server/middleware-codegen.d.ts +1 -1
  98. package/dist/server/middleware-codegen.d.ts.map +1 -1
  99. package/dist/server/middleware-codegen.js +177 -22
  100. package/dist/server/middleware-codegen.js.map +1 -1
  101. package/dist/server/middleware-request-headers.d.ts +9 -0
  102. package/dist/server/middleware-request-headers.d.ts.map +1 -0
  103. package/dist/server/middleware-request-headers.js +77 -0
  104. package/dist/server/middleware-request-headers.js.map +1 -0
  105. package/dist/server/middleware.d.ts +9 -8
  106. package/dist/server/middleware.d.ts.map +1 -1
  107. package/dist/server/middleware.js +112 -32
  108. package/dist/server/middleware.js.map +1 -1
  109. package/dist/server/normalize-path.js.map +1 -1
  110. package/dist/server/prod-server.d.ts +1 -1
  111. package/dist/server/prod-server.d.ts.map +1 -1
  112. package/dist/server/prod-server.js +127 -82
  113. package/dist/server/prod-server.js.map +1 -1
  114. package/dist/server/request-pipeline.d.ts +2 -1
  115. package/dist/server/request-pipeline.d.ts.map +1 -1
  116. package/dist/server/request-pipeline.js +5 -7
  117. package/dist/server/request-pipeline.js.map +1 -1
  118. package/dist/shims/cache-runtime.d.ts.map +1 -1
  119. package/dist/shims/cache-runtime.js +21 -16
  120. package/dist/shims/cache-runtime.js.map +1 -1
  121. package/dist/shims/cache.d.ts +2 -0
  122. package/dist/shims/cache.d.ts.map +1 -1
  123. package/dist/shims/cache.js +38 -25
  124. package/dist/shims/cache.js.map +1 -1
  125. package/dist/shims/constants.d.ts.map +1 -1
  126. package/dist/shims/constants.js +1 -6
  127. package/dist/shims/constants.js.map +1 -1
  128. package/dist/shims/dynamic.d.ts.map +1 -1
  129. package/dist/shims/dynamic.js +1 -1
  130. package/dist/shims/dynamic.js.map +1 -1
  131. package/dist/shims/error-boundary.d.ts.map +1 -1
  132. package/dist/shims/error-boundary.js +2 -3
  133. package/dist/shims/error-boundary.js.map +1 -1
  134. package/dist/shims/error.d.ts.map +1 -1
  135. package/dist/shims/error.js +1 -3
  136. package/dist/shims/error.js.map +1 -1
  137. package/dist/shims/fetch-cache.d.ts.map +1 -1
  138. package/dist/shims/fetch-cache.js +57 -30
  139. package/dist/shims/fetch-cache.js.map +1 -1
  140. package/dist/shims/font-google-base.d.ts.map +1 -1
  141. package/dist/shims/font-google-base.js +16 -4
  142. package/dist/shims/font-google-base.js.map +1 -1
  143. package/dist/shims/font-google.d.ts +1 -1
  144. package/dist/shims/font-google.d.ts.map +1 -1
  145. package/dist/shims/font-google.generated.d.ts.map +1 -1
  146. package/dist/shims/font-google.generated.js +412 -206
  147. package/dist/shims/font-google.generated.js.map +1 -1
  148. package/dist/shims/font-google.js +1 -1
  149. package/dist/shims/font-google.js.map +1 -1
  150. package/dist/shims/font-local.d.ts.map +1 -1
  151. package/dist/shims/font-local.js +13 -3
  152. package/dist/shims/font-local.js.map +1 -1
  153. package/dist/shims/form.d.ts.map +1 -1
  154. package/dist/shims/form.js +105 -10
  155. package/dist/shims/form.js.map +1 -1
  156. package/dist/shims/head.d.ts.map +1 -1
  157. package/dist/shims/head.js +10 -8
  158. package/dist/shims/head.js.map +1 -1
  159. package/dist/shims/headers.d.ts +34 -8
  160. package/dist/shims/headers.d.ts.map +1 -1
  161. package/dist/shims/headers.js +268 -53
  162. package/dist/shims/headers.js.map +1 -1
  163. package/dist/shims/image.d.ts.map +1 -1
  164. package/dist/shims/image.js +35 -8
  165. package/dist/shims/image.js.map +1 -1
  166. package/dist/shims/internal/parse-cookie-header.d.ts +12 -0
  167. package/dist/shims/internal/parse-cookie-header.d.ts.map +1 -0
  168. package/dist/shims/internal/parse-cookie-header.js +32 -0
  169. package/dist/shims/internal/parse-cookie-header.js.map +1 -0
  170. package/dist/shims/legacy-image.d.ts.map +1 -1
  171. package/dist/shims/legacy-image.js +1 -1
  172. package/dist/shims/legacy-image.js.map +1 -1
  173. package/dist/shims/link.d.ts +2 -1
  174. package/dist/shims/link.d.ts.map +1 -1
  175. package/dist/shims/link.js +37 -17
  176. package/dist/shims/link.js.map +1 -1
  177. package/dist/shims/metadata.d.ts +12 -2
  178. package/dist/shims/metadata.d.ts.map +1 -1
  179. package/dist/shims/metadata.js +10 -8
  180. package/dist/shims/metadata.js.map +1 -1
  181. package/dist/shims/navigation-state.d.ts.map +1 -1
  182. package/dist/shims/navigation-state.js +3 -2
  183. package/dist/shims/navigation-state.js.map +1 -1
  184. package/dist/shims/navigation.d.ts +3 -7
  185. package/dist/shims/navigation.d.ts.map +1 -1
  186. package/dist/shims/navigation.js +46 -29
  187. package/dist/shims/navigation.js.map +1 -1
  188. package/dist/shims/readonly-url-search-params.d.ts +11 -0
  189. package/dist/shims/readonly-url-search-params.d.ts.map +1 -0
  190. package/dist/shims/readonly-url-search-params.js +24 -0
  191. package/dist/shims/readonly-url-search-params.js.map +1 -0
  192. package/dist/shims/request-context.d.ts +50 -0
  193. package/dist/shims/request-context.d.ts.map +1 -0
  194. package/dist/shims/request-context.js +59 -0
  195. package/dist/shims/request-context.js.map +1 -0
  196. package/dist/shims/router-state.d.ts.map +1 -1
  197. package/dist/shims/router-state.js +2 -1
  198. package/dist/shims/router-state.js.map +1 -1
  199. package/dist/shims/router.d.ts +4 -3
  200. package/dist/shims/router.d.ts.map +1 -1
  201. package/dist/shims/router.js +59 -53
  202. package/dist/shims/router.js.map +1 -1
  203. package/dist/shims/script.d.ts.map +1 -1
  204. package/dist/shims/script.js.map +1 -1
  205. package/dist/shims/server.d.ts +14 -1
  206. package/dist/shims/server.d.ts.map +1 -1
  207. package/dist/shims/server.js +107 -47
  208. package/dist/shims/server.js.map +1 -1
  209. package/dist/shims/url-utils.d.ts.map +1 -1
  210. package/dist/shims/url-utils.js +1 -3
  211. package/dist/shims/url-utils.js.map +1 -1
  212. package/dist/utils/base-path.d.ts +17 -0
  213. package/dist/utils/base-path.d.ts.map +1 -0
  214. package/dist/utils/base-path.js +25 -0
  215. package/dist/utils/base-path.js.map +1 -0
  216. package/dist/utils/manifest-paths.d.ts +4 -0
  217. package/dist/utils/manifest-paths.d.ts.map +1 -0
  218. package/dist/utils/manifest-paths.js +20 -0
  219. package/dist/utils/manifest-paths.js.map +1 -0
  220. package/dist/utils/project.d.ts.map +1 -1
  221. package/dist/utils/project.js +2 -4
  222. package/dist/utils/project.js.map +1 -1
  223. package/dist/utils/query.d.ts +9 -0
  224. package/dist/utils/query.d.ts.map +1 -1
  225. package/dist/utils/query.js +59 -7
  226. package/dist/utils/query.js.map +1 -1
  227. package/package.json +47 -33
@@ -1 +1 @@
1
- {"version":3,"file":"kv-cache-handler.js","sourceRoot":"","sources":["../../src/cloudflare/kv-cache-handler.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Cloudflare KV-backed CacheHandler for vinext.\n *\n * Provides persistent ISR caching on Cloudflare Workers using KV as the\n * storage backend. Supports time-based expiry (stale-while-revalidate)\n * and tag-based invalidation.\n *\n * Usage in worker/index.ts:\n *\n * import { KVCacheHandler } from \"vinext/cloudflare\";\n * import { setCacheHandler } from \"vinext/shims/cache\";\n *\n * export default {\n * async fetch(request: Request, env: Env) {\n * setCacheHandler(new KVCacheHandler(env.VINEXT_CACHE));\n * // ... rest of worker handler\n * }\n * };\n *\n * Wrangler config (wrangler.jsonc):\n *\n * {\n * \"kv_namespaces\": [\n * { \"binding\": \"VINEXT_CACHE\", \"id\": \"<your-kv-namespace-id>\" }\n * ]\n * }\n */\n\nimport type {\n CacheHandler,\n CacheHandlerValue,\n IncrementalCacheValue,\n} from \"../shims/cache.js\";\n\n// Cloudflare KV namespace interface (matches Workers types)\ninterface KVNamespace {\n get(key: string, options?: { type?: string }): Promise<string | null>;\n get(key: string, options: { type: \"arrayBuffer\" }): Promise<ArrayBuffer | null>;\n put(\n key: string,\n value: string | ArrayBuffer | ReadableStream,\n options?: { expirationTtl?: number; metadata?: Record<string, unknown> },\n ): Promise<void>;\n delete(key: string): Promise<void>;\n list(options?: {\n prefix?: string;\n limit?: number;\n cursor?: string;\n }): Promise<{\n keys: Array<{ name: string; metadata?: Record<string, unknown> }>;\n list_complete: boolean;\n cursor?: string;\n }>;\n}\n\n/** Shape stored in KV for each cache entry. */\ninterface KVCacheEntry {\n value: IncrementalCacheValue | null;\n tags: string[];\n lastModified: number;\n /** Absolute timestamp (ms) after which the entry is \"stale\" (but still served). */\n revalidateAt: number | null;\n}\n\n/** Key prefix for tag invalidation timestamps. */\nconst TAG_PREFIX = \"__tag:\";\n\n/** Key prefix for cache entries. */\nconst ENTRY_PREFIX = \"cache:\";\n\n/** Max tag length to prevent KV key abuse. */\nconst MAX_TAG_LENGTH = 256;\n\n/**\n * Validate a cache tag. Returns null if invalid.\n * Note: `:` is rejected because TAG_PREFIX and ENTRY_PREFIX use `:` as a\n * separator — allowing `:` in user tags could cause ambiguous key lookups.\n */\nfunction validateTag(tag: string): string | null {\n if (typeof tag !== \"string\" || tag.length === 0 || tag.length > MAX_TAG_LENGTH) return null;\n // Block control characters, path separators, and KV-special characters.\n // eslint-disable-next-line no-control-regex -- intentional: reject control chars in tags\n if (/[\\x00-\\x1f/\\\\:]/.test(tag)) return null;\n return tag;\n}\n\nexport class KVCacheHandler implements CacheHandler {\n private kv: KVNamespace;\n private prefix: string;\n\n constructor(kvNamespace: KVNamespace, options?: { appPrefix?: string }) {\n this.kv = kvNamespace;\n this.prefix = options?.appPrefix ? `${options.appPrefix}:` : \"\";\n }\n\n async get(\n key: string,\n _ctx?: Record<string, unknown>,\n ): Promise<CacheHandlerValue | null> {\n const kvKey = this.prefix + ENTRY_PREFIX + key;\n const raw = await this.kv.get(kvKey);\n if (!raw) return null;\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n // Corrupted JSON — delete and treat as miss\n await this.kv.delete(kvKey);\n return null;\n }\n\n // Validate deserialized shape before using\n const entry = validateCacheEntry(parsed);\n if (!entry) {\n console.error(\"[vinext] Invalid cache entry shape for key:\", key);\n await this.kv.delete(kvKey);\n return null;\n }\n\n // Restore ArrayBuffer fields that were base64-encoded for JSON storage\n if (entry.value) {\n const ok = restoreArrayBuffers(entry.value);\n if (!ok) {\n // base64 decode failed — corrupted entry, treat as miss\n await this.kv.delete(kvKey);\n return null;\n }\n }\n\n // Check tag-based invalidation (parallel for lower latency)\n if (entry.tags.length > 0) {\n const tagResults = await Promise.all(\n entry.tags.map((tag) => this.kv.get(this.prefix + TAG_PREFIX + tag)),\n );\n for (let i = 0; i < entry.tags.length; i++) {\n const tagTime = tagResults[i];\n if (tagTime) {\n const tagTimestamp = Number(tagTime);\n if (Number.isNaN(tagTimestamp) || tagTimestamp >= entry.lastModified) {\n // Tag was invalidated after this entry, or timestamp is corrupted\n // — treat as miss to force re-render\n await this.kv.delete(kvKey);\n return null;\n }\n }\n }\n }\n\n // Check time-based expiry — return stale with cacheState\n if (entry.revalidateAt !== null && Date.now() > entry.revalidateAt) {\n return {\n lastModified: entry.lastModified,\n value: entry.value,\n cacheState: \"stale\",\n };\n }\n\n return {\n lastModified: entry.lastModified,\n value: entry.value,\n };\n }\n\n async set(\n key: string,\n data: IncrementalCacheValue | null,\n ctx?: Record<string, unknown>,\n ): Promise<void> {\n // Collect, validate, and dedupe tags from data and context\n const tagSet = new Set<string>();\n if (data && \"tags\" in data && Array.isArray(data.tags)) {\n for (const t of data.tags) {\n const validated = validateTag(t);\n if (validated) tagSet.add(validated);\n }\n }\n if (ctx && \"tags\" in ctx && Array.isArray(ctx.tags)) {\n for (const t of ctx.tags as string[]) {\n const validated = validateTag(t);\n if (validated) tagSet.add(validated);\n }\n }\n const tags = [...tagSet];\n\n // Determine revalidation time\n let revalidateAt: number | null = null;\n if (ctx) {\n const revalidate =\n (ctx as any).cacheControl?.revalidate ?? (ctx as any).revalidate;\n if (typeof revalidate === \"number\" && revalidate > 0) {\n revalidateAt = Date.now() + revalidate * 1000;\n }\n }\n if (\n data &&\n \"revalidate\" in data &&\n typeof data.revalidate === \"number\" &&\n data.revalidate > 0\n ) {\n revalidateAt = Date.now() + data.revalidate * 1000;\n }\n\n // Prepare entry — convert ArrayBuffers to base64 for JSON storage\n const serializable = data ? serializeForJSON(data) : null;\n\n const entry: KVCacheEntry = {\n value: serializable,\n tags,\n lastModified: Date.now(),\n revalidateAt,\n };\n\n // Calculate KV TTL — keep entries well beyond their revalidate window\n // (10x revalidate period, clamped to 60s–30d) so stale-while-revalidate\n // can serve stale content while background regeneration happens.\n let expirationTtl: number | undefined;\n if (revalidateAt !== null) {\n const revalidateSeconds = Math.ceil((revalidateAt - Date.now()) / 1000);\n // Keep in KV for 10x the revalidation period, up to 30 days\n expirationTtl = Math.min(revalidateSeconds * 10, 30 * 24 * 3600);\n // KV minimum TTL is 60 seconds\n expirationTtl = Math.max(expirationTtl, 60);\n }\n\n await this.kv.put(this.prefix + ENTRY_PREFIX + key, JSON.stringify(entry), {\n expirationTtl,\n });\n }\n\n async revalidateTag(\n tags: string | string[],\n _durations?: { expire?: number },\n ): Promise<void> {\n const tagList = Array.isArray(tags) ? tags : [tags];\n const now = Date.now();\n const validTags = tagList.filter((t) => validateTag(t) !== null);\n // Store invalidation timestamp for each tag\n // Use a long TTL (30 days) so recent invalidations are always found\n await Promise.all(\n validTags.map((tag) =>\n this.kv.put(this.prefix + TAG_PREFIX + tag, String(now), {\n expirationTtl: 30 * 24 * 3600,\n }),\n ),\n );\n }\n\n resetRequestCache(): void {\n // No-op — KV is stateless per request\n }\n}\n\n// ---------------------------------------------------------------------------\n// Validation helpers\n// ---------------------------------------------------------------------------\n\nconst VALID_KINDS = new Set([\n \"FETCH\",\n \"APP_PAGE\",\n \"PAGES\",\n \"APP_ROUTE\",\n \"REDIRECT\",\n \"IMAGE\",\n]);\n\n/**\n * Validate that a parsed JSON value has the expected KVCacheEntry shape.\n * Returns the validated entry or null if the shape is invalid.\n */\nfunction validateCacheEntry(raw: unknown): KVCacheEntry | null {\n if (!raw || typeof raw !== \"object\") return null;\n\n const obj = raw as Record<string, unknown>;\n\n // Required fields\n if (typeof obj.lastModified !== \"number\") return null;\n if (!Array.isArray(obj.tags)) return null;\n if (\n obj.revalidateAt !== null &&\n typeof obj.revalidateAt !== \"number\"\n )\n return null;\n\n // value must be null or a valid cache value object with a known kind\n if (obj.value !== null) {\n if (!obj.value || typeof obj.value !== \"object\") return null;\n const value = obj.value as Record<string, unknown>;\n if (typeof value.kind !== \"string\" || !VALID_KINDS.has(value.kind))\n return null;\n }\n\n return raw as KVCacheEntry;\n}\n\n// ---------------------------------------------------------------------------\n// ArrayBuffer serialization helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Deep-clone a cache value, converting ArrayBuffer fields to base64 strings\n * so the entire structure can be JSON.stringify'd for KV storage.\n */\nfunction serializeForJSON(value: IncrementalCacheValue): IncrementalCacheValue {\n if (value.kind === \"APP_PAGE\") {\n return {\n ...value,\n rscData: value.rscData\n ? (arrayBufferToBase64(value.rscData) as any)\n : undefined,\n };\n }\n if (value.kind === \"APP_ROUTE\") {\n return {\n ...value,\n body: arrayBufferToBase64(value.body) as any,\n };\n }\n if (value.kind === \"IMAGE\") {\n return {\n ...value,\n buffer: arrayBufferToBase64(value.buffer) as any,\n };\n }\n return value;\n}\n\n/**\n * Restore base64 strings back to ArrayBuffers after JSON.parse.\n * Returns false if any base64 decode fails (corrupted entry).\n */\nfunction restoreArrayBuffers(value: IncrementalCacheValue): boolean {\n if (value.kind === \"APP_PAGE\" && typeof value.rscData === \"string\") {\n const decoded = safeBase64ToArrayBuffer(value.rscData as any);\n if (!decoded) return false;\n (value as any).rscData = decoded;\n }\n if (value.kind === \"APP_ROUTE\" && typeof value.body === \"string\") {\n const decoded = safeBase64ToArrayBuffer(value.body as any);\n if (!decoded) return false;\n (value as any).body = decoded;\n }\n if (value.kind === \"IMAGE\" && typeof value.buffer === \"string\") {\n const decoded = safeBase64ToArrayBuffer(value.buffer as any);\n if (!decoded) return false;\n (value as any).buffer = decoded;\n }\n return true;\n}\n\nfunction arrayBufferToBase64(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer);\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n}\n\nfunction base64ToArrayBuffer(base64: string): ArrayBuffer {\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes.buffer;\n}\n\n/**\n * Safely decode base64 to ArrayBuffer. Returns null on invalid input\n * instead of throwing a DOMException.\n */\nfunction safeBase64ToArrayBuffer(base64: string): ArrayBuffer | null {\n try {\n return base64ToArrayBuffer(base64);\n } catch {\n console.error(\"[vinext] Invalid base64 in cache entry\");\n return null;\n }\n}\n"]}
1
+ {"version":3,"file":"kv-cache-handler.js","sourceRoot":"","sources":["../../src/cloudflare/kv-cache-handler.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Cloudflare KV-backed CacheHandler for vinext.\n *\n * Provides persistent ISR caching on Cloudflare Workers using KV as the\n * storage backend. Supports time-based expiry (stale-while-revalidate)\n * and tag-based invalidation.\n *\n * Usage in worker/index.ts:\n *\n * import { KVCacheHandler } from \"vinext/cloudflare\";\n * import { setCacheHandler } from \"vinext/shims/cache\";\n *\n * export default {\n * async fetch(request: Request, env: Env, ctx: ExecutionContext) {\n * setCacheHandler(new KVCacheHandler(env.VINEXT_CACHE));\n * // ctx is propagated automatically via runWithExecutionContext in\n * // the vinext handler — no need to pass it to KVCacheHandler.\n * // ... rest of worker handler\n * }\n * };\n *\n * Wrangler config (wrangler.jsonc):\n *\n * {\n * \"kv_namespaces\": [\n * { \"binding\": \"VINEXT_CACHE\", \"id\": \"<your-kv-namespace-id>\" }\n * ]\n * }\n */\n\nimport type { CacheHandler, CacheHandlerValue, IncrementalCacheValue } from \"../shims/cache.js\";\nimport { getRequestExecutionContext, type ExecutionContextLike } from \"../shims/request-context.js\";\n\n// Cloudflare KV namespace interface (matches Workers types)\ninterface KVNamespace {\n get(key: string, options?: { type?: string }): Promise<string | null>;\n get(key: string, options: { type: \"arrayBuffer\" }): Promise<ArrayBuffer | null>;\n put(\n key: string,\n value: string | ArrayBuffer | ReadableStream,\n options?: { expirationTtl?: number; metadata?: Record<string, unknown> },\n ): Promise<void>;\n delete(key: string): Promise<void>;\n list(options?: { prefix?: string; limit?: number; cursor?: string }): Promise<{\n keys: Array<{ name: string; metadata?: Record<string, unknown> }>;\n list_complete: boolean;\n cursor?: string;\n }>;\n}\n\n/** Shape stored in KV for each cache entry. */\ninterface KVCacheEntry {\n value: IncrementalCacheValue | null;\n tags: string[];\n lastModified: number;\n /** Absolute timestamp (ms) after which the entry is \"stale\" (but still served). */\n revalidateAt: number | null;\n}\n\n/** Key prefix for tag invalidation timestamps. */\nconst TAG_PREFIX = \"__tag:\";\n\n/** Key prefix for cache entries. */\nconst ENTRY_PREFIX = \"cache:\";\n\n/** Max tag length to prevent KV key abuse. */\nconst MAX_TAG_LENGTH = 256;\n\n/**\n * Validate a cache tag. Returns null if invalid.\n * Note: `:` is rejected because TAG_PREFIX and ENTRY_PREFIX use `:` as a\n * separator — allowing `:` in user tags could cause ambiguous key lookups.\n */\nfunction validateTag(tag: string): string | null {\n if (typeof tag !== \"string\" || tag.length === 0 || tag.length > MAX_TAG_LENGTH) return null;\n // Block control characters and reserved separators used in our own key format.\n // Slash is allowed because revalidatePath() relies on pathname tags like\n // \"/posts/hello\" and \"_N_T_/posts/hello\".\n // eslint-disable-next-line no-control-regex -- intentional: reject control chars in tags\n if (/[\\x00-\\x1f\\\\:]/.test(tag)) return null;\n return tag;\n}\n\nexport class KVCacheHandler implements CacheHandler {\n private kv: KVNamespace;\n private prefix: string;\n private ctx: ExecutionContextLike | undefined;\n private ttlSeconds: number;\n\n /** Local in-memory cache for tag invalidation timestamps. Avoids redundant KV reads. */\n private _tagCache = new Map<string, { timestamp: number; fetchedAt: number }>();\n /** TTL (ms) for local tag cache entries. After this, re-fetch from KV. */\n private _tagCacheTtl: number;\n\n constructor(\n kvNamespace: KVNamespace,\n options?: {\n appPrefix?: string;\n ctx?: ExecutionContextLike;\n ttlSeconds?: number;\n /** TTL in milliseconds for the local tag cache. Defaults to 5000ms. */\n tagCacheTtlMs?: number;\n },\n ) {\n this.kv = kvNamespace;\n this.prefix = options?.appPrefix ? `${options.appPrefix}:` : \"\";\n this.ctx = options?.ctx;\n this.ttlSeconds = options?.ttlSeconds ?? 30 * 24 * 3600;\n this._tagCacheTtl = options?.tagCacheTtlMs ?? 5_000;\n }\n\n async get(key: string, _ctx?: Record<string, unknown>): Promise<CacheHandlerValue | null> {\n const kvKey = this.prefix + ENTRY_PREFIX + key;\n const raw = await this.kv.get(kvKey);\n if (!raw) return null;\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(raw);\n } catch {\n // Corrupted JSON — fire cleanup delete in the background and treat as miss.\n // Using waitUntil ensures the delete isn't killed when the Response is returned.\n this._deleteInBackground(kvKey);\n return null;\n }\n\n // Validate deserialized shape before using\n const entry = validateCacheEntry(parsed);\n if (!entry) {\n console.error(\"[vinext] Invalid cache entry shape for key:\", key);\n this._deleteInBackground(kvKey);\n return null;\n }\n\n // Restore ArrayBuffer fields that were base64-encoded for JSON storage\n if (entry.value) {\n const ok = restoreArrayBuffers(entry.value);\n if (!ok) {\n // base64 decode failed — corrupted entry, treat as miss\n this._deleteInBackground(kvKey);\n return null;\n }\n }\n\n // Check tag-based invalidation.\n // Uses a local in-memory cache to avoid redundant KV reads for recently-seen tags.\n if (entry.tags.length > 0) {\n const now = Date.now();\n const uncachedTags: string[] = [];\n\n // First pass: check local cache for each tag.\n // Delete expired entries to prevent unbounded Map growth in long-lived isolates.\n for (const tag of entry.tags) {\n const cached = this._tagCache.get(tag);\n if (cached && now - cached.fetchedAt < this._tagCacheTtl) {\n // Local cache hit — check invalidation inline\n if (Number.isNaN(cached.timestamp) || cached.timestamp >= entry.lastModified) {\n this._deleteInBackground(kvKey);\n return null;\n }\n } else {\n // Expired or absent — evict stale entry and re-fetch from KV\n if (cached) this._tagCache.delete(tag);\n uncachedTags.push(tag);\n }\n }\n\n // Second pass: fetch uncached tags from KV in parallel.\n // Populate the local cache for ALL fetched tags before checking invalidation,\n // so that KV round-trips are not wasted when an earlier tag triggers an\n // early return — subsequent get() calls benefit from the already-fetched results.\n if (uncachedTags.length > 0) {\n const tagResults = await Promise.all(\n uncachedTags.map((tag) => this.kv.get(this.prefix + TAG_PREFIX + tag)),\n );\n\n // Populate cache for all results first, then check for invalidation.\n // Two-loop structure ensures all tag results are cached even when an\n // earlier tag would cause an early return — so subsequent get() calls\n // for entries sharing those tags don't redundantly re-fetch from KV.\n for (let i = 0; i < uncachedTags.length; i++) {\n const tagTime = tagResults[i];\n const tagTimestamp = tagTime ? Number(tagTime) : 0;\n this._tagCache.set(uncachedTags[i], { timestamp: tagTimestamp, fetchedAt: now });\n }\n\n // Then check for invalidation using the now-cached timestamps\n for (const tag of uncachedTags) {\n const cached = this._tagCache.get(tag)!;\n if (cached.timestamp !== 0) {\n if (Number.isNaN(cached.timestamp) || cached.timestamp >= entry.lastModified) {\n this._deleteInBackground(kvKey);\n return null;\n }\n }\n }\n }\n }\n\n // Check time-based expiry — return stale with cacheState\n if (entry.revalidateAt !== null && Date.now() > entry.revalidateAt) {\n return {\n lastModified: entry.lastModified,\n value: entry.value,\n cacheState: \"stale\",\n };\n }\n\n return {\n lastModified: entry.lastModified,\n value: entry.value,\n };\n }\n\n set(\n key: string,\n data: IncrementalCacheValue | null,\n ctx?: Record<string, unknown>,\n ): Promise<void> {\n // Collect, validate, and dedupe tags from data and context\n const tagSet = new Set<string>();\n if (data && \"tags\" in data && Array.isArray(data.tags)) {\n for (const t of data.tags) {\n const validated = validateTag(t);\n if (validated) tagSet.add(validated);\n }\n }\n if (ctx && \"tags\" in ctx && Array.isArray(ctx.tags)) {\n for (const t of ctx.tags as string[]) {\n const validated = validateTag(t);\n if (validated) tagSet.add(validated);\n }\n }\n const tags = [...tagSet];\n\n // Determine revalidation time\n let revalidateAt: number | null = null;\n if (ctx) {\n const revalidate = (ctx as any).cacheControl?.revalidate ?? (ctx as any).revalidate;\n if (typeof revalidate === \"number\" && revalidate > 0) {\n revalidateAt = Date.now() + revalidate * 1000;\n }\n }\n if (\n data &&\n \"revalidate\" in data &&\n typeof data.revalidate === \"number\" &&\n data.revalidate > 0\n ) {\n revalidateAt = Date.now() + data.revalidate * 1000;\n }\n\n // Prepare entry — convert ArrayBuffers to base64 for JSON storage\n const serializable = data ? serializeForJSON(data) : null;\n\n const entry: KVCacheEntry = {\n value: serializable,\n tags,\n lastModified: Date.now(),\n revalidateAt,\n };\n\n // KV TTL is decoupled from the revalidation period.\n //\n // Staleness (when to trigger background regen) is tracked by `revalidateAt`\n // in the stored JSON — not by KV eviction. KV eviction is purely a storage\n // hygiene mechanism and must never be the reason a stale entry disappears.\n //\n // If KV TTL were tied to the revalidate window (e.g. 10x), a page with\n // revalidate=5 would be evicted after ~50 seconds of no traffic, causing the\n // next request to block on a fresh render instead of serving stale content.\n //\n // Fix: always keep entries for 30 days regardless of revalidate frequency.\n // Background regen overwrites the key with a fresh entry + new revalidateAt,\n // so active pages always have something to serve. Entries only disappear after\n // 30 days of zero traffic, or when explicitly deleted via tag invalidation.\n const expirationTtl: number | undefined = revalidateAt !== null ? this.ttlSeconds : undefined;\n\n return this._put(this.prefix + ENTRY_PREFIX + key, JSON.stringify(entry), {\n expirationTtl,\n });\n }\n\n async revalidateTag(tags: string | string[], _durations?: { expire?: number }): Promise<void> {\n const tagList = Array.isArray(tags) ? tags : [tags];\n const now = Date.now();\n const validTags = tagList.filter((t) => validateTag(t) !== null);\n // Store invalidation timestamp for each tag\n // Use a long TTL (30 days) so recent invalidations are always found\n await Promise.all(\n validTags.map((tag) =>\n this.kv.put(this.prefix + TAG_PREFIX + tag, String(now), {\n expirationTtl: 30 * 24 * 3600,\n }),\n ),\n );\n // Update local tag cache immediately so invalidations are reflected\n // without waiting for the TTL to expire\n for (const tag of validTags) {\n this._tagCache.set(tag, { timestamp: now, fetchedAt: now });\n }\n }\n\n /**\n * Clear the in-memory tag cache for this KVCacheHandler instance.\n *\n * Note: KVCacheHandler instances are typically reused across multiple\n * requests in a Cloudflare Worker. The `_tagCache` is intentionally\n * cross-request — it reduces redundant KV reads for recently-seen tags\n * across all requests hitting the same isolate, bounded by `tagCacheTtlMs`\n * (default 5s). vinext does NOT call this method per request.\n *\n * This is an opt-in escape hatch for callers that need stricter isolation\n * (e.g., tests, or environments with custom lifecycle management).\n * Callers that require per-request isolation should either construct a\n * fresh KVCacheHandler per request or invoke this method explicitly.\n */\n resetRequestCache(): void {\n this._tagCache.clear();\n }\n\n /**\n * Fire a KV delete in the background.\n * Prefers the per-request ExecutionContext from ALS (set by\n * runWithExecutionContext in the worker entry) so that background KV\n * operations are registered with the correct request's waitUntil().\n * Falls back to the constructor-provided ctx for callers that set it\n * explicitly, and to fire-and-forget when neither is available (Node.js dev).\n */\n private _deleteInBackground(kvKey: string): void {\n const promise = this.kv.delete(kvKey);\n const ctx = getRequestExecutionContext() ?? this.ctx;\n if (ctx) {\n ctx.waitUntil(promise);\n }\n // else: fire-and-forget on Node.js\n }\n\n /**\n * Execute a KV put and return the promise so callers can await completion.\n * Also registers with ctx.waitUntil() so the Workers runtime keeps the\n * isolate alive even if the caller does not await the returned promise.\n */\n private _put(kvKey: string, value: string, options?: { expirationTtl?: number }): Promise<void> {\n const promise = this.kv.put(kvKey, value, options);\n const ctx = getRequestExecutionContext() ?? this.ctx;\n if (ctx) {\n ctx.waitUntil(promise);\n }\n return promise;\n }\n}\n\n// ---------------------------------------------------------------------------\n// Validation helpers\n// ---------------------------------------------------------------------------\n\nconst VALID_KINDS = new Set([\"FETCH\", \"APP_PAGE\", \"PAGES\", \"APP_ROUTE\", \"REDIRECT\", \"IMAGE\"]);\n\n/**\n * Validate that a parsed JSON value has the expected KVCacheEntry shape.\n * Returns the validated entry or null if the shape is invalid.\n */\nfunction validateCacheEntry(raw: unknown): KVCacheEntry | null {\n if (!raw || typeof raw !== \"object\") return null;\n\n const obj = raw as Record<string, unknown>;\n\n // Required fields\n if (typeof obj.lastModified !== \"number\") return null;\n if (!Array.isArray(obj.tags)) return null;\n if (obj.revalidateAt !== null && typeof obj.revalidateAt !== \"number\") return null;\n\n // value must be null or a valid cache value object with a known kind\n if (obj.value !== null) {\n if (!obj.value || typeof obj.value !== \"object\") return null;\n const value = obj.value as Record<string, unknown>;\n if (typeof value.kind !== \"string\" || !VALID_KINDS.has(value.kind)) return null;\n }\n\n return raw as KVCacheEntry;\n}\n\n// ---------------------------------------------------------------------------\n// ArrayBuffer serialization helpers\n// ---------------------------------------------------------------------------\n\n/**\n * Deep-clone a cache value, converting ArrayBuffer fields to base64 strings\n * so the entire structure can be JSON.stringify'd for KV storage.\n */\nfunction serializeForJSON(value: IncrementalCacheValue): IncrementalCacheValue {\n if (value.kind === \"APP_PAGE\") {\n return {\n ...value,\n rscData: value.rscData ? (arrayBufferToBase64(value.rscData) as any) : undefined,\n };\n }\n if (value.kind === \"APP_ROUTE\") {\n return {\n ...value,\n body: arrayBufferToBase64(value.body) as any,\n };\n }\n if (value.kind === \"IMAGE\") {\n return {\n ...value,\n buffer: arrayBufferToBase64(value.buffer) as any,\n };\n }\n return value;\n}\n\n/**\n * Restore base64 strings back to ArrayBuffers after JSON.parse.\n * Returns false if any base64 decode fails (corrupted entry).\n */\nfunction restoreArrayBuffers(value: IncrementalCacheValue): boolean {\n if (value.kind === \"APP_PAGE\" && typeof value.rscData === \"string\") {\n const decoded = safeBase64ToArrayBuffer(value.rscData as any);\n if (!decoded) return false;\n (value as any).rscData = decoded;\n }\n if (value.kind === \"APP_ROUTE\" && typeof value.body === \"string\") {\n const decoded = safeBase64ToArrayBuffer(value.body as any);\n if (!decoded) return false;\n (value as any).body = decoded;\n }\n if (value.kind === \"IMAGE\" && typeof value.buffer === \"string\") {\n const decoded = safeBase64ToArrayBuffer(value.buffer as any);\n if (!decoded) return false;\n (value as any).buffer = decoded;\n }\n return true;\n}\n\nfunction arrayBufferToBase64(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer);\n let binary = \"\";\n for (let i = 0; i < bytes.length; i++) {\n binary += String.fromCharCode(bytes[i]);\n }\n return btoa(binary);\n}\n\nfunction base64ToArrayBuffer(base64: string): ArrayBuffer {\n const binary = atob(base64);\n const bytes = new Uint8Array(binary.length);\n for (let i = 0; i < binary.length; i++) {\n bytes[i] = binary.charCodeAt(i);\n }\n return bytes.buffer;\n}\n\n/**\n * Safely decode base64 to ArrayBuffer. Returns null on invalid input\n * instead of throwing a DOMException.\n */\nfunction safeBase64ToArrayBuffer(base64: string): ArrayBuffer | null {\n try {\n return base64ToArrayBuffer(base64);\n } catch {\n console.error(\"[vinext] Invalid base64 in cache entry\");\n return null;\n }\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"tpr.d.ts","sourceRoot":"","sources":["../../src/cloudflare/tpr.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AASH,MAAM,WAAW,UAAU;IACzB,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,SAAS;IACxB,kDAAkD;IAClD,UAAU,EAAE,MAAM,CAAC;IACnB,8DAA8D;IAC9D,gBAAgB,EAAE,MAAM,CAAC;IACzB,qDAAqD;IACrD,gBAAgB,EAAE,MAAM,CAAC;IACzB,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,cAAc;IACtB,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;CACzB;AAQD,UAAU,cAAc;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAID;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAuBvE;AAkXD;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,YAAY,EAAE,EACvB,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,MAAM,GACZ,cAAc,CAuBhB;AAyPD;;;;;;GAMG;AACH,wBAAsB,MAAM,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CAmIpE"}
1
+ {"version":3,"file":"tpr.d.ts","sourceRoot":"","sources":["../../src/cloudflare/tpr.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AASH,MAAM,WAAW,UAAU;IACzB,8BAA8B;IAC9B,IAAI,EAAE,MAAM,CAAC;IACb,wDAAwD;IACxD,QAAQ,EAAE,MAAM,CAAC;IACjB,gEAAgE;IAChE,KAAK,EAAE,MAAM,CAAC;IACd,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,SAAS;IACxB,kDAAkD;IAClD,UAAU,EAAE,MAAM,CAAC;IACnB,8DAA8D;IAC9D,gBAAgB,EAAE,MAAM,CAAC;IACzB,qDAAqD;IACrD,gBAAgB,EAAE,MAAM,CAAC;IACzB,2DAA2D;IAC3D,UAAU,EAAE,MAAM,CAAC;IACnB,sCAAsC;IACtC,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,YAAY;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,QAAQ,EAAE,MAAM,CAAC;CAClB;AAED,UAAU,cAAc;IACtB,MAAM,EAAE,YAAY,EAAE,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,eAAe,EAAE,MAAM,CAAC;IACxB,eAAe,EAAE,MAAM,CAAC;CACzB;AAQD,UAAU,cAAc;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAID;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,cAAc,GAAG,IAAI,CAuBvE;AAmXD;;;GAGG;AACH,wBAAgB,YAAY,CAC1B,OAAO,EAAE,YAAY,EAAE,EACvB,cAAc,EAAE,MAAM,EACtB,KAAK,EAAE,MAAM,GACZ,cAAc,CAuBhB;AAsPD;;;;;;GAMG;AACH,wBAAsB,MAAM,CAAC,OAAO,EAAE,UAAU,GAAG,OAAO,CAAC,SAAS,CAAC,CA2HpE"}
@@ -122,9 +122,7 @@ function extractFromJSON(config) {
122
122
  // KV namespace ID for VINEXT_CACHE
123
123
  if (Array.isArray(config.kv_namespaces)) {
124
124
  const vinextKV = config.kv_namespaces.find((ns) => ns && typeof ns === "object" && ns.binding === "VINEXT_CACHE");
125
- if (vinextKV &&
126
- typeof vinextKV.id === "string" &&
127
- vinextKV.id !== "<your-kv-namespace-id>") {
125
+ if (vinextKV && typeof vinextKV.id === "string" && vinextKV.id !== "<your-kv-namespace-id>") {
128
126
  result.kvNamespaceId = vinextKV.id;
129
127
  }
130
128
  }
@@ -238,7 +236,20 @@ async function resolveZoneId(domain, apiToken) {
238
236
  // lookup fails. Cloudflare's zone API will match on the correct registrable
239
237
  // domain regardless.
240
238
  const parts = domain.split(".");
241
- const MULTI_PART_TLDS = ["co.uk", "com.br", "com.au", "co.jp", "co.kr", "co.nz", "co.za", "com.mx", "com.ar", "com.cn", "org.uk", "net.au"];
239
+ const MULTI_PART_TLDS = [
240
+ "co.uk",
241
+ "com.br",
242
+ "com.au",
243
+ "co.jp",
244
+ "co.kr",
245
+ "co.nz",
246
+ "co.za",
247
+ "com.mx",
248
+ "com.ar",
249
+ "com.cn",
250
+ "org.uk",
251
+ "net.au",
252
+ ];
242
253
  const lastTwo = parts.slice(-2).join(".");
243
254
  let rootDomain;
244
255
  if (MULTI_PART_TLDS.includes(lastTwo) && parts.length > 2) {
@@ -1 +1 @@
1
- {"version":3,"file":"tpr.js","sourceRoot":"","sources":["../../src/cloudflare/tpr.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * TPR: Traffic-aware Pre-Rendering\n *\n * Uses Cloudflare zone analytics to determine which pages actually get\n * traffic, and pre-renders only those during deploy. The pre-rendered\n * HTML is uploaded to KV in the same format ISR uses at runtime — no\n * runtime changes needed.\n *\n * Flow:\n * 1. Parse wrangler config to find custom domain and KV namespace\n * 2. Resolve the Cloudflare zone for the custom domain\n * 3. Query zone analytics (GraphQL) for top pages by request count\n * 4. Walk ranked list until coverage threshold is met\n * 5. Start the built production server locally\n * 6. Fetch each hot route to produce HTML\n * 7. Upload pre-rendered HTML to KV (same KVCacheEntry format ISR reads)\n *\n * TPR is an experimental feature enabled via --experimental-tpr. It\n * gracefully skips when no custom domain, no API token, no traffic data,\n * or no KV namespace is configured.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { spawn, type ChildProcess } from \"node:child_process\";\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport interface TPROptions {\n /** Project root directory. */\n root: string;\n /** Traffic coverage percentage (0–100). Default: 90. */\n coverage: number;\n /** Hard cap on number of pages to pre-render. Default: 1000. */\n limit: number;\n /** Analytics lookback window in hours. Default: 24. */\n window: number;\n}\n\nexport interface TPRResult {\n /** Total unique page paths found in analytics. */\n totalPaths: number;\n /** Number of pages successfully pre-rendered and uploaded. */\n prerenderedCount: number;\n /** Actual traffic coverage achieved (percentage). */\n coverageAchieved: number;\n /** Wall-clock duration of the TPR step in milliseconds. */\n durationMs: number;\n /** If TPR was skipped, the reason. */\n skipped?: string;\n}\n\ninterface TrafficEntry {\n path: string;\n requests: number;\n}\n\ninterface SelectedRoutes {\n routes: TrafficEntry[];\n totalRequests: number;\n coveredRequests: number;\n coveragePercent: number;\n}\n\ninterface PrerenderResult {\n html: string;\n status: number;\n headers: Record<string, string>;\n}\n\ninterface WranglerConfig {\n accountId?: string;\n kvNamespaceId?: string;\n customDomain?: string;\n}\n\n// ─── Wrangler Config Parsing ─────────────────────────────────────────────────\n\n/**\n * Parse wrangler config (JSONC or TOML) to extract the fields TPR needs:\n * account_id, VINEXT_CACHE KV namespace ID, and custom domain.\n */\nexport function parseWranglerConfig(root: string): WranglerConfig | null {\n // Try JSONC / JSON first\n for (const filename of [\"wrangler.jsonc\", \"wrangler.json\"]) {\n const filepath = path.join(root, filename);\n if (fs.existsSync(filepath)) {\n const content = fs.readFileSync(filepath, \"utf-8\");\n try {\n const json = JSON.parse(stripJsonComments(content));\n return extractFromJSON(json);\n } catch {\n continue;\n }\n }\n }\n\n // Try TOML\n const tomlPath = path.join(root, \"wrangler.toml\");\n if (fs.existsSync(tomlPath)) {\n const content = fs.readFileSync(tomlPath, \"utf-8\");\n return extractFromTOML(content);\n }\n\n return null;\n}\n\n/**\n * Strip single-line (//) and multi-line comments from JSONC while\n * preserving strings that contain slashes.\n */\nfunction stripJsonComments(str: string): string {\n let result = \"\";\n let inString = false;\n let inSingleLine = false;\n let inMultiLine = false;\n let escapeNext = false;\n\n for (let i = 0; i < str.length; i++) {\n const ch = str[i];\n const next = str[i + 1];\n\n if (escapeNext) {\n if (!inSingleLine && !inMultiLine) result += ch;\n escapeNext = false;\n continue;\n }\n\n if (ch === \"\\\\\" && inString) {\n result += ch;\n escapeNext = true;\n continue;\n }\n\n if (inSingleLine) {\n if (ch === \"\\n\") {\n inSingleLine = false;\n result += ch;\n }\n continue;\n }\n\n if (inMultiLine) {\n if (ch === \"*\" && next === \"/\") {\n inMultiLine = false;\n i++;\n }\n continue;\n }\n\n if (ch === '\"' && !inString) {\n inString = true;\n result += ch;\n continue;\n }\n\n if (ch === '\"' && inString) {\n inString = false;\n result += ch;\n continue;\n }\n\n if (!inString && ch === \"/\" && next === \"/\") {\n inSingleLine = true;\n i++;\n continue;\n }\n\n if (!inString && ch === \"/\" && next === \"*\") {\n inMultiLine = true;\n i++;\n continue;\n }\n\n result += ch;\n }\n\n return result;\n}\n\nfunction extractFromJSON(config: Record<string, unknown>): WranglerConfig {\n const result: WranglerConfig = {};\n\n // account_id\n if (typeof config.account_id === \"string\") {\n result.accountId = config.account_id;\n }\n\n // KV namespace ID for VINEXT_CACHE\n if (Array.isArray(config.kv_namespaces)) {\n const vinextKV = config.kv_namespaces.find(\n (ns: Record<string, unknown>) =>\n ns && typeof ns === \"object\" && ns.binding === \"VINEXT_CACHE\",\n );\n if (\n vinextKV &&\n typeof vinextKV.id === \"string\" &&\n vinextKV.id !== \"<your-kv-namespace-id>\"\n ) {\n result.kvNamespaceId = vinextKV.id;\n }\n }\n\n // Custom domain — check routes[] and custom_domains[]\n const domain = extractDomainFromRoutes(config.routes) ?? extractDomainFromCustomDomains(config);\n if (domain) result.customDomain = domain;\n\n return result;\n}\n\nfunction extractDomainFromRoutes(routes: unknown): string | null {\n if (!Array.isArray(routes)) return null;\n\n for (const route of routes) {\n if (typeof route === \"string\") {\n const domain = cleanDomain(route);\n if (domain && !domain.includes(\"workers.dev\")) return domain;\n } else if (route && typeof route === \"object\") {\n const r = route as Record<string, unknown>;\n const pattern =\n typeof r.zone_name === \"string\"\n ? r.zone_name\n : typeof r.pattern === \"string\"\n ? r.pattern\n : null;\n if (pattern) {\n const domain = cleanDomain(pattern);\n if (domain && !domain.includes(\"workers.dev\")) return domain;\n }\n }\n }\n return null;\n}\n\nfunction extractDomainFromCustomDomains(config: Record<string, unknown>): string | null {\n // Workers Custom Domains: \"custom_domains\": [\"example.com\"]\n if (Array.isArray(config.custom_domains)) {\n for (const d of config.custom_domains) {\n if (typeof d === \"string\" && !d.includes(\"workers.dev\")) {\n return cleanDomain(d);\n }\n }\n }\n return null;\n}\n\n/** Strip protocol and trailing wildcards from a route pattern to get a bare domain. */\nfunction cleanDomain(raw: string): string | null {\n const cleaned = raw\n .replace(/^https?:\\/\\//, \"\")\n .replace(/\\/\\*$/, \"\")\n .replace(/\\/+$/, \"\")\n .split(\"/\")[0]; // Take only the host part\n return cleaned || null;\n}\n\n/**\n * Simple extraction of specific fields from wrangler.toml content.\n * Not a full TOML parser — just enough for the fields we need.\n */\nfunction extractFromTOML(content: string): WranglerConfig {\n const result: WranglerConfig = {};\n\n // account_id = \"...\"\n const accountMatch = content.match(/^account_id\\s*=\\s*\"([^\"]+)\"/m);\n if (accountMatch) result.accountId = accountMatch[1];\n\n // KV namespace with binding = \"VINEXT_CACHE\"\n // Look for [[kv_namespaces]] blocks\n const kvBlocks = content.split(/\\[\\[kv_namespaces\\]\\]/);\n for (let i = 1; i < kvBlocks.length; i++) {\n const block = kvBlocks[i].split(/\\[\\[/)[0]; // Take until next section\n const bindingMatch = block.match(/binding\\s*=\\s*\"([^\"]+)\"/);\n const idMatch = block.match(/\\bid\\s*=\\s*\"([^\"]+)\"/);\n if (\n bindingMatch?.[1] === \"VINEXT_CACHE\" &&\n idMatch?.[1] &&\n idMatch[1] !== \"<your-kv-namespace-id>\"\n ) {\n result.kvNamespaceId = idMatch[1];\n }\n }\n\n // routes — both string and table forms\n // route = \"example.com/*\"\n const routeMatch = content.match(/^route\\s*=\\s*\"([^\"]+)\"/m);\n if (routeMatch) {\n const domain = cleanDomain(routeMatch[1]);\n if (domain && !domain.includes(\"workers.dev\")) {\n result.customDomain = domain;\n }\n }\n\n // [[routes]] blocks\n if (!result.customDomain) {\n const routeBlocks = content.split(/\\[\\[routes\\]\\]/);\n for (let i = 1; i < routeBlocks.length; i++) {\n const block = routeBlocks[i].split(/\\[\\[/)[0];\n const patternMatch = block.match(/pattern\\s*=\\s*\"([^\"]+)\"/);\n if (patternMatch) {\n const domain = cleanDomain(patternMatch[1]);\n if (domain && !domain.includes(\"workers.dev\")) {\n result.customDomain = domain;\n break;\n }\n }\n }\n }\n\n return result;\n}\n\n// ─── Cloudflare API ──────────────────────────────────────────────────────────\n\n/** Resolve zone ID from a domain name via the Cloudflare API. */\nasync function resolveZoneId(\n domain: string,\n apiToken: string,\n): Promise<string | null> {\n // Extract the registrable domain (e.g., \"shop.example.com\" → \"example.com\").\n // TODO: This doesn't handle multi-part TLDs like .co.uk, .com.br, .com.au.\n // For those, we'd need a public suffix list. For now, we try the simple\n // two-part extraction first, then fall back to the full domain if the zone\n // lookup fails. Cloudflare's zone API will match on the correct registrable\n // domain regardless.\n const parts = domain.split(\".\");\n const MULTI_PART_TLDS = [\"co.uk\", \"com.br\", \"com.au\", \"co.jp\", \"co.kr\", \"co.nz\", \"co.za\", \"com.mx\", \"com.ar\", \"com.cn\", \"org.uk\", \"net.au\"];\n const lastTwo = parts.slice(-2).join(\".\");\n let rootDomain: string;\n if (MULTI_PART_TLDS.includes(lastTwo) && parts.length > 2) {\n rootDomain = parts.slice(-3).join(\".\");\n } else {\n rootDomain = parts.length > 2 ? parts.slice(-2).join(\".\") : domain;\n }\n\n const response = await fetch(\n `https://api.cloudflare.com/client/v4/zones?name=${encodeURIComponent(rootDomain)}`,\n {\n headers: {\n Authorization: `Bearer ${apiToken}`,\n \"Content-Type\": \"application/json\",\n },\n },\n );\n\n if (!response.ok) return null;\n\n const data = (await response.json()) as {\n success: boolean;\n result?: Array<{ id: string }>;\n };\n if (!data.success || !data.result?.length) return null;\n\n return data.result[0].id;\n}\n\n/** Resolve the account ID associated with the API token. */\nasync function resolveAccountId(apiToken: string): Promise<string | null> {\n const response = await fetch(\n \"https://api.cloudflare.com/client/v4/accounts?per_page=1\",\n {\n headers: {\n Authorization: `Bearer ${apiToken}`,\n \"Content-Type\": \"application/json\",\n },\n },\n );\n\n if (!response.ok) return null;\n\n const data = (await response.json()) as {\n success: boolean;\n result?: Array<{ id: string }>;\n };\n if (!data.success || !data.result?.length) return null;\n\n return data.result[0].id;\n}\n\n// ─── Traffic Querying ────────────────────────────────────────────────────────\n\n/**\n * Query Cloudflare zone analytics for top page paths by request count\n * over the given time window.\n */\nasync function queryTraffic(\n zoneTag: string,\n apiToken: string,\n windowHours: number,\n): Promise<TrafficEntry[]> {\n const now = new Date();\n const start = new Date(now.getTime() - windowHours * 60 * 60 * 1000);\n\n const query = `{\n viewer {\n zones(filter: { zoneTag: \"${zoneTag}\" }) {\n httpRequestsAdaptiveGroups(\n limit: 10000\n orderBy: [sum_requests_DESC]\n filter: {\n datetime_geq: \"${start.toISOString()}\"\n datetime_lt: \"${now.toISOString()}\"\n requestSource: \"eyeball\"\n }\n ) {\n sum { requests }\n dimensions { clientRequestPath }\n }\n }\n }\n }`;\n\n const response = await fetch(\"https://api.cloudflare.com/client/v4/graphql\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ query }),\n });\n\n if (!response.ok) {\n throw new Error(\n `Zone analytics query failed: ${response.status} ${response.statusText}`,\n );\n }\n\n const data = (await response.json()) as {\n errors?: Array<{ message: string }>;\n data?: {\n viewer?: {\n zones?: Array<{\n httpRequestsAdaptiveGroups?: Array<{\n sum: { requests: number };\n dimensions: { clientRequestPath: string };\n }>;\n }>;\n };\n };\n };\n\n if (data.errors?.length) {\n throw new Error(`Zone analytics error: ${data.errors[0].message}`);\n }\n\n const groups = data.data?.viewer?.zones?.[0]?.httpRequestsAdaptiveGroups;\n if (!groups || groups.length === 0) return [];\n\n return filterTrafficPaths(\n groups.map((g) => ({\n path: g.dimensions.clientRequestPath,\n requests: g.sum.requests,\n })),\n );\n}\n\n/** Filter out non-page requests (static assets, API routes, internal routes). */\nfunction filterTrafficPaths(entries: TrafficEntry[]): TrafficEntry[] {\n return entries.filter((e) => {\n if (!e.path.startsWith(\"/\")) return false;\n // Static assets\n if (/\\.(js|css|png|jpg|jpeg|gif|svg|ico|woff2?|ttf|eot|map|webp|avif)$/i.test(e.path))\n return false;\n // API routes\n if (e.path.startsWith(\"/api/\")) return false;\n // Internal routes\n if (e.path.startsWith(\"/_vinext/\") || e.path.startsWith(\"/_next/\")) return false;\n // RSC requests\n if (e.path.endsWith(\".rsc\")) return false;\n return true;\n });\n}\n\n// ─── Route Selection ─────────────────────────────────────────────────────────\n\n/**\n * Walk the ranked traffic list, accumulating request counts until the\n * coverage target is met or the hard cap is reached.\n */\nexport function selectRoutes(\n traffic: TrafficEntry[],\n coverageTarget: number,\n limit: number,\n): SelectedRoutes {\n const totalRequests = traffic.reduce((sum, e) => sum + e.requests, 0);\n if (totalRequests === 0) {\n return { routes: [], totalRequests: 0, coveredRequests: 0, coveragePercent: 0 };\n }\n\n const target = totalRequests * (coverageTarget / 100);\n const selected: TrafficEntry[] = [];\n let accumulated = 0;\n\n // Traffic is already sorted DESC by requests from the GraphQL query\n for (const entry of traffic) {\n if (accumulated >= target || selected.length >= limit) break;\n selected.push(entry);\n accumulated += entry.requests;\n }\n\n return {\n routes: selected,\n totalRequests,\n coveredRequests: accumulated,\n coveragePercent: (accumulated / totalRequests) * 100,\n };\n}\n\n// ─── Pre-rendering ───────────────────────────────────────────────────────────\n\n/** Pre-render port — high number to avoid collisions with dev servers. */\nconst PRERENDER_PORT = 19384;\n\n/** Max time to wait for the local server to start (ms). */\nconst SERVER_STARTUP_TIMEOUT = 30_000;\n\n/** Max concurrent fetch requests during pre-rendering. */\nconst FETCH_CONCURRENCY = 10;\n\n/**\n * Start a local production server, fetch each route to produce HTML,\n * and return the results. Pages that fail to render are skipped.\n */\nasync function prerenderRoutes(\n routes: string[],\n root: string,\n hostDomain?: string,\n): Promise<Map<string, PrerenderResult>> {\n const results = new Map<string, PrerenderResult>();\n let failedCount = 0;\n const port = PRERENDER_PORT;\n\n // Verify dist/ exists\n const distDir = path.join(root, \"dist\");\n if (!fs.existsSync(distDir)) {\n console.log(\" TPR: Skipping pre-render — dist/ directory not found\");\n return results;\n }\n\n // Start the local production server as a subprocess\n const serverProcess = startLocalServer(root, port);\n\n try {\n await waitForServer(port, SERVER_STARTUP_TIMEOUT);\n\n // Fetch routes in batches to limit concurrency\n for (let i = 0; i < routes.length; i += FETCH_CONCURRENCY) {\n const batch = routes.slice(i, i + FETCH_CONCURRENCY);\n const promises = batch.map(async (routePath) => {\n try {\n const response = await fetch(`http://127.0.0.1:${port}${routePath}`, {\n headers: {\n \"User-Agent\": \"vinext-tpr/1.0\",\n ...(hostDomain ? { Host: hostDomain } : {}),\n },\n redirect: \"manual\", // Don't follow redirects — cache the redirect itself\n });\n\n // Only cache successful responses (2xx and 3xx)\n if (response.status < 400) {\n const html = await response.text();\n const headers: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n // Only keep relevant headers\n if (\n key === \"content-type\" ||\n key === \"cache-control\" ||\n key === \"x-vinext-revalidate\" ||\n key === \"location\"\n ) {\n headers[key] = value;\n }\n });\n results.set(routePath, {\n html,\n status: response.status,\n headers,\n });\n }\n } catch {\n // Skip pages that fail to render — they may depend on\n // request-specific data (cookies, headers, auth) that\n // isn't available during pre-rendering.\n failedCount++;\n }\n });\n\n await Promise.all(promises);\n }\n\n if (failedCount > 0) {\n console.log(` TPR: ${failedCount} page(s) failed to pre-render (skipped)`);\n }\n } finally {\n serverProcess.kill(\"SIGTERM\");\n // Give it a moment to clean up\n await new Promise<void>((resolve) => {\n serverProcess.on(\"exit\", resolve);\n setTimeout(resolve, 2000);\n });\n }\n\n return results;\n}\n\n/**\n * Spawn a subprocess running the vinext production server.\n * Uses the same Node.js binary and resolves prod-server.js relative\n * to the current module (works whether vinext is installed or linked).\n */\nfunction startLocalServer(root: string, port: number): ChildProcess {\n const thisDir = fileURLToPath(new URL(\".\", import.meta.url));\n const prodServerPath = path.resolve(thisDir, \"..\", \"server\", \"prod-server.js\");\n const outDir = path.join(root, \"dist\");\n\n // Escape backslashes for Windows paths inside the JS string\n const escapedProdServer = prodServerPath.replace(/\\\\/g, \"\\\\\\\\\");\n const escapedOutDir = outDir.replace(/\\\\/g, \"\\\\\\\\\");\n\n const script = [\n `import(\"file://${escapedProdServer}\")`,\n `.then(m => m.startProdServer({ port: ${port}, host: \"127.0.0.1\", outDir: \"${escapedOutDir}\" }))`,\n `.catch(e => { console.error(\"[vinext-tpr] Server failed to start:\", e); process.exit(1); });`,\n ].join(\"\");\n\n const proc = spawn(process.execPath, [\"--input-type=module\", \"-e\", script], {\n cwd: root,\n stdio: \"pipe\",\n env: { ...process.env, NODE_ENV: \"production\" },\n });\n\n // Forward server errors to the parent's stderr for debugging\n proc.stderr?.on(\"data\", (chunk: Buffer) => {\n const msg = chunk.toString().trim();\n if (msg) console.error(` [tpr-server] ${msg}`);\n });\n\n return proc;\n}\n\n/** Poll the local server until it responds or the timeout is reached. */\nasync function waitForServer(port: number, timeoutMs: number): Promise<void> {\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n try {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 2000);\n const response = await fetch(`http://127.0.0.1:${port}/`, {\n redirect: \"manual\",\n signal: controller.signal,\n });\n clearTimeout(timer);\n // Any response means the server is up\n await response.text(); // consume body\n return;\n } catch {\n await new Promise<void>((r) => setTimeout(r, 300));\n }\n }\n throw new Error(\n `Local production server failed to start within ${timeoutMs / 1000}s`,\n );\n}\n\n// ─── KV Upload ───────────────────────────────────────────────────────────────\n\n/**\n * Upload pre-rendered pages to KV using the Cloudflare REST API.\n * Writes in the same KVCacheEntry format that KVCacheHandler reads\n * at runtime, so ISR serves these entries without any code changes.\n */\nasync function uploadToKV(\n entries: Map<string, PrerenderResult>,\n namespaceId: string,\n accountId: string,\n apiToken: string,\n defaultRevalidateSeconds: number,\n): Promise<void> {\n const now = Date.now();\n\n // Build the bulk write payload\n const pairs: Array<{\n key: string;\n value: string;\n expiration_ttl?: number;\n }> = [];\n\n for (const [routePath, result] of entries) {\n // Determine revalidation window — use the page's revalidate header\n // if present, otherwise fall back to the default\n const revalidateHeader = result.headers[\"x-vinext-revalidate\"];\n const revalidateSeconds =\n revalidateHeader && !isNaN(Number(revalidateHeader))\n ? Number(revalidateHeader)\n : defaultRevalidateSeconds;\n\n const revalidateAt =\n revalidateSeconds > 0 ? now + revalidateSeconds * 1000 : null;\n\n // KV TTL: 10x the revalidation period, clamped to [60s, 30d]\n // (matches the logic in KVCacheHandler.set)\n const kvTtl =\n revalidateSeconds > 0\n ? Math.max(Math.min(revalidateSeconds * 10, 30 * 24 * 3600), 60)\n : 24 * 3600; // 24h fallback if no revalidation\n\n const entry = {\n value: {\n kind: \"APP_PAGE\" as const,\n html: result.html,\n headers: result.headers,\n status: result.status,\n },\n tags: [] as string[],\n lastModified: now,\n revalidateAt,\n };\n\n pairs.push({\n key: `cache:${routePath}`,\n value: JSON.stringify(entry),\n expiration_ttl: kvTtl,\n });\n }\n\n // Upload in batches (KV bulk API accepts up to 10,000 per request)\n const BATCH_SIZE = 10_000;\n for (let i = 0; i < pairs.length; i += BATCH_SIZE) {\n const batch = pairs.slice(i, i + BATCH_SIZE);\n const response = await fetch(\n `https://api.cloudflare.com/client/v4/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/bulk`,\n {\n method: \"PUT\",\n headers: {\n Authorization: `Bearer ${apiToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(batch),\n },\n );\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(\n `KV bulk upload failed (batch ${Math.floor(i / BATCH_SIZE) + 1}): ${response.status} — ${text}`,\n );\n }\n }\n}\n\n// ─── Main Entry ──────────────────────────────────────────────────────────────\n\n/** Default revalidation TTL for pre-rendered pages (1 hour). */\nconst DEFAULT_REVALIDATE_SECONDS = 3600;\n\n/**\n * Run the TPR pipeline: query traffic, select routes, pre-render, upload.\n *\n * Designed to be called between the build step and wrangler deploy in\n * the `vinext deploy` pipeline. Gracefully skips (never errors) when\n * the prerequisites aren't met.\n */\nexport async function runTPR(options: TPROptions): Promise<TPRResult> {\n const startTime = Date.now();\n const { root, coverage, limit, window: windowHours } = options;\n\n const skip = (reason: string): TPRResult => ({\n totalPaths: 0,\n prerenderedCount: 0,\n coverageAchieved: 0,\n durationMs: Date.now() - startTime,\n skipped: reason,\n });\n\n // ── 1. Check for API token ────────────────────────────────────\n const apiToken = process.env.CLOUDFLARE_API_TOKEN;\n if (!apiToken) {\n return skip(\"no CLOUDFLARE_API_TOKEN set\");\n }\n\n // ── 2. Parse wrangler config ──────────────────────────────────\n const wranglerConfig = parseWranglerConfig(root);\n if (!wranglerConfig) {\n return skip(\"could not parse wrangler config\");\n }\n\n // ── 3. Check for custom domain ────────────────────────────────\n if (!wranglerConfig.customDomain) {\n return skip(\"no custom domain — zone analytics unavailable\");\n }\n\n // ── 4. Check for KV namespace ─────────────────────────────────\n if (!wranglerConfig.kvNamespaceId) {\n return skip(\"no VINEXT_CACHE KV namespace configured\");\n }\n\n // ── 5. Resolve account ID ─────────────────────────────────────\n const accountId = wranglerConfig.accountId ?? (await resolveAccountId(apiToken));\n if (!accountId) {\n return skip(\"could not resolve Cloudflare account ID\");\n }\n\n // ── 6. Resolve zone ID ────────────────────────────────────────\n console.log(` TPR: Analyzing traffic for ${wranglerConfig.customDomain} (last ${windowHours}h)`);\n\n const zoneId = await resolveZoneId(wranglerConfig.customDomain, apiToken);\n if (!zoneId) {\n return skip(\n `could not resolve zone for ${wranglerConfig.customDomain}`,\n );\n }\n\n // ── 7. Query traffic data ─────────────────────────────────────\n let traffic: TrafficEntry[];\n try {\n traffic = await queryTraffic(zoneId, apiToken, windowHours);\n } catch (err) {\n return skip(\n `analytics query failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n if (traffic.length === 0) {\n return skip(\"no traffic data available (first deploy?)\");\n }\n\n // ── 8. Select routes by coverage ──────────────────────────────\n const selection = selectRoutes(traffic, coverage, limit);\n\n console.log(\n ` TPR: ${traffic.length.toLocaleString()} unique paths — ` +\n `${selection.routes.length} pages cover ${Math.round(selection.coveragePercent)}% of traffic`,\n );\n\n if (selection.routes.length === 0) {\n return {\n totalPaths: traffic.length,\n prerenderedCount: 0,\n coverageAchieved: 0,\n durationMs: Date.now() - startTime,\n skipped: \"no pre-renderable routes after filtering\",\n };\n }\n\n // ── 9. Pre-render selected routes ─────────────────────────────\n console.log(` TPR: Pre-rendering ${selection.routes.length} pages...`);\n\n const routePaths = selection.routes.map((r) => r.path);\n let rendered: Map<string, PrerenderResult>;\n try {\n rendered = await prerenderRoutes(routePaths, root, wranglerConfig.customDomain);\n } catch (err) {\n return skip(\n `pre-rendering failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n if (rendered.size === 0) {\n return {\n totalPaths: traffic.length,\n prerenderedCount: 0,\n coverageAchieved: selection.coveragePercent,\n durationMs: Date.now() - startTime,\n skipped: \"all pages failed to pre-render (request-dependent?)\",\n };\n }\n\n // ── 10. Upload to KV ──────────────────────────────────────────\n try {\n await uploadToKV(\n rendered,\n wranglerConfig.kvNamespaceId,\n accountId,\n apiToken,\n DEFAULT_REVALIDATE_SECONDS,\n );\n } catch (err) {\n return skip(\n `KV upload failed: ${err instanceof Error ? err.message : String(err)}`,\n );\n }\n\n const durationMs = Date.now() - startTime;\n console.log(\n ` TPR: Pre-rendered ${rendered.size} pages in ${(durationMs / 1000).toFixed(1)}s → KV cache`,\n );\n\n return {\n totalPaths: traffic.length,\n prerenderedCount: rendered.size,\n coverageAchieved: selection.coveragePercent,\n durationMs,\n };\n}\n"]}
1
+ {"version":3,"file":"tpr.js","sourceRoot":"","sources":["../../src/cloudflare/tpr.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * TPR: Traffic-aware Pre-Rendering\n *\n * Uses Cloudflare zone analytics to determine which pages actually get\n * traffic, and pre-renders only those during deploy. The pre-rendered\n * HTML is uploaded to KV in the same format ISR uses at runtime — no\n * runtime changes needed.\n *\n * Flow:\n * 1. Parse wrangler config to find custom domain and KV namespace\n * 2. Resolve the Cloudflare zone for the custom domain\n * 3. Query zone analytics (GraphQL) for top pages by request count\n * 4. Walk ranked list until coverage threshold is met\n * 5. Start the built production server locally\n * 6. Fetch each hot route to produce HTML\n * 7. Upload pre-rendered HTML to KV (same KVCacheEntry format ISR reads)\n *\n * TPR is an experimental feature enabled via --experimental-tpr. It\n * gracefully skips when no custom domain, no API token, no traffic data,\n * or no KV namespace is configured.\n */\n\nimport fs from \"node:fs\";\nimport path from \"node:path\";\nimport { fileURLToPath } from \"node:url\";\nimport { spawn, type ChildProcess } from \"node:child_process\";\n\n// ─── Types ───────────────────────────────────────────────────────────────────\n\nexport interface TPROptions {\n /** Project root directory. */\n root: string;\n /** Traffic coverage percentage (0–100). Default: 90. */\n coverage: number;\n /** Hard cap on number of pages to pre-render. Default: 1000. */\n limit: number;\n /** Analytics lookback window in hours. Default: 24. */\n window: number;\n}\n\nexport interface TPRResult {\n /** Total unique page paths found in analytics. */\n totalPaths: number;\n /** Number of pages successfully pre-rendered and uploaded. */\n prerenderedCount: number;\n /** Actual traffic coverage achieved (percentage). */\n coverageAchieved: number;\n /** Wall-clock duration of the TPR step in milliseconds. */\n durationMs: number;\n /** If TPR was skipped, the reason. */\n skipped?: string;\n}\n\ninterface TrafficEntry {\n path: string;\n requests: number;\n}\n\ninterface SelectedRoutes {\n routes: TrafficEntry[];\n totalRequests: number;\n coveredRequests: number;\n coveragePercent: number;\n}\n\ninterface PrerenderResult {\n html: string;\n status: number;\n headers: Record<string, string>;\n}\n\ninterface WranglerConfig {\n accountId?: string;\n kvNamespaceId?: string;\n customDomain?: string;\n}\n\n// ─── Wrangler Config Parsing ─────────────────────────────────────────────────\n\n/**\n * Parse wrangler config (JSONC or TOML) to extract the fields TPR needs:\n * account_id, VINEXT_CACHE KV namespace ID, and custom domain.\n */\nexport function parseWranglerConfig(root: string): WranglerConfig | null {\n // Try JSONC / JSON first\n for (const filename of [\"wrangler.jsonc\", \"wrangler.json\"]) {\n const filepath = path.join(root, filename);\n if (fs.existsSync(filepath)) {\n const content = fs.readFileSync(filepath, \"utf-8\");\n try {\n const json = JSON.parse(stripJsonComments(content));\n return extractFromJSON(json);\n } catch {\n continue;\n }\n }\n }\n\n // Try TOML\n const tomlPath = path.join(root, \"wrangler.toml\");\n if (fs.existsSync(tomlPath)) {\n const content = fs.readFileSync(tomlPath, \"utf-8\");\n return extractFromTOML(content);\n }\n\n return null;\n}\n\n/**\n * Strip single-line (//) and multi-line comments from JSONC while\n * preserving strings that contain slashes.\n */\nfunction stripJsonComments(str: string): string {\n let result = \"\";\n let inString = false;\n let inSingleLine = false;\n let inMultiLine = false;\n let escapeNext = false;\n\n for (let i = 0; i < str.length; i++) {\n const ch = str[i];\n const next = str[i + 1];\n\n if (escapeNext) {\n if (!inSingleLine && !inMultiLine) result += ch;\n escapeNext = false;\n continue;\n }\n\n if (ch === \"\\\\\" && inString) {\n result += ch;\n escapeNext = true;\n continue;\n }\n\n if (inSingleLine) {\n if (ch === \"\\n\") {\n inSingleLine = false;\n result += ch;\n }\n continue;\n }\n\n if (inMultiLine) {\n if (ch === \"*\" && next === \"/\") {\n inMultiLine = false;\n i++;\n }\n continue;\n }\n\n if (ch === '\"' && !inString) {\n inString = true;\n result += ch;\n continue;\n }\n\n if (ch === '\"' && inString) {\n inString = false;\n result += ch;\n continue;\n }\n\n if (!inString && ch === \"/\" && next === \"/\") {\n inSingleLine = true;\n i++;\n continue;\n }\n\n if (!inString && ch === \"/\" && next === \"*\") {\n inMultiLine = true;\n i++;\n continue;\n }\n\n result += ch;\n }\n\n return result;\n}\n\nfunction extractFromJSON(config: Record<string, unknown>): WranglerConfig {\n const result: WranglerConfig = {};\n\n // account_id\n if (typeof config.account_id === \"string\") {\n result.accountId = config.account_id;\n }\n\n // KV namespace ID for VINEXT_CACHE\n if (Array.isArray(config.kv_namespaces)) {\n const vinextKV = config.kv_namespaces.find(\n (ns: Record<string, unknown>) =>\n ns && typeof ns === \"object\" && ns.binding === \"VINEXT_CACHE\",\n );\n if (vinextKV && typeof vinextKV.id === \"string\" && vinextKV.id !== \"<your-kv-namespace-id>\") {\n result.kvNamespaceId = vinextKV.id;\n }\n }\n\n // Custom domain — check routes[] and custom_domains[]\n const domain = extractDomainFromRoutes(config.routes) ?? extractDomainFromCustomDomains(config);\n if (domain) result.customDomain = domain;\n\n return result;\n}\n\nfunction extractDomainFromRoutes(routes: unknown): string | null {\n if (!Array.isArray(routes)) return null;\n\n for (const route of routes) {\n if (typeof route === \"string\") {\n const domain = cleanDomain(route);\n if (domain && !domain.includes(\"workers.dev\")) return domain;\n } else if (route && typeof route === \"object\") {\n const r = route as Record<string, unknown>;\n const pattern =\n typeof r.zone_name === \"string\"\n ? r.zone_name\n : typeof r.pattern === \"string\"\n ? r.pattern\n : null;\n if (pattern) {\n const domain = cleanDomain(pattern);\n if (domain && !domain.includes(\"workers.dev\")) return domain;\n }\n }\n }\n return null;\n}\n\nfunction extractDomainFromCustomDomains(config: Record<string, unknown>): string | null {\n // Workers Custom Domains: \"custom_domains\": [\"example.com\"]\n if (Array.isArray(config.custom_domains)) {\n for (const d of config.custom_domains) {\n if (typeof d === \"string\" && !d.includes(\"workers.dev\")) {\n return cleanDomain(d);\n }\n }\n }\n return null;\n}\n\n/** Strip protocol and trailing wildcards from a route pattern to get a bare domain. */\nfunction cleanDomain(raw: string): string | null {\n const cleaned = raw\n .replace(/^https?:\\/\\//, \"\")\n .replace(/\\/\\*$/, \"\")\n .replace(/\\/+$/, \"\")\n .split(\"/\")[0]; // Take only the host part\n return cleaned || null;\n}\n\n/**\n * Simple extraction of specific fields from wrangler.toml content.\n * Not a full TOML parser — just enough for the fields we need.\n */\nfunction extractFromTOML(content: string): WranglerConfig {\n const result: WranglerConfig = {};\n\n // account_id = \"...\"\n const accountMatch = content.match(/^account_id\\s*=\\s*\"([^\"]+)\"/m);\n if (accountMatch) result.accountId = accountMatch[1];\n\n // KV namespace with binding = \"VINEXT_CACHE\"\n // Look for [[kv_namespaces]] blocks\n const kvBlocks = content.split(/\\[\\[kv_namespaces\\]\\]/);\n for (let i = 1; i < kvBlocks.length; i++) {\n const block = kvBlocks[i].split(/\\[\\[/)[0]; // Take until next section\n const bindingMatch = block.match(/binding\\s*=\\s*\"([^\"]+)\"/);\n const idMatch = block.match(/\\bid\\s*=\\s*\"([^\"]+)\"/);\n if (\n bindingMatch?.[1] === \"VINEXT_CACHE\" &&\n idMatch?.[1] &&\n idMatch[1] !== \"<your-kv-namespace-id>\"\n ) {\n result.kvNamespaceId = idMatch[1];\n }\n }\n\n // routes — both string and table forms\n // route = \"example.com/*\"\n const routeMatch = content.match(/^route\\s*=\\s*\"([^\"]+)\"/m);\n if (routeMatch) {\n const domain = cleanDomain(routeMatch[1]);\n if (domain && !domain.includes(\"workers.dev\")) {\n result.customDomain = domain;\n }\n }\n\n // [[routes]] blocks\n if (!result.customDomain) {\n const routeBlocks = content.split(/\\[\\[routes\\]\\]/);\n for (let i = 1; i < routeBlocks.length; i++) {\n const block = routeBlocks[i].split(/\\[\\[/)[0];\n const patternMatch = block.match(/pattern\\s*=\\s*\"([^\"]+)\"/);\n if (patternMatch) {\n const domain = cleanDomain(patternMatch[1]);\n if (domain && !domain.includes(\"workers.dev\")) {\n result.customDomain = domain;\n break;\n }\n }\n }\n }\n\n return result;\n}\n\n// ─── Cloudflare API ──────────────────────────────────────────────────────────\n\n/** Resolve zone ID from a domain name via the Cloudflare API. */\nasync function resolveZoneId(domain: string, apiToken: string): Promise<string | null> {\n // Extract the registrable domain (e.g., \"shop.example.com\" → \"example.com\").\n // TODO: This doesn't handle multi-part TLDs like .co.uk, .com.br, .com.au.\n // For those, we'd need a public suffix list. For now, we try the simple\n // two-part extraction first, then fall back to the full domain if the zone\n // lookup fails. Cloudflare's zone API will match on the correct registrable\n // domain regardless.\n const parts = domain.split(\".\");\n const MULTI_PART_TLDS = [\n \"co.uk\",\n \"com.br\",\n \"com.au\",\n \"co.jp\",\n \"co.kr\",\n \"co.nz\",\n \"co.za\",\n \"com.mx\",\n \"com.ar\",\n \"com.cn\",\n \"org.uk\",\n \"net.au\",\n ];\n const lastTwo = parts.slice(-2).join(\".\");\n let rootDomain: string;\n if (MULTI_PART_TLDS.includes(lastTwo) && parts.length > 2) {\n rootDomain = parts.slice(-3).join(\".\");\n } else {\n rootDomain = parts.length > 2 ? parts.slice(-2).join(\".\") : domain;\n }\n\n const response = await fetch(\n `https://api.cloudflare.com/client/v4/zones?name=${encodeURIComponent(rootDomain)}`,\n {\n headers: {\n Authorization: `Bearer ${apiToken}`,\n \"Content-Type\": \"application/json\",\n },\n },\n );\n\n if (!response.ok) return null;\n\n const data = (await response.json()) as {\n success: boolean;\n result?: Array<{ id: string }>;\n };\n if (!data.success || !data.result?.length) return null;\n\n return data.result[0].id;\n}\n\n/** Resolve the account ID associated with the API token. */\nasync function resolveAccountId(apiToken: string): Promise<string | null> {\n const response = await fetch(\"https://api.cloudflare.com/client/v4/accounts?per_page=1\", {\n headers: {\n Authorization: `Bearer ${apiToken}`,\n \"Content-Type\": \"application/json\",\n },\n });\n\n if (!response.ok) return null;\n\n const data = (await response.json()) as {\n success: boolean;\n result?: Array<{ id: string }>;\n };\n if (!data.success || !data.result?.length) return null;\n\n return data.result[0].id;\n}\n\n// ─── Traffic Querying ────────────────────────────────────────────────────────\n\n/**\n * Query Cloudflare zone analytics for top page paths by request count\n * over the given time window.\n */\nasync function queryTraffic(\n zoneTag: string,\n apiToken: string,\n windowHours: number,\n): Promise<TrafficEntry[]> {\n const now = new Date();\n const start = new Date(now.getTime() - windowHours * 60 * 60 * 1000);\n\n const query = `{\n viewer {\n zones(filter: { zoneTag: \"${zoneTag}\" }) {\n httpRequestsAdaptiveGroups(\n limit: 10000\n orderBy: [sum_requests_DESC]\n filter: {\n datetime_geq: \"${start.toISOString()}\"\n datetime_lt: \"${now.toISOString()}\"\n requestSource: \"eyeball\"\n }\n ) {\n sum { requests }\n dimensions { clientRequestPath }\n }\n }\n }\n }`;\n\n const response = await fetch(\"https://api.cloudflare.com/client/v4/graphql\", {\n method: \"POST\",\n headers: {\n Authorization: `Bearer ${apiToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify({ query }),\n });\n\n if (!response.ok) {\n throw new Error(`Zone analytics query failed: ${response.status} ${response.statusText}`);\n }\n\n const data = (await response.json()) as {\n errors?: Array<{ message: string }>;\n data?: {\n viewer?: {\n zones?: Array<{\n httpRequestsAdaptiveGroups?: Array<{\n sum: { requests: number };\n dimensions: { clientRequestPath: string };\n }>;\n }>;\n };\n };\n };\n\n if (data.errors?.length) {\n throw new Error(`Zone analytics error: ${data.errors[0].message}`);\n }\n\n const groups = data.data?.viewer?.zones?.[0]?.httpRequestsAdaptiveGroups;\n if (!groups || groups.length === 0) return [];\n\n return filterTrafficPaths(\n groups.map((g) => ({\n path: g.dimensions.clientRequestPath,\n requests: g.sum.requests,\n })),\n );\n}\n\n/** Filter out non-page requests (static assets, API routes, internal routes). */\nfunction filterTrafficPaths(entries: TrafficEntry[]): TrafficEntry[] {\n return entries.filter((e) => {\n if (!e.path.startsWith(\"/\")) return false;\n // Static assets\n if (/\\.(js|css|png|jpg|jpeg|gif|svg|ico|woff2?|ttf|eot|map|webp|avif)$/i.test(e.path))\n return false;\n // API routes\n if (e.path.startsWith(\"/api/\")) return false;\n // Internal routes\n if (e.path.startsWith(\"/_vinext/\") || e.path.startsWith(\"/_next/\")) return false;\n // RSC requests\n if (e.path.endsWith(\".rsc\")) return false;\n return true;\n });\n}\n\n// ─── Route Selection ─────────────────────────────────────────────────────────\n\n/**\n * Walk the ranked traffic list, accumulating request counts until the\n * coverage target is met or the hard cap is reached.\n */\nexport function selectRoutes(\n traffic: TrafficEntry[],\n coverageTarget: number,\n limit: number,\n): SelectedRoutes {\n const totalRequests = traffic.reduce((sum, e) => sum + e.requests, 0);\n if (totalRequests === 0) {\n return { routes: [], totalRequests: 0, coveredRequests: 0, coveragePercent: 0 };\n }\n\n const target = totalRequests * (coverageTarget / 100);\n const selected: TrafficEntry[] = [];\n let accumulated = 0;\n\n // Traffic is already sorted DESC by requests from the GraphQL query\n for (const entry of traffic) {\n if (accumulated >= target || selected.length >= limit) break;\n selected.push(entry);\n accumulated += entry.requests;\n }\n\n return {\n routes: selected,\n totalRequests,\n coveredRequests: accumulated,\n coveragePercent: (accumulated / totalRequests) * 100,\n };\n}\n\n// ─── Pre-rendering ───────────────────────────────────────────────────────────\n\n/** Pre-render port — high number to avoid collisions with dev servers. */\nconst PRERENDER_PORT = 19384;\n\n/** Max time to wait for the local server to start (ms). */\nconst SERVER_STARTUP_TIMEOUT = 30_000;\n\n/** Max concurrent fetch requests during pre-rendering. */\nconst FETCH_CONCURRENCY = 10;\n\n/**\n * Start a local production server, fetch each route to produce HTML,\n * and return the results. Pages that fail to render are skipped.\n */\nasync function prerenderRoutes(\n routes: string[],\n root: string,\n hostDomain?: string,\n): Promise<Map<string, PrerenderResult>> {\n const results = new Map<string, PrerenderResult>();\n let failedCount = 0;\n const port = PRERENDER_PORT;\n\n // Verify dist/ exists\n const distDir = path.join(root, \"dist\");\n if (!fs.existsSync(distDir)) {\n console.log(\" TPR: Skipping pre-render — dist/ directory not found\");\n return results;\n }\n\n // Start the local production server as a subprocess\n const serverProcess = startLocalServer(root, port);\n\n try {\n await waitForServer(port, SERVER_STARTUP_TIMEOUT);\n\n // Fetch routes in batches to limit concurrency\n for (let i = 0; i < routes.length; i += FETCH_CONCURRENCY) {\n const batch = routes.slice(i, i + FETCH_CONCURRENCY);\n const promises = batch.map(async (routePath) => {\n try {\n const response = await fetch(`http://127.0.0.1:${port}${routePath}`, {\n headers: {\n \"User-Agent\": \"vinext-tpr/1.0\",\n ...(hostDomain ? { Host: hostDomain } : {}),\n },\n redirect: \"manual\", // Don't follow redirects — cache the redirect itself\n });\n\n // Only cache successful responses (2xx and 3xx)\n if (response.status < 400) {\n const html = await response.text();\n const headers: Record<string, string> = {};\n response.headers.forEach((value, key) => {\n // Only keep relevant headers\n if (\n key === \"content-type\" ||\n key === \"cache-control\" ||\n key === \"x-vinext-revalidate\" ||\n key === \"location\"\n ) {\n headers[key] = value;\n }\n });\n results.set(routePath, {\n html,\n status: response.status,\n headers,\n });\n }\n } catch {\n // Skip pages that fail to render — they may depend on\n // request-specific data (cookies, headers, auth) that\n // isn't available during pre-rendering.\n failedCount++;\n }\n });\n\n await Promise.all(promises);\n }\n\n if (failedCount > 0) {\n console.log(` TPR: ${failedCount} page(s) failed to pre-render (skipped)`);\n }\n } finally {\n serverProcess.kill(\"SIGTERM\");\n // Give it a moment to clean up\n await new Promise<void>((resolve) => {\n serverProcess.on(\"exit\", resolve);\n setTimeout(resolve, 2000);\n });\n }\n\n return results;\n}\n\n/**\n * Spawn a subprocess running the vinext production server.\n * Uses the same Node.js binary and resolves prod-server.js relative\n * to the current module (works whether vinext is installed or linked).\n */\nfunction startLocalServer(root: string, port: number): ChildProcess {\n const thisDir = fileURLToPath(new URL(\".\", import.meta.url));\n const prodServerPath = path.resolve(thisDir, \"..\", \"server\", \"prod-server.js\");\n const outDir = path.join(root, \"dist\");\n\n // Escape backslashes for Windows paths inside the JS string\n const escapedProdServer = prodServerPath.replace(/\\\\/g, \"\\\\\\\\\");\n const escapedOutDir = outDir.replace(/\\\\/g, \"\\\\\\\\\");\n\n const script = [\n `import(\"file://${escapedProdServer}\")`,\n `.then(m => m.startProdServer({ port: ${port}, host: \"127.0.0.1\", outDir: \"${escapedOutDir}\" }))`,\n `.catch(e => { console.error(\"[vinext-tpr] Server failed to start:\", e); process.exit(1); });`,\n ].join(\"\");\n\n const proc = spawn(process.execPath, [\"--input-type=module\", \"-e\", script], {\n cwd: root,\n stdio: \"pipe\",\n env: { ...process.env, NODE_ENV: \"production\" },\n });\n\n // Forward server errors to the parent's stderr for debugging\n proc.stderr?.on(\"data\", (chunk: Buffer) => {\n const msg = chunk.toString().trim();\n if (msg) console.error(` [tpr-server] ${msg}`);\n });\n\n return proc;\n}\n\n/** Poll the local server until it responds or the timeout is reached. */\nasync function waitForServer(port: number, timeoutMs: number): Promise<void> {\n const start = Date.now();\n while (Date.now() - start < timeoutMs) {\n try {\n const controller = new AbortController();\n const timer = setTimeout(() => controller.abort(), 2000);\n const response = await fetch(`http://127.0.0.1:${port}/`, {\n redirect: \"manual\",\n signal: controller.signal,\n });\n clearTimeout(timer);\n // Any response means the server is up\n await response.text(); // consume body\n return;\n } catch {\n await new Promise<void>((r) => setTimeout(r, 300));\n }\n }\n throw new Error(`Local production server failed to start within ${timeoutMs / 1000}s`);\n}\n\n// ─── KV Upload ───────────────────────────────────────────────────────────────\n\n/**\n * Upload pre-rendered pages to KV using the Cloudflare REST API.\n * Writes in the same KVCacheEntry format that KVCacheHandler reads\n * at runtime, so ISR serves these entries without any code changes.\n */\nasync function uploadToKV(\n entries: Map<string, PrerenderResult>,\n namespaceId: string,\n accountId: string,\n apiToken: string,\n defaultRevalidateSeconds: number,\n): Promise<void> {\n const now = Date.now();\n\n // Build the bulk write payload\n const pairs: Array<{\n key: string;\n value: string;\n expiration_ttl?: number;\n }> = [];\n\n for (const [routePath, result] of entries) {\n // Determine revalidation window — use the page's revalidate header\n // if present, otherwise fall back to the default\n const revalidateHeader = result.headers[\"x-vinext-revalidate\"];\n const revalidateSeconds =\n revalidateHeader && !isNaN(Number(revalidateHeader))\n ? Number(revalidateHeader)\n : defaultRevalidateSeconds;\n\n const revalidateAt = revalidateSeconds > 0 ? now + revalidateSeconds * 1000 : null;\n\n // KV TTL: 10x the revalidation period, clamped to [60s, 30d]\n // (matches the logic in KVCacheHandler.set)\n const kvTtl =\n revalidateSeconds > 0\n ? Math.max(Math.min(revalidateSeconds * 10, 30 * 24 * 3600), 60)\n : 24 * 3600; // 24h fallback if no revalidation\n\n const entry = {\n value: {\n kind: \"APP_PAGE\" as const,\n html: result.html,\n headers: result.headers,\n status: result.status,\n },\n tags: [] as string[],\n lastModified: now,\n revalidateAt,\n };\n\n pairs.push({\n key: `cache:${routePath}`,\n value: JSON.stringify(entry),\n expiration_ttl: kvTtl,\n });\n }\n\n // Upload in batches (KV bulk API accepts up to 10,000 per request)\n const BATCH_SIZE = 10_000;\n for (let i = 0; i < pairs.length; i += BATCH_SIZE) {\n const batch = pairs.slice(i, i + BATCH_SIZE);\n const response = await fetch(\n `https://api.cloudflare.com/client/v4/accounts/${accountId}/storage/kv/namespaces/${namespaceId}/bulk`,\n {\n method: \"PUT\",\n headers: {\n Authorization: `Bearer ${apiToken}`,\n \"Content-Type\": \"application/json\",\n },\n body: JSON.stringify(batch),\n },\n );\n\n if (!response.ok) {\n const text = await response.text();\n throw new Error(\n `KV bulk upload failed (batch ${Math.floor(i / BATCH_SIZE) + 1}): ${response.status} — ${text}`,\n );\n }\n }\n}\n\n// ─── Main Entry ──────────────────────────────────────────────────────────────\n\n/** Default revalidation TTL for pre-rendered pages (1 hour). */\nconst DEFAULT_REVALIDATE_SECONDS = 3600;\n\n/**\n * Run the TPR pipeline: query traffic, select routes, pre-render, upload.\n *\n * Designed to be called between the build step and wrangler deploy in\n * the `vinext deploy` pipeline. Gracefully skips (never errors) when\n * the prerequisites aren't met.\n */\nexport async function runTPR(options: TPROptions): Promise<TPRResult> {\n const startTime = Date.now();\n const { root, coverage, limit, window: windowHours } = options;\n\n const skip = (reason: string): TPRResult => ({\n totalPaths: 0,\n prerenderedCount: 0,\n coverageAchieved: 0,\n durationMs: Date.now() - startTime,\n skipped: reason,\n });\n\n // ── 1. Check for API token ────────────────────────────────────\n const apiToken = process.env.CLOUDFLARE_API_TOKEN;\n if (!apiToken) {\n return skip(\"no CLOUDFLARE_API_TOKEN set\");\n }\n\n // ── 2. Parse wrangler config ──────────────────────────────────\n const wranglerConfig = parseWranglerConfig(root);\n if (!wranglerConfig) {\n return skip(\"could not parse wrangler config\");\n }\n\n // ── 3. Check for custom domain ────────────────────────────────\n if (!wranglerConfig.customDomain) {\n return skip(\"no custom domain — zone analytics unavailable\");\n }\n\n // ── 4. Check for KV namespace ─────────────────────────────────\n if (!wranglerConfig.kvNamespaceId) {\n return skip(\"no VINEXT_CACHE KV namespace configured\");\n }\n\n // ── 5. Resolve account ID ─────────────────────────────────────\n const accountId = wranglerConfig.accountId ?? (await resolveAccountId(apiToken));\n if (!accountId) {\n return skip(\"could not resolve Cloudflare account ID\");\n }\n\n // ── 6. Resolve zone ID ────────────────────────────────────────\n console.log(` TPR: Analyzing traffic for ${wranglerConfig.customDomain} (last ${windowHours}h)`);\n\n const zoneId = await resolveZoneId(wranglerConfig.customDomain, apiToken);\n if (!zoneId) {\n return skip(`could not resolve zone for ${wranglerConfig.customDomain}`);\n }\n\n // ── 7. Query traffic data ─────────────────────────────────────\n let traffic: TrafficEntry[];\n try {\n traffic = await queryTraffic(zoneId, apiToken, windowHours);\n } catch (err) {\n return skip(`analytics query failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (traffic.length === 0) {\n return skip(\"no traffic data available (first deploy?)\");\n }\n\n // ── 8. Select routes by coverage ──────────────────────────────\n const selection = selectRoutes(traffic, coverage, limit);\n\n console.log(\n ` TPR: ${traffic.length.toLocaleString()} unique paths — ` +\n `${selection.routes.length} pages cover ${Math.round(selection.coveragePercent)}% of traffic`,\n );\n\n if (selection.routes.length === 0) {\n return {\n totalPaths: traffic.length,\n prerenderedCount: 0,\n coverageAchieved: 0,\n durationMs: Date.now() - startTime,\n skipped: \"no pre-renderable routes after filtering\",\n };\n }\n\n // ── 9. Pre-render selected routes ─────────────────────────────\n console.log(` TPR: Pre-rendering ${selection.routes.length} pages...`);\n\n const routePaths = selection.routes.map((r) => r.path);\n let rendered: Map<string, PrerenderResult>;\n try {\n rendered = await prerenderRoutes(routePaths, root, wranglerConfig.customDomain);\n } catch (err) {\n return skip(`pre-rendering failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n if (rendered.size === 0) {\n return {\n totalPaths: traffic.length,\n prerenderedCount: 0,\n coverageAchieved: selection.coveragePercent,\n durationMs: Date.now() - startTime,\n skipped: \"all pages failed to pre-render (request-dependent?)\",\n };\n }\n\n // ── 10. Upload to KV ──────────────────────────────────────────\n try {\n await uploadToKV(\n rendered,\n wranglerConfig.kvNamespaceId,\n accountId,\n apiToken,\n DEFAULT_REVALIDATE_SECONDS,\n );\n } catch (err) {\n return skip(`KV upload failed: ${err instanceof Error ? err.message : String(err)}`);\n }\n\n const durationMs = Date.now() - startTime;\n console.log(\n ` TPR: Pre-rendered ${rendered.size} pages in ${(durationMs / 1000).toFixed(1)}s → KV cache`,\n );\n\n return {\n totalPaths: traffic.length,\n prerenderedCount: rendered.size,\n coverageAchieved: selection.coveragePercent,\n durationMs,\n };\n}\n"]}
@@ -49,6 +49,7 @@ export declare function parseCookies(cookieHeader: string | null): Record<string
49
49
  * Build a RequestContext from a Web Request object.
50
50
  */
51
51
  export declare function requestContextFromRequest(request: Request): RequestContext;
52
+ export declare function normalizeHost(hostHeader: string | null, fallbackHostname: string): string;
52
53
  /**
53
54
  * Unpack `x-middleware-request-*` headers from the collected middleware
54
55
  * response headers into the actual request, and strip all `x-middleware-*`
@@ -97,6 +98,33 @@ export declare function matchConfigPattern(pathname: string, pattern: string): R
97
98
  * `ctx` provides the request context (cookies, headers, query, host) used
98
99
  * to evaluate has/missing conditions. Next.js always has request context
99
100
  * when evaluating redirects, so this parameter is required.
101
+ *
102
+ * ## Performance
103
+ *
104
+ * Rules with a locale-capture-group prefix (the dominant pattern in large
105
+ * Next.js apps — e.g. `/:locale(en|es|fr|...)?/some-path`) are handled via
106
+ * a pre-built index. Instead of running exec() on each locale regex
107
+ * individually, we:
108
+ *
109
+ * 1. Strip the optional locale prefix from the pathname with one cheap
110
+ * string-slice check (no regex exec on the hot path).
111
+ * 2. Look up the stripped suffix in a Map<suffix, entry[]>.
112
+ * 3. For each matching entry, validate the captured locale string against
113
+ * a small, anchored alternation regex.
114
+ *
115
+ * This reduces the per-request cost from O(n × regex) to O(1) map lookup +
116
+ * O(matches × tiny-regex), eliminating the ~2992ms self-time reported in
117
+ * profiles for apps with 63+ locale-prefixed rules.
118
+ *
119
+ * Rules that don't fit the locale-static pattern fall back to the original
120
+ * linear matchConfigPattern scan.
121
+ *
122
+ * ## Ordering invariant
123
+ *
124
+ * First match wins, preserving the original redirect array order. When a
125
+ * locale-static fast-path match is found at position N, all linear rules with
126
+ * an original index < N are checked via matchConfigPattern first — they are
127
+ * few in practice (typically zero) so this is not a hot-path concern.
100
128
  */
101
129
  export declare function matchRedirect(pathname: string, redirects: NextRedirect[], ctx: RequestContext): {
102
130
  destination: string;
@@ -1 +1 @@
1
- {"version":3,"file":"config-matchers.d.ts","sourceRoot":"","sources":["../../src/config/config-matchers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAc5F;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAsGpD;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAczE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAsDzD;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,KAAK,EAAE,eAAe,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAWhF;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,CAQ1E;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,6BAA6B,CAC3C,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,EACpD,OAAO,EAAE,OAAO,GACf;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,cAAc,CAAA;CAAE,CA8BpD;AAmDD;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,YAAY,EAAE,GAAG,SAAS,EAC/B,OAAO,EAAE,YAAY,EAAE,GAAG,SAAS,EACnC,GAAG,EAAE,cAAc,GAClB,OAAO,CAYT;AAsBD;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACd,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAsG/B;AAED;;;;;;;GAOG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,YAAY,EAAE,EACzB,GAAG,EAAE,cAAc,GAClB;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAuBpD;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,WAAW,EAAE,EACvB,GAAG,EAAE,cAAc,GAClB,MAAM,GAAG,IAAI,CAuBf;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAWxD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAElD;AAED;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,QAAQ,CAAC,CAoFnB;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,UAAU,EAAE,EACrB,GAAG,EAAE,cAAc,GAClB,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAevC"}
1
+ {"version":3,"file":"config-matchers.d.ts","sourceRoot":"","sources":["../../src/config/config-matchers.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,WAAW,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AA6K5F;;;;;;;;;GASG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAsGpD;AAED;;;;;GAKG;AACH,wBAAgB,UAAU,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAczE;AAED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,CAgEzD;AAED;;;GAGG;AACH,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAChC,KAAK,EAAE,eAAe,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,YAAY,EAAE,MAAM,GAAG,IAAI,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAWhF;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,OAAO,EAAE,OAAO,GAAG,cAAc,CAQ1E;AAED,wBAAgB,aAAa,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,EAAE,gBAAgB,EAAE,MAAM,GAAG,MAAM,CAGzF;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,6BAA6B,CAC3C,iBAAiB,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC,EACpD,OAAO,EAAE,OAAO,GACf;IAAE,OAAO,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,cAAc,CAAA;CAAE,CAqBpD;AAiED;;;;;;GAMG;AACH,wBAAgB,kBAAkB,CAChC,GAAG,EAAE,YAAY,EAAE,GAAG,SAAS,EAC/B,OAAO,EAAE,YAAY,EAAE,GAAG,SAAS,EACnC,GAAG,EAAE,cAAc,GAClB,OAAO,CAYT;AAsBD;;;;;;;;;;GAUG;AACH,wBAAgB,kBAAkB,CAChC,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,MAAM,GACd,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAAI,CAgH/B;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,MAAM,EAChB,SAAS,EAAE,YAAY,EAAE,EACzB,GAAG,EAAE,cAAc,GAClB;IAAE,WAAW,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAA;CAAE,GAAG,IAAI,CAiGpD;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,QAAQ,EAAE,WAAW,EAAE,EACvB,GAAG,EAAE,cAAc,GAClB,MAAM,GAAG,IAAI,CAgBf;AA+BD;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,CAWxD;AAED;;;;GAIG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAElD;AAED;;;;;;;;GAQG;AACH,wBAAsB,oBAAoB,CACxC,OAAO,EAAE,OAAO,EAChB,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,QAAQ,CAAC,CAqFnB;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAC1B,QAAQ,EAAE,MAAM,EAChB,OAAO,EAAE,UAAU,EAAE,EACrB,GAAG,EAAE,cAAc,GAClB,KAAK,CAAC;IAAE,GAAG,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC,CAqBvC"}