@alepha/react 0.14.0 → 0.14.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (72) hide show
  1. package/README.md +1 -1
  2. package/dist/auth/index.browser.js +1488 -4
  3. package/dist/auth/index.browser.js.map +1 -1
  4. package/dist/auth/index.d.ts +2 -2
  5. package/dist/auth/index.js +1827 -4
  6. package/dist/auth/index.js.map +1 -1
  7. package/dist/core/index.d.ts +54 -937
  8. package/dist/core/index.d.ts.map +1 -1
  9. package/dist/core/index.js +132 -2010
  10. package/dist/core/index.js.map +1 -1
  11. package/dist/form/index.d.ts.map +1 -1
  12. package/dist/form/index.js +6 -1
  13. package/dist/form/index.js.map +1 -1
  14. package/dist/head/index.browser.js +191 -17
  15. package/dist/head/index.browser.js.map +1 -1
  16. package/dist/head/index.d.ts +652 -31
  17. package/dist/head/index.d.ts.map +1 -1
  18. package/dist/head/index.js +209 -18
  19. package/dist/head/index.js.map +1 -1
  20. package/dist/{core → router}/index.browser.js +126 -516
  21. package/dist/router/index.browser.js.map +1 -0
  22. package/dist/router/index.d.ts +1334 -0
  23. package/dist/router/index.d.ts.map +1 -0
  24. package/dist/router/index.js +1939 -0
  25. package/dist/router/index.js.map +1 -0
  26. package/package.json +12 -6
  27. package/src/auth/index.ts +1 -1
  28. package/src/auth/services/ReactAuth.ts +1 -1
  29. package/src/core/components/ClientOnly.tsx +14 -0
  30. package/src/core/components/ErrorBoundary.tsx +3 -2
  31. package/src/core/contexts/AlephaContext.ts +3 -0
  32. package/src/core/contexts/AlephaProvider.tsx +2 -1
  33. package/src/core/index.ts +13 -102
  34. package/src/form/services/FormModel.ts +5 -0
  35. package/src/head/helpers/SeoExpander.ts +141 -0
  36. package/src/head/index.browser.ts +1 -0
  37. package/src/head/index.ts +17 -7
  38. package/src/head/interfaces/Head.ts +69 -27
  39. package/src/head/providers/BrowserHeadProvider.ts +45 -12
  40. package/src/head/providers/HeadProvider.ts +32 -8
  41. package/src/head/providers/ServerHeadProvider.ts +34 -2
  42. package/src/{core → router}/components/ErrorViewer.tsx +2 -0
  43. package/src/router/components/Link.tsx +21 -0
  44. package/src/{core → router}/components/NestedView.tsx +3 -5
  45. package/src/router/components/NotFound.tsx +30 -0
  46. package/src/router/errors/Redirection.ts +28 -0
  47. package/src/{core → router}/hooks/useActive.ts +6 -2
  48. package/src/{core → router}/hooks/useQueryParams.ts +2 -2
  49. package/src/{core → router}/hooks/useRouter.ts +1 -1
  50. package/src/{core → router}/hooks/useRouterState.ts +1 -1
  51. package/src/{core → router}/index.browser.ts +14 -12
  52. package/src/{core/index.shared-router.ts → router/index.shared.ts} +6 -3
  53. package/src/router/index.ts +125 -0
  54. package/src/{core → router}/primitives/$page.ts +1 -1
  55. package/src/{core → router}/providers/ReactBrowserProvider.ts +3 -13
  56. package/src/{core → router}/providers/ReactBrowserRendererProvider.ts +3 -0
  57. package/src/{core → router}/providers/ReactBrowserRouterProvider.ts +3 -0
  58. package/src/{core → router}/providers/ReactPageProvider.ts +5 -3
  59. package/src/{core → router}/providers/ReactServerProvider.ts +9 -28
  60. package/src/{core → router}/services/ReactPageServerService.ts +3 -0
  61. package/src/{core → router}/services/ReactPageService.ts +5 -5
  62. package/src/{core → router}/services/ReactRouter.ts +26 -5
  63. package/dist/core/index.browser.js.map +0 -1
  64. package/dist/core/index.native.js +0 -403
  65. package/dist/core/index.native.js.map +0 -1
  66. package/src/core/components/Link.tsx +0 -18
  67. package/src/core/components/NotFound.tsx +0 -27
  68. package/src/core/errors/Redirection.ts +0 -13
  69. package/src/core/hooks/useSchema.ts +0 -88
  70. package/src/core/index.native.ts +0 -21
  71. package/src/core/index.shared.ts +0 -9
  72. /package/src/{core → router}/contexts/RouterLayerContext.ts +0 -0
@@ -1,5 +1,5 @@
1
1
  import { ReactRouter } from "../services/ReactRouter.ts";
2
- import { useInject } from "./useInject.ts";
2
+ import { useInject } from "@alepha/react";
3
3
 
4
4
  /**
5
5
  * Use this hook to access the React Router instance.
@@ -1,6 +1,6 @@
1
1
  import { AlephaError } from "alepha";
2
2
  import type { ReactRouterState } from "../providers/ReactPageProvider.ts";
3
- import { useStore } from "./useStore.ts";
3
+ import { useStore } from "@alepha/react";
4
4
 
5
5
  export const useRouterState = (): ReactRouterState => {
6
6
  const [state] = useStore("alepha.react.router.state");
@@ -1,27 +1,27 @@
1
1
  import { $module } from "alepha";
2
- import { AlephaDateTime } from "alepha/datetime";
3
- import { AlephaServer } from "alepha/server";
4
- import { AlephaServerLinks } from "alepha/server/links";
5
2
  import { $page } from "./primitives/$page.ts";
6
- import { ReactBrowserProvider } from "./providers/ReactBrowserProvider.ts";
3
+ import { ReactRouter } from "./services/ReactRouter.ts";
7
4
  import { ReactBrowserRendererProvider } from "./providers/ReactBrowserRendererProvider.ts";
8
5
  import { ReactBrowserRouterProvider } from "./providers/ReactBrowserRouterProvider.ts";
9
- import { ReactPageProvider } from "./providers/ReactPageProvider.ts";
10
6
  import { ReactPageService } from "./services/ReactPageService.ts";
11
- import { ReactRouter } from "./services/ReactRouter.ts";
7
+ import { ReactPageProvider } from "./providers/ReactPageProvider.ts";
8
+ import { ReactBrowserProvider } from "./providers/ReactBrowserProvider.ts";
9
+ import { AlephaDateTime } from "alepha/datetime";
10
+ import { AlephaServer } from "alepha/server";
11
+ import { AlephaServerLinks } from "alepha/server/links";
12
+ import { AlephaReact } from "@alepha/react";
12
13
 
13
14
  // ---------------------------------------------------------------------------------------------------------------------
14
15
 
15
16
  export * from "./index.shared.ts";
16
- export * from "./index.shared-router.ts";
17
- export * from "./providers/ReactBrowserProvider.ts";
18
- export * from "./providers/ReactBrowserRouterProvider.ts";
19
- export * from "./providers/ReactPageProvider.ts";
17
+ export * from "./providers/ReactBrowserProvider.ts"
18
+ export * from "./providers/ReactBrowserRouterProvider.ts"
19
+ export * from "./providers/ReactBrowserRendererProvider.ts"
20
20
 
21
21
  // ---------------------------------------------------------------------------------------------------------------------
22
22
 
23
- export const AlephaReact = $module({
24
- name: "alepha.react",
23
+ export const AlephaReactRouter = $module({
24
+ name: "alepha.react.router",
25
25
  primitives: [$page],
26
26
  services: [
27
27
  ReactPageProvider,
@@ -33,6 +33,7 @@ export const AlephaReact = $module({
33
33
  ],
34
34
  register: (alepha) =>
35
35
  alepha
36
+ .with(AlephaReact)
36
37
  .with(AlephaDateTime)
37
38
  .with(AlephaServer)
38
39
  .with(AlephaServerLinks)
@@ -42,3 +43,4 @@ export const AlephaReact = $module({
42
43
  .with(ReactBrowserRendererProvider)
43
44
  .with(ReactRouter),
44
45
  });
46
+
@@ -1,9 +1,11 @@
1
- export { default as ClientOnly } from "./components/ClientOnly.tsx";
2
- export { default as ErrorBoundary } from "./components/ErrorBoundary.tsx";
3
1
  export { default as ErrorViewer } from "./components/ErrorViewer.tsx";
2
+ export type * from "./components/ErrorViewer.tsx";
4
3
  export { default as Link, type LinkProps } from "./components/Link.tsx";
4
+ export type * from "./components/Link.tsx";
5
5
  export { default as NestedView } from "./components/NestedView.tsx";
6
+ export type * from "./components/NestedView.tsx";
6
7
  export { default as NotFound } from "./components/NotFound.tsx";
8
+ export type * from "./components/NotFound.tsx";
7
9
  export * from "./contexts/RouterLayerContext.ts";
8
10
  export * from "./primitives/$page.ts";
9
11
  export * from "./errors/Redirection.ts";
@@ -11,5 +13,6 @@ export * from "./hooks/useActive.ts";
11
13
  export * from "./hooks/useQueryParams.ts";
12
14
  export * from "./hooks/useRouter.ts";
13
15
  export * from "./hooks/useRouterState.ts";
14
- export * from "./hooks/useSchema.ts";
15
16
  export * from "./services/ReactRouter.ts";
17
+ export * from "./services/ReactPageService.ts"
18
+ export * from "./providers/ReactPageProvider.ts";
@@ -0,0 +1,125 @@
1
+ import { AlephaReact } from "@alepha/react";
2
+ import { $module } from "alepha";
3
+ import { $page, type PageAnimation } from "./primitives/$page.ts";
4
+ import { ReactRouter } from "./services/ReactRouter.ts";
5
+ import { ReactPageProvider, type ReactRouterState } from "./providers/ReactPageProvider.ts";
6
+ import { AlephaServer, type ServerRequest } from "alepha/server";
7
+ import type { ReactNode } from "react";
8
+ import type { ReactHydrationState } from "./providers/ReactBrowserProvider.ts";
9
+ import { ReactServerProvider } from "./providers/ReactServerProvider.ts";
10
+ import { ReactPageServerService } from "./services/ReactPageServerService.ts";
11
+ import { AlephaServerCache } from "alepha/server/cache";
12
+ import { AlephaServerLinks } from "alepha/server/links";
13
+ import { ReactPageService } from "./services/ReactPageService.ts";
14
+ import { AlephaDateTime } from "alepha/datetime";
15
+
16
+ // ---------------------------------------------------------------------------------------------------------------------
17
+
18
+ export * from "./index.shared.ts";
19
+ export * from "./providers/ReactPageProvider.ts";
20
+ export * from "./providers/ReactBrowserProvider.ts";
21
+ export * from "./providers/ReactServerProvider.ts";
22
+
23
+ // ---------------------------------------------------------------------------------------------------------------------
24
+
25
+ declare module "alepha" {
26
+ interface State {
27
+ "alepha.react.router.state"?: ReactRouterState;
28
+ }
29
+
30
+ interface Hooks {
31
+ /**
32
+ * Fires when the React application is starting to be rendered on the server.
33
+ */
34
+ "react:server:render:begin": {
35
+ request?: ServerRequest;
36
+ state: ReactRouterState;
37
+ };
38
+ /**
39
+ * Fires when the React application has been rendered on the server.
40
+ */
41
+ "react:server:render:end": {
42
+ request?: ServerRequest;
43
+ state: ReactRouterState;
44
+ html: string;
45
+ };
46
+ // -----------------------------------------------------------------------------------------------------------------
47
+ /**
48
+ * Fires when the React application is being rendered on the browser.
49
+ */
50
+ "react:browser:render": {
51
+ root: HTMLElement;
52
+ element: ReactNode;
53
+ state: ReactRouterState;
54
+ hydration?: ReactHydrationState;
55
+ };
56
+ // -----------------------------------------------------------------------------------------------------------------
57
+ // SPECIFIC: Route transitions
58
+ /**
59
+ * Fires when a route transition is starting.
60
+ */
61
+ "react:transition:begin": {
62
+ previous: ReactRouterState;
63
+ state: ReactRouterState;
64
+ animation?: PageAnimation;
65
+ };
66
+ /**
67
+ * Fires when a route transition has succeeded.
68
+ */
69
+ "react:transition:success": {
70
+ state: ReactRouterState;
71
+ };
72
+ /**
73
+ * Fires when a route transition has failed.
74
+ */
75
+ "react:transition:error": {
76
+ state: ReactRouterState;
77
+ error: Error;
78
+ };
79
+ /**
80
+ * Fires when a route transition has completed, regardless of success or failure.
81
+ */
82
+ "react:transition:end": {
83
+ state: ReactRouterState;
84
+ };
85
+ }
86
+ }
87
+
88
+ // ---------------------------------------------------------------------------------------------------------------------
89
+
90
+ /**
91
+ * Provides declarative routing with the `$page` primitive for building type-safe React routes.
92
+ *
93
+ * This module enables:
94
+ * - URL pattern matching with parameters (e.g., `/users/:id`)
95
+ * - Nested routing with parent-child relationships
96
+ * - Type-safe URL parameter and query string validation
97
+ * - Server-side data fetching with the `resolve` function
98
+ * - Lazy loading and code splitting
99
+ * - Page animations and error handling
100
+ *
101
+ * @see {@link $page}
102
+ * @module alepha.react.router
103
+ */
104
+ export const AlephaReactRouter = $module({
105
+ name: "alepha.react.router",
106
+ primitives: [$page],
107
+ services: [
108
+ ReactPageProvider,
109
+ ReactPageService,
110
+ ReactRouter, ReactServerProvider, ReactPageServerService],
111
+ register: (alepha) =>
112
+ alepha
113
+ .with(AlephaReact)
114
+ .with(AlephaDateTime)
115
+ .with(AlephaServer)
116
+ .with(AlephaServerCache)
117
+ .with(AlephaServerLinks)
118
+ .with({
119
+ provide: ReactPageService,
120
+ use: ReactPageServerService,
121
+ })
122
+ .with(ReactServerProvider)
123
+ .with(ReactPageProvider)
124
+ .with(ReactRouter),
125
+ });
@@ -10,10 +10,10 @@ import {
10
10
  import type { ServerRequest } from "alepha/server";
11
11
  import type { ServerRouteCache } from "alepha/server/cache";
12
12
  import type { FC, ReactNode } from "react";
13
- import type { ClientOnlyProps } from "../components/ClientOnly.tsx";
14
13
  import type { Redirection } from "../errors/Redirection.ts";
15
14
  import type { ReactRouterState } from "../providers/ReactPageProvider.ts";
16
15
  import { ReactPageService } from "../services/ReactPageService.ts";
16
+ import type { ClientOnlyProps } from "@alepha/react";
17
17
 
18
18
  /**
19
19
  * Main primitive for defining a React route in the application.
@@ -18,6 +18,9 @@ import type {
18
18
  ReactRouterState,
19
19
  TransitionOptions,
20
20
  } from "./ReactPageProvider.ts";
21
+ import type { RouterGoOptions } from "../services/ReactRouter.ts";
22
+
23
+ export type { RouterGoOptions } from "../services/ReactRouter.ts";
21
24
 
22
25
  // ---------------------------------------------------------------------------------------------------------------------
23
26
 
@@ -297,19 +300,6 @@ export class ReactBrowserProvider {
297
300
 
298
301
  // ---------------------------------------------------------------------------------------------------------------------
299
302
 
300
- export interface RouterGoOptions {
301
- replace?: boolean;
302
- match?: TransitionOptions;
303
- params?: Record<string, string>;
304
- query?: Record<string, string>;
305
- meta?: Record<string, any>;
306
-
307
- /**
308
- * Recreate the whole page, ignoring the current state.
309
- */
310
- force?: boolean;
311
- }
312
-
313
303
  export type ReactHydrationState = {
314
304
  layers?: Array<PreviousLayerData>;
315
305
  } & {
@@ -2,6 +2,9 @@ import { $hook } from "alepha";
2
2
  import { $logger } from "alepha/logger";
3
3
  import { createRoot, hydrateRoot, type Root } from "react-dom/client";
4
4
 
5
+ /**
6
+ * Browser specific React renderer (react-dom/client interface)
7
+ */
5
8
  export class ReactBrowserRendererProvider {
6
9
  protected readonly log = $logger();
7
10
  protected root?: Root;
@@ -16,6 +16,9 @@ export interface BrowserRoute extends Route {
16
16
  page: PageRoute;
17
17
  }
18
18
 
19
+ /**
20
+ * Implementation of AlephaRouter for React in browser environment.
21
+ */
19
22
  export class ReactBrowserRouterProvider extends RouterProvider<BrowserRoute> {
20
23
  protected readonly log = $logger();
21
24
  protected readonly alepha = $inject(Alepha);
@@ -10,19 +10,18 @@ import {
10
10
  } from "alepha";
11
11
  import { $logger } from "alepha/logger";
12
12
  import { createElement, type ReactNode, StrictMode } from "react";
13
- import ClientOnly from "../components/ClientOnly.tsx";
13
+ import { AlephaContext, ClientOnly } from "@alepha/react";
14
14
  import ErrorViewer from "../components/ErrorViewer.tsx";
15
15
  import NestedView from "../components/NestedView.tsx";
16
16
  import NotFoundPage from "../components/NotFound.tsx";
17
- import { AlephaContext } from "../contexts/AlephaContext.ts";
18
17
  import { RouterLayerContext } from "../contexts/RouterLayerContext.ts";
18
+ import { Redirection } from "../errors/Redirection.ts";
19
19
  import {
20
20
  $page,
21
21
  type ErrorHandler,
22
22
  type PagePrimitive,
23
23
  type PagePrimitiveOptions,
24
24
  } from "../primitives/$page.ts";
25
- import { Redirection } from "../errors/Redirection.ts";
26
25
 
27
26
  const envSchema = t.object({
28
27
  REACT_STRICT_MODE: t.boolean({ default: true }),
@@ -32,6 +31,9 @@ declare module "alepha" {
32
31
  export interface Env extends Partial<Static<typeof envSchema>> {}
33
32
  }
34
33
 
34
+ /**
35
+ * Handle page routes for React applications. (Browser and Server)
36
+ */
35
37
  export class ReactPageProvider {
36
38
  protected readonly log = $logger();
37
39
  protected readonly env = $env(envSchema);
@@ -1,38 +1,15 @@
1
1
  import { existsSync } from "node:fs";
2
2
  import { join } from "node:path";
3
- import {
4
- $atom,
5
- $env,
6
- $hook,
7
- $inject,
8
- $use,
9
- Alepha,
10
- AlephaError,
11
- type Static,
12
- t,
13
- } from "alepha";
3
+ import { $atom, $env, $hook, $inject, $use, Alepha, AlephaError, type Static, t, } from "alepha";
14
4
  import { $logger } from "alepha/logger";
15
- import {
16
- type ServerHandler,
17
- ServerProvider,
18
- ServerRouterProvider,
19
- ServerTimingProvider,
20
- } from "alepha/server";
5
+ import { type ServerHandler, ServerRouterProvider, ServerTimingProvider, } from "alepha/server";
21
6
  import { ServerLinksProvider } from "alepha/server/links";
22
7
  import { ServerStaticProvider } from "alepha/server/static";
23
8
  import { renderToString } from "react-dom/server";
24
- import {
25
- $page,
26
- type PagePrimitiveRenderOptions,
27
- type PagePrimitiveRenderResult,
28
- } from "../primitives/$page.ts";
29
9
  import { Redirection } from "../errors/Redirection.ts";
10
+ import { $page, type PagePrimitiveRenderOptions, type PagePrimitiveRenderResult, } from "../primitives/$page.ts";
30
11
  import type { ReactHydrationState } from "./ReactBrowserProvider.ts";
31
- import {
32
- type PageRoute,
33
- ReactPageProvider,
34
- type ReactRouterState,
35
- } from "./ReactPageProvider.ts";
12
+ import { type PageRoute, ReactPageProvider, type ReactRouterState, } from "./ReactPageProvider.ts";
36
13
 
37
14
  // ---------------------------------------------------------------------------------------------------------------------
38
15
 
@@ -84,12 +61,16 @@ declare module "alepha" {
84
61
 
85
62
  // ---------------------------------------------------------------------------------------------------------------------
86
63
 
64
+ /**
65
+ * React server provider responsible for SSR and static file serving.
66
+ *
67
+ * Use `react-dom/server` under the hood.
68
+ */
87
69
  export class ReactServerProvider {
88
70
  protected readonly log = $logger();
89
71
  protected readonly alepha = $inject(Alepha);
90
72
  protected readonly env = $env(envSchema);
91
73
  protected readonly pageApi = $inject(ReactPageProvider);
92
- protected readonly serverProvider = $inject(ServerProvider);
93
74
  protected readonly serverStaticProvider = $inject(ServerStaticProvider);
94
75
  protected readonly serverRouterProvider = $inject(ServerRouterProvider);
95
76
  protected readonly serverTimingProvider = $inject(ServerTimingProvider);
@@ -7,6 +7,9 @@ import type {
7
7
  import { ReactServerProvider } from "../providers/ReactServerProvider.ts";
8
8
  import { ReactPageService } from "./ReactPageService.ts";
9
9
 
10
+ /**
11
+ * $page methods for server-side.
12
+ */
10
13
  export class ReactPageServerService extends ReactPageService {
11
14
  protected readonly reactServerProvider = $inject(ReactServerProvider);
12
15
  protected readonly serverProvider = $inject(ServerProvider);
@@ -1,10 +1,10 @@
1
1
  import { AlephaError } from "alepha";
2
- import type {
3
- PagePrimitiveRenderOptions,
4
- PagePrimitiveRenderResult,
5
- } from "../primitives/$page.ts";
2
+ import type { PagePrimitiveRenderOptions, PagePrimitiveRenderResult, } from "../../router/primitives/$page.ts";
6
3
 
7
- export class ReactPageService {
4
+ /**
5
+ * $page methods interface.
6
+ */
7
+ export abstract class ReactPageService {
8
8
  public fetch(
9
9
  pathname: string,
10
10
  options: PagePrimitiveRenderOptions = {},
@@ -1,15 +1,28 @@
1
1
  import { $inject, Alepha } from "alepha";
2
- import type { PagePrimitive } from "../primitives/$page.ts";
3
- import {
4
- ReactBrowserProvider,
5
- type RouterGoOptions,
6
- } from "../providers/ReactBrowserProvider.ts";
2
+ import { ReactBrowserProvider } from "../providers/ReactBrowserProvider.ts";
7
3
  import {
8
4
  type AnchorProps,
9
5
  ReactPageProvider,
10
6
  type ReactRouterState,
11
7
  } from "../providers/ReactPageProvider.ts";
8
+ import type { PagePrimitive } from "../primitives/$page.ts";
9
+
10
+ export interface RouterGoOptions {
11
+ replace?: boolean;
12
+ params?: Record<string, string>;
13
+ query?: Record<string, string>;
14
+ meta?: Record<string, any>;
15
+ /**
16
+ * Recreate the whole page, ignoring the current state.
17
+ */
18
+ force?: boolean;
19
+ }
12
20
 
21
+ /**
22
+ * Friendly browser router API.
23
+ *
24
+ * Can be safely used server-side, but most methods will be no-op.
25
+ */
13
26
  export class ReactRouter<T extends object> {
14
27
  protected readonly alepha = $inject(Alepha);
15
28
  protected readonly pageApi = $inject(ReactPageProvider);
@@ -59,6 +72,14 @@ export class ReactRouter<T extends object> {
59
72
  } = {},
60
73
  ) {
61
74
  const page = this.pageApi.page(name as string);
75
+ if (!page.lazy && !page.component) {
76
+ return {
77
+ ...page,
78
+ label: page.label ?? page.name,
79
+ children: undefined,
80
+ }
81
+ }
82
+
62
83
  return {
63
84
  ...page,
64
85
  label: page.label ?? page.name,