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,220 @@
1
+ import { use, useReducer, useTransition, useCallback, useState, useEffect, useRef } from "react";
2
+ // Promise cache keyed by URL.
3
+ // • On the SERVER: cleared before each page render (via _clearALabSSRCache) so
4
+ // re-renders after Suspense resolution return the same promise object, which
5
+ // is required for renderToPipeableStream to correctly resolve Suspense.
6
+ // • On the CLIENT: intentionally persists for the session to avoid redundant
7
+ // network round-trips on subsequent re-renders.
8
+ const _promiseCache = new Map();
9
+ /** Clear the server-side promise cache between SSR renders. Called by alab's dev server. */
10
+ export function _clearALabSSRCache() {
11
+ _promiseCache.clear();
12
+ }
13
+ /**
14
+ * Fetch server data with full type inference from a `ServerFn`.
15
+ *
16
+ * Use `import type` to reference the server function — type-only imports
17
+ * are erased at compile time and never cross the server/client boundary.
18
+ *
19
+ * @example
20
+ * ```tsx
21
+ * // app/posts/[id]/page.tsx
22
+ * import type { getPost } from "./page.server"; // ← import type, safe
23
+ * import { useServerData } from "alabjs/client";
24
+ *
25
+ * export default function PostPage({ params }: { params: { id: string } }) {
26
+ * // Return type is inferred from getPost — no manual type annotation needed
27
+ * const post = useServerData<typeof getPost>("getPost", params);
28
+ * post.title; // ✅ typed
29
+ * post.foo; // ✅ TS error — doesn't exist on the return type
30
+ * }
31
+ * ```
32
+ */
33
+ export function useServerData(fnName, params) {
34
+ const searchParams = params
35
+ ? new URLSearchParams(params).toString()
36
+ : "";
37
+ // When running in Node.js (SSR) fetch requires an absolute URL.
38
+ // Alab's dev/prod server sets ALAB_ORIGIN before rendering each page.
39
+ const origin = typeof window !== "undefined"
40
+ ? ""
41
+ : (process.env["ALAB_ORIGIN"] ?? "http://localhost:3000");
42
+ const url = `${origin}/_alabjs/data/${fnName}${searchParams ? `?${searchParams}` : ""}`;
43
+ let promise = _promiseCache.get(url);
44
+ if (!promise) {
45
+ promise = fetch(url).then((r) => {
46
+ if (!r.ok)
47
+ throw new Error(`[alabjs] server data fetch failed: ${r.status} ${r.statusText} — ${url}`);
48
+ return r.json();
49
+ });
50
+ _promiseCache.set(url, promise);
51
+ }
52
+ return use(promise);
53
+ }
54
+ function mutationReducer(_state, action) {
55
+ switch (action.type) {
56
+ case "start": return { status: "pending", data: undefined, error: undefined, zodError: undefined };
57
+ case "success": return { status: "success", data: action.data, error: undefined, zodError: undefined };
58
+ case "error": return { status: "error", data: undefined, error: action.error, zodError: undefined };
59
+ case "invalid": return { status: "invalid", data: undefined, error: undefined, zodError: action.zodError };
60
+ case "reset": return { status: "idle", data: undefined, error: undefined, zodError: undefined };
61
+ }
62
+ }
63
+ /**
64
+ * Trigger a server function mutation from the client, with full async state
65
+ * and optional optimistic updates.
66
+ *
67
+ * @example
68
+ * ```tsx
69
+ * import type { updateTodo } from "./page.server";
70
+ * import { useMutation } from "alabjs/client";
71
+ *
72
+ * // Basic
73
+ * const { mutate, data, isPending, error, zodError, reset } =
74
+ * useMutation<typeof updateTodo>("updateTodo");
75
+ *
76
+ * // With optimistic update
77
+ * const { mutate, optimisticData } = useMutation<typeof updateTodo>("updateTodo", {
78
+ * optimistic: (input) => ({ ...currentTodo, ...input }),
79
+ * onError: (err, rollback) => rollback(),
80
+ * });
81
+ * ```
82
+ */
83
+ export function useMutation(fnName, options) {
84
+ const [state, dispatch] = useReducer((mutationReducer), { status: "idle", data: undefined, error: undefined, zodError: undefined });
85
+ // Optimistic value lives in separate state so it can be cleared independently.
86
+ const [optimisticData, setOptimisticData] = useState(undefined);
87
+ const [isPending, startTransition] = useTransition();
88
+ const rollback = useCallback(() => {
89
+ setOptimisticData(undefined);
90
+ dispatch({ type: "reset" });
91
+ }, []);
92
+ const mutate = useCallback((input) => {
93
+ dispatch({ type: "start" });
94
+ if (options?.optimistic) {
95
+ setOptimisticData(options.optimistic(input));
96
+ }
97
+ startTransition(() => {
98
+ void (async () => {
99
+ try {
100
+ const r = await fetch(`/_alabjs/fn/${fnName}`, {
101
+ method: "POST",
102
+ headers: { "content-type": "application/json" },
103
+ body: JSON.stringify(input),
104
+ });
105
+ // Zod validation error from server
106
+ if (r.status === 422) {
107
+ const body = await r.json();
108
+ setOptimisticData(undefined);
109
+ dispatch({ type: "invalid", zodError: body["zodError"] });
110
+ return;
111
+ }
112
+ if (!r.ok)
113
+ throw new Error(`[alabjs] mutation failed: ${r.status} ${r.statusText}`);
114
+ const data = await r.json();
115
+ setOptimisticData(undefined);
116
+ dispatch({ type: "success", data });
117
+ options?.onSuccess?.(data);
118
+ }
119
+ catch (err) {
120
+ const error = err instanceof Error ? err : new Error(String(err));
121
+ setOptimisticData(undefined);
122
+ dispatch({ type: "error", error });
123
+ options?.onError?.(error, rollback);
124
+ }
125
+ })();
126
+ });
127
+ // eslint-disable-next-line react-hooks/exhaustive-deps
128
+ }, [fnName, rollback]);
129
+ const reset = useCallback(() => {
130
+ setOptimisticData(undefined);
131
+ dispatch({ type: "reset" });
132
+ }, []);
133
+ return {
134
+ mutate,
135
+ data: state.data,
136
+ /** Present when `optimistic` option is set and the request is in flight. */
137
+ optimisticData,
138
+ isPending: isPending || state.status === "pending",
139
+ error: state.error,
140
+ /** Zod validation errors returned by `defineServerFn` schema checks (HTTP 422). */
141
+ zodError: state.zodError,
142
+ isSuccess: state.status === "success",
143
+ isError: state.status === "error",
144
+ isInvalid: state.status === "invalid",
145
+ reset,
146
+ };
147
+ }
148
+ /**
149
+ * Subscribe to a server-sent event stream from a `defineSSEHandler` route.
150
+ *
151
+ * The EventSource is created when the component mounts and closed on unmount.
152
+ * Data is parsed as JSON automatically.
153
+ *
154
+ * @example
155
+ * ```tsx
156
+ * import { useSSE } from "alabjs/client";
157
+ *
158
+ * export default function PricesPage() {
159
+ * const { data, readyState } = useSSE<{ ticker: string; price: number }>(
160
+ * "/api/prices?ticker=BTC",
161
+ * { event: "price" },
162
+ * );
163
+ *
164
+ * return <div>{readyState === "open" ? data?.price ?? "—" : "connecting…"}</div>;
165
+ * }
166
+ * ```
167
+ */
168
+ export function useSSE(url, options = {}) {
169
+ const { event = "message", enabled = true, onOpen, onError } = options;
170
+ const [data, setData] = useState(undefined);
171
+ const [lastEventId, setLastEventId] = useState("");
172
+ const [readyState, setReadyState] = useState("connecting");
173
+ // Keep callbacks stable across renders without re-subscribing
174
+ const onOpenRef = useRef(onOpen);
175
+ const onErrorRef = useRef(onError);
176
+ onOpenRef.current = onOpen;
177
+ onErrorRef.current = onError;
178
+ const esRef = useRef(null);
179
+ const close = useCallback(() => {
180
+ esRef.current?.close();
181
+ esRef.current = null;
182
+ setReadyState("closed");
183
+ }, []);
184
+ useEffect(() => {
185
+ if (!enabled || typeof EventSource === "undefined")
186
+ return;
187
+ const es = new EventSource(url);
188
+ esRef.current = es;
189
+ setReadyState("connecting");
190
+ es.addEventListener("open", () => {
191
+ setReadyState("open");
192
+ onOpenRef.current?.();
193
+ });
194
+ es.addEventListener(event, (e) => {
195
+ setLastEventId(e.lastEventId ?? "");
196
+ try {
197
+ setData(e.data ? JSON.parse(e.data) : undefined);
198
+ }
199
+ catch {
200
+ // Server sent data that isn't valid JSON — log and discard rather than
201
+ // silently casting a raw string to T (which would cause runtime type errors).
202
+ console.warn("[alabjs] useSSE: received non-JSON data on", url, "— discarding:", e.data);
203
+ }
204
+ });
205
+ es.addEventListener("error", (e) => {
206
+ onErrorRef.current?.(e);
207
+ if (es.readyState === EventSource.CLOSED) {
208
+ setReadyState("closed");
209
+ }
210
+ });
211
+ return () => {
212
+ es.close();
213
+ esRef.current = null;
214
+ };
215
+ // Re-subscribe if url or event type changes
216
+ // eslint-disable-next-line react-hooks/exhaustive-deps
217
+ }, [url, event, enabled]);
218
+ return { data, lastEventId, readyState, close };
219
+ }
220
+ //# sourceMappingURL=hooks.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../src/client/hooks.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=hooks.test.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.test.d.ts","sourceRoot":"","sources":["../../src/client/hooks.test.ts"],"names":[],"mappings":""}
@@ -0,0 +1,45 @@
1
+ import { describe, it, expect } from "vitest";
2
+ import { _clearALabSSRCache } from "./hooks.js";
3
+ // Note: useServerData, useMutation, and useSSE are React hooks that require a
4
+ // component context and browser APIs (fetch, EventSource). Testing the full hooks
5
+ // would require a React test renderer + fetch mocking. Here we test the exported
6
+ // utility and the mutation state machine logic that can be exercised without React.
7
+ describe("_clearALabSSRCache", () => {
8
+ it("is a function", () => {
9
+ expect(typeof _clearALabSSRCache).toBe("function");
10
+ });
11
+ it("can be called without error", () => {
12
+ // This clears the internal promise cache used during SSR.
13
+ // Should not throw even when cache is empty.
14
+ expect(() => _clearALabSSRCache()).not.toThrow();
15
+ });
16
+ it("can be called multiple times", () => {
17
+ expect(() => {
18
+ _clearALabSSRCache();
19
+ _clearALabSSRCache();
20
+ _clearALabSSRCache();
21
+ }).not.toThrow();
22
+ });
23
+ });
24
+ // ─── Mutation state machine (via module internals) ────────────────────────────
25
+ // The mutationReducer is not exported, but we can test the state machine logic
26
+ // by verifying the expected types and shape of the module exports.
27
+ describe("hooks module exports", () => {
28
+ it("exports useServerData", async () => {
29
+ const mod = await import("./hooks.js");
30
+ expect(typeof mod.useServerData).toBe("function");
31
+ });
32
+ it("exports useMutation", async () => {
33
+ const mod = await import("./hooks.js");
34
+ expect(typeof mod.useMutation).toBe("function");
35
+ });
36
+ it("exports useSSE", async () => {
37
+ const mod = await import("./hooks.js");
38
+ expect(typeof mod.useSSE).toBe("function");
39
+ });
40
+ it("exports _clearALabSSRCache", async () => {
41
+ const mod = await import("./hooks.js");
42
+ expect(typeof mod._clearALabSSRCache).toBe("function");
43
+ });
44
+ });
45
+ //# sourceMappingURL=hooks.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hooks.test.js","sourceRoot":"","sources":["../../src/client/hooks.test.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,EAAE,EAAE,MAAM,EAAE,MAAM,QAAQ,CAAC;AAC9C,OAAO,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEhD,8EAA8E;AAC9E,kFAAkF;AAClF,iFAAiF;AACjF,oFAAoF;AAEpF,QAAQ,CAAC,oBAAoB,EAAE,GAAG,EAAE;IAClC,EAAE,CAAC,eAAe,EAAE,GAAG,EAAE;QACvB,MAAM,CAAC,OAAO,kBAAkB,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACrD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,6BAA6B,EAAE,GAAG,EAAE;QACrC,0DAA0D;QAC1D,6CAA6C;QAC7C,MAAM,CAAC,GAAG,EAAE,CAAC,kBAAkB,EAAE,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IACnD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,8BAA8B,EAAE,GAAG,EAAE;QACtC,MAAM,CAAC,GAAG,EAAE;YACV,kBAAkB,EAAE,CAAC;YACrB,kBAAkB,EAAE,CAAC;YACrB,kBAAkB,EAAE,CAAC;QACvB,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,EAAE,CAAC;IACnB,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC;AAEH,iFAAiF;AAEjF,+EAA+E;AAC/E,mEAAmE;AAEnE,QAAQ,CAAC,sBAAsB,EAAE,GAAG,EAAE;IACpC,EAAE,CAAC,uBAAuB,EAAE,KAAK,IAAI,EAAE;QACrC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;QACvC,MAAM,CAAC,OAAO,GAAG,CAAC,aAAa,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACpD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,qBAAqB,EAAE,KAAK,IAAI,EAAE;QACnC,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;QACvC,MAAM,CAAC,OAAO,GAAG,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAClD,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,gBAAgB,EAAE,KAAK,IAAI,EAAE;QAC9B,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;QACvC,MAAM,CAAC,OAAO,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IAC7C,CAAC,CAAC,CAAC;IAEH,EAAE,CAAC,4BAA4B,EAAE,KAAK,IAAI,EAAE;QAC1C,MAAM,GAAG,GAAG,MAAM,MAAM,CAAC,YAAY,CAAC,CAAC;QACvC,MAAM,CAAC,OAAO,GAAG,CAAC,kBAAkB,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;IACzD,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC"}
@@ -0,0 +1,6 @@
1
+ export { useServerData, useMutation, useSSE, _clearALabSSRCache } from "./hooks.js";
2
+ export type { UseSSEOptions, UseSSEResult, SSEReadyState } from "./hooks.js";
3
+ export { useOfflineMutations } from "./offline.js";
4
+ export type { UseOfflineMutationsResult, OfflineMutationResult } from "./offline.js";
5
+ export { AlabProvider } from "./provider.js";
6
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AACpF,YAAY,EAAE,aAAa,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAC7E,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AACnD,YAAY,EAAE,yBAAyB,EAAE,qBAAqB,EAAE,MAAM,cAAc,CAAC;AACrF,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,4 @@
1
+ export { useServerData, useMutation, useSSE, _clearALabSSRCache } from "./hooks.js";
2
+ export { useOfflineMutations } from "./offline.js";
3
+ export { AlabProvider } from "./provider.js";
4
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/client/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,WAAW,EAAE,MAAM,EAAE,kBAAkB,EAAE,MAAM,YAAY,CAAC;AAEpF,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAEnD,OAAO,EAAE,YAAY,EAAE,MAAM,eAAe,CAAC"}
@@ -0,0 +1,52 @@
1
+ /**
2
+ * Alab offline support — client-side registration + queue observation.
3
+ *
4
+ * @example
5
+ * ```tsx
6
+ * // app/layout.tsx
7
+ * import { useOfflineMutations } from "alabjs/client";
8
+ *
9
+ * export default function RootLayout({ children }) {
10
+ * const { isOffline, queuedCount, replay } = useOfflineMutations();
11
+ *
12
+ * return (
13
+ * <>
14
+ * {isOffline && (
15
+ * <div className="offline-banner">
16
+ * You're offline — {queuedCount} mutation(s) will sync when reconnected.
17
+ * <button onClick={replay}>Retry now</button>
18
+ * </div>
19
+ * )}
20
+ * {children}
21
+ * </>
22
+ * );
23
+ * }
24
+ * ```
25
+ */
26
+ export interface OfflineMutationResult {
27
+ replayed: {
28
+ fn: string;
29
+ ok: boolean;
30
+ }[];
31
+ }
32
+ export interface UseOfflineMutationsResult {
33
+ /** True when `navigator.onLine` is false. */
34
+ isOffline: boolean;
35
+ /** Number of mutations currently held in the offline queue. */
36
+ queuedCount: number;
37
+ /** Manually trigger a replay of the offline queue. */
38
+ replay: () => void;
39
+ /** History of replayed mutations since mount (cleared on replay start). */
40
+ replayed: OfflineMutationResult["replayed"];
41
+ }
42
+ /**
43
+ * Register the Alab offline service worker and observe queue state.
44
+ *
45
+ * On mount this registers `/_alabjs/offline-sw.js` (emitted by `alab build`).
46
+ * The hook listens for SW messages and browser online/offline events to keep
47
+ * state in sync.
48
+ *
49
+ * Safe to call in SSR — all browser APIs are guarded behind `typeof window`.
50
+ */
51
+ export declare function useOfflineMutations(): UseOfflineMutationsResult;
52
+ //# sourceMappingURL=offline.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"offline.d.ts","sourceRoot":"","sources":["../../src/client/offline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AAIH,MAAM,WAAW,qBAAqB;IACpC,QAAQ,EAAE;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,EAAE,EAAE,OAAO,CAAA;KAAE,EAAE,CAAC;CACzC;AAED,MAAM,WAAW,yBAAyB;IACxC,6CAA6C;IAC7C,SAAS,EAAE,OAAO,CAAC;IACnB,+DAA+D;IAC/D,WAAW,EAAE,MAAM,CAAC;IACpB,sDAAsD;IACtD,MAAM,EAAE,MAAM,IAAI,CAAC;IACnB,2EAA2E;IAC3E,QAAQ,EAAE,qBAAqB,CAAC,UAAU,CAAC,CAAC;CAC7C;AAID;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,IAAI,yBAAyB,CA2D/D"}
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Alab offline support — client-side registration + queue observation.
3
+ *
4
+ * @example
5
+ * ```tsx
6
+ * // app/layout.tsx
7
+ * import { useOfflineMutations } from "alabjs/client";
8
+ *
9
+ * export default function RootLayout({ children }) {
10
+ * const { isOffline, queuedCount, replay } = useOfflineMutations();
11
+ *
12
+ * return (
13
+ * <>
14
+ * {isOffline && (
15
+ * <div className="offline-banner">
16
+ * You're offline — {queuedCount} mutation(s) will sync when reconnected.
17
+ * <button onClick={replay}>Retry now</button>
18
+ * </div>
19
+ * )}
20
+ * {children}
21
+ * </>
22
+ * );
23
+ * }
24
+ * ```
25
+ */
26
+ import { useState, useEffect, useCallback } from "react";
27
+ const SW_PATH = "/_alabjs/offline-sw.js";
28
+ /**
29
+ * Register the Alab offline service worker and observe queue state.
30
+ *
31
+ * On mount this registers `/_alabjs/offline-sw.js` (emitted by `alab build`).
32
+ * The hook listens for SW messages and browser online/offline events to keep
33
+ * state in sync.
34
+ *
35
+ * Safe to call in SSR — all browser APIs are guarded behind `typeof window`.
36
+ */
37
+ export function useOfflineMutations() {
38
+ const [isOffline, setIsOffline] = useState(typeof navigator !== "undefined" ? !navigator.onLine : false);
39
+ const [queuedCount, setQueuedCount] = useState(0);
40
+ const [replayed, setReplayed] = useState([]);
41
+ // Stable SW controller ref
42
+ const [swReg, setSwReg] = useState(null);
43
+ useEffect(() => {
44
+ if (typeof window === "undefined" || !("serviceWorker" in navigator))
45
+ return;
46
+ // Register SW
47
+ navigator.serviceWorker.register(SW_PATH, { scope: "/" }).then((reg) => {
48
+ setSwReg(reg);
49
+ }).catch(() => { });
50
+ // Online / offline events
51
+ const handleOnline = () => setIsOffline(false);
52
+ const handleOffline = () => setIsOffline(true);
53
+ window.addEventListener("online", handleOnline);
54
+ window.addEventListener("offline", handleOffline);
55
+ // Messages from the SW
56
+ const handleMessage = (event) => {
57
+ const msg = event.data;
58
+ if (!msg)
59
+ return;
60
+ switch (msg["type"]) {
61
+ case "ALAB_QUEUED":
62
+ setQueuedCount(msg["count"]);
63
+ break;
64
+ case "ALAB_REPLAYED":
65
+ setReplayed((prev) => [...prev, { fn: msg["fn"], ok: msg["ok"] }]);
66
+ break;
67
+ case "ALAB_QUEUE_EMPTY":
68
+ setQueuedCount(0);
69
+ break;
70
+ }
71
+ };
72
+ navigator.serviceWorker.addEventListener("message", handleMessage);
73
+ return () => {
74
+ window.removeEventListener("online", handleOnline);
75
+ window.removeEventListener("offline", handleOffline);
76
+ navigator.serviceWorker.removeEventListener("message", handleMessage);
77
+ };
78
+ }, []);
79
+ const replay = useCallback(() => {
80
+ setReplayed([]);
81
+ if (swReg?.active) {
82
+ swReg.active.postMessage({ type: "ALAB_REPLAY" });
83
+ }
84
+ else {
85
+ navigator.serviceWorker.controller?.postMessage({ type: "ALAB_REPLAY" });
86
+ }
87
+ }, [swReg]);
88
+ return { isOffline, queuedCount, replay, replayed };
89
+ }
90
+ //# sourceMappingURL=offline.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"offline.js","sourceRoot":"","sources":["../../src/client/offline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,12 @@
1
+ import { type ReactNode } from "react";
2
+ interface AlabProviderProps {
3
+ children: ReactNode;
4
+ fallback?: ReactNode;
5
+ }
6
+ /**
7
+ * Root provider for Alab apps.
8
+ * Wraps the app in a Suspense boundary for `useServerData` hooks.
9
+ */
10
+ export declare function AlabProvider({ children, fallback }: AlabProviderProps): import("react/jsx-runtime").JSX.Element;
11
+ export {};
12
+ //# sourceMappingURL=provider.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.d.ts","sourceRoot":"","sources":["../../src/client/provider.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAY,KAAK,SAAS,EAAE,MAAM,OAAO,CAAC;AAEjD,UAAU,iBAAiB;IACzB,QAAQ,EAAE,SAAS,CAAC;IACpB,QAAQ,CAAC,EAAE,SAAS,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,QAAe,EAAE,EAAE,iBAAiB,2CAE5E"}
@@ -0,0 +1,10 @@
1
+ import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { Suspense } from "react";
3
+ /**
4
+ * Root provider for Alab apps.
5
+ * Wraps the app in a Suspense boundary for `useServerData` hooks.
6
+ */
7
+ export function AlabProvider({ children, fallback = null }) {
8
+ return _jsx(Suspense, { fallback: fallback, children: children });
9
+ }
10
+ //# sourceMappingURL=provider.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"provider.js","sourceRoot":"","sources":["../../src/client/provider.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAkB,MAAM,OAAO,CAAC;AAOjD;;;GAGG;AACH,MAAM,UAAU,YAAY,CAAC,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,EAAqB;IAC3E,OAAO,KAAC,QAAQ,IAAC,QAAQ,EAAE,QAAQ,YAAG,QAAQ,GAAY,CAAC;AAC7D,CAAC"}
@@ -0,0 +1,18 @@
1
+ interface BuildOptions {
2
+ cwd: string;
3
+ /** Skip TypeScript type checking (--skip-typecheck flag). */
4
+ skipTypecheck?: boolean;
5
+ /**
6
+ * Build mode:
7
+ * - `"ssr"` (default) — full SSR + client bundle, requires a Node.js server.
8
+ * - `"spa"` — pure client-side bundle, deployable to any CDN. No Node.js needed.
9
+ * Server functions become direct fetch calls to `/_alabjs/fn/*`; point these at
10
+ * a separate API server or use Cloudflare Workers for the data layer.
11
+ */
12
+ mode?: "ssr" | "spa";
13
+ /** Open an interactive bundle size treemap after build (`--analyze`). */
14
+ analyze?: boolean;
15
+ }
16
+ export declare function build({ cwd, skipTypecheck, mode, analyze }: BuildOptions): Promise<void>;
17
+ export {};
18
+ //# sourceMappingURL=build.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build.d.ts","sourceRoot":"","sources":["../../src/commands/build.ts"],"names":[],"mappings":"AAKA,UAAU,YAAY;IACpB,GAAG,EAAE,MAAM,CAAC;IACZ,6DAA6D;IAC7D,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB;;;;;;OAMG;IACH,IAAI,CAAC,EAAE,KAAK,GAAG,KAAK,CAAC;IACrB,yEAAyE;IACzE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB;AAsED,wBAAsB,KAAK,CAAC,EAAE,GAAG,EAAE,aAAqB,EAAE,IAAY,EAAE,OAAe,EAAE,EAAE,YAAY,iBAmFtG"}