@textjs/app 0.0.0-stage → 1.0.2

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,451 @@
1
+ /**
2
+ * `@textjs/app/server` — the `middleware.ts` API: `NextRequest`, `NextResponse`,
3
+ * request / response cookies and the matcher config types.
4
+ *
5
+ * Web-standard only (no Node imports), so the same middleware runs on
6
+ * `text dev`, `text start`, Bun, Vercel Edge and Cloudflare Workers.
7
+ */
8
+
9
+ // ---------------------------------------------------------------------------
10
+ // Internal header protocol (the same headers Next.js and Vercel's edge
11
+ // routing understand, so middleware output can be handed to Vercel verbatim).
12
+ // ---------------------------------------------------------------------------
13
+
14
+ export const MIDDLEWARE_NEXT = "x-middleware-next";
15
+ export const MIDDLEWARE_REWRITE = "x-middleware-rewrite";
16
+ export const MIDDLEWARE_OVERRIDE_HEADERS = "x-middleware-override-headers";
17
+ export const MIDDLEWARE_REQUEST_PREFIX = "x-middleware-request-";
18
+
19
+ /**
20
+ * Brands `NextResponse.next()` / `NextResponse.rewrite()` results. Only these
21
+ * steer routing: an arbitrary `Response` (say, a proxied `fetch()`) carrying
22
+ * `x-middleware-*` headers is returned to the client as-is.
23
+ */
24
+ export const MIDDLEWARE_CONTROL = Symbol.for("textjs.middlewareControl");
25
+ const NEXT_URL_BRAND = Symbol.for("textjs.nextUrl");
26
+ const NEXT_RESPONSE_BRAND = Symbol.for("textjs.nextResponse");
27
+
28
+ /** `true` for responses created by `NextResponse.next()` / `NextResponse.rewrite()` (from any copy of this module). */
29
+ export function isMiddlewareControl(response: Response): boolean {
30
+ return (response as unknown as Record<symbol, unknown>)[MIDDLEWARE_CONTROL] === true;
31
+ }
32
+
33
+ // ---------------------------------------------------------------------------
34
+ // Cookies
35
+ // ---------------------------------------------------------------------------
36
+
37
+ export interface RequestCookie {
38
+ name: string;
39
+ value: string;
40
+ }
41
+
42
+ export interface ResponseCookie extends RequestCookie {
43
+ domain?: string;
44
+ path?: string;
45
+ expires?: Date | number;
46
+ maxAge?: number;
47
+ httpOnly?: boolean;
48
+ secure?: boolean;
49
+ sameSite?: "strict" | "lax" | "none" | boolean;
50
+ priority?: "low" | "medium" | "high";
51
+ partitioned?: boolean;
52
+ }
53
+
54
+ export type CookieOptions = Omit<ResponseCookie, "name" | "value">;
55
+
56
+ function decode(value: string): string {
57
+ try {
58
+ return decodeURIComponent(value);
59
+ } catch {
60
+ return value;
61
+ }
62
+ }
63
+
64
+ /** Parse a `Cookie` request header. The first occurrence of a name wins. */
65
+ export function parseCookieHeader(header: string | null | undefined): Map<string, string> {
66
+ const out = new Map<string, string>();
67
+ if (!header) return out;
68
+ for (const part of header.split(";")) {
69
+ const eq = part.indexOf("=");
70
+ if (eq < 0) continue;
71
+ const name = part.slice(0, eq).trim();
72
+ if (!name || out.has(name)) continue;
73
+ let value = part.slice(eq + 1).trim();
74
+ if (value.startsWith('"') && value.endsWith('"') && value.length >= 2) value = value.slice(1, -1);
75
+ out.set(name, decode(value));
76
+ }
77
+ return out;
78
+ }
79
+
80
+ const COOKIE_NAME = /^[!#$%&'*+\-.^_`|~0-9A-Za-z]+$/;
81
+
82
+ /** Serialize one `Set-Cookie` header value (RFC 6265bis). `path` defaults to `/`. */
83
+ export function serializeCookie(cookie: ResponseCookie): string {
84
+ if (!COOKIE_NAME.test(cookie.name)) throw new TypeError(`Invalid cookie name: ${JSON.stringify(cookie.name)}`);
85
+ const parts = [`${cookie.name}=${encodeURIComponent(cookie.value)}`];
86
+ for (const [attr, v] of [["path", cookie.path], ["domain", cookie.domain]] as const) {
87
+ if (v !== undefined && /[;\u0000-\u001f\u007f]/.test(v)) throw new TypeError(`Invalid cookie ${attr}: ${JSON.stringify(v)}`);
88
+ }
89
+ parts.push(`Path=${cookie.path ?? "/"}`);
90
+ if (cookie.domain) parts.push(`Domain=${cookie.domain}`);
91
+ if (cookie.expires !== undefined) parts.push(`Expires=${new Date(cookie.expires).toUTCString()}`);
92
+ if (cookie.maxAge !== undefined) parts.push(`Max-Age=${Math.floor(cookie.maxAge)}`);
93
+ if (cookie.httpOnly) parts.push("HttpOnly");
94
+ if (cookie.secure) parts.push("Secure");
95
+ if (cookie.sameSite !== undefined && cookie.sameSite !== false) {
96
+ const s = cookie.sameSite === true ? "strict" : cookie.sameSite;
97
+ parts.push(`SameSite=${s.charAt(0).toUpperCase()}${s.slice(1)}`);
98
+ }
99
+ if (cookie.priority) parts.push(`Priority=${cookie.priority.charAt(0).toUpperCase()}${cookie.priority.slice(1)}`);
100
+ if (cookie.partitioned) parts.push("Partitioned");
101
+ return parts.join("; ");
102
+ }
103
+
104
+ /** Parse a `Set-Cookie` header value back into a cookie (name, value and attributes). */
105
+ export function parseSetCookie(header: string): ResponseCookie | null {
106
+ const [pair, ...attrs] = header.split(";");
107
+ const eq = (pair ?? "").indexOf("=");
108
+ if (!pair || eq <= 0) return null;
109
+ const cookie: ResponseCookie = { name: pair.slice(0, eq).trim(), value: decode(pair.slice(eq + 1).trim()) };
110
+ for (const attr of attrs) {
111
+ const i = attr.indexOf("=");
112
+ const key = (i < 0 ? attr : attr.slice(0, i)).trim().toLowerCase();
113
+ const val = i < 0 ? "" : attr.slice(i + 1).trim();
114
+ if (key === "path") cookie.path = val;
115
+ else if (key === "domain") cookie.domain = val;
116
+ else if (key === "expires") cookie.expires = new Date(val);
117
+ else if (key === "max-age") cookie.maxAge = Number(val);
118
+ else if (key === "httponly") cookie.httpOnly = true;
119
+ else if (key === "secure") cookie.secure = true;
120
+ else if (key === "samesite") cookie.sameSite = val.toLowerCase() as "strict" | "lax" | "none";
121
+ else if (key === "priority") cookie.priority = val.toLowerCase() as "low" | "medium" | "high";
122
+ else if (key === "partitioned") cookie.partitioned = true;
123
+ }
124
+ return cookie;
125
+ }
126
+
127
+ /** `request.cookies`: reads the `Cookie` header; `set()` / `delete()` update it for downstream handlers. */
128
+ export class RequestCookies {
129
+ readonly #headers: Headers;
130
+ #map: Map<string, string>;
131
+
132
+ constructor(headers: Headers) {
133
+ this.#headers = headers;
134
+ this.#map = parseCookieHeader(headers.get("cookie"));
135
+ }
136
+
137
+ get size(): number {
138
+ return this.#map.size;
139
+ }
140
+
141
+ get(name: string | RequestCookie): RequestCookie | undefined {
142
+ const n = typeof name === "string" ? name : name.name;
143
+ const value = this.#map.get(n);
144
+ return value === undefined ? undefined : { name: n, value };
145
+ }
146
+
147
+ getAll(name?: string | RequestCookie): RequestCookie[] {
148
+ const all = [...this.#map].map(([n, value]) => ({ name: n, value }));
149
+ if (name === undefined) return all;
150
+ const n = typeof name === "string" ? name : name.name;
151
+ return all.filter((c) => c.name === n);
152
+ }
153
+
154
+ has(name: string): boolean {
155
+ return this.#map.has(name);
156
+ }
157
+
158
+ set(name: string | RequestCookie, value?: string): this {
159
+ const c = typeof name === "string" ? { name, value: value ?? "" } : name;
160
+ this.#map.set(c.name, c.value);
161
+ this.#sync();
162
+ return this;
163
+ }
164
+
165
+ delete(names: string | string[]): boolean | boolean[] {
166
+ const list = Array.isArray(names) ? names : [names];
167
+ const result = list.map((n) => this.#map.delete(n));
168
+ this.#sync();
169
+ return Array.isArray(names) ? result : (result[0] as boolean);
170
+ }
171
+
172
+ clear(): this {
173
+ this.#map.clear();
174
+ this.#sync();
175
+ return this;
176
+ }
177
+
178
+ [Symbol.iterator](): IterableIterator<[string, RequestCookie]> {
179
+ return new Map([...this.#map].map(([n, value]) => [n, { name: n, value }] as [string, RequestCookie]))[Symbol.iterator]();
180
+ }
181
+
182
+ toString(): string {
183
+ return [...this.#map].map(([n, v]) => `${n}=${encodeURIComponent(v)}`).join("; ");
184
+ }
185
+
186
+ #sync(): void {
187
+ if (this.#map.size === 0) this.#headers.delete("cookie");
188
+ else this.#headers.set("cookie", this.toString());
189
+ }
190
+ }
191
+
192
+ /** `response.cookies`: a typed view over the response's `Set-Cookie` headers. */
193
+ export class ResponseCookies {
194
+ readonly #headers: Headers;
195
+ readonly #map = new Map<string, ResponseCookie>();
196
+
197
+ constructor(headers: Headers) {
198
+ this.#headers = headers;
199
+ for (const line of headers.getSetCookie()) {
200
+ const c = parseSetCookie(line);
201
+ if (c) this.#map.set(c.name, c);
202
+ }
203
+ }
204
+
205
+ get(name: string | ResponseCookie): ResponseCookie | undefined {
206
+ return this.#map.get(typeof name === "string" ? name : name.name);
207
+ }
208
+
209
+ getAll(name?: string | ResponseCookie): ResponseCookie[] {
210
+ const all = [...this.#map.values()];
211
+ if (name === undefined) return all;
212
+ const n = typeof name === "string" ? name : name.name;
213
+ return all.filter((c) => c.name === n);
214
+ }
215
+
216
+ has(name: string): boolean {
217
+ return this.#map.has(name);
218
+ }
219
+
220
+ set(name: string | ResponseCookie, value?: string, options?: CookieOptions): this {
221
+ const c: ResponseCookie = typeof name === "string" ? { ...options, name, value: value ?? "" } : { ...name };
222
+ serializeCookie(c);
223
+ this.#map.set(c.name, c);
224
+ this.#sync();
225
+ return this;
226
+ }
227
+
228
+ /** Expire a cookie on the client (`Max-Age=0`). */
229
+ delete(name: string | (CookieOptions & { name: string })): this {
230
+ const opts = typeof name === "string" ? { name } : name;
231
+ return this.set({ ...opts, value: "", expires: new Date(0), maxAge: 0 });
232
+ }
233
+
234
+ [Symbol.iterator](): IterableIterator<[string, ResponseCookie]> {
235
+ return this.#map[Symbol.iterator]();
236
+ }
237
+
238
+ toString(): string {
239
+ return [...this.#map.values()].map(serializeCookie).join(", ");
240
+ }
241
+
242
+ #sync(): void {
243
+ this.#headers.delete("set-cookie");
244
+ for (const c of this.#map.values()) this.#headers.append("set-cookie", serializeCookie(c));
245
+ }
246
+ }
247
+
248
+ // ---------------------------------------------------------------------------
249
+ // NextURL / NextRequest
250
+ // ---------------------------------------------------------------------------
251
+
252
+ /**
253
+ * A `URL` whose `pathname` excludes the app's `basePath`. `href`, `toString()`
254
+ * and `toJSON()` include it, so `new URL("/x", request.nextUrl)` and
255
+ * `redirect(request.nextUrl)` stay inside the app.
256
+ */
257
+ export class NextURL extends URL {
258
+ basePath: string;
259
+
260
+ constructor(input: string | URL, base?: string | URL, basePath = "") {
261
+ super(input, base);
262
+ this.basePath = basePath;
263
+ (this as unknown as Record<symbol, boolean>)[NEXT_URL_BRAND] = true;
264
+ if (basePath && (this.pathname === basePath || this.pathname.startsWith(`${basePath}/`))) {
265
+ this.pathname = this.pathname.slice(basePath.length) || "/";
266
+ }
267
+ }
268
+
269
+ /** Recognizes `NextURL`s from other copies of this module (dev SSR vs the CLI). */
270
+ static [Symbol.hasInstance](value: unknown): boolean {
271
+ return typeof value === "object" && value !== null && (value as Record<symbol, unknown>)[NEXT_URL_BRAND] === true;
272
+ }
273
+
274
+ /** The full URL including `basePath`. */
275
+ toAbsolute(): URL {
276
+ const u = new URL(rawHref(this));
277
+ if (this.basePath) u.pathname = this.basePath + (this.pathname === "/" ? "" : this.pathname) || "/";
278
+ return u;
279
+ }
280
+
281
+ clone(): NextURL {
282
+ return new NextURL(this.toAbsolute(), undefined, this.basePath);
283
+ }
284
+
285
+ override toString(): string {
286
+ return this.toAbsolute().href;
287
+ }
288
+
289
+ override toJSON(): string {
290
+ return this.toString();
291
+ }
292
+ }
293
+
294
+ const URL_HREF = Object.getOwnPropertyDescriptor(URL.prototype, "href") as PropertyDescriptor;
295
+
296
+ function rawHref(url: URL): string {
297
+ return URL_HREF.get?.call(url) as string;
298
+ }
299
+
300
+ Object.defineProperty(NextURL.prototype, "href", {
301
+ configurable: true,
302
+ enumerable: true,
303
+ get(this: NextURL) {
304
+ return this.toAbsolute().href;
305
+ },
306
+ set(this: NextURL, value: string) {
307
+ URL_HREF.set?.call(this, value);
308
+ const { basePath } = this;
309
+ if (basePath && (this.pathname === basePath || this.pathname.startsWith(`${basePath}/`))) this.pathname = this.pathname.slice(basePath.length) || "/";
310
+ },
311
+ });
312
+
313
+ export interface NextRequestInit extends RequestInit {
314
+ basePath?: string;
315
+ }
316
+
317
+ /** The request passed to `middleware()`: a Web `Request` plus `nextUrl` and `cookies`. */
318
+ export class NextRequest extends Request {
319
+ readonly nextUrl: NextURL;
320
+ readonly cookies: RequestCookies;
321
+
322
+ constructor(input: Request | string | URL, init: NextRequestInit = {}) {
323
+ const { basePath, ...rest } = init;
324
+ const url = typeof input === "string" || input instanceof URL ? String(input) : input.url;
325
+ const base = input instanceof Request ? input : undefined;
326
+ const reqInit: RequestInit & { duplex?: "half" } = { ...rest };
327
+ if (base && reqInit.body === undefined && base.body) reqInit.duplex = "half";
328
+ super(base ?? url, reqInit);
329
+ this.nextUrl = new NextURL(url, undefined, basePath ?? "");
330
+ this.cookies = new RequestCookies(this.headers);
331
+ }
332
+ }
333
+
334
+ // ---------------------------------------------------------------------------
335
+ // NextResponse
336
+ // ---------------------------------------------------------------------------
337
+
338
+ export interface MiddlewareResponseInit extends ResponseInit {
339
+ /** Replace the headers forwarded to route handlers, static files and pages. */
340
+ request?: { headers?: HeadersInit };
341
+ }
342
+
343
+ function destination(url: string | URL): string {
344
+ if (url instanceof NextURL) return url.toAbsolute().href;
345
+ if (url instanceof URL) return url.href;
346
+ if (typeof url === "object" && url !== null && typeof (url as { toAbsolute?: unknown }).toAbsolute === "function") return (url as NextURL).toAbsolute().href;
347
+ try {
348
+ return new URL(url).href;
349
+ } catch {
350
+ throw new TypeError(`URL is malformed "${String(url)}". Please use only absolute URLs, e.g. new URL("/login", request.url).`);
351
+ }
352
+ }
353
+
354
+ function applyRequestOverride(headers: Headers, init: MiddlewareResponseInit | undefined): void {
355
+ if (!init?.request?.headers) return;
356
+ const next = new Headers(init.request.headers);
357
+ const keys: string[] = [];
358
+ next.forEach((value, key) => {
359
+ keys.push(key);
360
+ headers.set(`${MIDDLEWARE_REQUEST_PREFIX}${key}`, value);
361
+ });
362
+ headers.set(MIDDLEWARE_OVERRIDE_HEADERS, keys.join(","));
363
+ }
364
+
365
+ function control(response: NextResponse): NextResponse {
366
+ (response as unknown as Record<symbol, boolean>)[MIDDLEWARE_CONTROL] = true;
367
+ return response;
368
+ }
369
+
370
+ const REDIRECTS = new Set([301, 302, 303, 307, 308]);
371
+
372
+ export class NextResponse<Body = unknown> extends Response {
373
+ readonly cookies: ResponseCookies;
374
+ /** Type-only marker for `NextResponse.json<T>()`. */
375
+ declare readonly __body?: Body;
376
+
377
+ constructor(body?: BodyInit | null, init?: ResponseInit) {
378
+ super(body, init);
379
+ this.cookies = new ResponseCookies(this.headers);
380
+ (this as unknown as Record<symbol, boolean>)[NEXT_RESPONSE_BRAND] = true;
381
+ }
382
+
383
+ static [Symbol.hasInstance](value: unknown): boolean {
384
+ return typeof value === "object" && value !== null && (value as Record<symbol, unknown>)[NEXT_RESPONSE_BRAND] === true;
385
+ }
386
+
387
+ static override json<T>(body: T, init?: ResponseInit): NextResponse<T> {
388
+ const headers = new Headers(init?.headers);
389
+ if (!headers.has("content-type")) headers.set("content-type", "application/json");
390
+ return new NextResponse<T>(JSON.stringify(body), { ...init, headers });
391
+ }
392
+
393
+ /** Redirect (`307` by default). The URL must be absolute: `new URL("/login", request.url)`. */
394
+ static override redirect(url: string | URL, init?: number | ResponseInit): NextResponse {
395
+ const status = typeof init === "number" ? init : (init?.status ?? 307);
396
+ if (!REDIRECTS.has(status)) throw new RangeError(`Failed to execute "redirect" on "response": Invalid status code ${status}`);
397
+ const headers = new Headers(typeof init === "object" ? init.headers : undefined);
398
+ headers.set("location", destination(url));
399
+ return new NextResponse(null, { ...(typeof init === "object" ? init : {}), status, headers });
400
+ }
401
+
402
+ /** Serve another URL without changing the address bar. External origins are proxied. */
403
+ static rewrite(url: string | URL, init?: MiddlewareResponseInit): NextResponse {
404
+ const headers = new Headers(init?.headers);
405
+ headers.set(MIDDLEWARE_REWRITE, destination(url));
406
+ applyRequestOverride(headers, init);
407
+ return control(new NextResponse(null, { ...init, headers }));
408
+ }
409
+
410
+ /** Continue to the route handler / page, optionally adding response or request headers. */
411
+ static next(init?: MiddlewareResponseInit): NextResponse {
412
+ const headers = new Headers(init?.headers);
413
+ headers.set(MIDDLEWARE_NEXT, "1");
414
+ applyRequestOverride(headers, init);
415
+ return control(new NextResponse(null, { ...init, headers }));
416
+ }
417
+ }
418
+
419
+ // ---------------------------------------------------------------------------
420
+ // Middleware module types
421
+ // ---------------------------------------------------------------------------
422
+
423
+ export type MatcherCondition =
424
+ | { type: "header" | "cookie" | "query"; key: string; value?: string }
425
+ | { type: "host"; key?: undefined; value: string };
426
+
427
+ export interface MatcherObject {
428
+ source: string;
429
+ has?: MatcherCondition[];
430
+ missing?: MatcherCondition[];
431
+ }
432
+
433
+ export type Matcher = string | MatcherObject | Array<string | MatcherObject>;
434
+
435
+ export interface MiddlewareConfig {
436
+ matcher?: Matcher;
437
+ }
438
+
439
+ export interface NextFetchEvent {
440
+ /** Keep the runtime alive for background work after the response is sent. */
441
+ waitUntil(promise: Promise<unknown>): void;
442
+ }
443
+
444
+ export type MiddlewareResult = Response | NextResponse | null | undefined | void;
445
+ export type NextMiddleware = (request: NextRequest, event: NextFetchEvent) => MiddlewareResult | Promise<MiddlewareResult>;
446
+
447
+ export interface MiddlewareModule {
448
+ middleware?: NextMiddleware;
449
+ default?: NextMiddleware;
450
+ config?: MiddlewareConfig;
451
+ }
@@ -0,0 +1,62 @@
1
+ /**
2
+ * Partial Prerendering for the console: `text build` renders each static
3
+ * route's first frame (the static shell, with `<Suspense>` fallbacks where
4
+ * data is still loading) and inlines it into that route's HTML as a classic
5
+ * `<script>` that replays the driver's exact `console.clear()` +
6
+ * `console.log(format, ...css)`, before any module loads. The client (or the
7
+ * frame player) then boots with `initialClear: false`, so its first frame is
8
+ * appended below the shell and swaps in within one DevTools paint: the
9
+ * console is cleared exactly once.
10
+ */
11
+
12
+ import type { ShellCall } from "./live-app";
13
+ import { inlineJson } from "./live-routes";
14
+
15
+ /** The URL path a shell paints on (`basePath` + route pattern, percent-encoded like `location.pathname`). */
16
+ export function shellPath(basePath: string, pattern: string): string {
17
+ const p = pattern === "/" ? basePath || "/" : `${basePath}${pattern}`;
18
+ return encodeURI(p);
19
+ }
20
+
21
+ /**
22
+ * A self-contained `<script>` replaying `calls` when `location.pathname`
23
+ * matches `path` (so an SPA-fallback copy of the document never paints the
24
+ * wrong route). CSS arguments are deduplicated into a table.
25
+ */
26
+ export function shellScript(calls: readonly ShellCall[], path: string): string {
27
+ const table: string[] = [];
28
+ const index = new Map<string, number>();
29
+ const ops: Array<0 | [string, number[]]> = [];
30
+ for (const c of calls) {
31
+ if (c.method === "clear") {
32
+ ops.push(0);
33
+ continue;
34
+ }
35
+ const [format = "", ...args] = c.args;
36
+ ops.push([
37
+ format,
38
+ args.map((a) => {
39
+ let i = index.get(a);
40
+ if (i === undefined) {
41
+ i = table.length;
42
+ table.push(a);
43
+ index.set(a, i);
44
+ }
45
+ return i;
46
+ }),
47
+ ]);
48
+ }
49
+ const body =
50
+ `(function(p,c,s){var l=location.pathname;if(l.length>1&&l.charAt(l.length-1)==="/")l=l.slice(0,-1);if(l!==p||typeof console==="undefined")return;` +
51
+ `for(var i=0;i<s.length;i++){var o=s[i];if(o===0){console.clear();continue}var a=[o[0]];for(var j=0;j<o[1].length;j++)a.push(c[o[1][j]]);console.log.apply(console,a)}` +
52
+ `window.__TEXTJS_SHELL__=1;window.__TEXTJS_SHELL_AT__=typeof performance!=="undefined"?performance.now():void 0})(${inlineJson(path)},${inlineJson(table)},${inlineJson(ops)});`;
53
+ return `<script data-textjs-shell>${body}</script>`;
54
+ }
55
+
56
+ /** Insert a shell script into an HTML document, ahead of every module script. */
57
+ export function injectShell(html: string, script: string): string {
58
+ const at = html.search(/<script\b[^>]*type=["']module["']/i);
59
+ if (at >= 0) return `${html.slice(0, at)}${script}\n ${html.slice(at)}`;
60
+ const body = html.lastIndexOf("</body>");
61
+ return body >= 0 ? `${html.slice(0, body)}${script}\n${html.slice(body)}` : html + script;
62
+ }