@alepha/react 0.13.1 → 0.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (49) hide show
  1. package/dist/auth/index.browser.js +5 -5
  2. package/dist/auth/index.browser.js.map +1 -1
  3. package/dist/auth/index.d.ts +330 -330
  4. package/dist/auth/index.js +7 -7
  5. package/dist/auth/index.js.map +1 -1
  6. package/dist/core/index.browser.js +19 -18
  7. package/dist/core/index.browser.js.map +1 -1
  8. package/dist/core/index.d.ts +352 -344
  9. package/dist/core/index.js +25 -24
  10. package/dist/core/index.js.map +1 -1
  11. package/dist/core/index.native.js +381 -0
  12. package/dist/core/index.native.js.map +1 -0
  13. package/dist/form/index.d.ts +2 -2
  14. package/dist/head/index.browser.js +7 -7
  15. package/dist/head/index.browser.js.map +1 -1
  16. package/dist/head/index.d.ts +265 -265
  17. package/dist/head/index.js +7 -7
  18. package/dist/head/index.js.map +1 -1
  19. package/dist/i18n/index.d.ts +20 -20
  20. package/dist/i18n/index.js +12 -12
  21. package/dist/i18n/index.js.map +1 -1
  22. package/dist/websocket/index.d.ts +7 -7
  23. package/dist/websocket/index.js.map +1 -1
  24. package/package.json +18 -9
  25. package/src/auth/index.ts +1 -1
  26. package/src/auth/providers/ReactAuthProvider.ts +1 -1
  27. package/src/auth/services/ReactAuth.ts +5 -5
  28. package/src/core/components/NestedView.tsx +1 -1
  29. package/src/core/hooks/useStore.ts +4 -4
  30. package/src/core/index.browser.ts +2 -2
  31. package/src/core/index.native.ts +1 -1
  32. package/src/core/index.shared-router.ts +1 -1
  33. package/src/core/index.ts +3 -3
  34. package/src/core/{descriptors → primitives}/$page.ts +20 -20
  35. package/src/core/providers/ReactBrowserProvider.ts +2 -2
  36. package/src/core/providers/ReactBrowserRouterProvider.ts +2 -2
  37. package/src/core/providers/ReactPageProvider.ts +25 -11
  38. package/src/core/providers/ReactServerProvider.ts +12 -12
  39. package/src/core/services/ReactPageServerService.ts +6 -6
  40. package/src/core/services/ReactPageService.ts +6 -6
  41. package/src/core/services/ReactRouter.ts +3 -3
  42. package/src/head/index.browser.ts +3 -3
  43. package/src/head/index.ts +4 -4
  44. package/src/head/{descriptors → primitives}/$head.ts +6 -6
  45. package/src/i18n/hooks/useI18n.ts +2 -2
  46. package/src/i18n/index.ts +3 -3
  47. package/src/i18n/{descriptors → primitives}/$dictionary.ts +8 -8
  48. package/src/i18n/providers/I18nProvider.ts +5 -5
  49. package/src/websocket/hooks/useRoom.tsx +3 -3
@@ -0,0 +1,381 @@
1
+ import { $module, AlephaError, Atom } from "alepha";
2
+ import { AlephaDateTime, DateTimeProvider } from "alepha/datetime";
3
+ import { AlephaServerLinks, LinkProvider } from "alepha/server/links";
4
+ import { createContext, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
5
+ import { HttpClient } from "alepha/server";
6
+
7
+ //#region src/core/contexts/AlephaContext.ts
8
+ const AlephaContext = createContext(void 0);
9
+
10
+ //#endregion
11
+ //#region src/core/hooks/useAlepha.ts
12
+ /**
13
+ * Main Alepha hook.
14
+ *
15
+ * It provides access to the Alepha instance within a React component.
16
+ *
17
+ * With Alepha, you can access the core functionalities of the framework:
18
+ *
19
+ * - alepha.state() for state management
20
+ * - alepha.inject() for dependency injection
21
+ * - alepha.events.emit() for event handling
22
+ * etc...
23
+ */
24
+ const useAlepha = () => {
25
+ const alepha = useContext(AlephaContext);
26
+ if (!alepha) throw new AlephaError("Hook 'useAlepha()' must be used within an AlephaContext.Provider");
27
+ return alepha;
28
+ };
29
+
30
+ //#endregion
31
+ //#region src/core/hooks/useInject.ts
32
+ /**
33
+ * Hook to inject a service instance.
34
+ * It's a wrapper of `useAlepha().inject(service)` with a memoization.
35
+ */
36
+ const useInject = (service) => {
37
+ const alepha = useAlepha();
38
+ return useMemo(() => alepha.inject(service), []);
39
+ };
40
+
41
+ //#endregion
42
+ //#region src/core/hooks/useAction.ts
43
+ /**
44
+ * Hook for handling async actions with automatic error handling and event emission.
45
+ *
46
+ * By default, prevents concurrent executions - if an action is running and you call it again,
47
+ * the second call will be ignored. Use `debounce` option to delay execution instead.
48
+ *
49
+ * Emits lifecycle events:
50
+ * - `react:action:begin` - When action starts
51
+ * - `react:action:success` - When action completes successfully
52
+ * - `react:action:error` - When action throws an error
53
+ * - `react:action:end` - Always emitted at the end
54
+ *
55
+ * @example Basic usage
56
+ * ```tsx
57
+ * const action = useAction({
58
+ * handler: async (data) => {
59
+ * await api.save(data);
60
+ * }
61
+ * }, []);
62
+ *
63
+ * <button onClick={() => action.run(data)} disabled={action.loading}>
64
+ * Save
65
+ * </button>
66
+ * ```
67
+ *
68
+ * @example With debounce (search input)
69
+ * ```tsx
70
+ * const search = useAction({
71
+ * handler: async (query: string) => {
72
+ * await api.search(query);
73
+ * },
74
+ * debounce: 300 // Wait 300ms after last call
75
+ * }, []);
76
+ *
77
+ * <input onChange={(e) => search.run(e.target.value)} />
78
+ * ```
79
+ *
80
+ * @example Run on component mount
81
+ * ```tsx
82
+ * const fetchData = useAction({
83
+ * handler: async () => {
84
+ * const data = await api.getData();
85
+ * return data;
86
+ * },
87
+ * runOnInit: true // Runs once when component mounts
88
+ * }, []);
89
+ * ```
90
+ *
91
+ * @example Run periodically (polling)
92
+ * ```tsx
93
+ * const pollStatus = useAction({
94
+ * handler: async () => {
95
+ * const status = await api.getStatus();
96
+ * return status;
97
+ * },
98
+ * runEvery: 5000 // Run every 5 seconds
99
+ * }, []);
100
+ *
101
+ * // Or with duration tuple
102
+ * const pollStatus = useAction({
103
+ * handler: async () => {
104
+ * const status = await api.getStatus();
105
+ * return status;
106
+ * },
107
+ * runEvery: [30, 'seconds'] // Run every 30 seconds
108
+ * }, []);
109
+ * ```
110
+ *
111
+ * @example With AbortController
112
+ * ```tsx
113
+ * const fetch = useAction({
114
+ * handler: async (url, { signal }) => {
115
+ * const response = await fetch(url, { signal });
116
+ * return response.json();
117
+ * }
118
+ * }, []);
119
+ * // Automatically cancelled on unmount or when new request starts
120
+ * ```
121
+ *
122
+ * @example With error handling
123
+ * ```tsx
124
+ * const deleteAction = useAction({
125
+ * handler: async (id: string) => {
126
+ * await api.delete(id);
127
+ * },
128
+ * onError: (error) => {
129
+ * if (error.code === 'NOT_FOUND') {
130
+ * // Custom error handling
131
+ * }
132
+ * }
133
+ * }, []);
134
+ *
135
+ * {deleteAction.error && <div>Error: {deleteAction.error.message}</div>}
136
+ * ```
137
+ *
138
+ * @example Global error handling
139
+ * ```tsx
140
+ * // In your root app setup
141
+ * alepha.events.on("react:action:error", ({ error }) => {
142
+ * toast.danger(error.message);
143
+ * Sentry.captureException(error);
144
+ * });
145
+ * ```
146
+ */
147
+ function useAction(options, deps) {
148
+ const alepha = useAlepha();
149
+ const dateTimeProvider = useInject(DateTimeProvider);
150
+ const [loading, setLoading] = useState(false);
151
+ const [error, setError] = useState();
152
+ const isExecutingRef = useRef(false);
153
+ const debounceTimerRef = useRef(void 0);
154
+ const abortControllerRef = useRef(void 0);
155
+ const isMountedRef = useRef(true);
156
+ const intervalRef = useRef(void 0);
157
+ useEffect(() => {
158
+ return () => {
159
+ isMountedRef.current = false;
160
+ if (debounceTimerRef.current) {
161
+ dateTimeProvider.clearTimeout(debounceTimerRef.current);
162
+ debounceTimerRef.current = void 0;
163
+ }
164
+ if (intervalRef.current) {
165
+ dateTimeProvider.clearInterval(intervalRef.current);
166
+ intervalRef.current = void 0;
167
+ }
168
+ if (abortControllerRef.current) {
169
+ abortControllerRef.current.abort();
170
+ abortControllerRef.current = void 0;
171
+ }
172
+ };
173
+ }, []);
174
+ const executeAction = useCallback(async (...args) => {
175
+ if (isExecutingRef.current) return;
176
+ if (abortControllerRef.current) abortControllerRef.current.abort();
177
+ const abortController = new AbortController();
178
+ abortControllerRef.current = abortController;
179
+ isExecutingRef.current = true;
180
+ setLoading(true);
181
+ setError(void 0);
182
+ await alepha.events.emit("react:action:begin", {
183
+ type: "custom",
184
+ id: options.id
185
+ });
186
+ try {
187
+ const result = await options.handler(...args, { signal: abortController.signal });
188
+ if (!isMountedRef.current || abortController.signal.aborted) return;
189
+ await alepha.events.emit("react:action:success", {
190
+ type: "custom",
191
+ id: options.id
192
+ });
193
+ if (options.onSuccess) await options.onSuccess(result);
194
+ return result;
195
+ } catch (err) {
196
+ if (err instanceof Error && err.name === "AbortError") return;
197
+ if (!isMountedRef.current) return;
198
+ const error$1 = err;
199
+ setError(error$1);
200
+ await alepha.events.emit("react:action:error", {
201
+ type: "custom",
202
+ id: options.id,
203
+ error: error$1
204
+ });
205
+ if (options.onError) await options.onError(error$1);
206
+ else throw error$1;
207
+ } finally {
208
+ isExecutingRef.current = false;
209
+ setLoading(false);
210
+ await alepha.events.emit("react:action:end", {
211
+ type: "custom",
212
+ id: options.id
213
+ });
214
+ if (abortControllerRef.current === abortController) abortControllerRef.current = void 0;
215
+ }
216
+ }, [
217
+ ...deps,
218
+ options.id,
219
+ options.onError,
220
+ options.onSuccess
221
+ ]);
222
+ const handler = useCallback(async (...args) => {
223
+ if (options.debounce) {
224
+ if (debounceTimerRef.current) dateTimeProvider.clearTimeout(debounceTimerRef.current);
225
+ return new Promise((resolve) => {
226
+ debounceTimerRef.current = dateTimeProvider.createTimeout(async () => {
227
+ resolve(await executeAction(...args));
228
+ }, options.debounce ?? 0);
229
+ });
230
+ }
231
+ return executeAction(...args);
232
+ }, [executeAction, options.debounce]);
233
+ const cancel = useCallback(() => {
234
+ if (debounceTimerRef.current) {
235
+ dateTimeProvider.clearTimeout(debounceTimerRef.current);
236
+ debounceTimerRef.current = void 0;
237
+ }
238
+ if (abortControllerRef.current) {
239
+ abortControllerRef.current.abort();
240
+ abortControllerRef.current = void 0;
241
+ }
242
+ if (isMountedRef.current) {
243
+ isExecutingRef.current = false;
244
+ setLoading(false);
245
+ }
246
+ }, []);
247
+ useEffect(() => {
248
+ if (options.runOnInit) handler(...[]);
249
+ }, deps);
250
+ useEffect(() => {
251
+ if (!options.runEvery) return;
252
+ intervalRef.current = dateTimeProvider.createInterval(() => handler(...[]), options.runEvery, true);
253
+ return () => {
254
+ if (intervalRef.current) {
255
+ dateTimeProvider.clearInterval(intervalRef.current);
256
+ intervalRef.current = void 0;
257
+ }
258
+ };
259
+ }, [handler, options.runEvery]);
260
+ return {
261
+ run: handler,
262
+ loading,
263
+ error,
264
+ cancel
265
+ };
266
+ }
267
+
268
+ //#endregion
269
+ //#region src/core/hooks/useClient.ts
270
+ /**
271
+ * Hook to get a virtual client for the specified scope.
272
+ *
273
+ * It's the React-hook version of `$client()`, from `AlephaServerLinks` module.
274
+ */
275
+ const useClient = (scope) => {
276
+ return useInject(LinkProvider).client(scope);
277
+ };
278
+
279
+ //#endregion
280
+ //#region src/core/hooks/useEvents.ts
281
+ /**
282
+ * Allow subscribing to multiple Alepha events. See {@link Hooks} for available events.
283
+ *
284
+ * useEvents is fully typed to ensure correct event callback signatures.
285
+ *
286
+ * @example
287
+ * ```tsx
288
+ * useEvents(
289
+ * {
290
+ * "react:transition:begin": (ev) => {
291
+ * console.log("Transition began to:", ev.to);
292
+ * },
293
+ * "react:transition:error": {
294
+ * priority: "first",
295
+ * callback: (ev) => {
296
+ * console.error("Transition error:", ev.error);
297
+ * },
298
+ * },
299
+ * },
300
+ * [],
301
+ * );
302
+ * ```
303
+ */
304
+ const useEvents = (opts, deps) => {
305
+ const alepha = useAlepha();
306
+ useEffect(() => {
307
+ if (!alepha.isBrowser()) return;
308
+ const subs = [];
309
+ for (const [name, hook] of Object.entries(opts)) subs.push(alepha.events.on(name, hook));
310
+ return () => {
311
+ for (const clear of subs) clear();
312
+ };
313
+ }, deps);
314
+ };
315
+
316
+ //#endregion
317
+ //#region src/core/hooks/useSchema.ts
318
+ const useSchema = (action) => {
319
+ const name = action.name;
320
+ const alepha = useAlepha();
321
+ const httpClient = useInject(HttpClient);
322
+ const [schema, setSchema] = useState(ssrSchemaLoading(alepha, name));
323
+ useEffect(() => {
324
+ if (!schema.loading) return;
325
+ httpClient.fetch(`${LinkProvider.path.apiLinks}/${name}/schema`, { localCache: true }).then((it) => setSchema(it.data));
326
+ }, [name]);
327
+ return schema;
328
+ };
329
+ /**
330
+ * Get an action schema during server-side rendering (SSR) or client-side rendering (CSR).
331
+ */
332
+ const ssrSchemaLoading = (alepha, name) => {
333
+ if (!alepha.isBrowser()) {
334
+ const linkProvider = alepha.inject(LinkProvider);
335
+ const can = linkProvider.getServerLinks().find((link) => link.name === name);
336
+ if (can) {
337
+ const schema$1 = linkProvider.links.find((it) => it.name === name)?.schema;
338
+ if (schema$1) {
339
+ can.schema = schema$1;
340
+ return schema$1;
341
+ }
342
+ }
343
+ return { loading: true };
344
+ }
345
+ const schema = alepha.inject(LinkProvider).links.find((it) => it.name === name)?.schema;
346
+ if (schema) return schema;
347
+ return { loading: true };
348
+ };
349
+
350
+ //#endregion
351
+ //#region src/core/hooks/useStore.ts
352
+ function useStore(target, defaultValue) {
353
+ const alepha = useAlepha();
354
+ useMemo(() => {
355
+ if (defaultValue != null && alepha.store.get(target) == null) alepha.store.set(target, defaultValue);
356
+ }, [defaultValue]);
357
+ const [state, setState] = useState(alepha.store.get(target));
358
+ useEffect(() => {
359
+ if (!alepha.isBrowser()) return;
360
+ const key = target instanceof Atom ? target.key : target;
361
+ return alepha.events.on("state:mutate", (ev) => {
362
+ if (ev.key === key) setState(ev.value);
363
+ });
364
+ }, []);
365
+ return [state, (value) => {
366
+ alepha.store.set(target, value);
367
+ }];
368
+ }
369
+
370
+ //#endregion
371
+ //#region src/core/index.native.ts
372
+ const AlephaReact = $module({
373
+ name: "alepha.react",
374
+ primitives: [],
375
+ services: [],
376
+ register: (alepha) => alepha.with(AlephaDateTime).with(AlephaServerLinks)
377
+ });
378
+
379
+ //#endregion
380
+ export { AlephaContext, AlephaReact, ssrSchemaLoading, useAction, useAlepha, useClient, useEvents, useInject, useSchema, useStore };
381
+ //# sourceMappingURL=index.native.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.native.js","names":["error","subs: Function[]","schema"],"sources":["../../src/core/contexts/AlephaContext.ts","../../src/core/hooks/useAlepha.ts","../../src/core/hooks/useInject.ts","../../src/core/hooks/useAction.ts","../../src/core/hooks/useClient.ts","../../src/core/hooks/useEvents.ts","../../src/core/hooks/useSchema.ts","../../src/core/hooks/useStore.ts","../../src/core/index.native.ts"],"sourcesContent":["import type { Alepha } from \"alepha\";\nimport { createContext } from \"react\";\n\nexport const AlephaContext = createContext<Alepha | undefined>(undefined);\n","import { type Alepha, AlephaError } from \"alepha\";\nimport { useContext } from \"react\";\nimport { AlephaContext } from \"../contexts/AlephaContext.ts\";\n\n/**\n * Main Alepha hook.\n *\n * It provides access to the Alepha instance within a React component.\n *\n * With Alepha, you can access the core functionalities of the framework:\n *\n * - alepha.state() for state management\n * - alepha.inject() for dependency injection\n * - alepha.events.emit() for event handling\n * etc...\n */\nexport const useAlepha = (): Alepha => {\n const alepha = useContext(AlephaContext);\n if (!alepha) {\n throw new AlephaError(\n \"Hook 'useAlepha()' must be used within an AlephaContext.Provider\",\n );\n }\n\n return alepha;\n};\n","import type { Service } from \"alepha\";\nimport { useMemo } from \"react\";\nimport { useAlepha } from \"./useAlepha.ts\";\n\n/**\n * Hook to inject a service instance.\n * It's a wrapper of `useAlepha().inject(service)` with a memoization.\n */\nexport const useInject = <T extends object>(service: Service<T>): T => {\n const alepha = useAlepha();\n return useMemo(() => alepha.inject(service), []);\n};\n","import {\n DateTimeProvider,\n type DurationLike,\n type Interval,\n type Timeout,\n} from \"alepha/datetime\";\nimport {\n type DependencyList,\n useCallback,\n useEffect,\n useRef,\n useState,\n} from \"react\";\nimport { useAlepha } from \"./useAlepha.ts\";\nimport { useInject } from \"./useInject.ts\";\n\n/**\n * Hook for handling async actions with automatic error handling and event emission.\n *\n * By default, prevents concurrent executions - if an action is running and you call it again,\n * the second call will be ignored. Use `debounce` option to delay execution instead.\n *\n * Emits lifecycle events:\n * - `react:action:begin` - When action starts\n * - `react:action:success` - When action completes successfully\n * - `react:action:error` - When action throws an error\n * - `react:action:end` - Always emitted at the end\n *\n * @example Basic usage\n * ```tsx\n * const action = useAction({\n * handler: async (data) => {\n * await api.save(data);\n * }\n * }, []);\n *\n * <button onClick={() => action.run(data)} disabled={action.loading}>\n * Save\n * </button>\n * ```\n *\n * @example With debounce (search input)\n * ```tsx\n * const search = useAction({\n * handler: async (query: string) => {\n * await api.search(query);\n * },\n * debounce: 300 // Wait 300ms after last call\n * }, []);\n *\n * <input onChange={(e) => search.run(e.target.value)} />\n * ```\n *\n * @example Run on component mount\n * ```tsx\n * const fetchData = useAction({\n * handler: async () => {\n * const data = await api.getData();\n * return data;\n * },\n * runOnInit: true // Runs once when component mounts\n * }, []);\n * ```\n *\n * @example Run periodically (polling)\n * ```tsx\n * const pollStatus = useAction({\n * handler: async () => {\n * const status = await api.getStatus();\n * return status;\n * },\n * runEvery: 5000 // Run every 5 seconds\n * }, []);\n *\n * // Or with duration tuple\n * const pollStatus = useAction({\n * handler: async () => {\n * const status = await api.getStatus();\n * return status;\n * },\n * runEvery: [30, 'seconds'] // Run every 30 seconds\n * }, []);\n * ```\n *\n * @example With AbortController\n * ```tsx\n * const fetch = useAction({\n * handler: async (url, { signal }) => {\n * const response = await fetch(url, { signal });\n * return response.json();\n * }\n * }, []);\n * // Automatically cancelled on unmount or when new request starts\n * ```\n *\n * @example With error handling\n * ```tsx\n * const deleteAction = useAction({\n * handler: async (id: string) => {\n * await api.delete(id);\n * },\n * onError: (error) => {\n * if (error.code === 'NOT_FOUND') {\n * // Custom error handling\n * }\n * }\n * }, []);\n *\n * {deleteAction.error && <div>Error: {deleteAction.error.message}</div>}\n * ```\n *\n * @example Global error handling\n * ```tsx\n * // In your root app setup\n * alepha.events.on(\"react:action:error\", ({ error }) => {\n * toast.danger(error.message);\n * Sentry.captureException(error);\n * });\n * ```\n */\nexport function useAction<Args extends any[], Result = void>(\n options: UseActionOptions<Args, Result>,\n deps: DependencyList,\n): UseActionReturn<Args, Result> {\n const alepha = useAlepha();\n const dateTimeProvider = useInject(DateTimeProvider);\n const [loading, setLoading] = useState(false);\n const [error, setError] = useState<Error | undefined>();\n const isExecutingRef = useRef(false);\n const debounceTimerRef = useRef<Timeout | undefined>(undefined);\n const abortControllerRef = useRef<AbortController | undefined>(undefined);\n const isMountedRef = useRef(true);\n const intervalRef = useRef<Interval | undefined>(undefined);\n\n // Cleanup on unmount\n useEffect(() => {\n return () => {\n isMountedRef.current = false;\n\n // clear debounce timer\n if (debounceTimerRef.current) {\n dateTimeProvider.clearTimeout(debounceTimerRef.current);\n debounceTimerRef.current = undefined;\n }\n\n // clear interval\n if (intervalRef.current) {\n dateTimeProvider.clearInterval(intervalRef.current);\n intervalRef.current = undefined;\n }\n\n // abort in-flight request\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n abortControllerRef.current = undefined;\n }\n };\n }, []);\n\n const executeAction = useCallback(\n async (...args: Args): Promise<Result | undefined> => {\n // Prevent concurrent executions\n if (isExecutingRef.current) {\n return;\n }\n\n // Abort previous request if still running\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n }\n\n // Create new AbortController for this request\n const abortController = new AbortController();\n abortControllerRef.current = abortController;\n\n isExecutingRef.current = true;\n setLoading(true);\n setError(undefined);\n\n await alepha.events.emit(\"react:action:begin\", {\n type: \"custom\",\n id: options.id,\n });\n\n try {\n // Pass abort signal as last argument to handler\n const result = await options.handler(...args, {\n signal: abortController.signal,\n } as any);\n\n // Only update state if still mounted and not aborted\n if (!isMountedRef.current || abortController.signal.aborted) {\n return;\n }\n\n await alepha.events.emit(\"react:action:success\", {\n type: \"custom\",\n id: options.id,\n });\n\n if (options.onSuccess) {\n await options.onSuccess(result);\n }\n\n return result;\n } catch (err) {\n // Ignore abort errors\n if (err instanceof Error && err.name === \"AbortError\") {\n return;\n }\n\n // Only update state if still mounted\n if (!isMountedRef.current) {\n return;\n }\n\n const error = err as Error;\n setError(error);\n\n await alepha.events.emit(\"react:action:error\", {\n type: \"custom\",\n id: options.id,\n error,\n });\n\n if (options.onError) {\n await options.onError(error);\n } else {\n // Re-throw if no custom error handler\n throw error;\n }\n } finally {\n isExecutingRef.current = false;\n setLoading(false);\n\n await alepha.events.emit(\"react:action:end\", {\n type: \"custom\",\n id: options.id,\n });\n\n // Clean up abort controller\n if (abortControllerRef.current === abortController) {\n abortControllerRef.current = undefined;\n }\n }\n },\n [...deps, options.id, options.onError, options.onSuccess],\n );\n\n const handler = useCallback(\n async (...args: Args): Promise<Result | undefined> => {\n if (options.debounce) {\n // clear existing timer\n if (debounceTimerRef.current) {\n dateTimeProvider.clearTimeout(debounceTimerRef.current);\n }\n\n // Set new timer\n return new Promise((resolve) => {\n debounceTimerRef.current = dateTimeProvider.createTimeout(\n async () => {\n const result = await executeAction(...args);\n resolve(result);\n },\n options.debounce ?? 0,\n );\n });\n }\n\n return executeAction(...args);\n },\n [executeAction, options.debounce],\n );\n\n const cancel = useCallback(() => {\n // clear debounce timer\n if (debounceTimerRef.current) {\n dateTimeProvider.clearTimeout(debounceTimerRef.current);\n debounceTimerRef.current = undefined;\n }\n\n // abort in-flight request\n if (abortControllerRef.current) {\n abortControllerRef.current.abort();\n abortControllerRef.current = undefined;\n }\n\n // reset state\n if (isMountedRef.current) {\n isExecutingRef.current = false;\n setLoading(false);\n }\n }, []);\n\n // Run action on mount if runOnInit is true\n useEffect(() => {\n if (options.runOnInit) {\n handler(...([] as any));\n }\n }, deps);\n\n // Run action periodically if runEvery is specified\n useEffect(() => {\n if (!options.runEvery) {\n return;\n }\n\n // Set up interval\n intervalRef.current = dateTimeProvider.createInterval(\n () => handler(...([] as any)),\n options.runEvery,\n true,\n );\n\n // cleanup on unmount or when runEvery changes\n return () => {\n if (intervalRef.current) {\n dateTimeProvider.clearInterval(intervalRef.current);\n intervalRef.current = undefined;\n }\n };\n }, [handler, options.runEvery]);\n\n return {\n run: handler,\n loading,\n error,\n cancel,\n };\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Context object passed as the last argument to action handlers.\n * Contains an AbortSignal that can be used to cancel the request.\n */\nexport interface ActionContext {\n /**\n * AbortSignal that can be passed to fetch or other async operations.\n * The signal will be aborted when:\n * - The component unmounts\n * - A new action is triggered (cancels previous)\n * - The cancel() method is called\n *\n * @example\n * ```tsx\n * const action = useAction({\n * handler: async (url, { signal }) => {\n * const response = await fetch(url, { signal });\n * return response.json();\n * }\n * }, []);\n * ```\n */\n signal: AbortSignal;\n}\n\nexport interface UseActionOptions<Args extends any[] = any[], Result = any> {\n /**\n * The async action handler function.\n * Receives the action arguments plus an ActionContext as the last parameter.\n */\n handler: (...args: [...Args, ActionContext]) => Promise<Result>;\n\n /**\n * Custom error handler. If provided, prevents default error re-throw.\n */\n onError?: (error: Error) => void | Promise<void>;\n\n /**\n * Custom success handler.\n */\n onSuccess?: (result: Result) => void | Promise<void>;\n\n /**\n * Optional identifier for this action (useful for debugging/analytics)\n */\n id?: string;\n\n /**\n * Debounce delay in milliseconds. If specified, the action will only execute\n * after the specified delay has passed since the last call. Useful for search inputs\n * or other high-frequency events.\n *\n * @example\n * ```tsx\n * // Execute search 300ms after user stops typing\n * const search = useAction({ handler: search, debounce: 300 }, [])\n * ```\n */\n debounce?: number;\n\n /**\n * If true, the action will be executed once when the component mounts.\n *\n * @example\n * ```tsx\n * const fetchData = useAction({\n * handler: async () => await api.getData(),\n * runOnInit: true\n * }, []);\n * ```\n */\n runOnInit?: boolean;\n\n /**\n * If specified, the action will be executed periodically at the given interval.\n * The interval is specified as a DurationLike value (number in ms, Duration object, or [number, unit] tuple).\n *\n * @example\n * ```tsx\n * // Run every 5 seconds\n * const poll = useAction({\n * handler: async () => await api.poll(),\n * runEvery: 5000\n * }, []);\n * ```\n *\n * @example\n * ```tsx\n * // Run every 1 minute\n * const poll = useAction({\n * handler: async () => await api.poll(),\n * runEvery: [1, 'minute']\n * }, []);\n * ```\n */\n runEvery?: DurationLike;\n}\n\nexport interface UseActionReturn<Args extends any[], Result> {\n /**\n * Execute the action with the provided arguments.\n *\n * @example\n * ```tsx\n * const action = useAction({ handler: async (data) => { ... } }, []);\n * action.run(data);\n * ```\n */\n run: (...args: Args) => Promise<Result | undefined>;\n\n /**\n * Loading state - true when action is executing.\n */\n loading: boolean;\n\n /**\n * Error state - contains error if action failed, undefined otherwise.\n */\n error?: Error;\n\n /**\n * Cancel any pending debounced action or abort the current in-flight request.\n *\n * @example\n * ```tsx\n * const action = useAction({ ... }, []);\n *\n * <button onClick={action.cancel} disabled={!action.loading}>\n * Cancel\n * </button>\n * ```\n */\n cancel: () => void;\n}\n","import {\n type ClientScope,\n type HttpVirtualClient,\n LinkProvider,\n} from \"alepha/server/links\";\nimport { useInject } from \"./useInject.ts\";\n\n/**\n * Hook to get a virtual client for the specified scope.\n *\n * It's the React-hook version of `$client()`, from `AlephaServerLinks` module.\n */\nexport const useClient = <T extends object>(\n scope?: ClientScope,\n): HttpVirtualClient<T> => {\n return useInject(LinkProvider).client<T>(scope);\n};\n","import type { Async, Hook, Hooks } from \"alepha\";\nimport { type DependencyList, useEffect } from \"react\";\nimport { useAlepha } from \"./useAlepha.ts\";\n\n/**\n * Allow subscribing to multiple Alepha events. See {@link Hooks} for available events.\n *\n * useEvents is fully typed to ensure correct event callback signatures.\n *\n * @example\n * ```tsx\n * useEvents(\n * {\n * \"react:transition:begin\": (ev) => {\n * console.log(\"Transition began to:\", ev.to);\n * },\n * \"react:transition:error\": {\n * priority: \"first\",\n * callback: (ev) => {\n * console.error(\"Transition error:\", ev.error);\n * },\n * },\n * },\n * [],\n * );\n * ```\n */\nexport const useEvents = (opts: UseEvents, deps: DependencyList) => {\n const alepha = useAlepha();\n\n useEffect(() => {\n if (!alepha.isBrowser()) {\n return;\n }\n\n const subs: Function[] = [];\n for (const [name, hook] of Object.entries(opts)) {\n subs.push(alepha.events.on(name as any, hook as any));\n }\n\n return () => {\n for (const clear of subs) {\n clear();\n }\n };\n }, deps);\n};\n\ntype UseEvents = {\n [T in keyof Hooks]?: Hook<T> | ((payload: Hooks[T]) => Async<void>);\n};\n","import type { Alepha } from \"alepha\";\nimport {\n type FetchOptions,\n HttpClient,\n type RequestConfigSchema,\n} from \"alepha/server\";\nimport { LinkProvider, type VirtualAction } from \"alepha/server/links\";\nimport { useEffect, useState } from \"react\";\nimport { useAlepha } from \"./useAlepha.ts\";\nimport { useInject } from \"./useInject.ts\";\n\nexport const useSchema = <TConfig extends RequestConfigSchema>(\n action: VirtualAction<TConfig>,\n): UseSchemaReturn<TConfig> => {\n const name = action.name;\n const alepha = useAlepha();\n const httpClient = useInject(HttpClient);\n const [schema, setSchema] = useState<UseSchemaReturn<TConfig>>(\n ssrSchemaLoading(alepha, name) as UseSchemaReturn<TConfig>,\n );\n\n useEffect(() => {\n if (!schema.loading) {\n return;\n }\n\n const opts: FetchOptions = {\n localCache: true,\n };\n\n httpClient\n .fetch(`${LinkProvider.path.apiLinks}/${name}/schema`, opts)\n .then((it) => setSchema(it.data as UseSchemaReturn<TConfig>));\n }, [name]);\n\n return schema;\n};\n\nexport type UseSchemaReturn<TConfig extends RequestConfigSchema> = TConfig & {\n loading: boolean;\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Get an action schema during server-side rendering (SSR) or client-side rendering (CSR).\n */\nexport const ssrSchemaLoading = (alepha: Alepha, name: string) => {\n // server-side rendering (SSR) context\n if (!alepha.isBrowser()) {\n // get user links\n const linkProvider = alepha.inject(LinkProvider);\n\n // check if user can access the link\n const can = linkProvider\n .getServerLinks()\n .find((link) => link.name === name);\n\n // yes!\n if (can) {\n // user-links have no schema, so we need to get it from the provider\n const schema = linkProvider.links.find((it) => it.name === name)?.schema;\n\n // oh, we have a schema!\n if (schema) {\n // attach to user link, it will be used in the client during hydration\n can.schema = schema;\n return schema;\n }\n }\n\n return { loading: true };\n }\n\n // browser side rendering (CSR) context\n // check if we have the schema already loaded\n const schema = alepha\n .inject(LinkProvider)\n .links.find((it) => it.name === name)?.schema;\n\n // yes!\n if (schema) {\n return schema;\n }\n\n // no, we need to load it\n return { loading: true };\n};\n","import type { State, Static, TAtomObject } from \"alepha\";\nimport { Atom } from \"alepha\";\nimport { useEffect, useMemo, useState } from \"react\";\nimport { useAlepha } from \"./useAlepha.ts\";\n\n/**\n * Hook to access and mutate the Alepha state.\n */\nfunction useStore<T extends TAtomObject>(\n target: Atom<T>,\n defaultValue?: Static<T>,\n): UseStoreReturn<Static<T>>;\nfunction useStore<Key extends keyof State>(\n target: Key,\n defaultValue?: State[Key],\n): UseStoreReturn<State[Key]>;\nfunction useStore(target: any, defaultValue?: any): any {\n const alepha = useAlepha();\n\n useMemo(() => {\n if (defaultValue != null && alepha.store.get(target) == null) {\n alepha.store.set(target, defaultValue);\n }\n }, [defaultValue]);\n\n const [state, setState] = useState(alepha.store.get(target));\n\n useEffect(() => {\n if (!alepha.isBrowser()) {\n return;\n }\n\n const key = target instanceof Atom ? target.key : target;\n\n return alepha.events.on(\"state:mutate\", (ev) => {\n if (ev.key === key) {\n setState(ev.value);\n }\n });\n }, []);\n\n return [\n state,\n (value: any) => {\n alepha.store.set(target, value);\n },\n ] as const;\n}\n\nexport type UseStoreReturn<T> = [T, (value: T) => void];\n\nexport { useStore };\n","import { $module } from \"alepha\";\nimport { AlephaDateTime } from \"alepha/datetime\";\nimport { AlephaServerLinks } from \"alepha/server/links\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./index.shared.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport const AlephaReact = $module({\n name: \"alepha.react\",\n primitives: [],\n services: [\n\n ],\n register: (alepha) =>\n alepha\n .with(AlephaDateTime)\n .with(AlephaServerLinks)\n});\n"],"mappings":"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"}
@@ -28,8 +28,8 @@ declare class DateTimeProvider {
28
28
  protected readonly timeouts: Timeout[];
29
29
  protected readonly intervals: Interval[];
30
30
  constructor();
31
- protected readonly onStart: alepha8.HookDescriptor<"start">;
32
- protected readonly onStop: alepha8.HookDescriptor<"stop">;
31
+ protected readonly onStart: alepha8.HookPrimitive<"start">;
32
+ protected readonly onStop: alepha8.HookPrimitive<"stop">;
33
33
  setLocale(locale: string): void;
34
34
  isDateTime(value: unknown): value is DateTime;
35
35
  /**
@@ -1,5 +1,5 @@
1
1
  import { AlephaReact, useInject } from "@alepha/react";
2
- import { $hook, $inject, $module, Alepha, Descriptor, KIND, createDescriptor } from "alepha";
2
+ import { $hook, $inject, $module, Alepha, KIND, Primitive, createPrimitive } from "alepha";
3
3
  import { useCallback, useEffect, useMemo } from "react";
4
4
 
5
5
  //#region src/head/providers/HeadProvider.ts
@@ -39,20 +39,20 @@ var HeadProvider = class {
39
39
  };
40
40
 
41
41
  //#endregion
42
- //#region src/head/descriptors/$head.ts
42
+ //#region src/head/primitives/$head.ts
43
43
  /**
44
44
  * Set global `<head>` options for the application.
45
45
  */
46
46
  const $head = (options) => {
47
- return createDescriptor(HeadDescriptor, options);
47
+ return createPrimitive(HeadPrimitive, options);
48
48
  };
49
- var HeadDescriptor = class extends Descriptor {
49
+ var HeadPrimitive = class extends Primitive {
50
50
  provider = $inject(HeadProvider);
51
51
  onInit() {
52
52
  this.provider.global = this.options;
53
53
  }
54
54
  };
55
- $head[KIND] = HeadDescriptor;
55
+ $head[KIND] = HeadPrimitive;
56
56
 
57
57
  //#endregion
58
58
  //#region src/head/providers/BrowserHeadProvider.ts
@@ -169,10 +169,10 @@ const useHead = (options) => {
169
169
  */
170
170
  const AlephaReactHead = $module({
171
171
  name: "alepha.react.head",
172
- descriptors: [$head],
172
+ primitives: [$head],
173
173
  services: [AlephaReact, BrowserHeadProvider]
174
174
  });
175
175
 
176
176
  //#endregion
177
- export { $head, AlephaReactHead, BrowserHeadProvider, HeadDescriptor, useHead };
177
+ export { $head, AlephaReactHead, BrowserHeadProvider, HeadPrimitive, useHead };
178
178
  //# sourceMappingURL=index.browser.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.browser.js","names":["attrs: Record<string, string>","metas: { name: string; content: string }[]"],"sources":["../../src/head/providers/HeadProvider.ts","../../src/head/descriptors/$head.ts","../../src/head/providers/BrowserHeadProvider.ts","../../src/head/hooks/useHead.ts","../../src/head/index.browser.ts"],"sourcesContent":["import type { PageRoute, ReactRouterState } from \"@alepha/react\";\nimport type { Head } from \"../interfaces/Head.ts\";\n\nexport class HeadProvider {\n public global?: Head | (() => Head);\n\n protected getGlobalHead(): Head | undefined {\n if (typeof this.global === \"function\") {\n return this.global();\n }\n return this.global;\n }\n\n public fillHead(state: ReactRouterState) {\n state.head = {\n ...state.head,\n ...this.getGlobalHead(),\n };\n\n for (const layer of state.layers) {\n if (layer.route?.head && !layer.error) {\n this.fillHeadByPage(layer.route, state, layer.props ?? {});\n }\n }\n }\n\n protected fillHeadByPage(\n page: PageRoute,\n state: ReactRouterState,\n props: Record<string, any>,\n ): void {\n if (!page.head) {\n return;\n }\n\n state.head ??= {};\n\n const head =\n typeof page.head === \"function\"\n ? page.head(props, state.head)\n : page.head;\n\n if (head.title) {\n state.head ??= {};\n\n if (state.head.titleSeparator) {\n state.head.title = `${head.title}${state.head.titleSeparator}${state.head.title}`;\n } else {\n state.head.title = head.title;\n }\n\n state.head.titleSeparator = head.titleSeparator;\n }\n\n if (head.htmlAttributes) {\n state.head.htmlAttributes = {\n ...state.head.htmlAttributes,\n ...head.htmlAttributes,\n };\n }\n\n if (head.bodyAttributes) {\n state.head.bodyAttributes = {\n ...state.head.bodyAttributes,\n ...head.bodyAttributes,\n };\n }\n\n if (head.meta) {\n state.head.meta = [...(state.head.meta ?? []), ...(head.meta ?? [])];\n }\n }\n}\n","import { $inject, createDescriptor, Descriptor, KIND } from \"alepha\";\nimport type { Head } from \"../interfaces/Head.ts\";\nimport { HeadProvider } from \"../providers/HeadProvider.ts\";\n\n/**\n * Set global `<head>` options for the application.\n */\nexport const $head = (options: HeadDescriptorOptions) => {\n return createDescriptor(HeadDescriptor, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport type HeadDescriptorOptions = Head | (() => Head);\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class HeadDescriptor extends Descriptor<HeadDescriptorOptions> {\n protected readonly provider = $inject(HeadProvider);\n protected onInit() {\n this.provider.global = this.options;\n }\n}\n\n$head[KIND] = HeadDescriptor;\n","import { $hook, $inject } from \"alepha\";\nimport type { Head } from \"../interfaces/Head.ts\";\nimport { HeadProvider } from \"./HeadProvider.ts\";\n\nexport class BrowserHeadProvider {\n protected readonly headProvider = $inject(HeadProvider);\n\n protected get document(): Document {\n return window.document;\n }\n\n protected readonly onBrowserRender = $hook({\n on: \"react:browser:render\",\n handler: async ({ state }) => {\n this.headProvider.fillHead(state);\n if (state.head) {\n this.renderHead(this.document, state.head);\n }\n },\n });\n\n protected readonly onTransitionEnd = $hook({\n on: \"react:transition:end\",\n handler: async ({ state }) => {\n this.headProvider.fillHead(state);\n if (state.head) {\n this.renderHead(this.document, state.head);\n }\n },\n });\n\n public getHead(document: Document): Head {\n return {\n get title() {\n return document.title;\n },\n get htmlAttributes() {\n const attrs: Record<string, string> = {};\n for (const attr of document.documentElement.attributes) {\n attrs[attr.name] = attr.value;\n }\n return attrs;\n },\n get bodyAttributes() {\n const attrs: Record<string, string> = {};\n for (const attr of document.body.attributes) {\n attrs[attr.name] = attr.value;\n }\n return attrs;\n },\n get meta() {\n const metas: { name: string; content: string }[] = [];\n for (const meta of document.head.querySelectorAll(\"meta[name]\")) {\n const name = meta.getAttribute(\"name\");\n const content = meta.getAttribute(\"content\");\n if (name && content) {\n metas.push({ name, content });\n }\n }\n return metas;\n },\n };\n }\n\n public renderHead(document: Document, head: Head): void {\n if (head.title) {\n document.title = head.title;\n }\n\n if (head.bodyAttributes) {\n for (const [key, value] of Object.entries(head.bodyAttributes)) {\n if (value) {\n document.body.setAttribute(key, value);\n } else {\n document.body.removeAttribute(key);\n }\n }\n }\n\n if (head.htmlAttributes) {\n for (const [key, value] of Object.entries(head.htmlAttributes)) {\n if (value) {\n document.documentElement.setAttribute(key, value);\n } else {\n document.documentElement.removeAttribute(key);\n }\n }\n }\n\n if (head.meta) {\n for (const it of head.meta) {\n const { name, content } = it;\n const meta = document.querySelector(`meta[name=\"${name}\"]`);\n if (meta) {\n meta.setAttribute(\"content\", content);\n } else {\n const newMeta = document.createElement(\"meta\");\n newMeta.setAttribute(\"name\", name);\n newMeta.setAttribute(\"content\", content);\n document.head.appendChild(newMeta);\n }\n }\n }\n }\n}\n","import { useInject } from \"@alepha/react\";\nimport { Alepha } from \"alepha\";\nimport { useCallback, useEffect, useMemo } from \"react\";\nimport type { Head } from \"../interfaces/Head.ts\";\nimport { BrowserHeadProvider } from \"../providers/BrowserHeadProvider.ts\";\n\n/**\n * ```tsx\n * const App = () => {\n * const [head, setHead] = useHead({\n * // will set the document title on the first render\n * title: \"My App\",\n * });\n *\n * return (\n * // This will update the document title when the button is clicked\n * <button onClick={() => setHead({ title: \"Change Title\" })}>\n * Change Title {head.title}\n * </button>\n * );\n * }\n * ```\n */\nexport const useHead = (options?: UseHeadOptions): UseHeadReturn => {\n const alepha = useInject(Alepha);\n\n const current = useMemo(() => {\n if (!alepha.isBrowser()) {\n return {};\n }\n\n return alepha.inject(BrowserHeadProvider).getHead(window.document);\n }, []);\n\n const setHead = useCallback((head?: Head | ((previous?: Head) => Head)) => {\n if (!alepha.isBrowser()) {\n return;\n }\n\n alepha\n .inject(BrowserHeadProvider)\n .renderHead(\n window.document,\n typeof head === \"function\" ? head(current) : head || {},\n );\n }, []);\n\n useEffect(() => {\n if (options) {\n setHead(options);\n }\n }, []);\n\n return [current, setHead];\n};\n\nexport type UseHeadOptions = Head | ((previous?: Head) => Head);\n\nexport type UseHeadReturn = [\n Head,\n (head?: Head | ((previous?: Head) => Head)) => void,\n];\n","import { AlephaReact } from \"@alepha/react\";\nimport { $module } from \"alepha\";\nimport { $head } from \"./descriptors/$head.ts\";\nimport { BrowserHeadProvider } from \"./providers/BrowserHeadProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./descriptors/$head.ts\";\nexport * from \"./hooks/useHead.ts\";\nexport * from \"./interfaces/Head.ts\";\nexport * from \"./providers/BrowserHeadProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Alepha React Head Module\n *\n * @see {@link BrowserHeadProvider}\n * @module alepha.react.head\n */\nexport const AlephaReactHead = $module({\n name: \"alepha.react.head\",\n descriptors: [$head],\n services: [AlephaReact, BrowserHeadProvider],\n});\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.browser.js","names":["attrs: Record<string, string>","metas: { name: string; content: string }[]"],"sources":["../../src/head/providers/HeadProvider.ts","../../src/head/primitives/$head.ts","../../src/head/providers/BrowserHeadProvider.ts","../../src/head/hooks/useHead.ts","../../src/head/index.browser.ts"],"sourcesContent":["import type { PageRoute, ReactRouterState } from \"@alepha/react\";\nimport type { Head } from \"../interfaces/Head.ts\";\n\nexport class HeadProvider {\n public global?: Head | (() => Head);\n\n protected getGlobalHead(): Head | undefined {\n if (typeof this.global === \"function\") {\n return this.global();\n }\n return this.global;\n }\n\n public fillHead(state: ReactRouterState) {\n state.head = {\n ...state.head,\n ...this.getGlobalHead(),\n };\n\n for (const layer of state.layers) {\n if (layer.route?.head && !layer.error) {\n this.fillHeadByPage(layer.route, state, layer.props ?? {});\n }\n }\n }\n\n protected fillHeadByPage(\n page: PageRoute,\n state: ReactRouterState,\n props: Record<string, any>,\n ): void {\n if (!page.head) {\n return;\n }\n\n state.head ??= {};\n\n const head =\n typeof page.head === \"function\"\n ? page.head(props, state.head)\n : page.head;\n\n if (head.title) {\n state.head ??= {};\n\n if (state.head.titleSeparator) {\n state.head.title = `${head.title}${state.head.titleSeparator}${state.head.title}`;\n } else {\n state.head.title = head.title;\n }\n\n state.head.titleSeparator = head.titleSeparator;\n }\n\n if (head.htmlAttributes) {\n state.head.htmlAttributes = {\n ...state.head.htmlAttributes,\n ...head.htmlAttributes,\n };\n }\n\n if (head.bodyAttributes) {\n state.head.bodyAttributes = {\n ...state.head.bodyAttributes,\n ...head.bodyAttributes,\n };\n }\n\n if (head.meta) {\n state.head.meta = [...(state.head.meta ?? []), ...(head.meta ?? [])];\n }\n }\n}\n","import { $inject, createPrimitive, Primitive, KIND } from \"alepha\";\nimport type { Head } from \"../interfaces/Head.ts\";\nimport { HeadProvider } from \"../providers/HeadProvider.ts\";\n\n/**\n * Set global `<head>` options for the application.\n */\nexport const $head = (options: HeadPrimitiveOptions) => {\n return createPrimitive(HeadPrimitive, options);\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport type HeadPrimitiveOptions = Head | (() => Head);\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class HeadPrimitive extends Primitive<HeadPrimitiveOptions> {\n protected readonly provider = $inject(HeadProvider);\n protected onInit() {\n this.provider.global = this.options;\n }\n}\n\n$head[KIND] = HeadPrimitive;\n","import { $hook, $inject } from \"alepha\";\nimport type { Head } from \"../interfaces/Head.ts\";\nimport { HeadProvider } from \"./HeadProvider.ts\";\n\nexport class BrowserHeadProvider {\n protected readonly headProvider = $inject(HeadProvider);\n\n protected get document(): Document {\n return window.document;\n }\n\n protected readonly onBrowserRender = $hook({\n on: \"react:browser:render\",\n handler: async ({ state }) => {\n this.headProvider.fillHead(state);\n if (state.head) {\n this.renderHead(this.document, state.head);\n }\n },\n });\n\n protected readonly onTransitionEnd = $hook({\n on: \"react:transition:end\",\n handler: async ({ state }) => {\n this.headProvider.fillHead(state);\n if (state.head) {\n this.renderHead(this.document, state.head);\n }\n },\n });\n\n public getHead(document: Document): Head {\n return {\n get title() {\n return document.title;\n },\n get htmlAttributes() {\n const attrs: Record<string, string> = {};\n for (const attr of document.documentElement.attributes) {\n attrs[attr.name] = attr.value;\n }\n return attrs;\n },\n get bodyAttributes() {\n const attrs: Record<string, string> = {};\n for (const attr of document.body.attributes) {\n attrs[attr.name] = attr.value;\n }\n return attrs;\n },\n get meta() {\n const metas: { name: string; content: string }[] = [];\n for (const meta of document.head.querySelectorAll(\"meta[name]\")) {\n const name = meta.getAttribute(\"name\");\n const content = meta.getAttribute(\"content\");\n if (name && content) {\n metas.push({ name, content });\n }\n }\n return metas;\n },\n };\n }\n\n public renderHead(document: Document, head: Head): void {\n if (head.title) {\n document.title = head.title;\n }\n\n if (head.bodyAttributes) {\n for (const [key, value] of Object.entries(head.bodyAttributes)) {\n if (value) {\n document.body.setAttribute(key, value);\n } else {\n document.body.removeAttribute(key);\n }\n }\n }\n\n if (head.htmlAttributes) {\n for (const [key, value] of Object.entries(head.htmlAttributes)) {\n if (value) {\n document.documentElement.setAttribute(key, value);\n } else {\n document.documentElement.removeAttribute(key);\n }\n }\n }\n\n if (head.meta) {\n for (const it of head.meta) {\n const { name, content } = it;\n const meta = document.querySelector(`meta[name=\"${name}\"]`);\n if (meta) {\n meta.setAttribute(\"content\", content);\n } else {\n const newMeta = document.createElement(\"meta\");\n newMeta.setAttribute(\"name\", name);\n newMeta.setAttribute(\"content\", content);\n document.head.appendChild(newMeta);\n }\n }\n }\n }\n}\n","import { useInject } from \"@alepha/react\";\nimport { Alepha } from \"alepha\";\nimport { useCallback, useEffect, useMemo } from \"react\";\nimport type { Head } from \"../interfaces/Head.ts\";\nimport { BrowserHeadProvider } from \"../providers/BrowserHeadProvider.ts\";\n\n/**\n * ```tsx\n * const App = () => {\n * const [head, setHead] = useHead({\n * // will set the document title on the first render\n * title: \"My App\",\n * });\n *\n * return (\n * // This will update the document title when the button is clicked\n * <button onClick={() => setHead({ title: \"Change Title\" })}>\n * Change Title {head.title}\n * </button>\n * );\n * }\n * ```\n */\nexport const useHead = (options?: UseHeadOptions): UseHeadReturn => {\n const alepha = useInject(Alepha);\n\n const current = useMemo(() => {\n if (!alepha.isBrowser()) {\n return {};\n }\n\n return alepha.inject(BrowserHeadProvider).getHead(window.document);\n }, []);\n\n const setHead = useCallback((head?: Head | ((previous?: Head) => Head)) => {\n if (!alepha.isBrowser()) {\n return;\n }\n\n alepha\n .inject(BrowserHeadProvider)\n .renderHead(\n window.document,\n typeof head === \"function\" ? head(current) : head || {},\n );\n }, []);\n\n useEffect(() => {\n if (options) {\n setHead(options);\n }\n }, []);\n\n return [current, setHead];\n};\n\nexport type UseHeadOptions = Head | ((previous?: Head) => Head);\n\nexport type UseHeadReturn = [\n Head,\n (head?: Head | ((previous?: Head) => Head)) => void,\n];\n","import { AlephaReact } from \"@alepha/react\";\nimport { $module } from \"alepha\";\nimport { $head } from \"./primitives/$head.ts\";\nimport { BrowserHeadProvider } from \"./providers/BrowserHeadProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./primitives/$head.ts\";\nexport * from \"./hooks/useHead.ts\";\nexport * from \"./interfaces/Head.ts\";\nexport * from \"./providers/BrowserHeadProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\n/**\n * Alepha React Head Module\n *\n * @see {@link BrowserHeadProvider}\n * @module alepha.react.head\n */\nexport const AlephaReactHead = $module({\n name: \"alepha.react.head\",\n primitives: [$head],\n services: [AlephaReact, BrowserHeadProvider],\n});\n"],"mappings":"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"}