@textjs/app 0.0.0-stage → 1.0.3

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 (335) hide show
  1. package/README.md +34 -2
  2. package/bin/text.js +29 -0
  3. package/bin/text.ts +13 -0
  4. package/dist/.tsbuildinfo +1 -0
  5. package/dist/cli/build.d.ts +39 -0
  6. package/dist/cli/build.d.ts.map +1 -0
  7. package/dist/cli/build.js +157 -0
  8. package/dist/cli/build.js.map +1 -0
  9. package/dist/cli/config.d.ts +22 -0
  10. package/dist/cli/config.d.ts.map +1 -0
  11. package/dist/cli/config.js +73 -0
  12. package/dist/cli/config.js.map +1 -0
  13. package/dist/cli/dev.d.ts +12 -0
  14. package/dist/cli/dev.d.ts.map +1 -0
  15. package/dist/cli/dev.js +49 -0
  16. package/dist/cli/dev.js.map +1 -0
  17. package/dist/cli/index.d.ts +26 -0
  18. package/dist/cli/index.d.ts.map +1 -0
  19. package/dist/cli/index.js +192 -0
  20. package/dist/cli/index.js.map +1 -0
  21. package/dist/cli/run.d.ts +31 -0
  22. package/dist/cli/run.d.ts.map +1 -0
  23. package/dist/cli/run.js +111 -0
  24. package/dist/cli/run.js.map +1 -0
  25. package/dist/cli/start.d.ts +14 -0
  26. package/dist/cli/start.d.ts.map +1 -0
  27. package/dist/cli/start.js +183 -0
  28. package/dist/cli/start.js.map +1 -0
  29. package/dist/cli/ui.d.ts +35 -0
  30. package/dist/cli/ui.d.ts.map +1 -0
  31. package/dist/cli/ui.js +73 -0
  32. package/dist/cli/ui.js.map +1 -0
  33. package/dist/client.d.ts +34 -0
  34. package/dist/client.d.ts.map +1 -0
  35. package/dist/client.js +93 -0
  36. package/dist/client.js.map +1 -0
  37. package/dist/config.d.ts +143 -0
  38. package/dist/config.d.ts.map +1 -0
  39. package/dist/config.js +28 -0
  40. package/dist/config.js.map +1 -0
  41. package/dist/context.d.ts +44 -0
  42. package/dist/context.d.ts.map +1 -0
  43. package/dist/context.js +26 -0
  44. package/dist/context.js.map +1 -0
  45. package/dist/dev-errors.d.ts +68 -0
  46. package/dist/dev-errors.d.ts.map +1 -0
  47. package/dist/dev-errors.js +121 -0
  48. package/dist/dev-errors.js.map +1 -0
  49. package/dist/devtools.d.ts +64 -0
  50. package/dist/devtools.d.ts.map +1 -0
  51. package/dist/devtools.js +186 -0
  52. package/dist/devtools.js.map +1 -0
  53. package/dist/frame-mirror.d.ts +19 -0
  54. package/dist/frame-mirror.d.ts.map +1 -0
  55. package/dist/frame-mirror.js +141 -0
  56. package/dist/frame-mirror.js.map +1 -0
  57. package/dist/frame-protocol.d.ts +175 -0
  58. package/dist/frame-protocol.d.ts.map +1 -0
  59. package/dist/frame-protocol.js +369 -0
  60. package/dist/frame-protocol.js.map +1 -0
  61. package/dist/history.d.ts +54 -0
  62. package/dist/history.d.ts.map +1 -0
  63. package/dist/history.js +152 -0
  64. package/dist/history.js.map +1 -0
  65. package/dist/index.d.ts +9 -0
  66. package/dist/index.d.ts.map +1 -0
  67. package/dist/index.js +8 -0
  68. package/dist/index.js.map +1 -0
  69. package/dist/link.d.ts +32 -0
  70. package/dist/link.d.ts.map +1 -0
  71. package/dist/link.js +54 -0
  72. package/dist/link.js.map +1 -0
  73. package/dist/live-player.d.ts +83 -0
  74. package/dist/live-player.d.ts.map +1 -0
  75. package/dist/live-player.js +413 -0
  76. package/dist/live-player.js.map +1 -0
  77. package/dist/live-protocol.d.ts +132 -0
  78. package/dist/live-protocol.d.ts.map +1 -0
  79. package/dist/live-protocol.js +278 -0
  80. package/dist/live-protocol.js.map +1 -0
  81. package/dist/live-stub.d.ts +10 -0
  82. package/dist/live-stub.d.ts.map +1 -0
  83. package/dist/live-stub.js +32 -0
  84. package/dist/live-stub.js.map +1 -0
  85. package/dist/mcp/cli.d.ts +22 -0
  86. package/dist/mcp/cli.d.ts.map +1 -0
  87. package/dist/mcp/cli.js +177 -0
  88. package/dist/mcp/cli.js.map +1 -0
  89. package/dist/mcp/frame.d.ts +58 -0
  90. package/dist/mcp/frame.d.ts.map +1 -0
  91. package/dist/mcp/frame.js +169 -0
  92. package/dist/mcp/frame.js.map +1 -0
  93. package/dist/mcp/index.d.ts +15 -0
  94. package/dist/mcp/index.d.ts.map +1 -0
  95. package/dist/mcp/index.js +15 -0
  96. package/dist/mcp/index.js.map +1 -0
  97. package/dist/mcp/inspect.d.ts +95 -0
  98. package/dist/mcp/inspect.d.ts.map +1 -0
  99. package/dist/mcp/inspect.js +386 -0
  100. package/dist/mcp/inspect.js.map +1 -0
  101. package/dist/mcp/keys.d.ts +20 -0
  102. package/dist/mcp/keys.d.ts.map +1 -0
  103. package/dist/mcp/keys.js +87 -0
  104. package/dist/mcp/keys.js.map +1 -0
  105. package/dist/mcp/logs.d.ts +63 -0
  106. package/dist/mcp/logs.d.ts.map +1 -0
  107. package/dist/mcp/logs.js +217 -0
  108. package/dist/mcp/logs.js.map +1 -0
  109. package/dist/mcp/server.d.ts +64 -0
  110. package/dist/mcp/server.d.ts.map +1 -0
  111. package/dist/mcp/server.js +485 -0
  112. package/dist/mcp/server.js.map +1 -0
  113. package/dist/mcp/session.d.ts +230 -0
  114. package/dist/mcp/session.d.ts.map +1 -0
  115. package/dist/mcp/session.js +742 -0
  116. package/dist/mcp/session.js.map +1 -0
  117. package/dist/navigation-errors.d.ts +28 -0
  118. package/dist/navigation-errors.d.ts.map +1 -0
  119. package/dist/navigation-errors.js +43 -0
  120. package/dist/navigation-errors.js.map +1 -0
  121. package/dist/navigation.d.ts +21 -0
  122. package/dist/navigation.d.ts.map +1 -0
  123. package/dist/navigation.js +33 -0
  124. package/dist/navigation.js.map +1 -0
  125. package/dist/router.d.ts +76 -0
  126. package/dist/router.d.ts.map +1 -0
  127. package/dist/router.js +296 -0
  128. package/dist/router.js.map +1 -0
  129. package/dist/routing.d.ts +101 -0
  130. package/dist/routing.d.ts.map +1 -0
  131. package/dist/routing.js +286 -0
  132. package/dist/routing.js.map +1 -0
  133. package/dist/server/adapters/build-step.d.ts +34 -0
  134. package/dist/server/adapters/build-step.d.ts.map +1 -0
  135. package/dist/server/adapters/build-step.js +112 -0
  136. package/dist/server/adapters/build-step.js.map +1 -0
  137. package/dist/server/adapters/bun.d.ts +8 -0
  138. package/dist/server/adapters/bun.d.ts.map +1 -0
  139. package/dist/server/adapters/bun.js +89 -0
  140. package/dist/server/adapters/bun.js.map +1 -0
  141. package/dist/server/adapters/bundle.d.ts +21 -0
  142. package/dist/server/adapters/bundle.d.ts.map +1 -0
  143. package/dist/server/adapters/bundle.js +86 -0
  144. package/dist/server/adapters/bundle.js.map +1 -0
  145. package/dist/server/adapters/cloudflare.d.ts +29 -0
  146. package/dist/server/adapters/cloudflare.d.ts.map +1 -0
  147. package/dist/server/adapters/cloudflare.js +105 -0
  148. package/dist/server/adapters/cloudflare.js.map +1 -0
  149. package/dist/server/adapters/index.d.ts +10 -0
  150. package/dist/server/adapters/index.d.ts.map +1 -0
  151. package/dist/server/adapters/index.js +24 -0
  152. package/dist/server/adapters/index.js.map +1 -0
  153. package/dist/server/adapters/runtime-node.d.ts +16 -0
  154. package/dist/server/adapters/runtime-node.d.ts.map +1 -0
  155. package/dist/server/adapters/runtime-node.js +29 -0
  156. package/dist/server/adapters/runtime-node.js.map +1 -0
  157. package/dist/server/adapters/runtime.d.ts +42 -0
  158. package/dist/server/adapters/runtime.d.ts.map +1 -0
  159. package/dist/server/adapters/runtime.js +115 -0
  160. package/dist/server/adapters/runtime.js.map +1 -0
  161. package/dist/server/adapters/shared.d.ts +66 -0
  162. package/dist/server/adapters/shared.d.ts.map +1 -0
  163. package/dist/server/adapters/shared.js +124 -0
  164. package/dist/server/adapters/shared.js.map +1 -0
  165. package/dist/server/adapters/static.d.ts +9 -0
  166. package/dist/server/adapters/static.d.ts.map +1 -0
  167. package/dist/server/adapters/static.js +70 -0
  168. package/dist/server/adapters/static.js.map +1 -0
  169. package/dist/server/adapters/vercel.d.ts +43 -0
  170. package/dist/server/adapters/vercel.d.ts.map +1 -0
  171. package/dist/server/adapters/vercel.js +119 -0
  172. package/dist/server/adapters/vercel.js.map +1 -0
  173. package/dist/server/bun-jsx.d.ts +18 -0
  174. package/dist/server/bun-jsx.d.ts.map +1 -0
  175. package/dist/server/bun-jsx.js +56 -0
  176. package/dist/server/bun-jsx.js.map +1 -0
  177. package/dist/server/cache-route.d.ts +38 -0
  178. package/dist/server/cache-route.d.ts.map +1 -0
  179. package/dist/server/cache-route.js +109 -0
  180. package/dist/server/cache-route.js.map +1 -0
  181. package/dist/server/cache.d.ts +131 -0
  182. package/dist/server/cache.d.ts.map +1 -0
  183. package/dist/server/cache.js +409 -0
  184. package/dist/server/cache.js.map +1 -0
  185. package/dist/server/frames.d.ts +45 -0
  186. package/dist/server/frames.d.ts.map +1 -0
  187. package/dist/server/frames.js +294 -0
  188. package/dist/server/frames.js.map +1 -0
  189. package/dist/server/http.d.ts +80 -0
  190. package/dist/server/http.d.ts.map +1 -0
  191. package/dist/server/http.js +453 -0
  192. package/dist/server/http.js.map +1 -0
  193. package/dist/server/jsx-dev-runtime.d.ts +5 -0
  194. package/dist/server/jsx-dev-runtime.d.ts.map +1 -0
  195. package/dist/server/jsx-dev-runtime.js +6 -0
  196. package/dist/server/jsx-dev-runtime.js.map +1 -0
  197. package/dist/server/jsx-runtime.d.ts +10 -0
  198. package/dist/server/jsx-runtime.d.ts.map +1 -0
  199. package/dist/server/jsx-runtime.js +11 -0
  200. package/dist/server/jsx-runtime.js.map +1 -0
  201. package/dist/server/live-app.d.ts +87 -0
  202. package/dist/server/live-app.d.ts.map +1 -0
  203. package/dist/server/live-app.js +307 -0
  204. package/dist/server/live-app.js.map +1 -0
  205. package/dist/server/live-build.d.ts +70 -0
  206. package/dist/server/live-build.d.ts.map +1 -0
  207. package/dist/server/live-build.js +222 -0
  208. package/dist/server/live-build.js.map +1 -0
  209. package/dist/server/live-dev.d.ts +62 -0
  210. package/dist/server/live-dev.d.ts.map +1 -0
  211. package/dist/server/live-dev.js +313 -0
  212. package/dist/server/live-dev.js.map +1 -0
  213. package/dist/server/live-manifest.d.ts +23 -0
  214. package/dist/server/live-manifest.d.ts.map +1 -0
  215. package/dist/server/live-manifest.js +7 -0
  216. package/dist/server/live-manifest.js.map +1 -0
  217. package/dist/server/live-prod.d.ts +30 -0
  218. package/dist/server/live-prod.d.ts.map +1 -0
  219. package/dist/server/live-prod.js +97 -0
  220. package/dist/server/live-prod.js.map +1 -0
  221. package/dist/server/live-routes.d.ts +72 -0
  222. package/dist/server/live-routes.d.ts.map +1 -0
  223. package/dist/server/live-routes.js +274 -0
  224. package/dist/server/live-routes.js.map +1 -0
  225. package/dist/server/live-web.d.ts +16 -0
  226. package/dist/server/live-web.d.ts.map +1 -0
  227. package/dist/server/live-web.js +159 -0
  228. package/dist/server/live-web.js.map +1 -0
  229. package/dist/server/live.d.ts +138 -0
  230. package/dist/server/live.d.ts.map +1 -0
  231. package/dist/server/live.js +542 -0
  232. package/dist/server/live.js.map +1 -0
  233. package/dist/server/middleware-node.d.ts +48 -0
  234. package/dist/server/middleware-node.d.ts.map +1 -0
  235. package/dist/server/middleware-node.js +167 -0
  236. package/dist/server/middleware-node.js.map +1 -0
  237. package/dist/server/middleware-runtime.d.ts +97 -0
  238. package/dist/server/middleware-runtime.d.ts.map +1 -0
  239. package/dist/server/middleware-runtime.js +447 -0
  240. package/dist/server/middleware-runtime.js.map +1 -0
  241. package/dist/server/middleware.d.ts +145 -0
  242. package/dist/server/middleware.d.ts.map +1 -0
  243. package/dist/server/middleware.js +365 -0
  244. package/dist/server/middleware.js.map +1 -0
  245. package/dist/server/ppr.d.ts +22 -0
  246. package/dist/server/ppr.d.ts.map +1 -0
  247. package/dist/server/ppr.js +58 -0
  248. package/dist/server/ppr.js.map +1 -0
  249. package/dist/server/scan.d.ts +48 -0
  250. package/dist/server/scan.d.ts.map +1 -0
  251. package/dist/server/scan.js +161 -0
  252. package/dist/server/scan.js.map +1 -0
  253. package/dist/server/server-components.d.ts +65 -0
  254. package/dist/server/server-components.d.ts.map +1 -0
  255. package/dist/server/server-components.js +215 -0
  256. package/dist/server/server-components.js.map +1 -0
  257. package/dist/terminal.d.ts +5 -0
  258. package/dist/terminal.d.ts.map +1 -0
  259. package/dist/terminal.js +5 -0
  260. package/dist/terminal.js.map +1 -0
  261. package/dist/testing.d.ts +5 -0
  262. package/dist/testing.d.ts.map +1 -0
  263. package/dist/testing.js +5 -0
  264. package/dist/testing.js.map +1 -0
  265. package/dist/vite-plugin.d.ts +51 -0
  266. package/dist/vite-plugin.d.ts.map +1 -0
  267. package/dist/vite-plugin.js +403 -0
  268. package/dist/vite-plugin.js.map +1 -0
  269. package/package.json +130 -4
  270. package/src/cli/build.ts +182 -0
  271. package/src/cli/config.ts +72 -0
  272. package/src/cli/dev.ts +54 -0
  273. package/src/cli/index.ts +185 -0
  274. package/src/cli/run.ts +124 -0
  275. package/src/cli/start.ts +194 -0
  276. package/src/cli/ui.ts +93 -0
  277. package/src/client.tsx +122 -0
  278. package/src/config.ts +155 -0
  279. package/src/context.ts +69 -0
  280. package/src/dev-errors.ts +167 -0
  281. package/src/devtools.tsx +419 -0
  282. package/src/frame-mirror.ts +145 -0
  283. package/src/frame-protocol.ts +435 -0
  284. package/src/history.ts +201 -0
  285. package/src/index.ts +9 -0
  286. package/src/link.tsx +108 -0
  287. package/src/live-player.ts +449 -0
  288. package/src/live-protocol.ts +317 -0
  289. package/src/live-stub.tsx +32 -0
  290. package/src/mcp/cli.ts +188 -0
  291. package/src/mcp/frame.ts +224 -0
  292. package/src/mcp/index.ts +15 -0
  293. package/src/mcp/inspect.ts +445 -0
  294. package/src/mcp/keys.ts +86 -0
  295. package/src/mcp/logs.ts +225 -0
  296. package/src/mcp/server.ts +595 -0
  297. package/src/mcp/session.ts +821 -0
  298. package/src/navigation-errors.ts +60 -0
  299. package/src/navigation.ts +44 -0
  300. package/src/router.tsx +497 -0
  301. package/src/routing.ts +344 -0
  302. package/src/server/adapters/build-step.ts +135 -0
  303. package/src/server/adapters/bun.ts +92 -0
  304. package/src/server/adapters/bundle.ts +97 -0
  305. package/src/server/adapters/cloudflare.ts +111 -0
  306. package/src/server/adapters/index.ts +29 -0
  307. package/src/server/adapters/runtime-node.ts +34 -0
  308. package/src/server/adapters/runtime.ts +145 -0
  309. package/src/server/adapters/shared.ts +159 -0
  310. package/src/server/adapters/static.ts +68 -0
  311. package/src/server/adapters/vercel.ts +149 -0
  312. package/src/server/bun-jsx.ts +65 -0
  313. package/src/server/cache-route.ts +139 -0
  314. package/src/server/cache.ts +498 -0
  315. package/src/server/frames.ts +326 -0
  316. package/src/server/http.ts +467 -0
  317. package/src/server/jsx-dev-runtime.ts +9 -0
  318. package/src/server/jsx-runtime.ts +13 -0
  319. package/src/server/live-app.tsx +374 -0
  320. package/src/server/live-build.ts +258 -0
  321. package/src/server/live-dev.ts +357 -0
  322. package/src/server/live-manifest.ts +25 -0
  323. package/src/server/live-prod.ts +120 -0
  324. package/src/server/live-routes.ts +298 -0
  325. package/src/server/live-web.ts +170 -0
  326. package/src/server/live.ts +551 -0
  327. package/src/server/middleware-node.ts +168 -0
  328. package/src/server/middleware-runtime.ts +478 -0
  329. package/src/server/middleware.ts +451 -0
  330. package/src/server/ppr.ts +62 -0
  331. package/src/server/scan.ts +190 -0
  332. package/src/server/server-components.ts +224 -0
  333. package/src/terminal.ts +5 -0
  334. package/src/testing.ts +5 -0
  335. package/src/vite-plugin.ts +413 -0
@@ -0,0 +1,109 @@
1
+ /**
2
+ * Full-route caching for `app/**\/route.ts` handlers: `export const dynamic`
3
+ * and `export const revalidate` (Next.js route segment config).
4
+ *
5
+ * - `dynamic = "force-static"` or `revalidate = N` (N > 0): `GET` / `HEAD` are
6
+ * cached per pathname with stale-while-revalidate. The handler receives a
7
+ * build-time-like request (no query string, headers, cookies or body).
8
+ * - Default, `"force-dynamic"` or `revalidate = 0`: run on every request.
9
+ */
10
+ import { txError } from "@textjs/core";
11
+ import { markGeneratedCacheControl, PRIVATE_CACHE_CONTROL, runRouteHandler } from "./http.js";
12
+ import { __validateRevalidate, EXPIRE_SECONDS, getOrCompute, implicitTags, normalizeCachePath, runInCacheScope } from "./cache.js";
13
+ export const CACHE_HEADER = "x-textjs-cache";
14
+ // Next.js's `"error"` (static, failing on request reads) is rejected: Text.js can't detect request reads.
15
+ const DYNAMIC_VALUES = new Set(["auto", "force-static", "force-dynamic"]);
16
+ /** Read and validate a route module's `dynamic` / `revalidate` exports. */
17
+ export function routeCacheConfig(mod, where = "route handler") {
18
+ const dynamic = mod.dynamic ?? "auto";
19
+ if (typeof dynamic !== "string" || !DYNAMIC_VALUES.has(dynamic))
20
+ throw txError("TX031", JSON.stringify(dynamic), where);
21
+ const revalidate = __validateRevalidate(mod.revalidate, where);
22
+ if (dynamic === "force-dynamic" || revalidate === 0)
23
+ return { static: false, revalidate: 0 };
24
+ if (dynamic === "force-static")
25
+ return { static: true, revalidate };
26
+ return { static: typeof revalidate === "number" && revalidate > 0, revalidate };
27
+ }
28
+ function toBase64(bytes) {
29
+ let s = "";
30
+ for (let i = 0; i < bytes.length; i += 0x8000)
31
+ s += String.fromCharCode(...bytes.subarray(i, i + 0x8000));
32
+ return btoa(s);
33
+ }
34
+ function fromBase64(b64) {
35
+ const s = atob(b64);
36
+ const out = new Uint8Array(new ArrayBuffer(s.length));
37
+ for (let i = 0; i < s.length; i++)
38
+ out[i] = s.charCodeAt(i);
39
+ return out;
40
+ }
41
+ /** The request a static handler sees: same origin and path, nothing request-specific. */
42
+ export function staticRequest(request) {
43
+ const url = new URL(request.url);
44
+ return new Request(new URL(url.pathname, url.origin), { method: "GET" });
45
+ }
46
+ /** `Cache-Control` for a cached route (Next.js: `s-maxage=N, stale-while-revalidate=<expire - N>`). */
47
+ export function cacheControlFor(revalidate) {
48
+ if (revalidate === false || !Number.isFinite(revalidate))
49
+ return `s-maxage=${EXPIRE_SECONDS}`;
50
+ const seconds = Math.min(EXPIRE_SECONDS, Math.max(1, Math.ceil(revalidate)));
51
+ return `s-maxage=${seconds}, stale-while-revalidate=${EXPIRE_SECONDS - seconds}`;
52
+ }
53
+ /** Responses a shared cache must never replay to other visitors. */
54
+ export function isUncacheableResponse(res) {
55
+ if (res.status >= 500)
56
+ return true;
57
+ if (res.headers.has("set-cookie"))
58
+ return true;
59
+ const cc = (res.headers.get("cache-control") ?? "").toLowerCase();
60
+ return /(?:^|,)\s*(?:private|no-store)\b/.test(cc);
61
+ }
62
+ /** Run a route handler with route segment config caching. */
63
+ export async function runRouteHandlerWithCache(mod, request, ctx) {
64
+ const pathname = normalizeCachePath(ctx.pathname ?? new URL(request.url).pathname);
65
+ const config = routeCacheConfig(mod, `${ctx.pattern}/route`);
66
+ const method = request.method.toUpperCase();
67
+ const tags = implicitTags(pathname, ctx.pattern);
68
+ const scope = () => ({ tags: new Set(tags), revalidate: config.revalidate, implicitTags: tags, waitUntil: ctx.waitUntil });
69
+ if (!config.static || (method !== "GET" && method !== "HEAD") || typeof mod.GET !== "function") {
70
+ return runInCacheScope(scope(), () => runRouteHandler(mod, request, ctx.params));
71
+ }
72
+ const input = staticRequest(request);
73
+ if (ctx.cache === false) {
74
+ const res = await runInCacheScope(scope(), () => runRouteHandler(mod, input, ctx.params));
75
+ return method === "HEAD" ? new Response(null, { status: res.status, statusText: res.statusText, headers: res.headers }) : res;
76
+ }
77
+ const resolved = await getOrCompute(`route:${pathname}`, async () => {
78
+ const s = scope();
79
+ const res = await runInCacheScope(s, () => runRouteHandler(mod, input, ctx.params));
80
+ if ((res.headers.get("content-type") ?? "").includes("text/event-stream")) {
81
+ await res.body?.cancel().catch(() => undefined);
82
+ throw new Error(`Route ${ctx.pattern} is static (dynamic = "force-static" / revalidate) but returned a text/event-stream response. Streaming route handlers must be dynamic: export const dynamic = "force-dynamic".`);
83
+ }
84
+ const body = new Uint8Array(await res.arrayBuffer());
85
+ const value = { status: res.status, statusText: res.statusText, headers: [...res.headers], body: toBase64(body) };
86
+ // Server errors, cookies and private responses are returned to this request but never stored.
87
+ return { value, tags: [...s.tags], revalidate: isUncacheableResponse(res) ? 0 : s.revalidate };
88
+ }, ctx.waitUntil);
89
+ const stored = resolved.entry.value;
90
+ const headers = new Headers();
91
+ for (const [k, v] of stored.headers)
92
+ headers.append(k, v);
93
+ let generated = false;
94
+ if (resolved.entry.revalidate === 0) {
95
+ if (stored.status >= 500 || !headers.has("cache-control"))
96
+ headers.set("cache-control", PRIVATE_CACHE_CONTROL);
97
+ }
98
+ else {
99
+ headers.set(CACHE_HEADER, resolved.status);
100
+ if (!headers.has("cache-control")) {
101
+ headers.set("cache-control", cacheControlFor(resolved.entry.revalidate));
102
+ generated = true;
103
+ }
104
+ }
105
+ const body = method === "HEAD" || stored.status === 204 || stored.status === 304 ? null : fromBase64(stored.body);
106
+ const response = new Response(body, { status: stored.status, statusText: stored.statusText, headers });
107
+ return generated ? markGeneratedCacheControl(response) : response;
108
+ }
109
+ //# sourceMappingURL=cache-route.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-route.js","sourceRoot":"","sources":["../../src/server/cache-route.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,131 @@
1
+ /**
2
+ * `@textjs/app/cache` — the data cache: `unstable_cache()` / `cache()` with
3
+ * tags and time-based revalidation, `revalidateTag()` / `revalidatePath()`,
4
+ * stale-while-revalidate semantics and a pluggable store (in-memory by default).
5
+ *
6
+ * State lives on `globalThis` so the CLI, the route-handler bundle and user
7
+ * code share one cache even when they load separate copies of this module.
8
+ */
9
+ type Awaitable<T> = T | Promise<T>;
10
+ export interface CacheEntry {
11
+ /** Serialized value (JSON for the data cache, `{ status, headers, body }` for routes). */
12
+ value: unknown;
13
+ /** Epoch ms when the computation that produced `value` started. */
14
+ lastModified: number;
15
+ /** Seconds until the entry turns stale; `false` = only on-demand revalidation. */
16
+ revalidate: number | false;
17
+ tags: string[];
18
+ }
19
+ export interface TagState {
20
+ /** Latest `revalidateTag()` / `revalidatePath()` that expired one of the tags (epoch ms, `0` = never). */
21
+ expiredAt: number;
22
+ /** Latest `revalidateTag(tag, "max")` that marked one of the tags stale. */
23
+ staleAt: number;
24
+ }
25
+ /**
26
+ * A cache backend. Implement this to share the cache across instances
27
+ * (Redis, Upstash, Workers KV, …) and point `cacheHandler` in
28
+ * `text.config.ts` at a module that default-exports it.
29
+ */
30
+ export interface CacheStore {
31
+ get(key: string): Awaitable<CacheEntry | undefined>;
32
+ set(key: string, entry: CacheEntry): Awaitable<void>;
33
+ delete(key: string): Awaitable<void>;
34
+ /** Record that `tags` were revalidated at `at`. */
35
+ markTags(tags: readonly string[], mode: "expire" | "stale", at: number): Awaitable<void>;
36
+ getTagState(tags: readonly string[]): Awaitable<TagState>;
37
+ }
38
+ export interface MemoryCacheStoreOptions {
39
+ /** Maximum entries before the least recently used are evicted (default `1000`). */
40
+ maxEntries?: number;
41
+ /** Approximate maximum size in bytes (default 50 MB). */
42
+ maxSize?: number;
43
+ /** Maximum remembered tag revalidations before the oldest are dropped (default `10000`). */
44
+ maxTags?: number;
45
+ }
46
+ /** The default store: a per-process LRU map. */
47
+ export declare class MemoryCacheStore implements CacheStore {
48
+ #private;
49
+ constructor(options?: MemoryCacheStoreOptions);
50
+ get size(): number;
51
+ get(key: string): CacheEntry | undefined;
52
+ set(key: string, entry: CacheEntry): void;
53
+ delete(key: string): void;
54
+ markTags(tags: readonly string[], mode: "expire" | "stale", at: number): void;
55
+ getTagState(tags: readonly string[]): TagState;
56
+ clear(): void;
57
+ }
58
+ /** Collects tags and the shortest `revalidate` of everything cached while a route handler runs. */
59
+ export interface CacheScope {
60
+ tags: Set<string>;
61
+ revalidate: number | false;
62
+ /** Path-derived tags (`revalidatePath`) inherited by data cached inside the scope. */
63
+ implicitTags: readonly string[];
64
+ waitUntil?: (promise: Promise<unknown>) => void;
65
+ }
66
+ /** Strictly increasing timestamps, so a revalidation in the same millisecond as a write still wins. */
67
+ export declare function cacheNow(): number;
68
+ /** Replace the cache backend (`text.config.ts` → `cacheHandler` does this for you). */
69
+ export declare function setCacheStore(store: CacheStore): void;
70
+ export declare function getCacheStore(): CacheStore;
71
+ /** @internal Test hooks: a fake clock, error sink, or a fresh in-memory store. */
72
+ export declare function __configureCache(options: {
73
+ clock?: () => number;
74
+ onError?: (error: unknown, key: string) => void;
75
+ reset?: boolean;
76
+ }): void;
77
+ export declare function currentCacheScope(): CacheScope | undefined;
78
+ export declare function runInCacheScope<T>(scope: CacheScope, fn: () => T): T;
79
+ /** Entries older than a year are regenerated in the foreground (Next.js `expireTime`). */
80
+ export declare const EXPIRE_SECONDS = 31536000;
81
+ export type CacheStatus = "HIT" | "STALE" | "MISS";
82
+ interface ResolvedEntry {
83
+ entry: CacheEntry;
84
+ status: CacheStatus;
85
+ }
86
+ export interface Computed {
87
+ value: unknown;
88
+ tags: readonly string[];
89
+ /** `0` = don't store (dynamic). */
90
+ revalidate: number | false;
91
+ }
92
+ export declare function classifyEntry(entry: CacheEntry, tags: TagState, now: number): "fresh" | "stale" | "expired";
93
+ /**
94
+ * Read `key`, serving stale entries while one deduplicated background
95
+ * refresh runs (registered with `waitUntil` on serverless runtimes).
96
+ */
97
+ export declare function getOrCompute(key: string, fn: () => Promise<Computed>, waitUntil?: (p: Promise<unknown>) => void): Promise<ResolvedEntry>;
98
+ export interface CacheOptions {
99
+ /** Tags for `revalidateTag()`. */
100
+ tags?: string[];
101
+ /** Seconds before the value turns stale (served while refreshing). `false` (default) = until revalidated. */
102
+ revalidate?: number | false;
103
+ }
104
+ declare function validateRevalidate(value: unknown, where: string): number | false;
105
+ /** A collision-free cache key for arguments (`undefined` ≠ `null`, `Map` / `Set` / `Date` / `bigint` by value). */
106
+ export declare function serializeCacheArgs(value: unknown, seen?: Set<object>): string;
107
+ /**
108
+ * Cache the result of an async function (Next.js `unstable_cache`). Results
109
+ * are JSON-serialized, so every call returns a fresh copy. The cache key is
110
+ * `keyParts` (or the function source) plus the JSON-encoded arguments.
111
+ */
112
+ export declare function unstable_cache<Args extends unknown[], R>(fn: (...args: Args) => Promise<R> | R, keyParts?: readonly string[], options?: CacheOptions): (...args: Args) => Promise<R>;
113
+ /** `unstable_cache` with an options object: `cache(fn, { key, tags, revalidate })`. */
114
+ export declare function cache<Args extends unknown[], R>(fn: (...args: Args) => Promise<R> | R, options?: CacheOptions & {
115
+ key?: string | string[];
116
+ }): (...args: Args) => Promise<R>;
117
+ /**
118
+ * Invalidate every entry tagged `tag`. By default the next request recomputes
119
+ * in the foreground; pass `"max"` to serve the stale value while it refreshes.
120
+ */
121
+ export declare function revalidateTag(tag: string | readonly string[], profile?: "max"): Promise<void>;
122
+ export declare function normalizeCachePath(path: string): string;
123
+ /** Tags a route entry carries: `_P:<path>` for itself and `_L:<prefix>` for every enclosing segment. */
124
+ export declare function implicitTags(pathname: string, pattern?: string): string[];
125
+ /**
126
+ * Invalidate a cached path (`"page"`, the default) or everything below it
127
+ * (`"layout"`). Dynamic patterns work too: `revalidatePath("/api/posts/[id]")`.
128
+ */
129
+ export declare function revalidatePath(path: string, type?: "page" | "layout"): Promise<void>;
130
+ export { validateRevalidate as __validateRevalidate };
131
+ //# sourceMappingURL=cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache.d.ts","sourceRoot":"","sources":["../../src/server/cache.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,409 @@
1
+ /**
2
+ * `@textjs/app/cache` — the data cache: `unstable_cache()` / `cache()` with
3
+ * tags and time-based revalidation, `revalidateTag()` / `revalidatePath()`,
4
+ * stale-while-revalidate semantics and a pluggable store (in-memory by default).
5
+ *
6
+ * State lives on `globalThis` so the CLI, the route-handler bundle and user
7
+ * code share one cache even when they load separate copies of this module.
8
+ */
9
+ import { AsyncLocalStorage } from "node:async_hooks";
10
+ function sizeOf(value) {
11
+ if (typeof value === "string")
12
+ return value.length * 2;
13
+ try {
14
+ return (JSON.stringify(value)?.length ?? 0) * 2;
15
+ }
16
+ catch {
17
+ return 1024;
18
+ }
19
+ }
20
+ /** The default store: a per-process LRU map. */
21
+ export class MemoryCacheStore {
22
+ #entries = new Map();
23
+ #tags = new Map();
24
+ #maxEntries;
25
+ #maxSize;
26
+ #maxTags;
27
+ #size = 0;
28
+ constructor(options = {}) {
29
+ this.#maxEntries = options.maxEntries ?? 1000;
30
+ this.#maxSize = options.maxSize ?? 50 * 1024 * 1024;
31
+ this.#maxTags = options.maxTags ?? 10_000;
32
+ }
33
+ get size() {
34
+ return this.#entries.size;
35
+ }
36
+ get(key) {
37
+ const hit = this.#entries.get(key);
38
+ if (!hit)
39
+ return undefined;
40
+ this.#entries.delete(key);
41
+ this.#entries.set(key, hit);
42
+ return hit.entry;
43
+ }
44
+ set(key, entry) {
45
+ this.delete(key);
46
+ const size = sizeOf(entry.value);
47
+ if (size > this.#maxSize)
48
+ return;
49
+ this.#entries.set(key, { entry, size });
50
+ this.#size += size;
51
+ for (const [k, v] of this.#entries) {
52
+ if (this.#entries.size <= this.#maxEntries && this.#size <= this.#maxSize)
53
+ break;
54
+ this.#entries.delete(k);
55
+ this.#size -= v.size;
56
+ }
57
+ }
58
+ delete(key) {
59
+ const hit = this.#entries.get(key);
60
+ if (!hit)
61
+ return;
62
+ this.#entries.delete(key);
63
+ this.#size -= hit.size;
64
+ }
65
+ markTags(tags, mode, at) {
66
+ for (const tag of tags) {
67
+ const s = this.#tags.get(tag) ?? { expiredAt: 0, staleAt: 0 };
68
+ if (mode === "expire")
69
+ s.expiredAt = Math.max(s.expiredAt, at);
70
+ else
71
+ s.staleAt = Math.max(s.staleAt, at);
72
+ this.#tags.delete(tag);
73
+ this.#tags.set(tag, s);
74
+ }
75
+ // Forgetting the oldest revalidation is safe for entries written after it;
76
+ // drop the entries that may still predate it too.
77
+ for (const [tag, st] of this.#tags) {
78
+ if (this.#tags.size <= this.#maxTags)
79
+ break;
80
+ this.#tags.delete(tag);
81
+ for (const [key, hit] of this.#entries)
82
+ if (hit.entry.lastModified <= Math.max(st.expiredAt, st.staleAt) && hit.entry.tags.includes(tag))
83
+ this.delete(key);
84
+ }
85
+ }
86
+ getTagState(tags) {
87
+ const out = { expiredAt: 0, staleAt: 0 };
88
+ for (const tag of tags) {
89
+ const s = this.#tags.get(tag);
90
+ if (!s)
91
+ continue;
92
+ out.expiredAt = Math.max(out.expiredAt, s.expiredAt);
93
+ out.staleAt = Math.max(out.staleAt, s.staleAt);
94
+ }
95
+ return out;
96
+ }
97
+ clear() {
98
+ this.#entries.clear();
99
+ this.#tags.clear();
100
+ this.#size = 0;
101
+ }
102
+ }
103
+ const STATE = Symbol.for("textjs.cache");
104
+ function state() {
105
+ const g = globalThis;
106
+ let s = g[STATE];
107
+ if (!s) {
108
+ s = {
109
+ store: new MemoryCacheStore(),
110
+ inflight: new Map(),
111
+ generation: 0,
112
+ scope: new AsyncLocalStorage(),
113
+ clock: Date.now,
114
+ last: 0,
115
+ onError: (error, key) => console.error(`▼ Text.js: background revalidation failed for ${key}\n`, error),
116
+ };
117
+ g[STATE] = s;
118
+ }
119
+ return s;
120
+ }
121
+ /** Strictly increasing timestamps, so a revalidation in the same millisecond as a write still wins. */
122
+ export function cacheNow() {
123
+ const s = state();
124
+ let t = Math.floor(s.clock());
125
+ if (t <= s.last)
126
+ t = s.last + 1;
127
+ s.last = t;
128
+ return t;
129
+ }
130
+ /** Replace the cache backend (`text.config.ts` → `cacheHandler` does this for you). */
131
+ export function setCacheStore(store) {
132
+ const s = state();
133
+ s.store = store;
134
+ s.inflight.clear();
135
+ }
136
+ export function getCacheStore() {
137
+ return state().store;
138
+ }
139
+ /** @internal Test hooks: a fake clock, error sink, or a fresh in-memory store. */
140
+ export function __configureCache(options) {
141
+ const s = state();
142
+ if (options.clock) {
143
+ s.clock = options.clock;
144
+ s.last = 0;
145
+ }
146
+ if (options.onError)
147
+ s.onError = options.onError;
148
+ if (options.reset) {
149
+ s.store = new MemoryCacheStore();
150
+ s.inflight.clear();
151
+ s.last = 0;
152
+ }
153
+ }
154
+ export function currentCacheScope() {
155
+ return state().scope.getStore();
156
+ }
157
+ export function runInCacheScope(scope, fn) {
158
+ return state().scope.run(scope, fn);
159
+ }
160
+ function track(scope, tags, revalidate) {
161
+ if (!scope)
162
+ return;
163
+ for (const t of tags)
164
+ scope.tags.add(t);
165
+ if (revalidate !== false && (scope.revalidate === false || revalidate < scope.revalidate))
166
+ scope.revalidate = revalidate;
167
+ }
168
+ // ---------------------------------------------------------------------------
169
+ // Core: stale-while-revalidate lookup
170
+ // ---------------------------------------------------------------------------
171
+ /** Entries older than a year are regenerated in the foreground (Next.js `expireTime`). */
172
+ export const EXPIRE_SECONDS = 31_536_000;
173
+ export function classifyEntry(entry, tags, now) {
174
+ if (tags.expiredAt > entry.lastModified)
175
+ return "expired";
176
+ const age = now - entry.lastModified;
177
+ if (entry.revalidate !== false && age > EXPIRE_SECONDS * 1000)
178
+ return "expired";
179
+ if (tags.staleAt > entry.lastModified)
180
+ return "stale";
181
+ if (entry.revalidate !== false && age > entry.revalidate * 1000)
182
+ return "stale";
183
+ return "fresh";
184
+ }
185
+ async function compute(key, fn) {
186
+ const s = state();
187
+ const startedAt = cacheNow();
188
+ const result = await fn();
189
+ const entry = { value: result.value, lastModified: startedAt, revalidate: result.revalidate, tags: [...new Set(result.tags)] };
190
+ if (result.revalidate !== 0)
191
+ await s.store.set(key, entry);
192
+ return { entry, status: "MISS" };
193
+ }
194
+ function dedupe(key, fn) {
195
+ const s = state();
196
+ const running = s.inflight.get(key);
197
+ if (running && running.generation === s.generation)
198
+ return running.promise;
199
+ const generation = s.generation;
200
+ const p = compute(key, fn).finally(() => {
201
+ if (s.inflight.get(key)?.promise === p)
202
+ s.inflight.delete(key);
203
+ });
204
+ s.inflight.set(key, { generation, promise: p });
205
+ return p;
206
+ }
207
+ /**
208
+ * Read `key`, serving stale entries while one deduplicated background
209
+ * refresh runs (registered with `waitUntil` on serverless runtimes).
210
+ */
211
+ export async function getOrCompute(key, fn, waitUntil) {
212
+ const s = state();
213
+ const entry = await s.store.get(key);
214
+ if (entry) {
215
+ const verdict = classifyEntry(entry, await s.store.getTagState(entry.tags), cacheNow());
216
+ if (verdict === "fresh")
217
+ return { entry, status: "HIT" };
218
+ if (verdict === "stale") {
219
+ if (s.inflight.get(key)?.generation !== s.generation) {
220
+ const refresh = dedupe(key, fn).catch((error) => {
221
+ s.onError(error, key);
222
+ });
223
+ if (waitUntil)
224
+ waitUntil(refresh);
225
+ }
226
+ return { entry, status: "STALE" };
227
+ }
228
+ }
229
+ return dedupe(key, fn);
230
+ }
231
+ function validateRevalidate(value, where) {
232
+ if (value === undefined || value === false)
233
+ return false;
234
+ if (typeof value === "number" && Number.isFinite(value) && value >= 0)
235
+ return value;
236
+ throw new TypeError(`Invalid revalidate value ${JSON.stringify(value)} in ${where}; must be a non-negative number or false.`);
237
+ }
238
+ function validateTags(tags, where) {
239
+ if (tags === undefined)
240
+ return [];
241
+ if (!Array.isArray(tags) || tags.some((t) => typeof t !== "string"))
242
+ throw new TypeError(`Invalid tags in ${where}; must be an array of strings.`);
243
+ return tags;
244
+ }
245
+ /** A collision-free cache key for arguments (`undefined` ≠ `null`, `Map` / `Set` / `Date` / `bigint` by value). */
246
+ export function serializeCacheArgs(value, seen = new Set()) {
247
+ if (value === undefined)
248
+ return "u";
249
+ if (value === null)
250
+ return "n";
251
+ switch (typeof value) {
252
+ case "string":
253
+ return JSON.stringify(value);
254
+ case "number":
255
+ return Object.is(value, -0) ? "-0" : Number.isNaN(value) ? "NaN" : String(value);
256
+ case "boolean":
257
+ return value ? "t" : "f";
258
+ case "bigint":
259
+ return `${value}n`;
260
+ case "function":
261
+ case "symbol":
262
+ throw new TypeError(`unstable_cache arguments must be serializable; got a ${typeof value}.`);
263
+ }
264
+ const obj = value;
265
+ if (seen.has(obj))
266
+ throw new TypeError("unstable_cache arguments must not be circular.");
267
+ seen.add(obj);
268
+ try {
269
+ if (Array.isArray(obj))
270
+ return `[${obj.map((v) => serializeCacheArgs(v, seen)).join(",")}]`;
271
+ if (obj instanceof Date)
272
+ return `D${obj.getTime()}`;
273
+ if (obj instanceof URL)
274
+ return `L${JSON.stringify(obj.href)}`;
275
+ if (obj instanceof URLSearchParams)
276
+ return `Q${JSON.stringify(obj.toString())}`;
277
+ if (obj instanceof RegExp)
278
+ return `R${JSON.stringify(obj.toString())}`;
279
+ if (obj instanceof Map)
280
+ return `M[${[...obj].map(([k, v]) => `${serializeCacheArgs(k, seen)}:${serializeCacheArgs(v, seen)}`).sort().join(",")}]`;
281
+ if (obj instanceof Set)
282
+ return `S[${[...obj].map((v) => serializeCacheArgs(v, seen)).sort().join(",")}]`;
283
+ const keys = Object.keys(obj).sort();
284
+ return `{${keys.map((k) => `${JSON.stringify(k)}:${serializeCacheArgs(obj[k], seen)}`).join(",")}}`;
285
+ }
286
+ finally {
287
+ seen.delete(obj);
288
+ }
289
+ }
290
+ /** `file:line:col` of the code that called `unstable_cache()` (stable across processes running one build). */
291
+ function callSite() {
292
+ const frames = (new Error().stack ?? "").split("\n").map((l) => l.trim()).filter((l) => l.startsWith("at "));
293
+ // [callSite, unstable_cache, (cache), caller, …]
294
+ let i = 2;
295
+ if (frames[i]?.startsWith("at cache "))
296
+ i++;
297
+ return (frames[i] ?? "").replace(/^at\s+/, "");
298
+ }
299
+ const warnedKeys = new Map();
300
+ function hashString(s) {
301
+ let h = 0x811c9dc5;
302
+ for (let i = 0; i < s.length; i++) {
303
+ h ^= s.charCodeAt(i);
304
+ h = Math.imul(h, 0x01000193);
305
+ }
306
+ return (h >>> 0).toString(36);
307
+ }
308
+ /**
309
+ * Cache the result of an async function (Next.js `unstable_cache`). Results
310
+ * are JSON-serialized, so every call returns a fresh copy. The cache key is
311
+ * `keyParts` (or the function source) plus the JSON-encoded arguments.
312
+ */
313
+ export function unstable_cache(fn, keyParts, options = {}) {
314
+ const where = `unstable_cache(${keyParts?.join(", ") ?? fn.name ?? "anonymous"})`;
315
+ const revalidate = validateRevalidate(options.revalidate, where);
316
+ const tags = validateTags(options.tags, where);
317
+ let base;
318
+ if (keyParts && keyParts.length > 0)
319
+ base = keyParts.join("\u0000");
320
+ else {
321
+ base = `fn:${hashString(fn.toString())}:${hashString(callSite())}`;
322
+ // Closures created at one call site (in a loop, per request) share source and site: they'd share entries.
323
+ const prior = warnedKeys.get(base)?.deref();
324
+ if (prior && prior !== fn) {
325
+ console.warn(`▼ Text.js: unstable_cache(${fn.name || "anonymous"}) was created more than once from the same code without keyParts; the instances share cache entries. Pass keyParts that identify what the function closes over.`);
326
+ }
327
+ warnedKeys.set(base, new WeakRef(fn));
328
+ }
329
+ return async (...args) => {
330
+ const parent = currentCacheScope();
331
+ if (revalidate === 0) {
332
+ track(parent, tags, 0);
333
+ return fn(...args);
334
+ }
335
+ const key = `data:${base}:${serializeCacheArgs(args)}`;
336
+ const inherited = parent?.implicitTags ?? [];
337
+ const resolved = await getOrCompute(key, async () => {
338
+ const child = { tags: new Set([...tags, ...inherited]), revalidate, implicitTags: inherited, waitUntil: parent?.waitUntil };
339
+ const value = await runInCacheScope(child, () => fn(...args));
340
+ let json;
341
+ try {
342
+ json = value === undefined ? null : (JSON.stringify(value) ?? null);
343
+ }
344
+ catch (error) {
345
+ throw new TypeError(`${where} returned a value that can't be cached as JSON: ${error instanceof Error ? error.message : String(error)}`);
346
+ }
347
+ return { value: json, tags: [...child.tags], revalidate: child.revalidate };
348
+ }, parent?.waitUntil);
349
+ track(parent, resolved.entry.tags, resolved.entry.revalidate);
350
+ const json = resolved.entry.value;
351
+ return (json === null ? undefined : JSON.parse(json));
352
+ };
353
+ }
354
+ /** `unstable_cache` with an options object: `cache(fn, { key, tags, revalidate })`. */
355
+ export function cache(fn, options = {}) {
356
+ const key = options.key === undefined ? undefined : Array.isArray(options.key) ? options.key : [options.key];
357
+ return unstable_cache(fn, key, options);
358
+ }
359
+ function after(promise) {
360
+ const scope = currentCacheScope();
361
+ const p = promise.catch((error) => state().onError(error, "revalidate"));
362
+ scope?.waitUntil?.(p);
363
+ return p;
364
+ }
365
+ /**
366
+ * Invalidate every entry tagged `tag`. By default the next request recomputes
367
+ * in the foreground; pass `"max"` to serve the stale value while it refreshes.
368
+ */
369
+ export function revalidateTag(tag, profile) {
370
+ const tags = typeof tag === "string" ? [tag] : [...tag];
371
+ state().generation++;
372
+ return after(Promise.resolve(state().store.markTags(tags, profile === "max" ? "stale" : "expire", cacheNow())));
373
+ }
374
+ export function normalizeCachePath(path) {
375
+ let p = (path.startsWith("/") ? path : `/${path}`).split(/[?#]/)[0];
376
+ try {
377
+ p = decodeURIComponent(p);
378
+ }
379
+ catch {
380
+ // keep the raw path
381
+ }
382
+ return p.length > 1 ? p.replace(/\/+$/, "") || "/" : p;
383
+ }
384
+ /** Tags a route entry carries: `_P:<path>` for itself and `_L:<prefix>` for every enclosing segment. */
385
+ export function implicitTags(pathname, pattern) {
386
+ const out = new Set();
387
+ for (const p of pattern && pattern !== pathname ? [pathname, pattern] : [pathname]) {
388
+ const path = normalizeCachePath(p);
389
+ out.add(`_P:${path}`);
390
+ out.add("_L:/");
391
+ let prefix = "";
392
+ for (const seg of path.split("/").filter(Boolean)) {
393
+ prefix += `/${seg}`;
394
+ out.add(`_L:${prefix}`);
395
+ }
396
+ }
397
+ return [...out];
398
+ }
399
+ /**
400
+ * Invalidate a cached path (`"page"`, the default) or everything below it
401
+ * (`"layout"`). Dynamic patterns work too: `revalidatePath("/api/posts/[id]")`.
402
+ */
403
+ export function revalidatePath(path, type = "page") {
404
+ const p = normalizeCachePath(path);
405
+ state().generation++;
406
+ return after(Promise.resolve(state().store.markTags([type === "layout" ? `_L:${p}` : `_P:${p}`], "expire", cacheNow())));
407
+ }
408
+ export { validateRevalidate as __validateRevalidate };
409
+ //# sourceMappingURL=cache.js.map