alabjs 0.1.0

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 (277) hide show
  1. package/dist/adapters/cloudflare.d.ts +31 -0
  2. package/dist/adapters/cloudflare.d.ts.map +1 -0
  3. package/dist/adapters/cloudflare.js +30 -0
  4. package/dist/adapters/cloudflare.js.map +1 -0
  5. package/dist/adapters/deno.d.ts +22 -0
  6. package/dist/adapters/deno.d.ts.map +1 -0
  7. package/dist/adapters/deno.js +21 -0
  8. package/dist/adapters/deno.js.map +1 -0
  9. package/dist/adapters/web.d.ts +47 -0
  10. package/dist/adapters/web.d.ts.map +1 -0
  11. package/dist/adapters/web.js +212 -0
  12. package/dist/adapters/web.js.map +1 -0
  13. package/dist/cli.d.ts +11 -0
  14. package/dist/cli.d.ts.map +1 -0
  15. package/dist/cli.js +61 -0
  16. package/dist/cli.js.map +1 -0
  17. package/dist/client/hooks.d.ts +119 -0
  18. package/dist/client/hooks.d.ts.map +1 -0
  19. package/dist/client/hooks.js +220 -0
  20. package/dist/client/hooks.js.map +1 -0
  21. package/dist/client/hooks.test.d.ts +2 -0
  22. package/dist/client/hooks.test.d.ts.map +1 -0
  23. package/dist/client/hooks.test.js +45 -0
  24. package/dist/client/hooks.test.js.map +1 -0
  25. package/dist/client/index.d.ts +6 -0
  26. package/dist/client/index.d.ts.map +1 -0
  27. package/dist/client/index.js +4 -0
  28. package/dist/client/index.js.map +1 -0
  29. package/dist/client/offline.d.ts +52 -0
  30. package/dist/client/offline.d.ts.map +1 -0
  31. package/dist/client/offline.js +90 -0
  32. package/dist/client/offline.js.map +1 -0
  33. package/dist/client/provider.d.ts +12 -0
  34. package/dist/client/provider.d.ts.map +1 -0
  35. package/dist/client/provider.js +10 -0
  36. package/dist/client/provider.js.map +1 -0
  37. package/dist/commands/build.d.ts +18 -0
  38. package/dist/commands/build.d.ts.map +1 -0
  39. package/dist/commands/build.js +173 -0
  40. package/dist/commands/build.js.map +1 -0
  41. package/dist/commands/dev.d.ts +8 -0
  42. package/dist/commands/dev.d.ts.map +1 -0
  43. package/dist/commands/dev.js +447 -0
  44. package/dist/commands/dev.js.map +1 -0
  45. package/dist/commands/info.d.ts +6 -0
  46. package/dist/commands/info.d.ts.map +1 -0
  47. package/dist/commands/info.js +92 -0
  48. package/dist/commands/info.js.map +1 -0
  49. package/dist/commands/ssg.d.ts +8 -0
  50. package/dist/commands/ssg.d.ts.map +1 -0
  51. package/dist/commands/ssg.js +124 -0
  52. package/dist/commands/ssg.js.map +1 -0
  53. package/dist/commands/start.d.ts +7 -0
  54. package/dist/commands/start.d.ts.map +1 -0
  55. package/dist/commands/start.js +26 -0
  56. package/dist/commands/start.js.map +1 -0
  57. package/dist/commands/test.d.ts +24 -0
  58. package/dist/commands/test.d.ts.map +1 -0
  59. package/dist/commands/test.js +87 -0
  60. package/dist/commands/test.js.map +1 -0
  61. package/dist/components/ErrorBoundary.d.ts +38 -0
  62. package/dist/components/ErrorBoundary.d.ts.map +1 -0
  63. package/dist/components/ErrorBoundary.js +46 -0
  64. package/dist/components/ErrorBoundary.js.map +1 -0
  65. package/dist/components/Font.d.ts +57 -0
  66. package/dist/components/Font.d.ts.map +1 -0
  67. package/dist/components/Font.js +33 -0
  68. package/dist/components/Font.js.map +1 -0
  69. package/dist/components/Image.d.ts +74 -0
  70. package/dist/components/Image.d.ts.map +1 -0
  71. package/dist/components/Image.js +85 -0
  72. package/dist/components/Image.js.map +1 -0
  73. package/dist/components/Link.d.ts +23 -0
  74. package/dist/components/Link.d.ts.map +1 -0
  75. package/dist/components/Link.js +48 -0
  76. package/dist/components/Link.js.map +1 -0
  77. package/dist/components/Script.d.ts +37 -0
  78. package/dist/components/Script.d.ts.map +1 -0
  79. package/dist/components/Script.js +70 -0
  80. package/dist/components/Script.js.map +1 -0
  81. package/dist/components/index.d.ts +10 -0
  82. package/dist/components/index.d.ts.map +1 -0
  83. package/dist/components/index.js +6 -0
  84. package/dist/components/index.js.map +1 -0
  85. package/dist/i18n/i18n.test.d.ts +2 -0
  86. package/dist/i18n/i18n.test.d.ts.map +1 -0
  87. package/dist/i18n/i18n.test.js +132 -0
  88. package/dist/i18n/i18n.test.js.map +1 -0
  89. package/dist/i18n/index.d.ts +135 -0
  90. package/dist/i18n/index.d.ts.map +1 -0
  91. package/dist/i18n/index.js +189 -0
  92. package/dist/i18n/index.js.map +1 -0
  93. package/dist/index.d.ts +4 -0
  94. package/dist/index.d.ts.map +1 -0
  95. package/dist/index.js +3 -0
  96. package/dist/index.js.map +1 -0
  97. package/dist/router/code-router.d.ts +204 -0
  98. package/dist/router/code-router.d.ts.map +1 -0
  99. package/dist/router/code-router.js +258 -0
  100. package/dist/router/code-router.js.map +1 -0
  101. package/dist/router/code-router.test.d.ts +2 -0
  102. package/dist/router/code-router.test.d.ts.map +1 -0
  103. package/dist/router/code-router.test.js +128 -0
  104. package/dist/router/code-router.test.js.map +1 -0
  105. package/dist/router/index.d.ts +4 -0
  106. package/dist/router/index.d.ts.map +1 -0
  107. package/dist/router/index.js +2 -0
  108. package/dist/router/index.js.map +1 -0
  109. package/dist/router/manifest.d.ts +12 -0
  110. package/dist/router/manifest.d.ts.map +1 -0
  111. package/dist/router/manifest.js +2 -0
  112. package/dist/router/manifest.js.map +1 -0
  113. package/dist/server/app.d.ts +13 -0
  114. package/dist/server/app.d.ts.map +1 -0
  115. package/dist/server/app.js +407 -0
  116. package/dist/server/app.js.map +1 -0
  117. package/dist/server/cache.d.ts +99 -0
  118. package/dist/server/cache.d.ts.map +1 -0
  119. package/dist/server/cache.js +161 -0
  120. package/dist/server/cache.js.map +1 -0
  121. package/dist/server/cache.test.d.ts +2 -0
  122. package/dist/server/cache.test.d.ts.map +1 -0
  123. package/dist/server/cache.test.js +150 -0
  124. package/dist/server/cache.test.js.map +1 -0
  125. package/dist/server/csrf.d.ts +28 -0
  126. package/dist/server/csrf.d.ts.map +1 -0
  127. package/dist/server/csrf.js +66 -0
  128. package/dist/server/csrf.js.map +1 -0
  129. package/dist/server/csrf.test.d.ts +2 -0
  130. package/dist/server/csrf.test.d.ts.map +1 -0
  131. package/dist/server/csrf.test.js +154 -0
  132. package/dist/server/csrf.test.js.map +1 -0
  133. package/dist/server/image.d.ts +18 -0
  134. package/dist/server/image.d.ts.map +1 -0
  135. package/dist/server/image.js +97 -0
  136. package/dist/server/image.js.map +1 -0
  137. package/dist/server/index.d.ts +57 -0
  138. package/dist/server/index.d.ts.map +1 -0
  139. package/dist/server/index.js +58 -0
  140. package/dist/server/index.js.map +1 -0
  141. package/dist/server/middleware.d.ts +53 -0
  142. package/dist/server/middleware.d.ts.map +1 -0
  143. package/dist/server/middleware.js +80 -0
  144. package/dist/server/middleware.js.map +1 -0
  145. package/dist/server/middleware.test.d.ts +2 -0
  146. package/dist/server/middleware.test.d.ts.map +1 -0
  147. package/dist/server/middleware.test.js +125 -0
  148. package/dist/server/middleware.test.js.map +1 -0
  149. package/dist/server/revalidate.d.ts +49 -0
  150. package/dist/server/revalidate.d.ts.map +1 -0
  151. package/dist/server/revalidate.js +62 -0
  152. package/dist/server/revalidate.js.map +1 -0
  153. package/dist/server/revalidate.test.d.ts +2 -0
  154. package/dist/server/revalidate.test.d.ts.map +1 -0
  155. package/dist/server/revalidate.test.js +93 -0
  156. package/dist/server/revalidate.test.js.map +1 -0
  157. package/dist/server/server-fn.test.d.ts +2 -0
  158. package/dist/server/server-fn.test.d.ts.map +1 -0
  159. package/dist/server/server-fn.test.js +105 -0
  160. package/dist/server/server-fn.test.js.map +1 -0
  161. package/dist/server/sitemap.d.ts +9 -0
  162. package/dist/server/sitemap.d.ts.map +1 -0
  163. package/dist/server/sitemap.js +26 -0
  164. package/dist/server/sitemap.js.map +1 -0
  165. package/dist/server/sitemap.test.d.ts +2 -0
  166. package/dist/server/sitemap.test.d.ts.map +1 -0
  167. package/dist/server/sitemap.test.js +61 -0
  168. package/dist/server/sitemap.test.js.map +1 -0
  169. package/dist/server/sse.d.ts +59 -0
  170. package/dist/server/sse.d.ts.map +1 -0
  171. package/dist/server/sse.js +91 -0
  172. package/dist/server/sse.js.map +1 -0
  173. package/dist/server/sse.test.d.ts +2 -0
  174. package/dist/server/sse.test.d.ts.map +1 -0
  175. package/dist/server/sse.test.js +68 -0
  176. package/dist/server/sse.test.js.map +1 -0
  177. package/dist/signals/index.d.ts +101 -0
  178. package/dist/signals/index.d.ts.map +1 -0
  179. package/dist/signals/index.js +149 -0
  180. package/dist/signals/index.js.map +1 -0
  181. package/dist/signals/signals.test.d.ts +2 -0
  182. package/dist/signals/signals.test.d.ts.map +1 -0
  183. package/dist/signals/signals.test.js +146 -0
  184. package/dist/signals/signals.test.js.map +1 -0
  185. package/dist/ssr/html.d.ts +27 -0
  186. package/dist/ssr/html.d.ts.map +1 -0
  187. package/dist/ssr/html.js +107 -0
  188. package/dist/ssr/html.js.map +1 -0
  189. package/dist/ssr/html.test.d.ts +2 -0
  190. package/dist/ssr/html.test.d.ts.map +1 -0
  191. package/dist/ssr/html.test.js +178 -0
  192. package/dist/ssr/html.test.js.map +1 -0
  193. package/dist/ssr/render.d.ts +46 -0
  194. package/dist/ssr/render.d.ts.map +1 -0
  195. package/dist/ssr/render.js +87 -0
  196. package/dist/ssr/render.js.map +1 -0
  197. package/dist/ssr/router-dev.d.ts +60 -0
  198. package/dist/ssr/router-dev.d.ts.map +1 -0
  199. package/dist/ssr/router-dev.js +205 -0
  200. package/dist/ssr/router-dev.js.map +1 -0
  201. package/dist/ssr/router-dev.test.d.ts +2 -0
  202. package/dist/ssr/router-dev.test.d.ts.map +1 -0
  203. package/dist/ssr/router-dev.test.js +189 -0
  204. package/dist/ssr/router-dev.test.js.map +1 -0
  205. package/dist/test/index.d.ts +93 -0
  206. package/dist/test/index.d.ts.map +1 -0
  207. package/dist/test/index.js +146 -0
  208. package/dist/test/index.js.map +1 -0
  209. package/dist/types/index.d.ts +117 -0
  210. package/dist/types/index.d.ts.map +1 -0
  211. package/dist/types/index.js +2 -0
  212. package/dist/types/index.js.map +1 -0
  213. package/dist/types/napi.d.ts +15 -0
  214. package/dist/types/napi.d.ts.map +1 -0
  215. package/dist/types/napi.js +2 -0
  216. package/dist/types/napi.js.map +1 -0
  217. package/package.json +107 -0
  218. package/src/adapters/cloudflare.ts +30 -0
  219. package/src/adapters/deno.ts +21 -0
  220. package/src/adapters/web.ts +259 -0
  221. package/src/cli.ts +68 -0
  222. package/src/client/hooks.test.ts +54 -0
  223. package/src/client/hooks.ts +329 -0
  224. package/src/client/index.ts +5 -0
  225. package/src/client/offline-sw.ts +191 -0
  226. package/src/client/offline.ts +114 -0
  227. package/src/client/provider.tsx +14 -0
  228. package/src/commands/build.ts +201 -0
  229. package/src/commands/dev.ts +509 -0
  230. package/src/commands/info.ts +111 -0
  231. package/src/commands/ssg.ts +177 -0
  232. package/src/commands/start.ts +32 -0
  233. package/src/commands/test.ts +102 -0
  234. package/src/components/ErrorBoundary.tsx +73 -0
  235. package/src/components/Font.tsx +100 -0
  236. package/src/components/Image.tsx +141 -0
  237. package/src/components/Link.tsx +64 -0
  238. package/src/components/Script.tsx +97 -0
  239. package/src/components/index.ts +9 -0
  240. package/src/i18n/i18n.test.tsx +169 -0
  241. package/src/i18n/index.tsx +256 -0
  242. package/src/index.ts +10 -0
  243. package/src/router/code-router.test.ts +146 -0
  244. package/src/router/code-router.tsx +459 -0
  245. package/src/router/index.ts +18 -0
  246. package/src/router/manifest.ts +13 -0
  247. package/src/server/app.ts +466 -0
  248. package/src/server/cache.test.ts +192 -0
  249. package/src/server/cache.ts +195 -0
  250. package/src/server/csrf.test.ts +199 -0
  251. package/src/server/csrf.ts +80 -0
  252. package/src/server/image.ts +112 -0
  253. package/src/server/index.ts +144 -0
  254. package/src/server/middleware.test.ts +151 -0
  255. package/src/server/middleware.ts +95 -0
  256. package/src/server/revalidate.test.ts +106 -0
  257. package/src/server/revalidate.ts +75 -0
  258. package/src/server/server-fn.test.ts +127 -0
  259. package/src/server/sitemap.test.ts +68 -0
  260. package/src/server/sitemap.ts +30 -0
  261. package/src/server/sse.test.ts +81 -0
  262. package/src/server/sse.ts +110 -0
  263. package/src/signals/index.ts +177 -0
  264. package/src/signals/signals.test.ts +164 -0
  265. package/src/ssr/html.test.ts +200 -0
  266. package/src/ssr/html.ts +140 -0
  267. package/src/ssr/render.ts +144 -0
  268. package/src/ssr/router-dev.test.ts +230 -0
  269. package/src/ssr/router-dev.ts +229 -0
  270. package/src/test/index.ts +206 -0
  271. package/src/types/compiler.d.ts +25 -0
  272. package/src/types/index.ts +147 -0
  273. package/src/types/napi.ts +20 -0
  274. package/src/types/plugins.d.ts +3 -0
  275. package/tsconfig.json +11 -0
  276. package/tsconfig.tsbuildinfo +1 -0
  277. package/vitest.config.ts +32 -0
@@ -0,0 +1,97 @@
1
+ import { readFile, access } from "node:fs/promises";
2
+ import { resolve } from "node:path";
3
+ /**
4
+ * Handle `/_alabjs/image` requests using the Rust image optimiser (alab-napi).
5
+ *
6
+ * Node.js reads the source file from `public/` then passes the raw bytes to
7
+ * Rust — same pattern as snapbolt-cli. Rust decodes, resizes, and encodes to
8
+ * WebP (libwebp-sys with `native` feature, or pure-Rust fallback).
9
+ *
10
+ * Query params:
11
+ * src — path relative to the project's `public/` directory (required)
12
+ * w — target width in pixels (required)
13
+ * q — quality 1–100 (default: 80)
14
+ * fmt — "webp" (default) | "jpeg" | "png"
15
+ *
16
+ * Cache-Control is set to 1 year / immutable for optimised responses.
17
+ */
18
+ export async function handleImageRequest(req, res, publicDir) {
19
+ const url = new URL(req.url ?? "/", "http://localhost");
20
+ const src = url.searchParams.get("src");
21
+ const wParam = url.searchParams.get("w");
22
+ const quality = Math.max(1, Math.min(100, parseInt(url.searchParams.get("q") ?? "80", 10)));
23
+ const fmt = url.searchParams.get("fmt") ?? "webp";
24
+ if (!src || !wParam) {
25
+ res.statusCode = 400;
26
+ res.end("[alabjs] Missing src or w parameter");
27
+ return;
28
+ }
29
+ const width = parseInt(wParam, 10);
30
+ if (!Number.isFinite(width) || width < 1 || width > 4096) {
31
+ res.statusCode = 400;
32
+ res.end("[alabjs] Invalid width — must be 1–4096");
33
+ return;
34
+ }
35
+ // Prevent path traversal
36
+ const safeSrc = src.replace(/\.\./g, "").replace(/^\/+/, "");
37
+ const filePath = resolve(publicDir, safeSrc);
38
+ if (!filePath.startsWith(publicDir)) {
39
+ res.statusCode = 403;
40
+ res.end("[alabjs] Forbidden");
41
+ return;
42
+ }
43
+ try {
44
+ await access(filePath);
45
+ }
46
+ catch {
47
+ res.statusCode = 404;
48
+ res.end("[alabjs] Image not found");
49
+ return;
50
+ }
51
+ const input = await readFile(filePath);
52
+ // Load napi binding (built by `cargo build --release -p alab-napi`).
53
+ // Fall back to serving the raw file when the binary isn't available so that
54
+ // images still load during development without the Rust toolchain.
55
+ let napi = null;
56
+ try {
57
+ const mod = await import("@alabjs/compiler");
58
+ napi = (mod.default ?? mod);
59
+ }
60
+ catch {
61
+ // napi not built — will serve raw file below.
62
+ }
63
+ if (!napi) {
64
+ const ext = safeSrc.split(".").pop()?.toLowerCase() ?? "";
65
+ const mime = ext === "jpg" || ext === "jpeg" ? "image/jpeg"
66
+ : ext === "png" ? "image/png"
67
+ : ext === "gif" ? "image/gif"
68
+ : ext === "webp" ? "image/webp"
69
+ : ext === "avif" ? "image/avif"
70
+ : "application/octet-stream";
71
+ res.statusCode = 200;
72
+ res.setHeader("content-type", mime);
73
+ res.setHeader("cache-control", "no-store");
74
+ res.setHeader("content-length", input.length);
75
+ res.end(input);
76
+ return;
77
+ }
78
+ try {
79
+ // Pass raw bytes to Rust — decode + resize + encode on a blocking thread pool.
80
+ const optimised = await napi.optimizeImage(input, quality, width, undefined, fmt);
81
+ const mime = fmt === "jpeg" || fmt === "jpg" ? "image/jpeg"
82
+ : fmt === "png" ? "image/png"
83
+ : "image/webp";
84
+ res.statusCode = 200;
85
+ res.setHeader("content-type", mime);
86
+ res.setHeader("cache-control", "public, max-age=31536000, immutable");
87
+ res.setHeader("content-length", optimised.length);
88
+ res.end(optimised);
89
+ }
90
+ catch (err) {
91
+ const msg = err instanceof Error ? err.message : String(err);
92
+ console.error(`[alabjs] image optimisation failed — src=${safeSrc} w=${width} fmt=${fmt}: ${msg}`);
93
+ res.statusCode = 500;
94
+ res.end("[alabjs] Image optimisation failed — check server logs");
95
+ }
96
+ }
97
+ //# sourceMappingURL=image.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"image.js","sourceRoot":"","sources":["../../src/server/image.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,57 @@
1
+ import type { ServerFn, ServerFnContext } from "../types/index.js";
2
+ export interface ServerFnCacheOptions<Input> {
3
+ /** How long to keep the result in seconds. Required when `cache` is set. */
4
+ ttl: number;
5
+ /**
6
+ * Tags for group invalidation via `invalidateCache({ tags })`.
7
+ * Can be a static array or a function that receives the input and returns tags,
8
+ * allowing per-argument granularity like `post:${input.id}`.
9
+ */
10
+ tags?: string[] | ((input: Input) => string[]);
11
+ }
12
+ export interface DefineServerFnOptions<Input> {
13
+ /** Opt-in result caching. Nothing is cached unless this is specified. */
14
+ cache?: ServerFnCacheOptions<Input>;
15
+ }
16
+ interface ZodLike<T> {
17
+ safeParse(input: unknown): {
18
+ success: true;
19
+ data: T;
20
+ } | {
21
+ success: false;
22
+ error: unknown;
23
+ };
24
+ }
25
+ /**
26
+ * Define a server-only function (no input schema).
27
+ *
28
+ * @example
29
+ * ```ts
30
+ * export const getPosts = defineServerFn(async () => db.posts.findAll());
31
+ * ```
32
+ */
33
+ export declare function defineServerFn<Input = undefined, Output = unknown>(handler: (ctx: ServerFnContext, input: Input) => Promise<Output>, options?: DefineServerFnOptions<Input>): ServerFn<Input, Output>;
34
+ /**
35
+ * Define a server-only function with **Zod input validation**.
36
+ *
37
+ * If validation fails, an HTTP 422 response with the Zod error is returned
38
+ * automatically — the handler is never called with invalid data.
39
+ *
40
+ * The client's `useMutation` / `useServerData` will receive
41
+ * `{ zodError: ZodError }` instead of throwing an untyped error.
42
+ *
43
+ * @example
44
+ * ```ts
45
+ * import { z } from "zod";
46
+ *
47
+ * export const createPost = defineServerFn(
48
+ * z.object({ title: z.string().min(1), body: z.string() }),
49
+ * async ({ params }, input) => db.posts.create(input),
50
+ * { cache: { ttl: 0, tags: ["posts"] } },
51
+ * );
52
+ * ```
53
+ */
54
+ export declare function defineServerFn<Schema extends ZodLike<unknown>, Output = unknown>(schema: Schema, handler: (ctx: ServerFnContext, input: Schema extends ZodLike<infer T> ? T : never) => Promise<Output>, options?: DefineServerFnOptions<Schema extends ZodLike<infer T> ? T : never>): ServerFn<Schema extends ZodLike<infer T> ? T : never, Output>;
55
+ export { defineSSEHandler } from "./sse.js";
56
+ export type { SSEEvent } from "./sse.js";
57
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAKnE,MAAM,WAAW,oBAAoB,CAAC,KAAK;IACzC,4EAA4E;IAC5E,GAAG,EAAE,MAAM,CAAC;IACZ;;;;OAIG;IACH,IAAI,CAAC,EAAE,MAAM,EAAE,GAAG,CAAC,CAAC,KAAK,EAAE,KAAK,KAAK,MAAM,EAAE,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,qBAAqB,CAAC,KAAK;IAC1C,yEAAyE;IACzE,KAAK,CAAC,EAAE,oBAAoB,CAAC,KAAK,CAAC,CAAC;CACrC;AAID,UAAU,OAAO,CAAC,CAAC;IACjB,SAAS,CAAC,KAAK,EAAE,OAAO,GAAG;QAAE,OAAO,EAAE,IAAI,CAAC;QAAC,IAAI,EAAE,CAAC,CAAA;KAAE,GAAG;QAAE,OAAO,EAAE,KAAK,CAAC;QAAC,KAAK,EAAE,OAAO,CAAA;KAAE,CAAC;CAC5F;AAaD;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,GAAG,SAAS,EAAE,MAAM,GAAG,OAAO,EAChE,OAAO,EAAE,CAAC,GAAG,EAAE,eAAe,EAAE,KAAK,EAAE,KAAK,KAAK,OAAO,CAAC,MAAM,CAAC,EAChE,OAAO,CAAC,EAAE,qBAAqB,CAAC,KAAK,CAAC,GACrC,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;AAE3B;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,cAAc,CAAC,MAAM,SAAS,OAAO,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,EAC9E,MAAM,EAAE,MAAM,EACd,OAAO,EAAE,CACP,GAAG,EAAE,eAAe,EACpB,KAAK,EAAE,MAAM,SAAS,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,KAC/C,OAAO,CAAC,MAAM,CAAC,EACpB,OAAO,CAAC,EAAE,qBAAqB,CAAC,MAAM,SAAS,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC,GAC3E,QAAQ,CAAC,MAAM,SAAS,OAAO,CAAC,MAAM,CAAC,CAAC,GAAG,CAAC,GAAG,KAAK,EAAE,MAAM,CAAC,CAAC;AAgEjE,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC;AAC5C,YAAY,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,58 @@
1
+ import { getCached, setCache, CACHE_MISS } from "./cache.js";
2
+ function isZodSchema(v) {
3
+ return (v !== null &&
4
+ typeof v === "object" &&
5
+ "safeParse" in v &&
6
+ typeof v["safeParse"] === "function");
7
+ }
8
+ // ─── Implementation ───────────────────────────────────────────────────────────
9
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
10
+ export function defineServerFn(...args) {
11
+ const [schemaOrHandler, handlerOrOptions, maybeOptions] = args;
12
+ let schema = null;
13
+ let handler;
14
+ let options;
15
+ if (isZodSchema(schemaOrHandler)) {
16
+ schema = schemaOrHandler;
17
+ handler = handlerOrOptions;
18
+ options = maybeOptions;
19
+ }
20
+ else {
21
+ handler = schemaOrHandler;
22
+ options = handlerOrOptions;
23
+ }
24
+ const cacheOpts = options?.cache;
25
+ const wrapped = async (ctx, input) => {
26
+ // ── Zod validation ───────────────────────────────────────────────────────
27
+ let validatedInput = input;
28
+ if (schema) {
29
+ const result = schema.safeParse(input);
30
+ if (!result.success) {
31
+ // Throw a structured validation error that the dev/prod server will
32
+ // serialise as { zodError: ... } with HTTP 422.
33
+ const err = new Error("[alabjs] Validation failed");
34
+ err.zodError = result.error;
35
+ throw err;
36
+ }
37
+ validatedInput = result.data;
38
+ }
39
+ // ── Cache lookup ─────────────────────────────────────────────────────────
40
+ if (cacheOpts) {
41
+ const cacheKey = `${handler.name}:${JSON.stringify(validatedInput)}`;
42
+ const cached = getCached(cacheKey);
43
+ if (cached !== CACHE_MISS)
44
+ return cached;
45
+ const data = await handler(ctx, validatedInput);
46
+ const tags = typeof cacheOpts.tags === "function"
47
+ ? cacheOpts.tags(validatedInput)
48
+ : (cacheOpts.tags ?? []);
49
+ setCache(cacheKey, data, { ttl: cacheOpts.ttl, tags });
50
+ return data;
51
+ }
52
+ return handler(ctx, validatedInput);
53
+ };
54
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
55
+ return wrapped;
56
+ }
57
+ export { defineSSEHandler } from "./sse.js";
58
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/server/index.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,SAAS,EAAE,QAAQ,EAAE,UAAU,EAAE,MAAM,YAAY,CAAC;AA0B7D,SAAS,WAAW,CAAC,CAAU;IAC7B,OAAO,CACL,CAAC,KAAK,IAAI;QACV,OAAO,CAAC,KAAK,QAAQ;QACrB,WAAW,IAAI,CAAC;QAChB,OAAQ,CAA6B,CAAC,WAAW,CAAC,KAAK,UAAU,CAClE,CAAC;AACJ,CAAC;AA8CD,iFAAiF;AAEjF,8DAA8D;AAC9D,MAAM,UAAU,cAAc,CAAC,GAAG,IAAW;IAC3C,MAAM,CAAC,eAAe,EAAE,gBAAgB,EAAE,YAAY,CAAC,GAAG,IAIzD,CAAC;IACF,IAAI,MAAM,GAA4B,IAAI,CAAC;IAC3C,IAAI,OAAiD,CAAC;IACtD,IAAI,OAAmD,CAAC;IAExD,IAAI,WAAW,CAAC,eAAe,CAAC,EAAE,CAAC;QACjC,MAAM,GAAG,eAAe,CAAC;QACzB,OAAO,GAAG,gBAA4D,CAAC;QACvE,OAAO,GAAG,YAAY,CAAC;IACzB,CAAC;SAAM,CAAC;QACN,OAAO,GAAG,eAA2D,CAAC;QACtE,OAAO,GAAG,gBAA8D,CAAC;IAC3E,CAAC;IAED,MAAM,SAAS,GAAG,OAAO,EAAE,KAAK,CAAC;IAEjC,MAAM,OAAO,GAAG,KAAK,EAAE,GAAoB,EAAE,KAAc,EAAoB,EAAE;QAC/E,4EAA4E;QAC5E,IAAI,cAAc,GAAG,KAAK,CAAC;QAC3B,IAAI,MAAM,EAAE,CAAC;YACX,MAAM,MAAM,GAAG,MAAM,CAAC,SAAS,CAAC,KAAK,CAAC,CAAC;YACvC,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;gBACpB,oEAAoE;gBACpE,gDAAgD;gBAChD,MAAM,GAAG,GAAG,IAAI,KAAK,CAAC,4BAA4B,CAAkC,CAAC;gBACrF,GAAG,CAAC,QAAQ,GAAG,MAAM,CAAC,KAAK,CAAC;gBAC5B,MAAM,GAAG,CAAC;YACZ,CAAC;YACD,cAAc,GAAG,MAAM,CAAC,IAAI,CAAC;QAC/B,CAAC;QAED,4EAA4E;QAC5E,IAAI,SAAS,EAAE,CAAC;YACd,MAAM,QAAQ,GAAG,GAAG,OAAO,CAAC,IAAI,IAAI,IAAI,CAAC,SAAS,CAAC,cAAc,CAAC,EAAE,CAAC;YACrE,MAAM,MAAM,GAAG,SAAS,CAAC,QAAQ,CAAC,CAAC;YACnC,IAAI,MAAM,KAAK,UAAU;gBAAE,OAAO,MAAM,CAAC;YAEzC,MAAM,IAAI,GAAG,MAAM,OAAO,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;YAEhD,MAAM,IAAI,GACR,OAAO,SAAS,CAAC,IAAI,KAAK,UAAU;gBAClC,CAAC,CAAC,SAAS,CAAC,IAAI,CAAC,cAAc,CAAC;gBAChC,CAAC,CAAC,CAAC,SAAS,CAAC,IAAI,IAAI,EAAE,CAAC,CAAC;YAC7B,QAAQ,CAAC,QAAQ,EAAE,IAAI,EAAE,EAAE,GAAG,EAAE,SAAS,CAAC,GAAG,EAAE,IAAI,EAAE,CAAC,CAAC;YACvD,OAAO,IAAI,CAAC;QACd,CAAC;QAED,OAAO,OAAO,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;IACtC,CAAC,CAAC;IAEF,8DAA8D;IAC9D,OAAO,OAA6B,CAAC;AACvC,CAAC;AAED,OAAO,EAAE,gBAAgB,EAAE,MAAM,UAAU,CAAC"}
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Middleware runner — loads and executes the user's `middleware.ts` file.
3
+ *
4
+ * Convention (mirrors Next.js):
5
+ * ```ts
6
+ * // middleware.ts (project root)
7
+ * export async function middleware(req: Request): Promise<Response | void> {
8
+ * if (!isAuthed(req)) return Response.redirect(new URL("/login", req.url));
9
+ * // return nothing (or return NextResponse.next()) to continue
10
+ * }
11
+ *
12
+ * // Optional — restrict which paths the middleware runs on.
13
+ * // Patterns support * and ** wildcards (like path-to-regexp lite).
14
+ * export const config = {
15
+ * matcher: ["/dashboard/:path*", "/api/:path*"],
16
+ * };
17
+ * ```
18
+ */
19
+ /** Redirect the request to a new URL (defaults to 307 Temporary Redirect). */
20
+ export declare function redirect(url: string, status?: 301 | 302 | 307 | 308): Response;
21
+ /**
22
+ * Pass the request through to the next handler (no-op).
23
+ * Return the result of `next()` from your middleware to signal "continue".
24
+ */
25
+ export declare function next(): null;
26
+ export interface MiddlewareModule {
27
+ middleware: (req: Request) => Promise<Response | null | void> | Response | null | void;
28
+ config?: {
29
+ matcher?: string[];
30
+ };
31
+ }
32
+ /**
33
+ * Convert a matcher pattern like `/dashboard/:path*` or `/api/*` to a RegExp.
34
+ *
35
+ * Supported syntax:
36
+ * - `:param` — one path segment (any chars except `/`)
37
+ * - `*` — one path segment wildcard
38
+ * - `**` — zero or more segments (greedy)
39
+ * - `:param*` — zero or more remaining segments (Next.js `:path*` style)
40
+ */
41
+ export declare function matcherToRegex(pattern: string): RegExp;
42
+ /**
43
+ * Test whether a pathname matches any of the given matcher patterns.
44
+ * If no patterns are provided, the middleware runs on every request.
45
+ */
46
+ export declare function matchesMiddleware(pathname: string, matchers?: string[]): boolean;
47
+ /**
48
+ * Run the user middleware against the current request.
49
+ * Returns a `Response` if the middleware handled the request (redirect, early
50
+ * return, etc.) or `null` if it passed through (return nothing / undefined).
51
+ */
52
+ export declare function runMiddleware(mod: MiddlewareModule, req: Request): Promise<Response | null>;
53
+ //# sourceMappingURL=middleware.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.d.ts","sourceRoot":"","sources":["../../src/server/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAIH,8EAA8E;AAC9E,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,MAAM,GAAE,GAAG,GAAG,GAAG,GAAG,GAAG,GAAG,GAAS,GAAG,QAAQ,CAEnF;AAED;;;GAGG;AACH,wBAAgB,IAAI,IAAI,IAAI,CAE3B;AAID,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,CAAC,GAAG,EAAE,OAAO,KAAK,OAAO,CAAC,QAAQ,GAAG,IAAI,GAAG,IAAI,CAAC,GAAG,QAAQ,GAAG,IAAI,GAAG,IAAI,CAAC;IACvF,MAAM,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAA;KAAE,CAAC;CACjC;AAED;;;;;;;;GAQG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAgBtD;AAED;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,MAAM,EAAE,GAAG,OAAO,CAGhF;AAED;;;;GAIG;AACH,wBAAsB,aAAa,CACjC,GAAG,EAAE,gBAAgB,EACrB,GAAG,EAAE,OAAO,GACX,OAAO,CAAC,QAAQ,GAAG,IAAI,CAAC,CAS1B"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Middleware runner — loads and executes the user's `middleware.ts` file.
3
+ *
4
+ * Convention (mirrors Next.js):
5
+ * ```ts
6
+ * // middleware.ts (project root)
7
+ * export async function middleware(req: Request): Promise<Response | void> {
8
+ * if (!isAuthed(req)) return Response.redirect(new URL("/login", req.url));
9
+ * // return nothing (or return NextResponse.next()) to continue
10
+ * }
11
+ *
12
+ * // Optional — restrict which paths the middleware runs on.
13
+ * // Patterns support * and ** wildcards (like path-to-regexp lite).
14
+ * export const config = {
15
+ * matcher: ["/dashboard/:path*", "/api/:path*"],
16
+ * };
17
+ * ```
18
+ */
19
+ // ─── Public helpers (importable from "alabjs/middleware") ───────────────────────
20
+ /** Redirect the request to a new URL (defaults to 307 Temporary Redirect). */
21
+ export function redirect(url, status = 307) {
22
+ return Response.redirect(url, status);
23
+ }
24
+ /**
25
+ * Pass the request through to the next handler (no-op).
26
+ * Return the result of `next()` from your middleware to signal "continue".
27
+ */
28
+ export function next() {
29
+ return null;
30
+ }
31
+ /**
32
+ * Convert a matcher pattern like `/dashboard/:path*` or `/api/*` to a RegExp.
33
+ *
34
+ * Supported syntax:
35
+ * - `:param` — one path segment (any chars except `/`)
36
+ * - `*` — one path segment wildcard
37
+ * - `**` — zero or more segments (greedy)
38
+ * - `:param*` — zero or more remaining segments (Next.js `:path*` style)
39
+ */
40
+ export function matcherToRegex(pattern) {
41
+ // Escape regex special chars except the ones we handle manually
42
+ const escaped = pattern
43
+ .replace(/[.+*^${}()|[\]\\]/g, "\\$&")
44
+ // :param* → zero-or-more segments (greedy, optional slash)
45
+ .replace(/\/:[a-zA-Z_][a-zA-Z0-9_]*\\\*/g, "(?:/.*)?")
46
+ .replace(/:[a-zA-Z_][a-zA-Z0-9_]*\\\*/g, "(?:/.*)?")
47
+ // :param → single segment
48
+ .replace(/:[a-zA-Z_][a-zA-Z0-9_]*/g, "[^/]+")
49
+ // ** → zero-or-more segments (greedy, optional slash)
50
+ .replace(/\/\*\*\\\*/g, "(?:/.*)?")
51
+ .replace(/\\\*\\\*/g, ".*")
52
+ // * → single segment
53
+ .replace(/\\\*/g, "[^/]+");
54
+ return new RegExp(`^${escaped}\\/?$`);
55
+ }
56
+ /**
57
+ * Test whether a pathname matches any of the given matcher patterns.
58
+ * If no patterns are provided, the middleware runs on every request.
59
+ */
60
+ export function matchesMiddleware(pathname, matchers) {
61
+ if (!matchers || matchers.length === 0)
62
+ return true;
63
+ return matchers.some((pattern) => matcherToRegex(pattern).test(pathname));
64
+ }
65
+ /**
66
+ * Run the user middleware against the current request.
67
+ * Returns a `Response` if the middleware handled the request (redirect, early
68
+ * return, etc.) or `null` if it passed through (return nothing / undefined).
69
+ */
70
+ export async function runMiddleware(mod, req) {
71
+ const { middleware, config } = mod;
72
+ const pathname = new URL(req.url).pathname;
73
+ if (!matchesMiddleware(pathname, config?.matcher))
74
+ return null;
75
+ const result = await middleware(req);
76
+ if (result instanceof Response)
77
+ return result;
78
+ return null;
79
+ }
80
+ //# sourceMappingURL=middleware.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.js","sourceRoot":"","sources":["../../src/server/middleware.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;GAiBG;AAEH,mFAAmF;AAEnF,8EAA8E;AAC9E,MAAM,UAAU,QAAQ,CAAC,GAAW,EAAE,SAAgC,GAAG;IACvE,OAAO,QAAQ,CAAC,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC,CAAC;AACxC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,IAAI;IAClB,OAAO,IAAI,CAAC;AACd,CAAC;AASD;;;;;;;;GAQG;AACH,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,gEAAgE;IAChE,MAAM,OAAO,GAAG,OAAO;SACpB,OAAO,CAAC,oBAAoB,EAAE,MAAM,CAAC;QACtC,2DAA2D;SAC1D,OAAO,CAAC,gCAAgC,EAAE,UAAU,CAAC;SACrD,OAAO,CAAC,8BAA8B,EAAE,UAAU,CAAC;QACpD,2BAA2B;SAC1B,OAAO,CAAC,0BAA0B,EAAE,OAAO,CAAC;QAC7C,sDAAsD;SACrD,OAAO,CAAC,aAAa,EAAE,UAAU,CAAC;SAClC,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC;QAC3B,qBAAqB;SACpB,OAAO,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC;IAE7B,OAAO,IAAI,MAAM,CAAC,IAAI,OAAO,OAAO,CAAC,CAAC;AACxC,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,QAAgB,EAAE,QAAmB;IACrE,IAAI,CAAC,QAAQ,IAAI,QAAQ,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACpD,OAAO,QAAQ,CAAC,IAAI,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC;AAC5E,CAAC;AAED;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,GAAqB,EACrB,GAAY;IAEZ,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,GAAG,GAAG,CAAC;IACnC,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC;IAE3C,IAAI,CAAC,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IAE/D,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,GAAG,CAAC,CAAC;IACrC,IAAI,MAAM,YAAY,QAAQ;QAAE,OAAO,MAAM,CAAC;IAC9C,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=middleware.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.test.d.ts","sourceRoot":"","sources":["../../src/server/middleware.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,125 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { matcherToRegex, matchesMiddleware, runMiddleware, redirect, next, } from "./middleware.js";
3
+ // ─── matcherToRegex ───────────────────────────────────────────────────────────
4
+ describe("matcherToRegex", () => {
5
+ it("matches exact static path", () => {
6
+ const re = matcherToRegex("/dashboard");
7
+ expect(re.test("/dashboard")).toBe(true);
8
+ expect(re.test("/dashboard/")).toBe(true);
9
+ expect(re.test("/other")).toBe(false);
10
+ });
11
+ it("handles :param as single segment", () => {
12
+ const re = matcherToRegex("/users/:id");
13
+ expect(re.test("/users/123")).toBe(true);
14
+ expect(re.test("/users/abc")).toBe(true);
15
+ expect(re.test("/users/")).toBe(false);
16
+ expect(re.test("/users/123/extra")).toBe(false);
17
+ });
18
+ it("handles * as single segment wildcard", () => {
19
+ const re = matcherToRegex("/api/*");
20
+ expect(re.test("/api/health")).toBe(true);
21
+ expect(re.test("/api/users")).toBe(true);
22
+ expect(re.test("/api/")).toBe(false);
23
+ expect(re.test("/api/a/b")).toBe(false);
24
+ });
25
+ it("handles ** as zero-or-more segments", () => {
26
+ const re = matcherToRegex("/docs/**");
27
+ expect(re.test("/docs/")).toBe(true);
28
+ expect(re.test("/docs/intro")).toBe(true);
29
+ expect(re.test("/docs/guides/auth")).toBe(true);
30
+ });
31
+ it("handles :param* (Next.js :path* style)", () => {
32
+ const re = matcherToRegex("/dashboard/:path*");
33
+ expect(re.test("/dashboard")).toBe(true);
34
+ expect(re.test("/dashboard/")).toBe(true);
35
+ expect(re.test("/dashboard/settings")).toBe(true);
36
+ expect(re.test("/dashboard/users/42")).toBe(true);
37
+ });
38
+ it("escapes special regex characters in static segments", () => {
39
+ const re = matcherToRegex("/search.html");
40
+ expect(re.test("/search.html")).toBe(true);
41
+ expect(re.test("/searchXhtml")).toBe(false);
42
+ });
43
+ });
44
+ // ─── matchesMiddleware ────────────────────────────────────────────────────────
45
+ describe("matchesMiddleware", () => {
46
+ it("matches all paths when no matchers provided", () => {
47
+ expect(matchesMiddleware("/anything")).toBe(true);
48
+ expect(matchesMiddleware("/anything", undefined)).toBe(true);
49
+ expect(matchesMiddleware("/anything", [])).toBe(true);
50
+ });
51
+ it("matches when pathname fits a pattern", () => {
52
+ const matchers = ["/dashboard/:path*", "/api/*"];
53
+ expect(matchesMiddleware("/dashboard/settings", matchers)).toBe(true);
54
+ expect(matchesMiddleware("/api/health", matchers)).toBe(true);
55
+ expect(matchesMiddleware("/login", matchers)).toBe(false);
56
+ });
57
+ });
58
+ // ─── redirect and next helpers ────────────────────────────────────────────────
59
+ describe("redirect", () => {
60
+ it("returns a Response with redirect status", () => {
61
+ const res = redirect("https://example.com/login");
62
+ expect(res).toBeInstanceOf(Response);
63
+ expect(res.status).toBe(307);
64
+ });
65
+ it("supports custom status codes", () => {
66
+ const res = redirect("https://example.com/login", 301);
67
+ expect(res.status).toBe(301);
68
+ });
69
+ });
70
+ describe("next", () => {
71
+ it("returns null", () => {
72
+ expect(next()).toBe(null);
73
+ });
74
+ });
75
+ // ─── runMiddleware ────────────────────────────────────────────────────────────
76
+ describe("runMiddleware", () => {
77
+ it("returns null if pathname does not match the matcher", async () => {
78
+ const mod = {
79
+ middleware: () => redirect("https://example.com/login"),
80
+ config: { matcher: ["/dashboard/:path*"] },
81
+ };
82
+ const req = new Request("http://localhost/login");
83
+ const result = await runMiddleware(mod, req);
84
+ expect(result).toBe(null);
85
+ });
86
+ it("returns Response when middleware redirects", async () => {
87
+ const mod = {
88
+ middleware: () => redirect("https://example.com/login"),
89
+ config: { matcher: ["/dashboard/:path*"] },
90
+ };
91
+ const req = new Request("http://localhost/dashboard/settings");
92
+ const result = await runMiddleware(mod, req);
93
+ expect(result).toBeInstanceOf(Response);
94
+ expect(result.status).toBe(307);
95
+ });
96
+ it("returns null when middleware passes through", async () => {
97
+ const mod = {
98
+ middleware: () => undefined,
99
+ };
100
+ const req = new Request("http://localhost/anything");
101
+ const result = await runMiddleware(mod, req);
102
+ expect(result).toBe(null);
103
+ });
104
+ it("returns null when middleware returns null (via next())", async () => {
105
+ const mod = {
106
+ middleware: () => next(),
107
+ };
108
+ const req = new Request("http://localhost/anything");
109
+ const result = await runMiddleware(mod, req);
110
+ expect(result).toBe(null);
111
+ });
112
+ it("runs on all paths when no config.matcher is set", async () => {
113
+ let called = false;
114
+ const mod = {
115
+ middleware: () => {
116
+ called = true;
117
+ return null;
118
+ },
119
+ };
120
+ const req = new Request("http://localhost/random/path");
121
+ await runMiddleware(mod, req);
122
+ expect(called).toBe(true);
123
+ });
124
+ });
125
+ //# sourceMappingURL=middleware.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"middleware.test.js","sourceRoot":"","sources":["../../src/server/middleware.test.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ /**
2
+ * On-demand ISR revalidation handler.
3
+ *
4
+ * Exposes `POST /_alabjs/revalidate` so CMS webhooks, deploy scripts, or
5
+ * API routes can purge the page HTML and/or server-function data cache
6
+ * without restarting the server.
7
+ *
8
+ * Authentication
9
+ * --------------
10
+ * Set `ALAB_REVALIDATE_SECRET` in your environment. Every request must then
11
+ * include `Authorization: Bearer <secret>`. If the env var is not set the
12
+ * endpoint is open — useful in development, unsafe in production.
13
+ *
14
+ * Request body (JSON) — supply one or more fields:
15
+ * ```json
16
+ * { "path": "/posts/1" } // purge a single page
17
+ * { "prefix": "/posts" } // purge /posts, /posts/1, /posts/2, …
18
+ * { "tags": ["posts", "post:1"] } // purge all entries tagged with any tag
19
+ * ```
20
+ *
21
+ * Response (200):
22
+ * ```json
23
+ * { "revalidated": true, "path": "/posts/1" }
24
+ * ```
25
+ */
26
+ export interface RevalidateBody {
27
+ /** Purge a single cached page path. */
28
+ path?: string;
29
+ /** Purge all cached pages whose path starts with this prefix. */
30
+ prefix?: string;
31
+ /** Purge all server-function and page cache entries carrying any of these tags. */
32
+ tags?: string[];
33
+ }
34
+ /** Returns `true` when the request is authorised to call the revalidate endpoint. */
35
+ export declare function checkRevalidateAuth(authorizationHeader: string | null | undefined): boolean;
36
+ /**
37
+ * Apply a revalidation request. Returns a result object on success or an
38
+ * `{ error }` object (with an HTTP status hint) on failure.
39
+ */
40
+ export declare function applyRevalidate(body: unknown): {
41
+ revalidated: true;
42
+ path?: string;
43
+ prefix?: string;
44
+ tags?: string[];
45
+ } | {
46
+ status: number;
47
+ error: string;
48
+ };
49
+ //# sourceMappingURL=revalidate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revalidate.d.ts","sourceRoot":"","sources":["../../src/server/revalidate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,MAAM,WAAW,cAAc;IAC7B,uCAAuC;IACvC,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iEAAiE;IACjE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,IAAI,CAAC,EAAE,MAAM,EAAE,CAAC;CACjB;AAED,qFAAqF;AACrF,wBAAgB,mBAAmB,CAAC,mBAAmB,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,GAAG,OAAO,CAO3F;AAED;;;GAGG;AACH,wBAAgB,eAAe,CAC7B,IAAI,EAAE,OAAO,GACZ;IAAE,WAAW,EAAE,IAAI,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,EAAE,CAAA;CAAE,GAAG;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAqB5G"}
@@ -0,0 +1,62 @@
1
+ /**
2
+ * On-demand ISR revalidation handler.
3
+ *
4
+ * Exposes `POST /_alabjs/revalidate` so CMS webhooks, deploy scripts, or
5
+ * API routes can purge the page HTML and/or server-function data cache
6
+ * without restarting the server.
7
+ *
8
+ * Authentication
9
+ * --------------
10
+ * Set `ALAB_REVALIDATE_SECRET` in your environment. Every request must then
11
+ * include `Authorization: Bearer <secret>`. If the env var is not set the
12
+ * endpoint is open — useful in development, unsafe in production.
13
+ *
14
+ * Request body (JSON) — supply one or more fields:
15
+ * ```json
16
+ * { "path": "/posts/1" } // purge a single page
17
+ * { "prefix": "/posts" } // purge /posts, /posts/1, /posts/2, …
18
+ * { "tags": ["posts", "post:1"] } // purge all entries tagged with any tag
19
+ * ```
20
+ *
21
+ * Response (200):
22
+ * ```json
23
+ * { "revalidated": true, "path": "/posts/1" }
24
+ * ```
25
+ */
26
+ import { revalidatePath, revalidatePathPrefix, revalidateTag } from "./cache.js";
27
+ /** Returns `true` when the request is authorised to call the revalidate endpoint. */
28
+ export function checkRevalidateAuth(authorizationHeader) {
29
+ const secret = process.env["ALAB_REVALIDATE_SECRET"];
30
+ if (!secret)
31
+ return true; // no secret configured — open endpoint
32
+ const provided = authorizationHeader?.startsWith("Bearer ")
33
+ ? authorizationHeader.slice(7)
34
+ : null;
35
+ return provided === secret;
36
+ }
37
+ /**
38
+ * Apply a revalidation request. Returns a result object on success or an
39
+ * `{ error }` object (with an HTTP status hint) on failure.
40
+ */
41
+ export function applyRevalidate(body) {
42
+ if (typeof body !== "object" || body === null) {
43
+ return { status: 400, error: "Request body must be a JSON object." };
44
+ }
45
+ const { path, prefix, tags } = body;
46
+ if (!path && !prefix && (!tags || tags.length === 0)) {
47
+ return { status: 400, error: "Provide at least one of: path, prefix, tags." };
48
+ }
49
+ if (path)
50
+ revalidatePath(path);
51
+ if (prefix)
52
+ revalidatePathPrefix(prefix);
53
+ if (tags?.length)
54
+ revalidateTag({ tags });
55
+ return {
56
+ revalidated: true,
57
+ ...(path !== undefined && { path }),
58
+ ...(prefix !== undefined && { prefix }),
59
+ ...(tags !== undefined && { tags }),
60
+ };
61
+ }
62
+ //# sourceMappingURL=revalidate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"revalidate.js","sourceRoot":"","sources":["../../src/server/revalidate.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAEH,OAAO,EAAE,cAAc,EAAE,oBAAoB,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAWjF,qFAAqF;AACrF,MAAM,UAAU,mBAAmB,CAAC,mBAA8C;IAChF,MAAM,MAAM,GAAG,OAAO,CAAC,GAAG,CAAC,wBAAwB,CAAC,CAAC;IACrD,IAAI,CAAC,MAAM;QAAE,OAAO,IAAI,CAAC,CAAC,uCAAuC;IACjE,MAAM,QAAQ,GAAG,mBAAmB,EAAE,UAAU,CAAC,SAAS,CAAC;QACzD,CAAC,CAAC,mBAAmB,CAAC,KAAK,CAAC,CAAC,CAAC;QAC9B,CAAC,CAAC,IAAI,CAAC;IACT,OAAO,QAAQ,KAAK,MAAM,CAAC;AAC7B,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,eAAe,CAC7B,IAAa;IAEb,IAAI,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,IAAI,EAAE,CAAC;QAC9C,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,qCAAqC,EAAE,CAAC;IACvE,CAAC;IAED,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,GAAG,IAAsB,CAAC;IAEtD,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,IAAI,CAAC,CAAC,IAAI,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,EAAE,CAAC;QACrD,OAAO,EAAE,MAAM,EAAE,GAAG,EAAE,KAAK,EAAE,8CAA8C,EAAE,CAAC;IAChF,CAAC;IAED,IAAI,IAAI;QAAE,cAAc,CAAC,IAAI,CAAC,CAAC;IAC/B,IAAI,MAAM;QAAE,oBAAoB,CAAC,MAAM,CAAC,CAAC;IACzC,IAAI,IAAI,EAAE,MAAM;QAAE,aAAa,CAAC,EAAE,IAAI,EAAE,CAAC,CAAC;IAE1C,OAAO;QACL,WAAW,EAAE,IAAI;QACjB,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,IAAI,EAAE,CAAC;QACnC,GAAG,CAAC,MAAM,KAAK,SAAS,IAAI,EAAE,MAAM,EAAE,CAAC;QACvC,GAAG,CAAC,IAAI,KAAK,SAAS,IAAI,EAAE,IAAI,EAAE,CAAC;KACpC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=revalidate.test.d.ts.map