@alepha/react 0.11.10 → 0.11.12
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.
- package/README.md +1 -183
- package/dist/auth/index.browser.js +1460 -0
- package/dist/auth/index.browser.js.map +1 -0
- package/dist/auth/index.cjs +3647 -0
- package/dist/auth/index.cjs.map +1 -0
- package/dist/auth/index.d.cts +564 -0
- package/dist/auth/index.d.cts.map +1 -0
- package/dist/auth/index.d.ts +564 -0
- package/dist/auth/index.d.ts.map +1 -0
- package/dist/auth/index.js +3615 -0
- package/dist/auth/index.js.map +1 -0
- package/dist/{index.browser.js → core/index.browser.js} +36 -35
- package/dist/core/index.browser.js.map +1 -0
- package/dist/{index.cjs → core/index.cjs} +141 -140
- package/dist/core/index.cjs.map +1 -0
- package/dist/{index.d.cts → core/index.d.cts} +68 -68
- package/dist/core/index.d.cts.map +1 -0
- package/dist/{index.d.ts → core/index.d.ts} +68 -68
- package/dist/core/index.d.ts.map +1 -0
- package/dist/{index.js → core/index.js} +39 -38
- package/dist/core/index.js.map +1 -0
- package/dist/form/index.cjs +2054 -0
- package/dist/form/index.cjs.map +1 -0
- package/dist/form/index.d.cts +211 -0
- package/dist/form/index.d.cts.map +1 -0
- package/dist/form/index.d.ts +211 -0
- package/dist/form/index.d.ts.map +1 -0
- package/dist/form/index.js +2026 -0
- package/dist/form/index.js.map +1 -0
- package/dist/head/index.browser.js +1503 -0
- package/dist/head/index.browser.js.map +1 -0
- package/dist/head/index.cjs +1908 -0
- package/dist/head/index.cjs.map +1 -0
- package/dist/head/index.d.cts +595 -0
- package/dist/head/index.d.cts.map +1 -0
- package/dist/head/index.d.ts +601 -0
- package/dist/head/index.d.ts.map +1 -0
- package/dist/head/index.js +1880 -0
- package/dist/head/index.js.map +1 -0
- package/dist/i18n/index.cjs +1886 -0
- package/dist/i18n/index.cjs.map +1 -0
- package/dist/i18n/index.d.cts +168 -0
- package/dist/i18n/index.d.cts.map +1 -0
- package/dist/i18n/index.d.ts +168 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +1857 -0
- package/dist/i18n/index.js.map +1 -0
- package/dist/websocket/index.cjs +1774 -0
- package/dist/websocket/index.cjs.map +1 -0
- package/dist/websocket/index.d.cts +118 -0
- package/dist/websocket/index.d.cts.map +1 -0
- package/dist/websocket/index.d.ts +118 -0
- package/dist/websocket/index.d.ts.map +1 -0
- package/dist/websocket/index.js +1750 -0
- package/dist/websocket/index.js.map +1 -0
- package/package.json +89 -67
- package/src/auth/descriptors/$auth.ts +436 -0
- package/src/auth/descriptors/$authApple.ts +8 -0
- package/src/auth/descriptors/$authGithub.ts +81 -0
- package/src/auth/descriptors/$authGoogle.ts +38 -0
- package/src/auth/errors/SessionExpiredError.ts +6 -0
- package/src/auth/hooks/useAuth.ts +31 -0
- package/src/auth/index.browser.ts +16 -0
- package/src/auth/index.shared.ts +3 -0
- package/src/auth/index.ts +47 -0
- package/src/auth/providers/ReactAuthProvider.ts +629 -0
- package/src/auth/schemas/tokenResponseSchema.ts +11 -0
- package/src/auth/schemas/tokensSchema.ts +21 -0
- package/src/auth/schemas/userinfoResponseSchema.ts +10 -0
- package/src/auth/services/ReactAuth.ts +124 -0
- package/src/{components → core/components}/ErrorViewer.tsx +3 -2
- package/src/{components → core/components}/NestedView.tsx +1 -1
- package/src/{contexts → core/contexts}/AlephaContext.ts +1 -1
- package/src/{descriptors → core/descriptors}/$page.ts +4 -4
- package/src/{hooks → core/hooks}/useAction.ts +1 -1
- package/src/{hooks → core/hooks}/useAlepha.ts +1 -1
- package/src/{hooks → core/hooks}/useClient.ts +1 -1
- package/src/{hooks → core/hooks}/useEvents.ts +1 -1
- package/src/{hooks → core/hooks}/useInject.ts +1 -1
- package/src/{hooks → core/hooks}/useQueryParams.ts +1 -1
- package/src/{hooks → core/hooks}/useRouterState.ts +1 -1
- package/src/{hooks → core/hooks}/useSchema.ts +3 -3
- package/src/{hooks → core/hooks}/useStore.ts +2 -2
- package/src/{index.browser.ts → core/index.browser.ts} +4 -4
- package/src/{index.ts → core/index.ts} +6 -6
- package/src/{providers → core/providers}/ReactBrowserProvider.ts +6 -6
- package/src/{providers → core/providers}/ReactBrowserRendererProvider.ts +2 -2
- package/src/{providers → core/providers}/ReactBrowserRouterProvider.ts +3 -3
- package/src/{providers → core/providers}/ReactPageProvider.ts +3 -3
- package/src/{providers → core/providers}/ReactServerProvider.ts +7 -7
- package/src/{services → core/services}/ReactPageServerService.ts +2 -2
- package/src/{services → core/services}/ReactPageService.ts +1 -1
- package/src/{services → core/services}/ReactRouter.ts +1 -1
- package/src/form/components/FormState.tsx +17 -0
- package/src/form/hooks/useForm.ts +47 -0
- package/src/form/hooks/useFormState.ts +130 -0
- package/src/form/index.ts +38 -0
- package/src/form/services/FormModel.ts +548 -0
- package/src/head/descriptors/$head.ts +25 -0
- package/src/head/hooks/useHead.ts +62 -0
- package/src/head/index.browser.ts +25 -0
- package/src/head/index.ts +47 -0
- package/src/head/interfaces/Head.ts +46 -0
- package/src/head/providers/BrowserHeadProvider.ts +105 -0
- package/src/head/providers/HeadProvider.ts +73 -0
- package/src/head/providers/ServerHeadProvider.ts +109 -0
- package/src/i18n/README.md +76 -0
- package/src/i18n/components/Localize.tsx +35 -0
- package/src/i18n/descriptors/$dictionary.ts +65 -0
- package/src/i18n/hooks/useI18n.ts +18 -0
- package/src/i18n/index.ts +34 -0
- package/src/i18n/providers/I18nProvider.ts +277 -0
- package/src/websocket/hooks/useRoom.tsx +223 -0
- package/src/websocket/index.ts +7 -0
- package/dist/index.browser.js.map +0 -1
- package/dist/index.cjs.map +0 -1
- package/dist/index.d.cts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- /package/src/{components → core/components}/ClientOnly.tsx +0 -0
- /package/src/{components → core/components}/ErrorBoundary.tsx +0 -0
- /package/src/{components → core/components}/Link.tsx +0 -0
- /package/src/{components → core/components}/NotFound.tsx +0 -0
- /package/src/{contexts → core/contexts}/RouterLayerContext.ts +0 -0
- /package/src/{errors → core/errors}/Redirection.ts +0 -0
- /package/src/{hooks → core/hooks}/useActive.ts +0 -0
- /package/src/{hooks → core/hooks}/useRouter.ts +0 -0
- /package/src/{index.shared.ts → core/index.shared.ts} +0 -0
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { $atom, $env, $hook, $inject, $module, $use, Alepha, AlephaError, Atom, Descriptor, KIND, createDescriptor, t } from "
|
|
2
|
-
import { AlephaDateTime, DateTimeProvider } from "
|
|
3
|
-
import { AlephaServer, HttpClient } from "
|
|
4
|
-
import { AlephaServerLinks, LinkProvider } from "
|
|
5
|
-
import { $logger } from "
|
|
6
|
-
import { RouterProvider } from "
|
|
1
|
+
import { $atom, $env, $hook, $inject, $module, $use, Alepha, AlephaError, Atom, Descriptor, KIND, createDescriptor, t } from "alepha";
|
|
2
|
+
import { AlephaDateTime, DateTimeProvider } from "alepha/datetime";
|
|
3
|
+
import { AlephaServer, HttpClient } from "alepha/server";
|
|
4
|
+
import { AlephaServerLinks, LinkProvider } from "alepha/server/links";
|
|
5
|
+
import { $logger } from "alepha/logger";
|
|
6
|
+
import { RouterProvider } from "alepha/router";
|
|
7
7
|
import React, { StrictMode, createContext, createElement, memo, use, useCallback, useContext, useEffect, useMemo, useRef, useState } from "react";
|
|
8
8
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
9
9
|
import { createRoot, hydrateRoot } from "react-dom/client";
|
|
10
10
|
|
|
11
|
-
//#region src/services/ReactPageService.ts
|
|
11
|
+
//#region src/core/services/ReactPageService.ts
|
|
12
12
|
var ReactPageService = class {
|
|
13
13
|
fetch(pathname, options = {}) {
|
|
14
14
|
throw new AlephaError("Fetch is not available for this environment.");
|
|
@@ -19,7 +19,7 @@ var ReactPageService = class {
|
|
|
19
19
|
};
|
|
20
20
|
|
|
21
21
|
//#endregion
|
|
22
|
-
//#region src/descriptors/$page.ts
|
|
22
|
+
//#region src/core/descriptors/$page.ts
|
|
23
23
|
/**
|
|
24
24
|
* Main descriptor for defining a React route in the application.
|
|
25
25
|
*
|
|
@@ -144,7 +144,7 @@ var PageDescriptor = class extends Descriptor {
|
|
|
144
144
|
$page[KIND] = PageDescriptor;
|
|
145
145
|
|
|
146
146
|
//#endregion
|
|
147
|
-
//#region src/components/NotFound.tsx
|
|
147
|
+
//#region src/core/components/NotFound.tsx
|
|
148
148
|
function NotFoundPage(props) {
|
|
149
149
|
return /* @__PURE__ */ jsx("div", {
|
|
150
150
|
style: {
|
|
@@ -169,7 +169,7 @@ function NotFoundPage(props) {
|
|
|
169
169
|
}
|
|
170
170
|
|
|
171
171
|
//#endregion
|
|
172
|
-
//#region src/components/ClientOnly.tsx
|
|
172
|
+
//#region src/core/components/ClientOnly.tsx
|
|
173
173
|
/**
|
|
174
174
|
* A small utility component that renders its children only on the client side.
|
|
175
175
|
*
|
|
@@ -189,7 +189,7 @@ const ClientOnly = (props) => {
|
|
|
189
189
|
var ClientOnly_default = ClientOnly;
|
|
190
190
|
|
|
191
191
|
//#endregion
|
|
192
|
-
//#region src/components/ErrorViewer.tsx
|
|
192
|
+
//#region src/core/components/ErrorViewer.tsx
|
|
193
193
|
const ErrorViewer = ({ error, alepha }) => {
|
|
194
194
|
const [expanded, setExpanded] = useState(false);
|
|
195
195
|
if (alepha.isProduction()) return /* @__PURE__ */ jsx(ErrorViewerProduction, {});
|
|
@@ -275,6 +275,7 @@ const ErrorViewer = ({ error, alepha }) => {
|
|
|
275
275
|
] }), stackLines.length > 0 && /* @__PURE__ */ jsxs("div", { children: [/* @__PURE__ */ jsxs("div", {
|
|
276
276
|
style: styles.sectionHeader,
|
|
277
277
|
children: [/* @__PURE__ */ jsx("span", { children: "Stack trace" }), /* @__PURE__ */ jsx("button", {
|
|
278
|
+
type: "button",
|
|
278
279
|
onClick: () => copyToClipboard(error.stack),
|
|
279
280
|
style: styles.copyButton,
|
|
280
281
|
children: "Copy all"
|
|
@@ -336,11 +337,11 @@ const ErrorViewerProduction = () => {
|
|
|
336
337
|
};
|
|
337
338
|
|
|
338
339
|
//#endregion
|
|
339
|
-
//#region src/contexts/RouterLayerContext.ts
|
|
340
|
+
//#region src/core/contexts/RouterLayerContext.ts
|
|
340
341
|
const RouterLayerContext = createContext(void 0);
|
|
341
342
|
|
|
342
343
|
//#endregion
|
|
343
|
-
//#region src/errors/Redirection.ts
|
|
344
|
+
//#region src/core/errors/Redirection.ts
|
|
344
345
|
/**
|
|
345
346
|
* Used for Redirection during the page loading.
|
|
346
347
|
*
|
|
@@ -355,11 +356,11 @@ var Redirection = class extends Error {
|
|
|
355
356
|
};
|
|
356
357
|
|
|
357
358
|
//#endregion
|
|
358
|
-
//#region src/contexts/AlephaContext.ts
|
|
359
|
+
//#region src/core/contexts/AlephaContext.ts
|
|
359
360
|
const AlephaContext = createContext(void 0);
|
|
360
361
|
|
|
361
362
|
//#endregion
|
|
362
|
-
//#region src/hooks/useAlepha.ts
|
|
363
|
+
//#region src/core/hooks/useAlepha.ts
|
|
363
364
|
/**
|
|
364
365
|
* Main Alepha hook.
|
|
365
366
|
*
|
|
@@ -379,7 +380,7 @@ const useAlepha = () => {
|
|
|
379
380
|
};
|
|
380
381
|
|
|
381
382
|
//#endregion
|
|
382
|
-
//#region src/hooks/useEvents.ts
|
|
383
|
+
//#region src/core/hooks/useEvents.ts
|
|
383
384
|
/**
|
|
384
385
|
* Allow subscribing to multiple Alepha events. See {@link Hooks} for available events.
|
|
385
386
|
*
|
|
@@ -416,7 +417,7 @@ const useEvents = (opts, deps) => {
|
|
|
416
417
|
};
|
|
417
418
|
|
|
418
419
|
//#endregion
|
|
419
|
-
//#region src/hooks/useStore.ts
|
|
420
|
+
//#region src/core/hooks/useStore.ts
|
|
420
421
|
function useStore(target, defaultValue) {
|
|
421
422
|
const alepha = useAlepha();
|
|
422
423
|
useMemo(() => {
|
|
@@ -436,7 +437,7 @@ function useStore(target, defaultValue) {
|
|
|
436
437
|
}
|
|
437
438
|
|
|
438
439
|
//#endregion
|
|
439
|
-
//#region src/hooks/useRouterState.ts
|
|
440
|
+
//#region src/core/hooks/useRouterState.ts
|
|
440
441
|
const useRouterState = () => {
|
|
441
442
|
const [state] = useStore("alepha.react.router.state");
|
|
442
443
|
if (!state) throw new AlephaError("Missing react router state");
|
|
@@ -444,7 +445,7 @@ const useRouterState = () => {
|
|
|
444
445
|
};
|
|
445
446
|
|
|
446
447
|
//#endregion
|
|
447
|
-
//#region src/components/ErrorBoundary.tsx
|
|
448
|
+
//#region src/core/components/ErrorBoundary.tsx
|
|
448
449
|
/**
|
|
449
450
|
* A reusable error boundary for catching rendering errors
|
|
450
451
|
* in any part of the React component tree.
|
|
@@ -475,7 +476,7 @@ var ErrorBoundary = class extends React.Component {
|
|
|
475
476
|
var ErrorBoundary_default = ErrorBoundary;
|
|
476
477
|
|
|
477
478
|
//#endregion
|
|
478
|
-
//#region src/components/NestedView.tsx
|
|
479
|
+
//#region src/core/components/NestedView.tsx
|
|
479
480
|
/**
|
|
480
481
|
* A component that renders the current view of the nested router layer.
|
|
481
482
|
*
|
|
@@ -483,7 +484,7 @@ var ErrorBoundary_default = ErrorBoundary;
|
|
|
483
484
|
*
|
|
484
485
|
* @example
|
|
485
486
|
* ```tsx
|
|
486
|
-
* import { NestedView } from "alepha/react";
|
|
487
|
+
* import { NestedView } from "@alepha/react";
|
|
487
488
|
*
|
|
488
489
|
* class App {
|
|
489
490
|
* parent = $page({
|
|
@@ -597,7 +598,7 @@ function parseAnimation(animationLike, state, type = "enter") {
|
|
|
597
598
|
}
|
|
598
599
|
|
|
599
600
|
//#endregion
|
|
600
|
-
//#region src/providers/ReactPageProvider.ts
|
|
601
|
+
//#region src/core/providers/ReactPageProvider.ts
|
|
601
602
|
const envSchema$1 = t.object({ REACT_STRICT_MODE: t.boolean({ default: true }) });
|
|
602
603
|
var ReactPageProvider = class {
|
|
603
604
|
log = $logger();
|
|
@@ -920,7 +921,7 @@ const isPageRoute = (it) => {
|
|
|
920
921
|
};
|
|
921
922
|
|
|
922
923
|
//#endregion
|
|
923
|
-
//#region src/providers/ReactBrowserRouterProvider.ts
|
|
924
|
+
//#region src/core/providers/ReactBrowserRouterProvider.ts
|
|
924
925
|
var ReactBrowserRouterProvider = class extends RouterProvider {
|
|
925
926
|
log = $logger();
|
|
926
927
|
alepha = $inject(Alepha);
|
|
@@ -1001,7 +1002,7 @@ var ReactBrowserRouterProvider = class extends RouterProvider {
|
|
|
1001
1002
|
};
|
|
1002
1003
|
|
|
1003
1004
|
//#endregion
|
|
1004
|
-
//#region src/providers/ReactBrowserProvider.ts
|
|
1005
|
+
//#region src/core/providers/ReactBrowserProvider.ts
|
|
1005
1006
|
const envSchema = t.object({ REACT_ROOT_ID: t.text({ default: "root" }) });
|
|
1006
1007
|
/**
|
|
1007
1008
|
* React browser renderer configuration atom
|
|
@@ -1166,7 +1167,7 @@ var ReactBrowserProvider = class {
|
|
|
1166
1167
|
};
|
|
1167
1168
|
|
|
1168
1169
|
//#endregion
|
|
1169
|
-
//#region src/providers/ReactBrowserRendererProvider.ts
|
|
1170
|
+
//#region src/core/providers/ReactBrowserRendererProvider.ts
|
|
1170
1171
|
var ReactBrowserRendererProvider = class {
|
|
1171
1172
|
log = $logger();
|
|
1172
1173
|
root;
|
|
@@ -1186,7 +1187,7 @@ var ReactBrowserRendererProvider = class {
|
|
|
1186
1187
|
};
|
|
1187
1188
|
|
|
1188
1189
|
//#endregion
|
|
1189
|
-
//#region src/services/ReactRouter.ts
|
|
1190
|
+
//#region src/core/services/ReactRouter.ts
|
|
1190
1191
|
var ReactRouter = class {
|
|
1191
1192
|
alepha = $inject(Alepha);
|
|
1192
1193
|
pageApi = $inject(ReactPageProvider);
|
|
@@ -1299,7 +1300,7 @@ var ReactRouter = class {
|
|
|
1299
1300
|
};
|
|
1300
1301
|
|
|
1301
1302
|
//#endregion
|
|
1302
|
-
//#region src/hooks/useInject.ts
|
|
1303
|
+
//#region src/core/hooks/useInject.ts
|
|
1303
1304
|
/**
|
|
1304
1305
|
* Hook to inject a service instance.
|
|
1305
1306
|
* It's a wrapper of `useAlepha().inject(service)` with a memoization.
|
|
@@ -1310,7 +1311,7 @@ const useInject = (service) => {
|
|
|
1310
1311
|
};
|
|
1311
1312
|
|
|
1312
1313
|
//#endregion
|
|
1313
|
-
//#region src/hooks/useRouter.ts
|
|
1314
|
+
//#region src/core/hooks/useRouter.ts
|
|
1314
1315
|
/**
|
|
1315
1316
|
* Use this hook to access the React Router instance.
|
|
1316
1317
|
*
|
|
@@ -1330,7 +1331,7 @@ const useRouter = () => {
|
|
|
1330
1331
|
};
|
|
1331
1332
|
|
|
1332
1333
|
//#endregion
|
|
1333
|
-
//#region src/components/Link.tsx
|
|
1334
|
+
//#region src/core/components/Link.tsx
|
|
1334
1335
|
const Link = (props) => {
|
|
1335
1336
|
const router = useRouter();
|
|
1336
1337
|
return /* @__PURE__ */ jsx("a", {
|
|
@@ -1342,7 +1343,7 @@ const Link = (props) => {
|
|
|
1342
1343
|
var Link_default = Link;
|
|
1343
1344
|
|
|
1344
1345
|
//#endregion
|
|
1345
|
-
//#region src/hooks/useAction.ts
|
|
1346
|
+
//#region src/core/hooks/useAction.ts
|
|
1346
1347
|
/**
|
|
1347
1348
|
* Hook for handling async actions with automatic error handling and event emission.
|
|
1348
1349
|
*
|
|
@@ -1569,7 +1570,7 @@ function useAction(options, deps) {
|
|
|
1569
1570
|
}
|
|
1570
1571
|
|
|
1571
1572
|
//#endregion
|
|
1572
|
-
//#region src/hooks/useActive.ts
|
|
1573
|
+
//#region src/core/hooks/useActive.ts
|
|
1573
1574
|
const useActive = (args) => {
|
|
1574
1575
|
const router = useRouter();
|
|
1575
1576
|
const [isPending, setPending] = useState(false);
|
|
@@ -1602,7 +1603,7 @@ const useActive = (args) => {
|
|
|
1602
1603
|
};
|
|
1603
1604
|
|
|
1604
1605
|
//#endregion
|
|
1605
|
-
//#region src/hooks/useClient.ts
|
|
1606
|
+
//#region src/core/hooks/useClient.ts
|
|
1606
1607
|
/**
|
|
1607
1608
|
* Hook to get a virtual client for the specified scope.
|
|
1608
1609
|
*
|
|
@@ -1613,7 +1614,7 @@ const useClient = (scope) => {
|
|
|
1613
1614
|
};
|
|
1614
1615
|
|
|
1615
1616
|
//#endregion
|
|
1616
|
-
//#region src/hooks/useQueryParams.ts
|
|
1617
|
+
//#region src/core/hooks/useQueryParams.ts
|
|
1617
1618
|
/**
|
|
1618
1619
|
* Not well tested. Use with caution.
|
|
1619
1620
|
*/
|
|
@@ -1648,7 +1649,7 @@ const decode = (alepha, schema, data) => {
|
|
|
1648
1649
|
};
|
|
1649
1650
|
|
|
1650
1651
|
//#endregion
|
|
1651
|
-
//#region src/hooks/useSchema.ts
|
|
1652
|
+
//#region src/core/hooks/useSchema.ts
|
|
1652
1653
|
const useSchema = (action) => {
|
|
1653
1654
|
const name = action.name;
|
|
1654
1655
|
const alepha = useAlepha();
|
|
@@ -1682,7 +1683,7 @@ const ssrSchemaLoading = (alepha, name) => {
|
|
|
1682
1683
|
};
|
|
1683
1684
|
|
|
1684
1685
|
//#endregion
|
|
1685
|
-
//#region src/index.browser.ts
|
|
1686
|
+
//#region src/core/index.browser.ts
|
|
1686
1687
|
const AlephaReact = $module({
|
|
1687
1688
|
name: "alepha.react",
|
|
1688
1689
|
descriptors: [$page],
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.browser.js","names":["subs: Function[]","state","ErrorBoundary","envSchema","pages: PageRoute[]","NestedView","context: Record<string, any>","stack: Array<RouterStackItem>","route","config: Record<string, any>","context","element: ReactNode | Redirection | undefined","ErrorViewer","ClientOnly","children","query: Record<string, string>","previous: PreviousLayerData[]","query: Record<string, string>","error","options: UseActiveOptions","queryParams","schema"],"sources":["../../src/core/services/ReactPageService.ts","../../src/core/descriptors/$page.ts","../../src/core/components/NotFound.tsx","../../src/core/components/ClientOnly.tsx","../../src/core/components/ErrorViewer.tsx","../../src/core/contexts/RouterLayerContext.ts","../../src/core/errors/Redirection.ts","../../src/core/contexts/AlephaContext.ts","../../src/core/hooks/useAlepha.ts","../../src/core/hooks/useEvents.ts","../../src/core/hooks/useStore.ts","../../src/core/hooks/useRouterState.ts","../../src/core/components/ErrorBoundary.tsx","../../src/core/components/NestedView.tsx","../../src/core/providers/ReactPageProvider.ts","../../src/core/providers/ReactBrowserRouterProvider.ts","../../src/core/providers/ReactBrowserProvider.ts","../../src/core/providers/ReactBrowserRendererProvider.ts","../../src/core/services/ReactRouter.ts","../../src/core/hooks/useInject.ts","../../src/core/hooks/useRouter.ts","../../src/core/components/Link.tsx","../../src/core/hooks/useAction.ts","../../src/core/hooks/useActive.ts","../../src/core/hooks/useClient.ts","../../src/core/hooks/useQueryParams.ts","../../src/core/hooks/useSchema.ts","../../src/core/index.browser.ts"],"sourcesContent":["import { AlephaError } from \"alepha\";\nimport type {\n PageDescriptorRenderOptions,\n PageDescriptorRenderResult,\n} from \"../descriptors/$page.ts\";\n\nexport class ReactPageService {\n public fetch(\n pathname: string,\n options: PageDescriptorRenderOptions = {},\n ): Promise<{\n html: string;\n response: Response;\n }> {\n throw new AlephaError(\"Fetch is not available for this environment.\");\n }\n\n public render(\n name: string,\n options: PageDescriptorRenderOptions = {},\n ): Promise<PageDescriptorRenderResult> {\n throw new AlephaError(\"Render is not available for this environment.\");\n }\n}\n","import {\n $inject,\n type Async,\n createDescriptor,\n Descriptor,\n KIND,\n type Static,\n type TSchema,\n} from \"alepha\";\nimport type { ServerRequest } from \"alepha/server\";\nimport type { ServerRouteCache } from \"alepha/server/cache\";\nimport type { FC, ReactNode } from \"react\";\nimport type { ClientOnlyProps } from \"../components/ClientOnly.tsx\";\nimport type { Redirection } from \"../errors/Redirection.ts\";\nimport type { ReactRouterState } from \"../providers/ReactPageProvider.ts\";\nimport { ReactPageService } from \"../services/ReactPageService.ts\";\n\n/**\n * Main descriptor for defining a React route in the application.\n *\n * The $page descriptor is the core building block for creating type-safe, SSR-enabled React routes.\n * It provides a declarative way to define pages with powerful features:\n *\n * **Routing & Navigation**\n * - URL pattern matching with parameters (e.g., `/users/:id`)\n * - Nested routing with parent-child relationships\n * - Type-safe URL parameter and query string validation\n *\n * **Data Loading**\n * - Server-side data fetching with the `resolve` function\n * - Automatic serialization and hydration for SSR\n * - Access to request context, URL params, and parent data\n *\n * **Component Loading**\n * - Direct component rendering or lazy loading for code splitting\n * - Client-only rendering when browser APIs are needed\n * - Automatic fallback handling during hydration\n *\n * **Performance Optimization**\n * - Static generation for pre-rendered pages at build time\n * - Server-side caching with configurable TTL and providers\n * - Code splitting through lazy component loading\n *\n * **Error Handling**\n * - Custom error handlers with support for redirects\n * - Hierarchical error handling (child → parent)\n * - HTTP status code handling (404, 401, etc.)\n *\n * **Page Animations**\n * - CSS-based enter/exit animations\n * - Dynamic animations based on page state\n * - Custom timing and easing functions\n *\n * **Lifecycle Management**\n * - Server response hooks for headers and status codes\n * - Page leave handlers for cleanup (browser only)\n * - Permission-based access control\n *\n * @example Simple page with data fetching\n * ```typescript\n * const userProfile = $page({\n * path: \"/users/:id\",\n * schema: {\n * params: t.object({ id: t.int() }),\n * query: t.object({ tab: t.optional(t.text()) })\n * },\n * resolve: async ({ params }) => {\n * const user = await userApi.getUser(params.id);\n * return { user };\n * },\n * lazy: () => import(\"./UserProfile.tsx\")\n * });\n * ```\n *\n * @example Nested routing with error handling\n * ```typescript\n * const projectSection = $page({\n * path: \"/projects/:id\",\n * children: () => [projectBoard, projectSettings],\n * resolve: async ({ params }) => {\n * const project = await projectApi.get(params.id);\n * return { project };\n * },\n * errorHandler: (error) => {\n * if (HttpError.is(error, 404)) {\n * return <ProjectNotFound />;\n * }\n * }\n * });\n * ```\n *\n * @example Static generation with caching\n * ```typescript\n * const blogPost = $page({\n * path: \"/blog/:slug\",\n * static: {\n * entries: posts.map(p => ({ params: { slug: p.slug } }))\n * },\n * resolve: async ({ params }) => {\n * const post = await loadPost(params.slug);\n * return { post };\n * }\n * });\n * ```\n */\nexport const $page = <\n TConfig extends PageConfigSchema = PageConfigSchema,\n TProps extends object = TPropsDefault,\n TPropsParent extends object = TPropsParentDefault,\n>(\n options: PageDescriptorOptions<TConfig, TProps, TPropsParent>,\n): PageDescriptor<TConfig, TProps, TPropsParent> => {\n return createDescriptor(\n PageDescriptor<TConfig, TProps, TPropsParent>,\n options,\n );\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface PageDescriptorOptions<\n TConfig extends PageConfigSchema = PageConfigSchema,\n TProps extends object = TPropsDefault,\n TPropsParent extends object = TPropsParentDefault,\n> {\n /**\n * Identifier name for the page. Must be unique.\n *\n * @default Descriptor key\n */\n name?: string;\n\n /**\n * Add a pathname to the page.\n *\n * Pathname can contain parameters, like `/post/:slug`.\n *\n * @default \"\"\n */\n path?: string;\n\n /**\n * Add an input schema to define:\n * - `params`: parameters from the pathname.\n * - `query`: query parameters from the URL.\n */\n schema?: TConfig;\n\n /**\n * Load data before rendering the page.\n *\n * This function receives\n * - the request context and\n * - the parent props (if page has a parent)\n *\n * In SSR, the returned data will be serialized and sent to the client, then reused during the client-side hydration.\n *\n * Resolve can be stopped by throwing an error, which will be handled by the `errorHandler` function.\n * It's common to throw a `NotFoundError` to display a 404 page.\n *\n * RedirectError can be thrown to redirect the user to another page.\n */\n resolve?: (context: PageResolve<TConfig, TPropsParent>) => Async<TProps>;\n\n /**\n * The component to render when the page is loaded.\n *\n * If `lazy` is defined, this will be ignored.\n * Prefer using `lazy` to improve the initial loading time.\n */\n component?: FC<TProps & TPropsParent>;\n\n /**\n * Lazy load the component when the page is loaded.\n *\n * It's recommended to use this for components to improve the initial loading time\n * and enable code-splitting.\n */\n lazy?: () => Promise<{ default: FC<TProps & TPropsParent> }>;\n\n /**\n * Attach child pages to create nested routes.\n * This will make the page a parent route.\n */\n children?: Array<PageDescriptor> | (() => Array<PageDescriptor>);\n\n /**\n * Define a parent page for nested routing.\n */\n parent?: PageDescriptor<PageConfigSchema, TPropsParent, any>;\n\n can?: () => boolean;\n\n /**\n * Catch any error from the `resolve` function or during `rendering`.\n *\n * Expected to return one of the following:\n * - a ReactNode to render an error page\n * - a Redirection to redirect the user\n * - undefined to let the error propagate\n *\n * If not defined, the error will be thrown and handled by the server or client error handler.\n * If a leaf $page does not define an error handler, the error can be caught by parent pages.\n *\n * @example Catch a 404 from API and render a custom not found component:\n * ```ts\n * resolve: async ({ params, query }) => {\n * api.fetch(\"/api/resource\", { params, query });\n * },\n * errorHandler: (error, context) => {\n * if (HttpError.is(error, 404)) {\n * return <ResourceNotFound />;\n * }\n * }\n * ```\n *\n * @example Catch an 401 error and redirect the user to the login page:\n * ```ts\n * resolve: async ({ params, query }) => {\n * // but the user is not authenticated\n * api.fetch(\"/api/resource\", { params, query });\n * },\n * errorHandler: (error, context) => {\n * if (HttpError.is(error, 401)) {\n * // throwing a Redirection is also valid!\n * return new Redirection(\"/login\");\n * }\n * }\n * ```\n */\n errorHandler?: ErrorHandler;\n\n /**\n * If true, the page will be considered as a static page, immutable and cacheable.\n * Replace boolean by an object to define static entries. (e.g. list of params/query)\n *\n * Browser-side: it only works with `alepha/vite`, which can pre-render the page at build time.\n *\n * Server-side: It will act as timeless cached page. You can use `cache` to configure the cache behavior.\n */\n static?:\n | boolean\n | {\n entries?: Array<Partial<PageRequestConfig<TConfig>>>;\n };\n\n cache?: ServerRouteCache;\n\n /**\n * If true, force the page to be rendered only on the client-side (browser).\n * It uses the `<ClientOnly/>` component to render the page.\n */\n client?: boolean | ClientOnlyProps;\n\n /**\n * Called before the server response is sent to the client. (server only)\n */\n onServerResponse?: (request: ServerRequest) => unknown;\n\n /**\n * Called when user leaves the page. (browser only)\n */\n onLeave?: () => void;\n\n /**\n * @experimental\n *\n * Add a css animation when the page is loaded or unloaded.\n * It uses CSS animations, so you need to define the keyframes in your CSS.\n *\n * @example Simple animation name\n * ```ts\n * animation: \"fadeIn\"\n * ```\n *\n * CSS example:\n * ```css\n * @keyframes fadeIn {\n * from { opacity: 0; }\n * to { opacity: 1; }\n * }\n * ```\n *\n * @example Detailed animation\n * ```ts\n * animation: {\n * enter: { name: \"fadeIn\", duration: 300 },\n * exit: { name: \"fadeOut\", duration: 200, timing: \"ease-in-out\" },\n * }\n * ```\n *\n * @example Only exit animation\n * ```ts\n * animation: {\n * exit: \"fadeOut\"\n * }\n * ```\n *\n * @example With custom timing function\n * ```ts\n * animation: {\n * enter: { name: \"fadeIn\", duration: 300, timing: \"cubic-bezier(0.4, 0, 0.2, 1)\" },\n * exit: { name: \"fadeOut\", duration: 200, timing: \"ease-in-out\" },\n * }\n * ```\n */\n animation?: PageAnimation;\n}\n\nexport type ErrorHandler = (\n error: Error,\n state: ReactRouterState,\n) => ReactNode | Redirection | undefined;\n\nexport class PageDescriptor<\n TConfig extends PageConfigSchema = PageConfigSchema,\n TProps extends object = TPropsDefault,\n TPropsParent extends object = TPropsParentDefault,\n> extends Descriptor<PageDescriptorOptions<TConfig, TProps, TPropsParent>> {\n protected readonly reactPageService = $inject(ReactPageService);\n\n protected onInit() {\n if (this.options.static) {\n this.options.cache ??= {\n store: {\n provider: \"memory\",\n ttl: [1, \"week\"],\n },\n };\n }\n }\n\n public get name(): string {\n return this.options.name ?? this.config.propertyKey;\n }\n\n /**\n * For testing or build purposes.\n *\n * This will render the page (HTML layout included or not) and return the HTML + context.\n * Only valid for server-side rendering, it will throw an error if called on the client-side.\n */\n public async render(\n options?: PageDescriptorRenderOptions,\n ): Promise<PageDescriptorRenderResult> {\n return this.reactPageService.render(this.name, options);\n }\n\n public async fetch(options?: PageDescriptorRenderOptions): Promise<{\n html: string;\n response: Response;\n }> {\n return this.reactPageService.fetch(this.options.path || \"\", options);\n }\n\n public match(url: string): boolean {\n // TODO: Implement a way to match the URL against the pathname\n return false;\n }\n\n public pathname(config: any) {\n // TODO: Implement a way to generate the pathname based on the config\n return this.options.path || \"\";\n }\n}\n\n$page[KIND] = PageDescriptor;\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface PageConfigSchema {\n query?: TSchema;\n params?: TSchema;\n}\n\nexport type TPropsDefault = any;\n\nexport type TPropsParentDefault = {};\n\nexport interface PageDescriptorRenderOptions {\n params?: Record<string, string>;\n query?: Record<string, string>;\n\n /**\n * If true, the HTML layout will be included in the response.\n * If false, only the page content will be returned.\n *\n * @default true\n */\n html?: boolean;\n hydration?: boolean;\n}\n\nexport interface PageDescriptorRenderResult {\n html: string;\n state: ReactRouterState;\n redirect?: string;\n}\n\nexport interface PageRequestConfig<\n TConfig extends PageConfigSchema = PageConfigSchema,\n> {\n params: TConfig[\"params\"] extends TSchema\n ? Static<TConfig[\"params\"]>\n : Record<string, string>;\n\n query: TConfig[\"query\"] extends TSchema\n ? Static<TConfig[\"query\"]>\n : Record<string, string>;\n}\n\nexport type PageResolve<\n TConfig extends PageConfigSchema = PageConfigSchema,\n TPropsParent extends object = TPropsParentDefault,\n> = PageRequestConfig<TConfig> &\n TPropsParent &\n Omit<ReactRouterState, \"layers\" | \"onError\">;\n\nexport type PageAnimation =\n | PageAnimationObject\n | ((state: ReactRouterState) => PageAnimationObject | undefined);\n\ntype PageAnimationObject =\n | CssAnimationName\n | {\n enter?: CssAnimation | CssAnimationName;\n exit?: CssAnimation | CssAnimationName;\n };\n\ntype CssAnimationName = string;\n\ntype CssAnimation = {\n name: string;\n duration?: number;\n timing?: string;\n};\n","import type { CSSProperties } from \"react\";\n\nexport default function NotFoundPage(props: { style?: CSSProperties }) {\n return (\n <div\n style={{\n height: \"100vh\",\n display: \"flex\",\n flexDirection: \"column\",\n justifyContent: \"center\",\n alignItems: \"center\",\n textAlign: \"center\",\n fontFamily: \"sans-serif\",\n padding: \"1rem\",\n ...props.style,\n }}\n >\n <h1 style={{ fontSize: \"1rem\", marginBottom: \"0.5rem\" }}>\n 404 - This page does not exist\n </h1>\n </div>\n );\n}\n","import {\n type PropsWithChildren,\n type ReactNode,\n useEffect,\n useState,\n} from \"react\";\n\nexport interface ClientOnlyProps {\n fallback?: ReactNode;\n disabled?: boolean;\n}\n\n/**\n * A small utility component that renders its children only on the client side.\n *\n * Optionally, you can provide a fallback React node that will be rendered.\n *\n * You should use this component when\n * - you have code that relies on browser-specific APIs\n * - you want to avoid server-side rendering for a specific part of your application\n * - you want to prevent pre-rendering of a component\n */\nconst ClientOnly = (props: PropsWithChildren<ClientOnlyProps>) => {\n const [mounted, setMounted] = useState(false);\n\n useEffect(() => setMounted(true), []);\n\n if (props.disabled) {\n return props.children;\n }\n\n return mounted ? props.children : props.fallback;\n};\n\nexport default ClientOnly;\n","import type { Alepha } from \"alepha\";\nimport { useState } from \"react\";\n\ninterface ErrorViewerProps {\n error: Error;\n alepha: Alepha;\n}\n\n// TODO: design this better\n\nconst ErrorViewer = ({ error, alepha }: ErrorViewerProps) => {\n const [expanded, setExpanded] = useState(false);\n const isProduction = alepha.isProduction();\n // const status = isHttpError(error) ? error.status : 500;\n\n if (isProduction) {\n return <ErrorViewerProduction />;\n }\n\n const stackLines = error.stack?.split(\"\\n\") ?? [];\n const previewLines = stackLines.slice(0, 5);\n const hiddenLineCount = stackLines.length - previewLines.length;\n\n const copyToClipboard = (text: string) => {\n navigator.clipboard.writeText(text).catch((err) => {\n console.error(\"Clipboard error:\", err);\n });\n };\n\n const styles = {\n container: {\n padding: \"24px\",\n backgroundColor: \"#FEF2F2\",\n color: \"#7F1D1D\",\n border: \"1px solid #FECACA\",\n borderRadius: \"16px\",\n boxShadow: \"0 8px 24px rgba(0,0,0,0.05)\",\n fontFamily: \"monospace\",\n maxWidth: \"768px\",\n margin: \"40px auto\",\n },\n heading: {\n fontSize: \"20px\",\n fontWeight: \"bold\",\n marginBottom: \"10px\",\n },\n name: {\n fontSize: \"16px\",\n fontWeight: 600,\n },\n message: {\n fontSize: \"14px\",\n marginBottom: \"16px\",\n },\n sectionHeader: {\n display: \"flex\",\n justifyContent: \"space-between\",\n alignItems: \"center\",\n fontSize: \"12px\",\n marginBottom: \"4px\",\n color: \"#991B1B\",\n },\n copyButton: {\n fontSize: \"12px\",\n color: \"#DC2626\",\n background: \"none\",\n border: \"none\",\n cursor: \"pointer\",\n textDecoration: \"underline\",\n },\n stackContainer: {\n backgroundColor: \"#FEE2E2\",\n padding: \"12px\",\n borderRadius: \"8px\",\n fontSize: \"13px\",\n lineHeight: \"1.4\",\n overflowX: \"auto\" as const,\n whiteSpace: \"pre-wrap\" as const,\n },\n expandLine: {\n color: \"#F87171\",\n cursor: \"pointer\",\n marginTop: \"8px\",\n },\n };\n\n return (\n <div style={styles.container}>\n <div>\n <div style={styles.heading}>🔥 Error</div>\n <div style={styles.name}>{error.name}</div>\n <div style={styles.message}>{error.message}</div>\n </div>\n\n {stackLines.length > 0 && (\n <div>\n <div style={styles.sectionHeader}>\n <span>Stack trace</span>\n <button\n type=\"button\"\n onClick={() => copyToClipboard(error.stack!)}\n style={styles.copyButton}\n >\n Copy all\n </button>\n </div>\n <pre style={styles.stackContainer}>\n {(expanded ? stackLines : previewLines).map((line, i) => (\n <div key={i}>{line}</div>\n ))}\n {!expanded && hiddenLineCount > 0 && (\n <div style={styles.expandLine} onClick={() => setExpanded(true)}>\n + {hiddenLineCount} more lines...\n </div>\n )}\n </pre>\n </div>\n )}\n </div>\n );\n};\n\nexport default ErrorViewer;\n\nconst ErrorViewerProduction = () => {\n const styles = {\n container: {\n padding: \"24px\",\n backgroundColor: \"#FEF2F2\",\n color: \"#7F1D1D\",\n border: \"1px solid #FECACA\",\n borderRadius: \"16px\",\n boxShadow: \"0 8px 24px rgba(0,0,0,0.05)\",\n fontFamily: \"monospace\",\n maxWidth: \"768px\",\n margin: \"40px auto\",\n textAlign: \"center\" as const,\n },\n heading: {\n fontSize: \"20px\",\n fontWeight: \"bold\",\n marginBottom: \"8px\",\n },\n name: {\n fontSize: \"16px\",\n fontWeight: 600,\n marginBottom: \"4px\",\n },\n message: {\n fontSize: \"14px\",\n opacity: 0.85,\n },\n };\n\n return (\n <div style={styles.container}>\n <div style={styles.heading}>🚨 An error occurred</div>\n <div style={styles.message}>\n Something went wrong. Please try again later.\n </div>\n </div>\n );\n};\n","import { createContext } from \"react\";\n\nexport interface RouterLayerContextValue {\n index: number;\n path: string;\n}\n\nexport const RouterLayerContext = createContext<\n RouterLayerContextValue | undefined\n>(undefined);\n","/**\n * Used for Redirection during the page loading.\n *\n * Depends on the context, it can be thrown or just returned.\n */\nexport class Redirection extends Error {\n public readonly redirect: string;\n\n constructor(redirect: string) {\n super(\"Redirection\");\n this.redirect = redirect;\n }\n}\n","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 { 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 { 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.state.get(target) == null) {\n alepha.state.set(target, defaultValue);\n }\n }, [defaultValue]);\n\n const [state, setState] = useState(alepha.state.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.state.set(target, value);\n },\n ] as const;\n}\n\nexport type UseStoreReturn<T> = [T, (value: T) => void];\n\nexport { useStore };\n","import { AlephaError } from \"alepha\";\nimport type { ReactRouterState } from \"../providers/ReactPageProvider.ts\";\nimport { useStore } from \"./useStore.ts\";\n\nexport const useRouterState = (): ReactRouterState => {\n const [state] = useStore(\"alepha.react.router.state\");\n if (!state) {\n throw new AlephaError(\"Missing react router state\");\n }\n return state;\n};\n","import React, {\n type ErrorInfo,\n type PropsWithChildren,\n type ReactNode,\n} from \"react\";\n\n/**\n * Props for the ErrorBoundary component.\n */\nexport interface ErrorBoundaryProps {\n /**\n * Fallback React node to render when an error is caught.\n * If not provided, a default error message will be shown.\n */\n fallback: (error: Error) => ReactNode;\n\n /**\n * Optional callback that receives the error and error info.\n * Use this to log errors to a monitoring service.\n */\n onError?: (error: Error, info: ErrorInfo) => void;\n}\n\n/**\n * State of the ErrorBoundary component.\n */\ninterface ErrorBoundaryState {\n error?: Error;\n}\n\n/**\n * A reusable error boundary for catching rendering errors\n * in any part of the React component tree.\n */\nexport class ErrorBoundary extends React.Component<\n PropsWithChildren<ErrorBoundaryProps>,\n ErrorBoundaryState\n> {\n constructor(props: ErrorBoundaryProps) {\n super(props);\n this.state = {};\n }\n\n /**\n * Update state so the next render shows the fallback UI.\n */\n static getDerivedStateFromError(error: Error): ErrorBoundaryState {\n return {\n error,\n };\n }\n\n /**\n * Lifecycle method called when an error is caught.\n * You can log the error or perform side effects here.\n */\n componentDidCatch(error: Error, info: ErrorInfo): void {\n if (this.props.onError) {\n this.props.onError(error, info);\n }\n }\n\n render(): ReactNode {\n if (this.state.error) {\n return this.props.fallback(this.state.error);\n }\n\n return this.props.children;\n }\n}\n\nexport default ErrorBoundary;\n","import { memo, type ReactNode, use, useRef, useState } from \"react\";\nimport { RouterLayerContext } from \"../contexts/RouterLayerContext.ts\";\nimport type { PageAnimation } from \"../descriptors/$page.ts\";\nimport { Redirection } from \"../errors/Redirection.ts\";\nimport { useEvents } from \"../hooks/useEvents.ts\";\nimport { useRouterState } from \"../hooks/useRouterState.ts\";\nimport type { ReactRouterState } from \"../providers/ReactPageProvider.ts\";\nimport ErrorBoundary from \"./ErrorBoundary.tsx\";\n\nexport interface NestedViewProps {\n children?: ReactNode;\n errorBoundary?: false | ((error: Error) => ReactNode);\n}\n\n/**\n * A component that renders the current view of the nested router layer.\n *\n * To be simple, it renders the `element` of the current child page of a parent page.\n *\n * @example\n * ```tsx\n * import { NestedView } from \"@alepha/react\";\n *\n * class App {\n * parent = $page({\n * component: () => <NestedView />,\n * });\n *\n * child = $page({\n * parent: this.root,\n * component: () => <div>Child Page</div>,\n * });\n * }\n * ```\n */\nconst NestedView = (props: NestedViewProps) => {\n const index = use(RouterLayerContext)?.index ?? 0;\n const state = useRouterState();\n\n const [view, setView] = useState<ReactNode | undefined>(\n state.layers[index]?.element,\n );\n\n const [animation, setAnimation] = useState(\"\");\n const animationExitDuration = useRef<number>(0);\n const animationExitNow = useRef<number>(0);\n\n useEvents(\n {\n \"react:transition:begin\": async ({ previous, state }) => {\n // --------- Animations Begin ---------\n const layer = previous.layers[index];\n if (`${state.url.pathname}/`.startsWith(`${layer?.path}/`)) {\n return;\n }\n\n const animationExit = parseAnimation(\n layer.route?.animation,\n state,\n \"exit\",\n );\n\n if (animationExit) {\n const duration = animationExit.duration || 200;\n animationExitNow.current = Date.now();\n animationExitDuration.current = duration;\n setAnimation(animationExit.animation);\n } else {\n animationExitNow.current = 0;\n animationExitDuration.current = 0;\n setAnimation(\"\");\n }\n // --------- Animations End ---------\n },\n \"react:transition:end\": async ({ state }) => {\n const layer = state.layers[index];\n\n // --------- Animations Begin ---------\n if (animationExitNow.current) {\n const duration = animationExitDuration.current;\n const diff = Date.now() - animationExitNow.current;\n if (diff < duration) {\n await new Promise((resolve) =>\n setTimeout(resolve, duration - diff),\n );\n }\n }\n // --------- Animations End ---------\n\n if (!layer?.cache) {\n setView(layer?.element);\n\n // --------- Animations Begin ---------\n const animationEnter = parseAnimation(\n layer?.route?.animation,\n state,\n \"enter\",\n );\n\n if (animationEnter) {\n setAnimation(animationEnter.animation);\n } else {\n setAnimation(\"\");\n }\n // --------- Animations End ---------\n }\n },\n },\n [],\n );\n\n let element = view ?? props.children ?? null;\n\n // --------- Animations Begin ---------\n if (animation) {\n element = (\n <div\n style={{\n display: \"flex\",\n flex: 1,\n height: \"100%\",\n width: \"100%\",\n position: \"relative\",\n overflow: \"hidden\",\n }}\n >\n <div\n style={{ height: \"100%\", width: \"100%\", display: \"flex\", animation }}\n >\n {element}\n </div>\n </div>\n );\n }\n // --------- Animations End ---------\n\n if (props.errorBoundary === false) {\n return <>{element}</>;\n }\n\n if (props.errorBoundary) {\n return (\n <ErrorBoundary fallback={props.errorBoundary}>{element}</ErrorBoundary>\n );\n }\n\n return (\n <ErrorBoundary\n fallback={(error) => {\n const result = state.onError(error, state); // TODO: onError is not refreshed\n if (result instanceof Redirection) {\n return \"Redirection inside ErrorBoundary is not allowed.\";\n }\n return result as ReactNode;\n }}\n >\n {element}\n </ErrorBoundary>\n );\n};\n\nexport default memo(NestedView);\n\nfunction parseAnimation(\n animationLike: PageAnimation | undefined,\n state: ReactRouterState,\n type: \"enter\" | \"exit\" = \"enter\",\n):\n | {\n duration: number;\n animation: string;\n }\n | undefined {\n if (!animationLike) {\n return undefined;\n }\n\n const DEFAULT_DURATION = 300;\n\n const animation =\n typeof animationLike === \"function\" ? animationLike(state) : animationLike;\n\n if (typeof animation === \"string\") {\n if (type === \"exit\") {\n return;\n }\n return {\n duration: DEFAULT_DURATION,\n animation: `${DEFAULT_DURATION}ms ${animation}`,\n };\n }\n\n if (typeof animation === \"object\") {\n const anim = animation[type];\n const duration =\n typeof anim === \"object\"\n ? (anim.duration ?? DEFAULT_DURATION)\n : DEFAULT_DURATION;\n const name = typeof anim === \"object\" ? anim.name : anim;\n\n if (type === \"exit\") {\n const timing = typeof anim === \"object\" ? (anim.timing ?? \"\") : \"\";\n return {\n duration,\n animation: `${duration}ms ${timing} ${name}`,\n };\n }\n\n const timing = typeof anim === \"object\" ? (anim.timing ?? \"\") : \"\";\n\n return {\n duration,\n animation: `${duration}ms ${timing} ${name}`,\n };\n }\n\n return undefined;\n}\n","import {\n $env,\n $hook,\n $inject,\n Alepha,\n AlephaError,\n type Static,\n type TSchema,\n t,\n} from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { createElement, type ReactNode, StrictMode } from \"react\";\nimport ClientOnly from \"../components/ClientOnly.tsx\";\nimport ErrorViewer from \"../components/ErrorViewer.tsx\";\nimport NestedView from \"../components/NestedView.tsx\";\nimport NotFoundPage from \"../components/NotFound.tsx\";\nimport { AlephaContext } from \"../contexts/AlephaContext.ts\";\nimport { RouterLayerContext } from \"../contexts/RouterLayerContext.ts\";\nimport {\n $page,\n type ErrorHandler,\n type PageDescriptor,\n type PageDescriptorOptions,\n} from \"../descriptors/$page.ts\";\nimport { Redirection } from \"../errors/Redirection.ts\";\n\nconst envSchema = t.object({\n REACT_STRICT_MODE: t.boolean({ default: true }),\n});\n\ndeclare module \"alepha\" {\n export interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\nexport class ReactPageProvider {\n protected readonly log = $logger();\n protected readonly env = $env(envSchema);\n protected readonly alepha = $inject(Alepha);\n protected readonly pages: PageRoute[] = [];\n\n public getPages(): PageRoute[] {\n return this.pages;\n }\n\n public getConcretePages(): PageRoute[] {\n const pages: PageRoute[] = [];\n for (const page of this.pages) {\n if (page.children && page.children.length > 0) {\n continue;\n }\n const fullPath = this.pathname(page.name);\n if (fullPath.includes(\":\") || fullPath.includes(\"*\")) {\n if (typeof page.static === \"object\") {\n const entries = page.static.entries;\n if (entries && entries.length > 0) {\n for (const entry of entries) {\n const params = entry.params as Record<string, string>;\n const path = this.compile(page.path ?? \"\", params);\n if (!path.includes(\":\") && !path.includes(\"*\")) {\n pages.push({\n ...page,\n name: params[Object.keys(params)[0]],\n path,\n ...entry,\n });\n }\n }\n }\n }\n\n continue;\n }\n pages.push(page);\n }\n return pages;\n }\n\n public page(name: string): PageRoute {\n for (const page of this.pages) {\n if (page.name === name) {\n return page;\n }\n }\n\n throw new AlephaError(`Page '${name}' not found`);\n }\n\n public pathname(\n name: string,\n options: {\n params?: Record<string, string>;\n query?: Record<string, string>;\n } = {},\n ) {\n const page = this.page(name);\n if (!page) {\n throw new Error(`Page ${name} not found`);\n }\n\n let url = page.path ?? \"\";\n let parent = page.parent;\n while (parent) {\n url = `${parent.path ?? \"\"}/${url}`;\n parent = parent.parent;\n }\n\n url = this.compile(url, options.params ?? {});\n\n if (options.query) {\n const query = new URLSearchParams(options.query);\n if (query.toString()) {\n url += `?${query.toString()}`;\n }\n }\n\n return url.replace(/\\/\\/+/g, \"/\") || \"/\";\n }\n\n public url(\n name: string,\n options: { params?: Record<string, string>; host?: string } = {},\n ): URL {\n return new URL(\n this.pathname(name, options),\n // use provided base or default to http://localhost\n options.host ?? `http://localhost`,\n );\n }\n\n public root(state: ReactRouterState): ReactNode {\n const root = createElement(\n AlephaContext.Provider,\n { value: this.alepha },\n createElement(NestedView, {}, state.layers[0]?.element),\n );\n\n if (this.env.REACT_STRICT_MODE) {\n return createElement(StrictMode, {}, root);\n }\n\n return root;\n }\n\n protected convertStringObjectToObject = (\n schema?: TSchema,\n value?: any,\n ): any => {\n if (t.schema.isObject(schema) && typeof value === \"object\") {\n for (const key in schema.properties) {\n if (\n t.schema.isObject(schema.properties[key]) &&\n typeof value[key] === \"string\"\n ) {\n try {\n value[key] = this.alepha.codec.decode(\n schema.properties[key],\n decodeURIComponent(value[key]),\n );\n } catch (e) {\n // ignore\n }\n }\n }\n }\n return value;\n };\n\n /**\n * Create a new RouterState based on a given route and request.\n * This method resolves the layers for the route, applying any query and params schemas defined in the route.\n * It also handles errors and redirects.\n */\n public async createLayers(\n route: PageRoute,\n state: ReactRouterState,\n previous: PreviousLayerData[] = [],\n ): Promise<CreateLayersResult> {\n let context: Record<string, any> = {}; // all props\n const stack: Array<RouterStackItem> = [{ route }]; // stack of routes\n\n let parent = route.parent;\n while (parent) {\n stack.unshift({ route: parent });\n parent = parent.parent;\n }\n\n let forceRefresh = false;\n\n for (let i = 0; i < stack.length; i++) {\n const it = stack[i];\n const route = it.route;\n const config: Record<string, any> = {};\n\n try {\n this.convertStringObjectToObject(route.schema?.query, state.query);\n config.query = route.schema?.query\n ? this.alepha.codec.decode(route.schema.query, state.query)\n : {};\n } catch (e) {\n it.error = e as Error;\n break;\n }\n\n try {\n config.params = route.schema?.params\n ? this.alepha.codec.decode(route.schema.params, state.params)\n : {};\n } catch (e) {\n it.error = e as Error;\n break;\n }\n\n // save config\n it.config = {\n ...config,\n };\n\n // check if previous layer is the same, reuse if possible\n if (previous?.[i] && !forceRefresh && previous[i].name === route.name) {\n const url = (str?: string) => (str ? str.replace(/\\/\\/+/g, \"/\") : \"/\");\n\n const prev = JSON.stringify({\n part: url(previous[i].part),\n params: previous[i].config?.params ?? {},\n });\n\n const curr = JSON.stringify({\n part: url(route.path),\n params: config.params ?? {},\n });\n\n if (prev === curr) {\n // part is the same, reuse previous layer\n it.props = previous[i].props;\n it.error = previous[i].error;\n it.cache = true;\n context = {\n ...context,\n ...it.props,\n };\n continue;\n }\n\n // part is different, force refresh of next layers\n forceRefresh = true;\n }\n\n // no resolve, render a basic view by default\n if (!route.resolve) {\n continue;\n }\n\n try {\n const args = Object.create(state);\n Object.assign(args, config, context);\n const props = (await route.resolve?.(args)) ?? {};\n\n // save props\n it.props = {\n ...props,\n };\n\n // add props to context\n context = {\n ...context,\n ...props,\n };\n } catch (e) {\n // check if we need to redirect\n if (e instanceof Redirection) {\n return {\n redirect: e.redirect,\n };\n }\n\n this.log.error(\"Page resolver has failed\", e);\n\n it.error = e as Error;\n break;\n }\n }\n\n let acc = \"\";\n for (let i = 0; i < stack.length; i++) {\n const it = stack[i];\n const props = it.props ?? {};\n\n const params = { ...it.config?.params };\n for (const key of Object.keys(params)) {\n params[key] = String(params[key]);\n }\n\n acc += \"/\";\n acc += it.route.path ? this.compile(it.route.path, params) : \"\";\n const path = acc.replace(/\\/+/, \"/\");\n const localErrorHandler = this.getErrorHandler(it.route);\n if (localErrorHandler) {\n const onErrorParent = state.onError;\n state.onError = (error, context) => {\n const result = localErrorHandler(error, context);\n // if nothing happen, call the parent\n if (result === undefined) {\n return onErrorParent(error, context);\n }\n return result;\n };\n }\n\n // normal use case\n if (!it.error) {\n try {\n const element = await this.createElement(it.route, {\n ...props,\n ...context,\n });\n\n state.layers.push({\n name: it.route.name,\n props,\n part: it.route.path,\n config: it.config,\n element: this.renderView(i + 1, path, element, it.route),\n index: i + 1,\n path,\n route: it.route,\n cache: it.cache,\n });\n } catch (e) {\n it.error = e as Error;\n }\n }\n\n // handler has thrown an error, render an error view\n if (it.error) {\n try {\n let element: ReactNode | Redirection | undefined =\n await state.onError(it.error, state);\n\n if (element === undefined) {\n throw it.error;\n }\n\n if (element instanceof Redirection) {\n return {\n redirect: element.redirect,\n };\n }\n\n if (element === null) {\n element = this.renderError(it.error);\n }\n\n state.layers.push({\n props,\n error: it.error,\n name: it.route.name,\n part: it.route.path,\n config: it.config,\n element: this.renderView(i + 1, path, element, it.route),\n index: i + 1,\n path,\n route: it.route,\n });\n break;\n } catch (e) {\n if (e instanceof Redirection) {\n return {\n redirect: e.redirect,\n };\n }\n throw e;\n }\n }\n }\n\n return { state };\n }\n\n protected createRedirectionLayer(redirect: string): CreateLayersResult {\n return {\n redirect,\n };\n }\n\n protected getErrorHandler(route: PageRoute): ErrorHandler | undefined {\n if (route.errorHandler) return route.errorHandler;\n let parent = route.parent;\n while (parent) {\n if (parent.errorHandler) return parent.errorHandler;\n parent = parent.parent;\n }\n }\n\n protected async createElement(\n page: PageRoute,\n props: Record<string, any>,\n ): Promise<ReactNode> {\n if (page.lazy && page.component) {\n this.log.warn(\n `Page ${page.name} has both lazy and component options, lazy will be used`,\n );\n }\n\n if (page.lazy) {\n const component = await page.lazy(); // load component\n return createElement(component.default, props);\n }\n\n if (page.component) {\n return createElement(page.component, props);\n }\n\n return undefined;\n }\n\n public renderError(error: Error): ReactNode {\n return createElement(ErrorViewer, { error, alepha: this.alepha });\n }\n\n public renderEmptyView(): ReactNode {\n return createElement(NestedView, {});\n }\n\n public href(\n page: { options: { name?: string } },\n params: Record<string, any> = {},\n ): string {\n const found = this.pages.find((it) => it.name === page.options.name);\n if (!found) {\n throw new Error(`Page ${page.options.name} not found`);\n }\n\n let url = found.path ?? \"\";\n let parent = found.parent;\n while (parent) {\n url = `${parent.path ?? \"\"}/${url}`;\n parent = parent.parent;\n }\n\n url = this.compile(url, params);\n\n return url.replace(/\\/\\/+/g, \"/\") || \"/\";\n }\n\n public compile(path: string, params: Record<string, string> = {}) {\n for (const [key, value] of Object.entries(params)) {\n path = path.replace(`:${key}`, value);\n }\n return path;\n }\n\n protected renderView(\n index: number,\n path: string,\n view: ReactNode | undefined,\n page: PageRoute,\n ): ReactNode {\n view ??= this.renderEmptyView();\n\n const element = page.client\n ? createElement(\n ClientOnly,\n typeof page.client === \"object\" ? page.client : {},\n view,\n )\n : view;\n\n return createElement(\n RouterLayerContext.Provider,\n {\n value: {\n index,\n path,\n },\n },\n element,\n );\n }\n\n protected readonly configure = $hook({\n on: \"configure\",\n handler: () => {\n let hasNotFoundHandler = false;\n const pages = this.alepha.descriptors($page);\n\n const hasParent = (it: PageDescriptor) => {\n if (it.options.parent) {\n return true;\n }\n\n for (const page of pages) {\n const children = page.options.children\n ? Array.isArray(page.options.children)\n ? page.options.children\n : page.options.children()\n : [];\n if (children.includes(it)) {\n return true;\n }\n }\n };\n\n for (const page of pages) {\n if (page.options.path === \"/*\") {\n hasNotFoundHandler = true;\n }\n\n // skip children, we only want root pages\n if (hasParent(page)) {\n continue;\n }\n\n this.add(this.map(pages, page));\n }\n\n if (!hasNotFoundHandler && pages.length > 0) {\n // add a default 404 page if not already defined\n this.add({\n path: \"/*\",\n name: \"notFound\",\n cache: true,\n component: NotFoundPage,\n onServerResponse: ({ reply }) => {\n reply.status = 404;\n },\n });\n }\n },\n });\n\n protected map(\n pages: Array<PageDescriptor>,\n target: PageDescriptor,\n ): PageRouteEntry {\n const children = target.options.children\n ? Array.isArray(target.options.children)\n ? target.options.children\n : target.options.children()\n : [];\n\n const getChildrenFromParent = (it: PageDescriptor): PageDescriptor[] => {\n const children = [];\n for (const page of pages) {\n if (page.options.parent === it) {\n children.push(page);\n }\n }\n return children;\n };\n\n children.push(...getChildrenFromParent(target));\n\n return {\n ...target.options,\n name: target.name,\n parent: undefined,\n children: children.map((it) => this.map(pages, it)),\n } as PageRoute;\n }\n\n public add(entry: PageRouteEntry) {\n if (this.alepha.isReady()) {\n throw new AlephaError(\"Router is already initialized\");\n }\n\n entry.name ??= this.nextId();\n const page = entry as PageRoute;\n\n page.match = this.createMatch(page);\n this.pages.push(page);\n\n if (page.children) {\n for (const child of page.children) {\n (child as PageRoute).parent = page;\n this.add(child);\n }\n }\n }\n\n protected createMatch(page: PageRoute): string {\n let url = page.path ?? \"/\";\n let target = page.parent;\n while (target) {\n url = `${target.path ?? \"\"}/${url}`;\n target = target.parent;\n }\n\n let path = url.replace(/\\/\\/+/g, \"/\");\n\n if (path.endsWith(\"/\") && path !== \"/\") {\n // remove trailing slash\n path = path.slice(0, -1);\n }\n\n return path;\n }\n\n protected _next = 0;\n\n protected nextId(): string {\n this._next += 1;\n return `P${this._next}`;\n }\n}\n\nexport const isPageRoute = (it: any): it is PageRoute => {\n return (\n it &&\n typeof it === \"object\" &&\n typeof it.path === \"string\" &&\n typeof it.page === \"object\"\n );\n};\n\nexport interface PageRouteEntry\n extends Omit<PageDescriptorOptions, \"children\" | \"parent\"> {\n children?: PageRouteEntry[];\n}\n\nexport interface PageRoute extends PageRouteEntry {\n type: \"page\";\n name: string;\n parent?: PageRoute;\n match: string;\n}\n\nexport interface Layer {\n config?: {\n query?: Record<string, any>;\n params?: Record<string, any>;\n // stack of resolved props\n context?: Record<string, any>;\n };\n\n name: string;\n props?: Record<string, any>;\n error?: Error;\n part?: string;\n element: ReactNode;\n index: number;\n path: string;\n route?: PageRoute;\n cache?: boolean;\n}\n\nexport type PreviousLayerData = Omit<Layer, \"element\" | \"index\" | \"path\">;\n\nexport interface AnchorProps {\n href: string;\n onClick: (ev?: any) => any;\n}\n\nexport interface ReactRouterState {\n /**\n * Stack of layers for the current page.\n */\n layers: Array<Layer>;\n\n /**\n * URL of the current page.\n */\n url: URL;\n\n /**\n * Error handler for the current page.\n */\n onError: ErrorHandler;\n\n /**\n * Params extracted from the URL for the current page.\n */\n params: Record<string, any>;\n\n /**\n * Query parameters extracted from the URL for the current page.\n */\n query: Record<string, string>;\n\n /**\n * Optional meta information associated with the current page.\n */\n meta: Record<string, any>;\n}\n\nexport interface RouterStackItem {\n route: PageRoute;\n config?: Record<string, any>;\n props?: Record<string, any>;\n error?: Error;\n cache?: boolean;\n}\n\nexport interface TransitionOptions {\n previous?: PreviousLayerData[];\n}\n\nexport interface CreateLayersResult {\n redirect?: string;\n state?: ReactRouterState;\n}\n","import { $hook, $inject, Alepha } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { type Route, RouterProvider } from \"alepha/router\";\nimport { createElement, type ReactNode } from \"react\";\nimport NotFoundPage from \"../components/NotFound.tsx\";\nimport {\n isPageRoute,\n type PageRoute,\n type PageRouteEntry,\n type PreviousLayerData,\n ReactPageProvider,\n type ReactRouterState,\n} from \"./ReactPageProvider.ts\";\n\nexport interface BrowserRoute extends Route {\n page: PageRoute;\n}\n\nexport class ReactBrowserRouterProvider extends RouterProvider<BrowserRoute> {\n protected readonly log = $logger();\n protected readonly alepha = $inject(Alepha);\n protected readonly pageApi = $inject(ReactPageProvider);\n\n public add(entry: PageRouteEntry) {\n this.pageApi.add(entry);\n }\n\n protected readonly configure = $hook({\n on: \"configure\",\n handler: async () => {\n for (const page of this.pageApi.getPages()) {\n // mount only if a view is provided\n if (page.component || page.lazy) {\n this.push({\n path: page.match,\n page,\n });\n }\n }\n },\n });\n\n public async transition(\n url: URL,\n previous: PreviousLayerData[] = [],\n meta = {},\n ): Promise<string | void> {\n const { pathname, search } = url;\n\n const entry: Partial<ReactRouterState> = {\n url,\n query: {},\n params: {},\n layers: [],\n onError: () => null,\n meta,\n };\n\n const state = entry as ReactRouterState;\n\n // Emit both action and transition events\n await this.alepha.events.emit(\"react:action:begin\", {\n type: \"transition\",\n });\n await this.alepha.events.emit(\"react:transition:begin\", {\n previous: this.alepha.state.get(\"alepha.react.router.state\")!,\n state,\n });\n\n try {\n const { route, params } = this.match(pathname);\n\n const query: Record<string, string> = {};\n if (search) {\n for (const [key, value] of new URLSearchParams(search).entries()) {\n query[key] = String(value);\n }\n }\n\n state.query = query;\n state.params = params ?? {};\n\n if (isPageRoute(route)) {\n const { redirect } = await this.pageApi.createLayers(\n route.page,\n state,\n previous,\n );\n if (redirect) {\n return redirect;\n }\n }\n\n if (state.layers.length === 0) {\n state.layers.push({\n name: \"not-found\",\n element: createElement(NotFoundPage),\n index: 0,\n path: \"/\",\n });\n }\n\n await this.alepha.events.emit(\"react:action:success\", {\n type: \"transition\",\n });\n await this.alepha.events.emit(\"react:transition:success\", { state });\n } catch (e) {\n this.log.error(\"Transition has failed\", e);\n state.layers = [\n {\n name: \"error\",\n element: this.pageApi.renderError(e as Error),\n index: 0,\n path: \"/\",\n },\n ];\n\n await this.alepha.events.emit(\"react:action:error\", {\n type: \"transition\",\n error: e as Error,\n });\n await this.alepha.events.emit(\"react:transition:error\", {\n error: e as Error,\n state,\n });\n }\n\n // [feature]: local hook for leaving a page\n if (previous) {\n for (let i = 0; i < previous.length; i++) {\n const layer = previous[i];\n if (state.layers[i]?.name !== layer.name) {\n this.pageApi.page(layer.name)?.onLeave?.();\n }\n }\n }\n\n this.alepha.state.set(\"alepha.react.router.state\", state);\n\n await this.alepha.events.emit(\"react:action:end\", {\n type: \"transition\",\n });\n await this.alepha.events.emit(\"react:transition:end\", {\n state,\n });\n }\n\n public root(state: ReactRouterState): ReactNode {\n return this.pageApi.root(state);\n }\n}\n","import {\n $atom,\n $env,\n $hook,\n $inject,\n $use,\n Alepha,\n type State,\n type Static,\n t,\n} from \"alepha\";\nimport { DateTimeProvider } from \"alepha/datetime\";\nimport { $logger } from \"alepha/logger\";\nimport { LinkProvider } from \"alepha/server/links\";\nimport { ReactBrowserRouterProvider } from \"./ReactBrowserRouterProvider.ts\";\nimport type {\n PreviousLayerData,\n ReactRouterState,\n TransitionOptions,\n} from \"./ReactPageProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nconst envSchema = t.object({\n REACT_ROOT_ID: t.text({ default: \"root\" }),\n});\n\ndeclare module \"alepha\" {\n interface Env extends Partial<Static<typeof envSchema>> {}\n}\n\n/**\n * React browser renderer configuration atom\n */\nexport const reactBrowserOptions = $atom({\n name: \"alepha.react.browser.options\",\n schema: t.object({\n scrollRestoration: t.enum([\"top\", \"manual\"]),\n }),\n default: {\n scrollRestoration: \"top\" as const,\n },\n});\n\nexport type ReactBrowserRendererOptions = Static<\n typeof reactBrowserOptions.schema\n>;\n\ndeclare module \"alepha\" {\n interface State {\n [reactBrowserOptions.key]: ReactBrowserRendererOptions;\n }\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport class ReactBrowserProvider {\n protected readonly env = $env(envSchema);\n protected readonly log = $logger();\n protected readonly client = $inject(LinkProvider);\n protected readonly alepha = $inject(Alepha);\n protected readonly router = $inject(ReactBrowserRouterProvider);\n protected readonly dateTimeProvider = $inject(DateTimeProvider);\n\n protected readonly options = $use(reactBrowserOptions);\n\n protected getRootElement() {\n const root = this.document.getElementById(this.env.REACT_ROOT_ID);\n if (root) {\n return root;\n }\n\n const div = this.document.createElement(\"div\");\n div.id = this.env.REACT_ROOT_ID;\n\n this.document.body.prepend(div);\n\n return div;\n }\n\n public transitioning?: {\n to: string;\n from?: string;\n };\n\n public get state(): ReactRouterState {\n return this.alepha.state.get(\"alepha.react.router.state\")!;\n }\n\n /**\n * Accessor for Document DOM API.\n */\n public get document() {\n return window.document;\n }\n\n /**\n * Accessor for History DOM API.\n */\n public get history() {\n return window.history;\n }\n\n /**\n * Accessor for Location DOM API.\n */\n public get location() {\n return window.location;\n }\n\n public get base() {\n const base = import.meta.env?.BASE_URL;\n if (!base || base === \"/\") {\n return \"\";\n }\n\n return base;\n }\n\n public get url(): string {\n const url = this.location.pathname + this.location.search;\n if (this.base) {\n return url.replace(this.base, \"\");\n }\n return url;\n }\n\n public pushState(path: string, replace?: boolean) {\n const url = this.base + path;\n\n if (replace) {\n this.history.replaceState({}, \"\", url);\n } else {\n this.history.pushState({}, \"\", url);\n }\n }\n\n public async invalidate(props?: Record<string, any>) {\n const previous: PreviousLayerData[] = [];\n\n this.log.trace(\"Invalidating layers\");\n\n if (props) {\n const [key] = Object.keys(props);\n const value = props[key];\n\n for (const layer of this.state.layers) {\n if (layer.props?.[key]) {\n previous.push({\n ...layer,\n props: {\n ...layer.props,\n [key]: value,\n },\n });\n break;\n }\n previous.push(layer);\n }\n }\n\n await this.render({ previous });\n }\n\n public async go(url: string, options: RouterGoOptions = {}): Promise<void> {\n this.log.trace(`Going to ${url}`, {\n url,\n options,\n });\n\n await this.render({\n url,\n previous: options.force ? [] : this.state.layers,\n meta: options.meta,\n });\n\n // when redirecting in browser\n if (this.state.url.pathname + this.state.url.search !== url) {\n this.pushState(this.state.url.pathname + this.state.url.search);\n return;\n }\n\n this.pushState(url, options.replace);\n }\n\n protected async render(options: RouterRenderOptions = {}): Promise<void> {\n const previous = options.previous ?? this.state.layers;\n const url = options.url ?? this.url;\n const start = this.dateTimeProvider.now();\n\n this.transitioning = {\n to: url,\n from: this.state?.url.pathname,\n };\n\n this.log.debug(\"Transitioning...\", {\n to: url,\n });\n\n const redirect = await this.router.transition(\n new URL(`http://localhost${url}`),\n previous,\n options.meta,\n );\n\n if (redirect) {\n this.log.info(\"Redirecting to\", {\n redirect,\n });\n\n // if redirect is an absolute URL, use window.location.href (full page reload)\n if (redirect.startsWith(\"http\")) {\n window.location.href = redirect;\n } else {\n // if redirect is a relative URL, use render() (single page app)\n return await this.render({ url: redirect });\n }\n }\n\n const ms = this.dateTimeProvider.now().diff(start);\n this.log.info(`Transition OK [${ms}ms]`, this.transitioning);\n\n this.transitioning = undefined;\n }\n\n /**\n * Get embedded layers from the server.\n */\n protected getHydrationState(): ReactHydrationState | undefined {\n try {\n if (\"__ssr\" in window && typeof window.__ssr === \"object\") {\n return window.__ssr as ReactHydrationState;\n }\n } catch (error) {\n console.error(error);\n }\n }\n\n // -------------------------------------------------------------------------------------------------------------------\n\n protected readonly onTransitionEnd = $hook({\n on: \"react:transition:end\",\n handler: () => {\n if (\n this.options.scrollRestoration === \"top\" &&\n typeof window !== \"undefined\" &&\n !this.alepha.isTest()\n ) {\n this.log.trace(\"Restoring scroll position to top\");\n window.scrollTo(0, 0);\n }\n },\n });\n\n public readonly ready = $hook({\n on: \"ready\",\n handler: async () => {\n const hydration = this.getHydrationState();\n const previous = hydration?.layers ?? [];\n\n if (hydration) {\n // low budget, but works for now\n for (const [key, value] of Object.entries(hydration)) {\n if (key !== \"layers\") {\n this.alepha.state.set(key as keyof State, value);\n }\n }\n }\n\n await this.render({ previous });\n\n const element = this.router.root(this.state);\n\n await this.alepha.events.emit(\"react:browser:render\", {\n element,\n root: this.getRootElement(),\n hydration,\n state: this.state,\n });\n\n window.addEventListener(\"popstate\", () => {\n // when you update silently queryParams or hash, skip rendering\n // if you want to force a rendering, use #go()\n if (this.base + this.state.url.pathname === this.location.pathname) {\n return;\n }\n\n this.log.debug(\"Popstate event triggered - rendering new state\", {\n url: this.location.pathname + this.location.search,\n });\n\n this.render();\n });\n },\n });\n}\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface RouterGoOptions {\n replace?: boolean;\n match?: TransitionOptions;\n params?: Record<string, string>;\n query?: Record<string, string>;\n meta?: Record<string, any>;\n\n /**\n * Recreate the whole page, ignoring the current state.\n */\n force?: boolean;\n}\n\nexport type ReactHydrationState = {\n layers?: Array<PreviousLayerData>;\n} & {\n [key: string]: any;\n};\n\nexport interface RouterRenderOptions {\n url?: string;\n previous?: PreviousLayerData[];\n meta?: Record<string, any>;\n}\n","import { $hook } from \"alepha\";\nimport { $logger } from \"alepha/logger\";\nimport { createRoot, hydrateRoot, type Root } from \"react-dom/client\";\n\nexport class ReactBrowserRendererProvider {\n protected readonly log = $logger();\n protected root?: Root;\n\n protected readonly onBrowserRender = $hook({\n on: \"react:browser:render\",\n handler: async ({ hydration, root, element }) => {\n if (hydration?.layers) {\n this.root = hydrateRoot(root, element);\n this.log.info(\"Hydrated root element\");\n } else {\n this.root ??= createRoot(root);\n this.root.render(element);\n this.log.info(\"Created root element\");\n }\n },\n });\n}\n","import { $inject, Alepha } from \"alepha\";\nimport type { PageDescriptor } from \"../descriptors/$page.ts\";\nimport {\n ReactBrowserProvider,\n type RouterGoOptions,\n} from \"../providers/ReactBrowserProvider.ts\";\nimport {\n type AnchorProps,\n ReactPageProvider,\n type ReactRouterState,\n} from \"../providers/ReactPageProvider.ts\";\n\nexport class ReactRouter<T extends object> {\n protected readonly alepha = $inject(Alepha);\n protected readonly pageApi = $inject(ReactPageProvider);\n\n public get state(): ReactRouterState {\n return this.alepha.state.get(\"alepha.react.router.state\")!;\n }\n\n public get pages() {\n return this.pageApi.getPages();\n }\n\n public get concretePages() {\n return this.pageApi.getConcretePages();\n }\n\n public get browser(): ReactBrowserProvider | undefined {\n if (this.alepha.isBrowser()) {\n return this.alepha.inject(ReactBrowserProvider);\n }\n // server-side\n return undefined;\n }\n\n public isActive(\n href: string,\n options: {\n startWith?: boolean;\n } = {},\n ): boolean {\n const current = this.state.url.pathname;\n let isActive =\n current === href || current === `${href}/` || `${current}/` === href;\n\n if (options.startWith && !isActive) {\n isActive = current.startsWith(href);\n }\n\n return isActive;\n }\n\n public path(\n name: keyof VirtualRouter<T>,\n config: {\n params?: Record<string, any>;\n query?: Record<string, any>;\n } = {},\n ): string {\n return this.pageApi.pathname(name as string, {\n params: {\n ...this.state.params,\n ...config.params,\n },\n query: config.query,\n });\n }\n\n /**\n * Reload the current page.\n * This is equivalent to calling `go()` with the current pathname and search.\n */\n public async reload() {\n if (!this.browser) {\n return;\n }\n\n await this.go(this.location.pathname + this.location.search, {\n replace: true,\n force: true,\n });\n }\n\n public getURL(): URL {\n if (!this.browser) {\n return this.state.url;\n }\n\n return new URL(this.location.href);\n }\n\n public get location(): Location {\n if (!this.browser) {\n throw new Error(\"Browser is required\");\n }\n\n return this.browser.location;\n }\n\n public get current(): ReactRouterState {\n return this.state;\n }\n\n public get pathname(): string {\n return this.state.url.pathname;\n }\n\n public get query(): Record<string, string> {\n const query: Record<string, string> = {};\n\n for (const [key, value] of new URLSearchParams(\n this.state.url.search,\n ).entries()) {\n query[key] = String(value);\n }\n\n return query;\n }\n\n public async back() {\n this.browser?.history.back();\n }\n\n public async forward() {\n this.browser?.history.forward();\n }\n\n public async invalidate(props?: Record<string, any>) {\n await this.browser?.invalidate(props);\n }\n\n public async go(path: string, options?: RouterGoOptions): Promise<void>;\n public async go(\n path: keyof VirtualRouter<T>,\n options?: RouterGoOptions,\n ): Promise<void>;\n public async go(\n path: string | keyof VirtualRouter<T>,\n options?: RouterGoOptions,\n ): Promise<void> {\n for (const page of this.pages) {\n if (page.name === path) {\n await this.browser?.go(\n this.path(path as keyof VirtualRouter<T>, options),\n options,\n );\n return;\n }\n }\n\n await this.browser?.go(path as string, options);\n }\n\n public anchor(path: string, options?: RouterGoOptions): AnchorProps;\n public anchor(\n path: keyof VirtualRouter<T>,\n options?: RouterGoOptions,\n ): AnchorProps;\n public anchor(\n path: string | keyof VirtualRouter<T>,\n options: RouterGoOptions = {},\n ): AnchorProps {\n let href = path as string;\n\n for (const page of this.pages) {\n if (page.name === path) {\n href = this.path(path as keyof VirtualRouter<T>, options);\n break;\n }\n }\n\n return {\n href: this.base(href),\n onClick: (ev: any) => {\n ev.stopPropagation();\n ev.preventDefault();\n\n this.go(href, options).catch(console.error);\n },\n };\n }\n\n public base(path: string): string {\n const base = import.meta.env?.BASE_URL;\n if (!base || base === \"/\") {\n return path;\n }\n\n return base + path;\n }\n\n /**\n * Set query params.\n *\n * @param record\n * @param options\n */\n public setQueryParams(\n record:\n | Record<string, any>\n | ((queryParams: Record<string, any>) => Record<string, any>),\n options: {\n /**\n * If true, this will add a new entry to the history stack.\n */\n push?: boolean;\n } = {},\n ) {\n const func = typeof record === \"function\" ? record : () => record;\n const search = new URLSearchParams(func(this.query)).toString();\n const state = search ? `${this.pathname}?${search}` : this.pathname;\n\n if (options.push) {\n window.history.pushState({}, \"\", state);\n } else {\n window.history.replaceState({}, \"\", state);\n }\n }\n}\n\nexport type VirtualRouter<T> = {\n [K in keyof T as T[K] extends PageDescriptor ? K : never]: T[K];\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 { ReactRouter } from \"../services/ReactRouter.ts\";\nimport { useInject } from \"./useInject.ts\";\n\n/**\n * Use this hook to access the React Router instance.\n *\n * You can add a type parameter to specify the type of your application.\n * This will allow you to use the router in a typesafe way.\n *\n * @example\n * class App {\n * home = $page();\n * }\n *\n * const router = useRouter<App>();\n * router.go(\"home\"); // typesafe\n */\nexport const useRouter = <T extends object = any>(): ReactRouter<T> => {\n return useInject(ReactRouter<T>);\n};\n","import type { AnchorHTMLAttributes } from \"react\";\nimport { useRouter } from \"../hooks/useRouter.ts\";\n\nexport interface LinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {\n href: string;\n}\n\nconst Link = (props: LinkProps) => {\n const router = useRouter();\n\n return (\n <a {...props} {...router.anchor(props.href)}>\n {props.children}\n </a>\n );\n};\n\nexport default Link;\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 { useState } from \"react\";\nimport type { AnchorProps } from \"../providers/ReactPageProvider.ts\";\nimport { useRouter } from \"./useRouter.ts\";\nimport { useRouterState } from \"./useRouterState.ts\";\n\nexport interface UseActiveOptions {\n href: string;\n startWith?: boolean;\n}\n\nexport const useActive = (args: string | UseActiveOptions): UseActiveHook => {\n const router = useRouter();\n const [isPending, setPending] = useState(false);\n const state = useRouterState();\n const current = state.url.pathname;\n\n const options: UseActiveOptions =\n typeof args === \"string\" ? { href: args } : { ...args, href: args.href };\n const href = options.href;\n const isActive = router.isActive(href, options);\n\n return {\n isPending,\n isActive,\n anchorProps: {\n href: router.base(href),\n onClick: async (ev?: any) => {\n ev?.stopPropagation();\n ev?.preventDefault();\n if (isActive) return;\n if (isPending) return;\n\n setPending(true);\n try {\n await router.go(href);\n } finally {\n setPending(false);\n }\n },\n },\n };\n};\n\nexport interface UseActiveHook {\n isActive: boolean;\n anchorProps: AnchorProps;\n isPending: boolean;\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 { Alepha, Static, TObject } from \"alepha\";\nimport { useEffect, useState } from \"react\";\nimport { useAlepha } from \"./useAlepha.ts\";\nimport { useRouter } from \"./useRouter.ts\";\n\n/**\n * Not well tested. Use with caution.\n */\nexport const useQueryParams = <T extends TObject>(\n schema: T,\n options: UseQueryParamsHookOptions = {},\n): [Partial<Static<T>>, (data: Static<T>) => void] => {\n const alepha = useAlepha();\n\n const key = options.key ?? \"q\";\n const router = useRouter();\n const querystring = router.query[key];\n\n const [queryParams = {}, setQueryParams] = useState<Static<T> | undefined>(\n decode(alepha, schema, router.query[key]),\n );\n\n useEffect(() => {\n setQueryParams(decode(alepha, schema, querystring));\n }, [querystring]);\n\n return [\n queryParams,\n (queryParams: Static<T>) => {\n setQueryParams(queryParams);\n router.setQueryParams((data) => {\n return { ...data, [key]: encode(alepha, schema, queryParams) };\n });\n },\n ];\n};\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport interface UseQueryParamsHookOptions {\n format?: \"base64\" | \"querystring\";\n key?: string;\n push?: boolean;\n}\n\nconst encode = (alepha: Alepha, schema: TObject, data: any) => {\n return btoa(JSON.stringify(alepha.codec.decode(schema, data)));\n};\n\nconst decode = <T extends TObject>(\n alepha: Alepha,\n schema: T,\n data: any,\n): Static<T> | undefined => {\n try {\n return alepha.codec.decode(\n schema,\n JSON.parse(atob(decodeURIComponent(data))),\n );\n } catch {\n return;\n }\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 { $module } from \"alepha\";\nimport { AlephaDateTime } from \"alepha/datetime\";\nimport { AlephaServer } from \"alepha/server\";\nimport { AlephaServerLinks } from \"alepha/server/links\";\nimport { $page } from \"./descriptors/$page.ts\";\nimport { ReactBrowserProvider } from \"./providers/ReactBrowserProvider.ts\";\nimport { ReactBrowserRendererProvider } from \"./providers/ReactBrowserRendererProvider.ts\";\nimport { ReactBrowserRouterProvider } from \"./providers/ReactBrowserRouterProvider.ts\";\nimport { ReactPageProvider } from \"./providers/ReactPageProvider.ts\";\nimport { ReactPageService } from \"./services/ReactPageService.ts\";\nimport { ReactRouter } from \"./services/ReactRouter.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport * from \"./index.shared.ts\";\nexport * from \"./providers/ReactBrowserProvider.ts\";\nexport * from \"./providers/ReactBrowserRouterProvider.ts\";\nexport * from \"./providers/ReactPageProvider.ts\";\n\n// ---------------------------------------------------------------------------------------------------------------------\n\nexport const AlephaReact = $module({\n name: \"alepha.react\",\n descriptors: [$page],\n services: [\n ReactPageProvider,\n ReactBrowserRouterProvider,\n ReactBrowserProvider,\n ReactRouter,\n ReactBrowserRendererProvider,\n ReactPageService,\n ],\n register: (alepha) =>\n alepha\n .with(AlephaDateTime)\n .with(AlephaServer)\n .with(AlephaServerLinks)\n .with(ReactPageProvider)\n .with(ReactBrowserProvider)\n .with(ReactBrowserRouterProvider)\n .with(ReactBrowserRendererProvider)\n .with(ReactRouter),\n});\n"],"mappings":"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"}
|