@alepha/react 0.14.3 → 0.15.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (57) hide show
  1. package/README.md +10 -0
  2. package/dist/auth/index.browser.js +29 -14
  3. package/dist/auth/index.browser.js.map +1 -1
  4. package/dist/auth/index.d.ts +4 -4
  5. package/dist/auth/index.d.ts.map +1 -1
  6. package/dist/auth/index.js +950 -194
  7. package/dist/auth/index.js.map +1 -1
  8. package/dist/core/index.d.ts +118 -118
  9. package/dist/core/index.d.ts.map +1 -1
  10. package/dist/form/index.d.ts +27 -28
  11. package/dist/form/index.d.ts.map +1 -1
  12. package/dist/head/index.browser.js +59 -19
  13. package/dist/head/index.browser.js.map +1 -1
  14. package/dist/head/index.d.ts +105 -576
  15. package/dist/head/index.d.ts.map +1 -1
  16. package/dist/head/index.js +91 -87
  17. package/dist/head/index.js.map +1 -1
  18. package/dist/i18n/index.d.ts +33 -33
  19. package/dist/i18n/index.d.ts.map +1 -1
  20. package/dist/router/index.browser.js +30 -15
  21. package/dist/router/index.browser.js.map +1 -1
  22. package/dist/router/index.d.ts +827 -403
  23. package/dist/router/index.d.ts.map +1 -1
  24. package/dist/router/index.js +951 -195
  25. package/dist/router/index.js.map +1 -1
  26. package/dist/websocket/index.d.ts +38 -39
  27. package/dist/websocket/index.d.ts.map +1 -1
  28. package/package.json +5 -5
  29. package/src/auth/__tests__/$auth.spec.ts +10 -11
  30. package/src/core/__tests__/Router.spec.tsx +4 -4
  31. package/src/head/{__tests__/expandSeo.spec.ts → helpers/SeoExpander.spec.ts} +1 -1
  32. package/src/head/index.ts +10 -28
  33. package/src/head/providers/BrowserHeadProvider.browser.spec.ts +1 -76
  34. package/src/head/providers/BrowserHeadProvider.ts +25 -19
  35. package/src/head/providers/HeadProvider.ts +76 -10
  36. package/src/head/providers/ServerHeadProvider.ts +22 -138
  37. package/src/router/__tests__/page-head-browser.browser.spec.ts +91 -0
  38. package/src/router/__tests__/page-head.spec.ts +44 -0
  39. package/src/{head → router}/__tests__/seo-head.spec.ts +2 -2
  40. package/src/router/atoms/ssrManifestAtom.ts +60 -0
  41. package/src/router/constants/PAGE_PRELOAD_KEY.ts +6 -0
  42. package/src/router/errors/Redirection.ts +1 -1
  43. package/src/router/index.shared.ts +1 -0
  44. package/src/router/index.ts +16 -2
  45. package/src/router/primitives/$page.browser.spec.tsx +15 -15
  46. package/src/router/primitives/$page.spec.tsx +18 -18
  47. package/src/router/primitives/$page.ts +46 -10
  48. package/src/router/providers/ReactBrowserProvider.ts +14 -29
  49. package/src/router/providers/ReactBrowserRouterProvider.ts +5 -0
  50. package/src/router/providers/ReactPageProvider.ts +11 -4
  51. package/src/router/providers/ReactServerProvider.ts +321 -316
  52. package/src/router/providers/ReactServerTemplateProvider.ts +793 -0
  53. package/src/router/providers/SSRManifestProvider.ts +365 -0
  54. package/src/router/services/ReactPageServerService.ts +5 -3
  55. package/src/router/services/ReactRouter.ts +3 -3
  56. package/src/head/__tests__/page-head.spec.ts +0 -39
  57. package/src/head/providers/ServerHeadProvider.spec.ts +0 -163
package/README.md CHANGED
@@ -10,3 +10,13 @@ This package is part of the Alepha framework and can be installed via the all-in
10
10
  npm install alepha
11
11
  ```
12
12
 
13
+ ## API Reference
14
+
15
+ ### Environment Variables
16
+
17
+ Environment variables used to configure this package.
18
+
19
+ | Variable | Type | Default | Description |
20
+ |----------|------|---------|-------------|
21
+ | `REACT_SSR_ENABLED` | boolean | - | |
22
+ | `REACT_STRICT_MODE` | boolean | true | |
@@ -2,6 +2,7 @@ import { $atom, $env, $hook, $inject, $module, $use, Alepha, AlephaError, KIND,
2
2
  import { AlephaDateTime, DateTimeProvider } from "alepha/datetime";
3
3
  import { $logger } from "alepha/logger";
4
4
  import { AlephaServerLinks, LinkProvider } from "alepha/server/links";
5
+ import { BrowserHeadProvider } from "@alepha/react/head";
5
6
  import { RouterProvider } from "alepha/router";
6
7
  import { StrictMode, createContext, createElement, memo, use, useRef, useState } from "react";
7
8
  import { Fragment, jsx, jsxs } from "react/jsx-runtime";
@@ -23,6 +24,15 @@ var ReactPageService = class {
23
24
  }
24
25
  };
25
26
 
27
+ //#endregion
28
+ //#region ../../src/router/constants/PAGE_PRELOAD_KEY.ts
29
+ /**
30
+ * Symbol key for SSR module preloading path.
31
+ * Using Symbol.for() allows the Vite plugin to inject this at build time.
32
+ * @internal
33
+ */
34
+ const PAGE_PRELOAD_KEY = Symbol.for("alepha.page.preload");
35
+
26
36
  //#endregion
27
37
  //#region ../../src/router/primitives/$page.ts
28
38
  /**
@@ -37,7 +47,7 @@ var ReactPageService = class {
37
47
  * - Type-safe URL parameter and query string validation
38
48
  *
39
49
  * **Data Loading**
40
- * - Server-side data fetching with the `resolve` function
50
+ * - Server-side data fetching with the `loader` function
41
51
  * - Automatic serialization and hydration for SSR
42
52
  * - Access to request context, URL params, and parent data
43
53
  *
@@ -74,7 +84,7 @@ var ReactPageService = class {
74
84
  * params: t.object({ id: t.integer() }),
75
85
  * query: t.object({ tab: t.optional(t.text()) })
76
86
  * },
77
- * resolve: async ({ params }) => {
87
+ * loader: async ({ params }) => {
78
88
  * const user = await userApi.getUser(params.id);
79
89
  * return { user };
80
90
  * },
@@ -87,7 +97,7 @@ var ReactPageService = class {
87
97
  * const projectSection = $page({
88
98
  * path: "/projects/:id",
89
99
  * children: () => [projectBoard, projectSettings],
90
- * resolve: async ({ params }) => {
100
+ * loader: async ({ params }) => {
91
101
  * const project = await projectApi.get(params.id);
92
102
  * return { project };
93
103
  * },
@@ -106,7 +116,7 @@ var ReactPageService = class {
106
116
  * static: {
107
117
  * entries: posts.map(p => ({ params: { slug: p.slug } }))
108
118
  * },
109
- * resolve: async ({ params }) => {
119
+ * loader: async ({ params }) => {
110
120
  * const post = await loadPost(params.slug);
111
121
  * return { post };
112
122
  * }
@@ -587,7 +597,7 @@ const RouterLayerContext = createContext(void 0);
587
597
  * import { Redirection } from "@alepha/react";
588
598
  *
589
599
  * const MyPage = $page({
590
- * resolve: async () => {
600
+ * loader: async () => {
591
601
  * if (needRedirect) {
592
602
  * throw new Redirection("/new-path");
593
603
  * }
@@ -743,13 +753,13 @@ function parseAnimation(animationLike, state, type = "enter") {
743
753
 
744
754
  //#endregion
745
755
  //#region ../../src/router/providers/ReactPageProvider.ts
746
- const envSchema$1 = t.object({ REACT_STRICT_MODE: t.boolean({ default: true }) });
756
+ const envSchema = t.object({ REACT_STRICT_MODE: t.boolean({ default: true }) });
747
757
  /**
748
758
  * Handle page routes for React applications. (Browser and Server)
749
759
  */
750
760
  var ReactPageProvider = class {
751
761
  log = $logger();
752
- env = $env(envSchema$1);
762
+ env = $env(envSchema);
753
763
  alepha = $inject(Alepha);
754
764
  pages = [];
755
765
  getPages() {
@@ -869,11 +879,11 @@ var ReactPageProvider = class {
869
879
  }
870
880
  forceRefresh = true;
871
881
  }
872
- if (!route$1.resolve) continue;
882
+ if (!route$1.loader) continue;
873
883
  try {
874
884
  const args = Object.create(state);
875
885
  Object.assign(args, config, context);
876
- const props = await route$1.resolve?.(args) ?? {};
886
+ const props = await route$1.loader?.(args) ?? {};
877
887
  it.props = { ...props };
878
888
  context = {
879
889
  ...context,
@@ -881,7 +891,7 @@ var ReactPageProvider = class {
881
891
  };
882
892
  } catch (e) {
883
893
  if (e instanceof Redirection) return { redirect: e.redirect };
884
- this.log.error("Page resolver has failed", e);
894
+ this.log.error("Page loader has failed", e);
885
895
  it.error = e;
886
896
  break;
887
897
  }
@@ -1076,6 +1086,7 @@ var ReactBrowserRouterProvider = class extends RouterProvider {
1076
1086
  log = $logger();
1077
1087
  alepha = $inject(Alepha);
1078
1088
  pageApi = $inject(ReactPageProvider);
1089
+ browserHeadProvider = $inject(BrowserHeadProvider);
1079
1090
  add(entry) {
1080
1091
  this.pageApi.add(entry);
1081
1092
  }
@@ -1146,6 +1157,7 @@ var ReactBrowserRouterProvider = class extends RouterProvider {
1146
1157
  this.alepha.store.set("alepha.react.router.state", state);
1147
1158
  await this.alepha.events.emit("react:action:end", { type: "transition" });
1148
1159
  await this.alepha.events.emit("react:transition:end", { state });
1160
+ this.browserHeadProvider.fillAndRenderHead(state);
1149
1161
  }
1150
1162
  root(state) {
1151
1163
  return this.pageApi.root(state);
@@ -1154,7 +1166,6 @@ var ReactBrowserRouterProvider = class extends RouterProvider {
1154
1166
 
1155
1167
  //#endregion
1156
1168
  //#region ../../src/router/providers/ReactBrowserProvider.ts
1157
- const envSchema = t.object({ REACT_ROOT_ID: t.text({ default: "root" }) });
1158
1169
  /**
1159
1170
  * React browser renderer configuration atom
1160
1171
  */
@@ -1164,18 +1175,21 @@ const reactBrowserOptions = $atom({
1164
1175
  default: { scrollRestoration: "top" }
1165
1176
  });
1166
1177
  var ReactBrowserProvider = class {
1167
- env = $env(envSchema);
1168
1178
  log = $logger();
1169
1179
  client = $inject(LinkProvider);
1170
1180
  alepha = $inject(Alepha);
1171
1181
  router = $inject(ReactBrowserRouterProvider);
1172
1182
  dateTimeProvider = $inject(DateTimeProvider);
1183
+ browserHeadProvider = $inject(BrowserHeadProvider);
1173
1184
  options = $use(reactBrowserOptions);
1185
+ get rootId() {
1186
+ return "root";
1187
+ }
1174
1188
  getRootElement() {
1175
- const root = this.document.getElementById(this.env.REACT_ROOT_ID);
1189
+ const root = this.document.getElementById(this.rootId);
1176
1190
  if (root) return root;
1177
1191
  const div = this.document.createElement("div");
1178
- div.id = this.env.REACT_ROOT_ID;
1192
+ div.id = this.rootId;
1179
1193
  this.document.body.prepend(div);
1180
1194
  return div;
1181
1195
  }
@@ -1308,6 +1322,7 @@ var ReactBrowserProvider = class {
1308
1322
  hydration,
1309
1323
  state: this.state
1310
1324
  });
1325
+ this.browserHeadProvider.fillAndRenderHead(this.state);
1311
1326
  window.addEventListener("popstate", () => {
1312
1327
  if (this.base + this.state.url.pathname === this.location.pathname) return;
1313
1328
  this.log.debug("Popstate event triggered - rendering new state", { url: this.location.pathname + this.location.search });