@farm.js/plugin 0.1.0-beta.10 → 0.1.0-beta.100

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 (70) hide show
  1. package/dist/api/index.d.ts +44 -0
  2. package/dist/api/index.d.ts.map +1 -0
  3. package/dist/api/index.js +497 -0
  4. package/dist/context/index.d.ts +61 -0
  5. package/dist/context/index.d.ts.map +1 -0
  6. package/dist/context/index.js +75 -0
  7. package/dist/dev-static.d.ts +8 -0
  8. package/dist/dev-static.d.ts.map +1 -0
  9. package/dist/dev-static.js +37 -0
  10. package/dist/index.d.ts +43 -0
  11. package/dist/index.d.ts.map +1 -0
  12. package/dist/index.js +49 -0
  13. package/dist/middleware/index.d.ts +97 -0
  14. package/dist/middleware/index.d.ts.map +1 -0
  15. package/dist/middleware/index.js +444 -0
  16. package/dist/middleware/matching.d.ts +29 -0
  17. package/dist/middleware/matching.d.ts.map +1 -0
  18. package/dist/middleware/matching.js +79 -0
  19. package/dist/observability/index.d.ts +190 -0
  20. package/dist/observability/index.d.ts.map +1 -0
  21. package/dist/observability/index.js +399 -0
  22. package/dist/rsc/automatic-optimized-boundary.d.ts +11 -0
  23. package/dist/rsc/automatic-optimized-boundary.d.ts.map +1 -0
  24. package/dist/rsc/automatic-optimized-boundary.js +162 -0
  25. package/dist/rsc/build-paths.d.ts +3 -0
  26. package/dist/rsc/build-paths.d.ts.map +1 -0
  27. package/dist/rsc/build-paths.js +8 -0
  28. package/dist/rsc/compatibility.d.ts +8 -0
  29. package/dist/rsc/compatibility.d.ts.map +1 -0
  30. package/dist/rsc/compatibility.js +46 -0
  31. package/dist/rsc/dev-response.d.ts +3 -0
  32. package/dist/rsc/dev-response.d.ts.map +1 -0
  33. package/dist/rsc/dev-response.js +10 -0
  34. package/dist/rsc/entries/client.d.ts +15 -0
  35. package/dist/rsc/entries/client.d.ts.map +1 -0
  36. package/dist/rsc/entries/client.js +320 -0
  37. package/dist/rsc/entries/error-fallback.d.ts +3 -0
  38. package/dist/rsc/entries/error-fallback.d.ts.map +1 -0
  39. package/dist/rsc/entries/error-fallback.js +12 -0
  40. package/dist/rsc/entries/rsc.d.ts +13 -0
  41. package/dist/rsc/entries/rsc.d.ts.map +1 -0
  42. package/dist/rsc/entries/rsc.js +930 -0
  43. package/dist/rsc/entries/ssr.d.ts +13 -0
  44. package/dist/rsc/entries/ssr.d.ts.map +1 -0
  45. package/dist/rsc/entries/ssr.js +215 -0
  46. package/dist/rsc/index.d.ts +91 -0
  47. package/dist/rsc/index.d.ts.map +1 -0
  48. package/dist/rsc/index.js +1466 -0
  49. package/dist/rsc/nitro-build.d.ts +36 -0
  50. package/dist/rsc/nitro-build.d.ts.map +1 -0
  51. package/dist/rsc/nitro-build.js +416 -0
  52. package/dist/rsc/optimized-boundary.d.ts +29 -0
  53. package/dist/rsc/optimized-boundary.d.ts.map +1 -0
  54. package/dist/rsc/optimized-boundary.js +243 -0
  55. package/dist/rsc/route-action-transform.d.ts +11 -0
  56. package/dist/rsc/route-action-transform.d.ts.map +1 -0
  57. package/dist/rsc/route-action-transform.js +369 -0
  58. package/dist/rsc/server-fn-transform.d.ts +6 -0
  59. package/dist/rsc/server-fn-transform.d.ts.map +1 -0
  60. package/dist/rsc/server-fn-transform.js +152 -0
  61. package/dist/rsc/test-fixture-dependencies.d.ts +2 -0
  62. package/dist/rsc/test-fixture-dependencies.d.ts.map +1 -0
  63. package/dist/rsc/test-fixture-dependencies.js +23 -0
  64. package/dist/rsc/types.d.ts +132 -0
  65. package/dist/rsc/types.d.ts.map +1 -0
  66. package/dist/rsc/types.js +1 -0
  67. package/dist/rsc/vite-plugin-nitro.d.ts +46 -0
  68. package/dist/rsc/vite-plugin-nitro.d.ts.map +1 -0
  69. package/dist/rsc/vite-plugin-nitro.js +148 -0
  70. package/package.json +4 -4
@@ -0,0 +1,444 @@
1
+ /**
2
+ * Farm.js Middleware Plugin
3
+ *
4
+ * Standalone middleware support that works with or without RSC.
5
+ * Discovers and executes middleware.ts files in the source directory.
6
+ *
7
+ * @example
8
+ * ```ts
9
+ * import { farmMiddleware } from '@farm.js/plugin/middleware'
10
+ *
11
+ * export default defineConfig({
12
+ * plugins: [farmMiddleware({ srcDir: 'src' })],
13
+ * })
14
+ * ```
15
+ */
16
+ import { createRequire } from "node:module";
17
+ import { _withAfterNodeMiddleware } from "@farm.js/core/after";
18
+ import { devServableFileExists } from "../dev-static.js";
19
+ import { middlewareMatchesPath, parseCookies, serializeCookie } from "./matching.js";
20
+ // Create require for ESM compatibility
21
+ const require_ = createRequire(import.meta.url);
22
+ // Get picocolors with forced color support (to handle NO_COLOR env being set)
23
+ const getColors = () => {
24
+ try {
25
+ const pico = require_("picocolors");
26
+ if (typeof pico?.createColors === "function") {
27
+ return pico.createColors(true);
28
+ }
29
+ if (typeof pico?.green === "function")
30
+ return pico;
31
+ }
32
+ catch { }
33
+ const id = (s) => s;
34
+ return {
35
+ bold: id,
36
+ dim: id,
37
+ cyan: id,
38
+ red: id,
39
+ yellow: id,
40
+ blue: id,
41
+ white: id,
42
+ gray: id,
43
+ green: id,
44
+ magenta: id,
45
+ };
46
+ };
47
+ /**
48
+ * Create a cookie jar for managing cookies
49
+ */
50
+ function createCookieJar(req, res) {
51
+ const cookies = parseCookies(req.headers.cookie);
52
+ const setCookies = [];
53
+ return {
54
+ get(name) {
55
+ return cookies[name];
56
+ },
57
+ set(name, value, options = {}) {
58
+ cookies[name] = value;
59
+ const cookieString = serializeCookie(name, value, options);
60
+ setCookies.push(cookieString);
61
+ res.setHeader("Set-Cookie", setCookies);
62
+ },
63
+ delete(name) {
64
+ delete cookies[name];
65
+ const cookieString = serializeCookie(name, "", {
66
+ maxAge: 0,
67
+ expires: new Date(0),
68
+ });
69
+ setCookies.push(cookieString);
70
+ res.setHeader("Set-Cookie", setCookies);
71
+ },
72
+ getAll() {
73
+ return { ...cookies };
74
+ },
75
+ };
76
+ }
77
+ /**
78
+ * Create a full middleware context
79
+ */
80
+ function createContext(req, res, viteServer) {
81
+ const url = new URL(req.url || "/", `http://${req.headers.host || "localhost"}`);
82
+ const headers = new Map();
83
+ const data = new Map();
84
+ const cookies = createCookieJar(req, res);
85
+ let handled = false;
86
+ // Mirror @farm.js/core's createContext: a header set on ctx.headers in the
87
+ // same handler that then short-circuits via a helper must still reach the
88
+ // response. The runner's tail flush is unreachable once a helper sets
89
+ // _handled, so each helper applies ctx.headers to res before its writeHead.
90
+ const applyResponseHeaders = () => {
91
+ for (const [key, value] of headers) {
92
+ try {
93
+ res.setHeader(key, value);
94
+ }
95
+ catch {
96
+ // An invalid optional header must not prevent the response itself
97
+ // from completing.
98
+ }
99
+ }
100
+ };
101
+ const ctx = {
102
+ request: req,
103
+ response: res,
104
+ url,
105
+ pathname: url.pathname,
106
+ searchParams: url.searchParams,
107
+ method: req.method || "GET",
108
+ params: {},
109
+ route: url.pathname,
110
+ vite: {
111
+ isDev: process.env.NODE_ENV !== "production",
112
+ hmr: !!viteServer?.hot,
113
+ server: viteServer,
114
+ },
115
+ data,
116
+ headers,
117
+ cookies,
118
+ _handled: false,
119
+ redirect(redirectUrl, status = 307) {
120
+ if (handled) {
121
+ console.warn("Response already sent, cannot redirect");
122
+ return;
123
+ }
124
+ ctx._redirectUrl = redirectUrl;
125
+ ctx._handled = true;
126
+ handled = true;
127
+ applyResponseHeaders();
128
+ res.writeHead(status, {
129
+ Location: redirectUrl,
130
+ "Content-Type": "text/plain",
131
+ });
132
+ res.end(`Redirecting to ${redirectUrl}`);
133
+ },
134
+ rewrite(rewriteUrl) {
135
+ ctx._rewriteUrl = rewriteUrl;
136
+ // Update the URL for downstream middleware
137
+ const newUrl = new URL(rewriteUrl, `http://${req.headers.host || "localhost"}`);
138
+ ctx.url = newUrl;
139
+ ctx.pathname = newUrl.pathname;
140
+ ctx.searchParams = newUrl.searchParams;
141
+ // Update the original request URL
142
+ req.url = rewriteUrl;
143
+ },
144
+ json(jsonData, status = 200) {
145
+ if (handled) {
146
+ console.warn("Response already sent, cannot send JSON");
147
+ return;
148
+ }
149
+ ctx._handled = true;
150
+ handled = true;
151
+ applyResponseHeaders();
152
+ res.writeHead(status, {
153
+ "Content-Type": "application/json",
154
+ });
155
+ res.end(JSON.stringify(jsonData));
156
+ },
157
+ text(content, status = 200) {
158
+ if (handled) {
159
+ console.warn("Response already sent, cannot send text");
160
+ return;
161
+ }
162
+ ctx._handled = true;
163
+ handled = true;
164
+ applyResponseHeaders();
165
+ res.writeHead(status, {
166
+ "Content-Type": "text/plain",
167
+ });
168
+ res.end(content);
169
+ },
170
+ html(content, status = 200) {
171
+ if (handled) {
172
+ console.warn("Response already sent, cannot send HTML");
173
+ return;
174
+ }
175
+ ctx._handled = true;
176
+ handled = true;
177
+ applyResponseHeaders();
178
+ res.writeHead(status, {
179
+ "Content-Type": "text/html",
180
+ });
181
+ res.end(content);
182
+ },
183
+ };
184
+ return ctx;
185
+ }
186
+ /**
187
+ * Farm.js Middleware Plugin
188
+ */
189
+ export default function farmMiddleware(options = {}) {
190
+ const srcDir = options.srcDir ?? "src";
191
+ const debug = options.debug ?? false;
192
+ // Middleware cache
193
+ let middlewareCache = new Map();
194
+ let discoveryComplete = false;
195
+ let discoveryPromise = null;
196
+ const log = (message) => {
197
+ if (!debug)
198
+ return;
199
+ try {
200
+ const pc = require("picocolors");
201
+ console.log(pc.dim("[") + pc.bold(pc.blue("FARM")) + pc.dim("]") + " " + pc.gray(message));
202
+ }
203
+ catch {
204
+ console.log(`[FARM] ${message}`);
205
+ }
206
+ };
207
+ return {
208
+ name: "@farm.js/plugin/middleware",
209
+ enforce: "pre",
210
+ configureServer(server) {
211
+ // Discover middleware
212
+ const discoverMiddleware = async (dir, routePath = "/") => {
213
+ const fs = await import("fs");
214
+ const path = await import("path");
215
+ if (!fs.existsSync(dir))
216
+ return;
217
+ // Check for middleware file
218
+ const extensions = [".ts", ".tsx", ".js", ".jsx"];
219
+ for (const ext of extensions) {
220
+ const middlewareFile = path.join(dir, `middleware${ext}`);
221
+ if (fs.existsSync(middlewareFile)) {
222
+ try {
223
+ const module = await server.ssrLoadModule(middlewareFile);
224
+ if (module.default) {
225
+ middlewareCache.set(routePath, {
226
+ path: routePath,
227
+ filePath: middlewareFile,
228
+ module: module.default,
229
+ config: module.config,
230
+ });
231
+ log(`Middleware discovered: ${routePath}`);
232
+ }
233
+ }
234
+ catch (e) {
235
+ log(`Middleware load failed at ${routePath}: ${e.message}`);
236
+ }
237
+ break;
238
+ }
239
+ }
240
+ // Recursively check subdirectories
241
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
242
+ for (const entry of entries) {
243
+ if (entry.isDirectory() &&
244
+ !entry.name.startsWith(".") &&
245
+ !entry.name.startsWith("_") &&
246
+ entry.name !== "api") {
247
+ const subDir = path.join(dir, entry.name);
248
+ const subRoutePath = routePath === "/" ? `/${entry.name}` : `${routePath}/${entry.name}`;
249
+ await discoverMiddleware(subDir, subRoutePath);
250
+ }
251
+ }
252
+ };
253
+ // Initialize discovery
254
+ const initializeDiscovery = async () => {
255
+ const path = await import("path");
256
+ const appDir = path.join(server.config.root, srcDir);
257
+ await discoverMiddleware(appDir);
258
+ discoveryComplete = true;
259
+ };
260
+ discoveryPromise = initializeDiscovery().catch((e) => {
261
+ console.error("[FARM] Middleware discovery error:", e);
262
+ });
263
+ // Execute middleware chain
264
+ const executeMiddleware = async (req, res, pathname, sharedData) => {
265
+ // Wait for discovery
266
+ if (discoveryPromise && !discoveryComplete) {
267
+ await discoveryPromise;
268
+ }
269
+ // Find applicable middleware (cascading from root to specific)
270
+ const applicable = Array.from(middlewareCache.values())
271
+ .filter((mw) => middlewareMatchesPath(pathname, mw.path))
272
+ .sort((a, b) => a.path.split("/").length - b.path.split("/").length);
273
+ if (applicable.length === 0)
274
+ return false;
275
+ const startTime = Date.now();
276
+ const method = req.method || "GET";
277
+ // Log middleware execution in the same format as @farm.js/core
278
+ const pc = getColors();
279
+ const logMsg = [
280
+ pc.dim("[") + pc.bold(pc.blue("FARM")) + pc.dim("]"),
281
+ pc.dim("[") + pc.bold(pc.magenta("MIDDLEWARE")) + pc.dim("]"),
282
+ pc.dim("[") + pc.bold(pc.white(method.padEnd(3))) + pc.dim("]"),
283
+ pc.gray("Executing middleware: "),
284
+ pc.gray(pathname),
285
+ pc.dim(` (${applicable.length} middleware)`),
286
+ ].join(" ");
287
+ console.log(logMsg);
288
+ // Create full middleware context
289
+ const ctx = createContext(req, res, server);
290
+ // Use shared data if provided
291
+ if (sharedData) {
292
+ for (const [key, value] of sharedData) {
293
+ ctx.data.set(key, value);
294
+ }
295
+ }
296
+ for (const mw of applicable) {
297
+ const middleware = mw.module;
298
+ // Handle middleware chain object (has .build method)
299
+ if (middleware && typeof middleware === "object" && "build" in middleware) {
300
+ if (typeof middleware.setBasePath === "function") {
301
+ middleware.setBasePath(mw.path);
302
+ }
303
+ const built = middleware.build();
304
+ const handlers = built.handlers || [];
305
+ let handlerIndex = 0;
306
+ const executeNext = async () => {
307
+ if (handlerIndex < handlers.length) {
308
+ const handler = handlers[handlerIndex++];
309
+ await handler(ctx, executeNext);
310
+ }
311
+ };
312
+ await executeNext();
313
+ }
314
+ else if (typeof middleware === "function") {
315
+ // Plain middleware function
316
+ await middleware(ctx, async () => { });
317
+ }
318
+ // Check if response was sent
319
+ if (ctx._handled || res.headersSent || res.writableEnded) {
320
+ return true;
321
+ }
322
+ }
323
+ // Apply collected headers to response
324
+ for (const [key, value] of ctx.headers) {
325
+ try {
326
+ res.setHeader(key, value);
327
+ }
328
+ catch (e) { }
329
+ }
330
+ // Store middleware data on request for pages to access
331
+ req.__FARM_MIDDLEWARE_DATA__ = Object.fromEntries(ctx.data);
332
+ // Log middleware completion
333
+ const duration = Date.now() - startTime;
334
+ const pc2 = getColors();
335
+ const completeLogMsg = [
336
+ pc2.dim("[") + pc2.bold(pc2.blue("FARM")) + pc2.dim("]"),
337
+ pc2.dim("[") + pc2.bold(pc2.magenta("MIDDLEWARE")) + pc2.dim("]"),
338
+ pc2.dim("[") + pc2.bold(pc2.white(method.padEnd(3))) + pc2.dim("]"),
339
+ pc2.gray("Completed"),
340
+ pc2.gray(pathname),
341
+ pc2.dim(`(${duration}ms)`),
342
+ ].join(" ");
343
+ console.log(completeLogMsg);
344
+ return false;
345
+ };
346
+ // Expose middleware execution for other plugins
347
+ server.__farmMiddleware__ = {
348
+ execute: executeMiddleware,
349
+ getCache: () => middlewareCache,
350
+ isReady: () => discoveryComplete,
351
+ waitForDiscovery: () => discoveryPromise,
352
+ };
353
+ // Add middleware to handle requests
354
+ return () => {
355
+ server.middlewares.use(_withAfterNodeMiddleware(async (req, res, next) => {
356
+ const url = req.url || "/";
357
+ const pathname = url.split("?")[0];
358
+ // Skip Vite internal requests and real static assets. Dotted
359
+ // paths that don't map to a file on disk are app routes (route
360
+ // segments may contain dots) and must still run middleware.
361
+ if (pathname.startsWith("/@") ||
362
+ pathname.startsWith("/__") ||
363
+ pathname.startsWith("/node_modules") ||
364
+ (pathname.includes(".") &&
365
+ !pathname.endsWith("/") &&
366
+ devServableFileExists(pathname, [server.config.publicDir, server.config.root]))) {
367
+ return next();
368
+ }
369
+ try {
370
+ // Execute middleware
371
+ const middlewareData = new Map();
372
+ const handled = await executeMiddleware(req, res, pathname, middlewareData);
373
+ if (handled) {
374
+ return;
375
+ }
376
+ }
377
+ catch (e) {
378
+ console.error("[FARM] Middleware error:", e);
379
+ }
380
+ next();
381
+ }));
382
+ };
383
+ },
384
+ async handleHotUpdate({ file, server, modules }) {
385
+ const fileName = file.split("/").pop() || "";
386
+ if (fileName.startsWith("middleware.")) {
387
+ log(`Middleware updated: ${fileName}`);
388
+ // Invalidate the module in Vite's module graph
389
+ for (const mod of modules) {
390
+ server.moduleGraph.invalidateModule(mod);
391
+ }
392
+ // Find and update the cached middleware
393
+ for (const [routePath, mw] of middlewareCache.entries()) {
394
+ if (mw.filePath === file) {
395
+ try {
396
+ // Reload the module
397
+ const module = await server.ssrLoadModule(file);
398
+ if (module.default) {
399
+ middlewareCache.set(routePath, {
400
+ path: routePath,
401
+ filePath: file,
402
+ module: module.default,
403
+ config: module.config,
404
+ });
405
+ log(`Middleware reloaded: ${routePath}`);
406
+ }
407
+ }
408
+ catch (e) {
409
+ log(`Middleware reload failed: ${e.message}`);
410
+ }
411
+ break;
412
+ }
413
+ }
414
+ // Also check if this is a new middleware file not yet in cache
415
+ if (!Array.from(middlewareCache.values()).some((mw) => mw.filePath === file)) {
416
+ try {
417
+ const path = await import("path");
418
+ const srcPath = path.join(server.config.root, srcDir);
419
+ const relativePath = file
420
+ .replace(srcPath, "")
421
+ .replace(/\/middleware\.(ts|tsx|js|jsx)$/, "");
422
+ const routePath = relativePath === "" ? "/" : relativePath;
423
+ const module = await server.ssrLoadModule(file);
424
+ if (module.default) {
425
+ middlewareCache.set(routePath, {
426
+ path: routePath,
427
+ filePath: file,
428
+ module: module.default,
429
+ config: module.config,
430
+ });
431
+ log(`New middleware discovered: ${routePath}`);
432
+ }
433
+ }
434
+ catch (e) {
435
+ log(`New middleware load failed: ${e.message}`);
436
+ }
437
+ }
438
+ server.ws.send({ type: "full-reload", path: "*" });
439
+ return [];
440
+ }
441
+ },
442
+ };
443
+ }
444
+ export { farmMiddleware };
@@ -0,0 +1,29 @@
1
+ import type { CookieOptions } from "./index.js";
2
+ /**
3
+ * Parse cookies from a Cookie header.
4
+ *
5
+ * A single request cookie whose value is not valid UTF-8 percent-encoding
6
+ * (a latin-1 value from an old link, a crawler, or a bare `%`) must not take
7
+ * the whole map down: an unguarded `decodeURIComponent` throws `URIError`,
8
+ * which — because the middleware runner swallows the throw and calls `next()` —
9
+ * would silently skip every middleware for that request (a dev-time auth
10
+ * bypass). Decoding falls back to the raw value per entry instead.
11
+ */
12
+ export declare function parseCookies(cookieHeader?: string): Record<string, string>;
13
+ /**
14
+ * Whether a middleware registered at `middlewarePath` applies to `pathname`.
15
+ *
16
+ * Matching is by path segment, not raw string prefix: middleware at `/admin`
17
+ * covers `/admin` and everything under `/admin/`, but not sibling routes like
18
+ * `/administrator` or `/admin-public` that merely share a textual prefix.
19
+ */
20
+ export declare function middlewareMatchesPath(pathname: string, middlewarePath: string): boolean;
21
+ /**
22
+ * Serialize a cookie.
23
+ *
24
+ * `Max-Age=0` is the canonical immediate-expiry directive, so the attribute is
25
+ * emitted whenever `maxAge` is provided — a `0` must not be dropped the way a
26
+ * plain truthiness check would drop it.
27
+ */
28
+ export declare function serializeCookie(name: string, value: string, options?: CookieOptions): string;
29
+ //# sourceMappingURL=matching.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"matching.d.ts","sourceRoot":"","sources":["../../src/middleware/matching.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAEhD;;;;;;;;;GASG;AACH,wBAAgB,YAAY,CAAC,YAAY,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAc1E;AAUD;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,EAAE,cAAc,EAAE,MAAM,GAAG,OAAO,CAIvF;AAED;;;;;;GAMG;AACH,wBAAgB,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CAkChG"}
@@ -0,0 +1,79 @@
1
+ /**
2
+ * Parse cookies from a Cookie header.
3
+ *
4
+ * A single request cookie whose value is not valid UTF-8 percent-encoding
5
+ * (a latin-1 value from an old link, a crawler, or a bare `%`) must not take
6
+ * the whole map down: an unguarded `decodeURIComponent` throws `URIError`,
7
+ * which — because the middleware runner swallows the throw and calls `next()` —
8
+ * would silently skip every middleware for that request (a dev-time auth
9
+ * bypass). Decoding falls back to the raw value per entry instead.
10
+ */
11
+ export function parseCookies(cookieHeader) {
12
+ if (!cookieHeader)
13
+ return {};
14
+ return cookieHeader.split(";").reduce((cookies, cookie) => {
15
+ const [name, ...rest] = cookie.split("=");
16
+ const value = rest.join("=").trim();
17
+ if (name && value) {
18
+ cookies[name.trim()] = decodeCookieValue(value);
19
+ }
20
+ return cookies;
21
+ }, {});
22
+ }
23
+ function decodeCookieValue(value) {
24
+ try {
25
+ return decodeURIComponent(value);
26
+ }
27
+ catch {
28
+ return value;
29
+ }
30
+ }
31
+ /**
32
+ * Whether a middleware registered at `middlewarePath` applies to `pathname`.
33
+ *
34
+ * Matching is by path segment, not raw string prefix: middleware at `/admin`
35
+ * covers `/admin` and everything under `/admin/`, but not sibling routes like
36
+ * `/administrator` or `/admin-public` that merely share a textual prefix.
37
+ */
38
+ export function middlewareMatchesPath(pathname, middlewarePath) {
39
+ if (middlewarePath === "/")
40
+ return true;
41
+ if (pathname === middlewarePath)
42
+ return true;
43
+ return pathname.startsWith(`${middlewarePath}/`);
44
+ }
45
+ /**
46
+ * Serialize a cookie.
47
+ *
48
+ * `Max-Age=0` is the canonical immediate-expiry directive, so the attribute is
49
+ * emitted whenever `maxAge` is provided — a `0` must not be dropped the way a
50
+ * plain truthiness check would drop it.
51
+ */
52
+ export function serializeCookie(name, value, options = {}) {
53
+ let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`;
54
+ if (options.maxAge != null) {
55
+ cookie += `; Max-Age=${options.maxAge}`;
56
+ }
57
+ if (options.expires) {
58
+ cookie += `; Expires=${options.expires.toUTCString()}`;
59
+ }
60
+ if (options.path) {
61
+ cookie += `; Path=${options.path}`;
62
+ }
63
+ else {
64
+ cookie += "; Path=/";
65
+ }
66
+ if (options.domain) {
67
+ cookie += `; Domain=${options.domain}`;
68
+ }
69
+ if (options.secure) {
70
+ cookie += "; Secure";
71
+ }
72
+ if (options.httpOnly) {
73
+ cookie += "; HttpOnly";
74
+ }
75
+ if (options.sameSite) {
76
+ cookie += `; SameSite=${options.sameSite.charAt(0).toUpperCase() + options.sameSite.slice(1)}`;
77
+ }
78
+ return cookie;
79
+ }