@alepha/react 0.12.0 → 0.13.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 (85) hide show
  1. package/dist/auth/chunk-DhGyd7sr.js +28 -0
  2. package/dist/auth/index.browser.js +394 -114
  3. package/dist/auth/index.browser.js.map +1 -1
  4. package/dist/auth/index.cjs +80 -1927
  5. package/dist/auth/index.cjs.map +1 -1
  6. package/dist/auth/index.d.cts +1130 -420
  7. package/dist/auth/index.d.ts +1130 -420
  8. package/dist/auth/index.js +72 -1918
  9. package/dist/auth/index.js.map +1 -1
  10. package/dist/core/chunk-DhGyd7sr.js +28 -0
  11. package/dist/core/index.browser.js +79 -79
  12. package/dist/core/index.browser.js.map +1 -1
  13. package/dist/core/index.cjs +89 -85
  14. package/dist/core/index.cjs.map +1 -1
  15. package/dist/core/index.d.cts +1654 -154
  16. package/dist/core/index.d.ts +1654 -154
  17. package/dist/core/index.js +79 -79
  18. package/dist/core/index.js.map +1 -1
  19. package/dist/form/chunk-DhGyd7sr.js +28 -0
  20. package/dist/form/index.cjs +28 -8
  21. package/dist/form/index.cjs.map +1 -1
  22. package/dist/form/index.d.cts +215 -7
  23. package/dist/form/index.d.ts +215 -7
  24. package/dist/form/index.js +18 -3
  25. package/dist/form/index.js.map +1 -1
  26. package/dist/head/chunk-DhGyd7sr.js +28 -0
  27. package/dist/head/index.browser.js +385 -59
  28. package/dist/head/index.browser.js.map +1 -1
  29. package/dist/head/index.cjs +12 -8
  30. package/dist/head/index.cjs.map +1 -1
  31. package/dist/head/index.d.cts +1230 -24
  32. package/dist/head/index.d.ts +1230 -29
  33. package/dist/head/index.js +2 -2
  34. package/dist/head/index.js.map +1 -1
  35. package/dist/i18n/chunk-DhGyd7sr.js +28 -0
  36. package/dist/i18n/index.cjs +33 -20
  37. package/dist/i18n/index.cjs.map +1 -1
  38. package/dist/i18n/index.d.cts +282 -13
  39. package/dist/i18n/index.d.ts +282 -13
  40. package/dist/i18n/index.js +23 -14
  41. package/dist/i18n/index.js.map +1 -1
  42. package/dist/websocket/index.cjs +21 -8
  43. package/dist/websocket/index.cjs.map +1 -1
  44. package/dist/websocket/index.js +11 -2
  45. package/dist/websocket/index.js.map +1 -1
  46. package/package.json +7 -6
  47. package/src/auth/index.browser.ts +3 -6
  48. package/src/auth/index.shared.ts +0 -1
  49. package/src/auth/index.ts +3 -16
  50. package/src/auth/providers/ReactAuthProvider.ts +1 -614
  51. package/src/auth/services/ReactAuth.ts +6 -17
  52. package/src/core/descriptors/$page.ts +1 -1
  53. package/src/core/index.browser.ts +1 -0
  54. package/src/core/index.native.ts +21 -0
  55. package/src/core/index.shared-router.ts +15 -0
  56. package/src/core/index.shared.ts +0 -14
  57. package/src/core/index.ts +1 -0
  58. package/src/core/services/ReactRouter.ts +2 -2
  59. package/src/form/errors/FormValidationError.ts +20 -0
  60. package/src/form/hooks/useForm.ts +1 -1
  61. package/src/form/index.ts +1 -0
  62. package/src/head/providers/BrowserHeadProvider.ts +1 -1
  63. package/src/i18n/descriptors/$dictionary.ts +7 -3
  64. package/src/i18n/providers/I18nProvider.ts +9 -10
  65. package/src/websocket/hooks/useRoom.tsx +21 -2
  66. package/dist/auth/index.d.cts.map +0 -1
  67. package/dist/auth/index.d.ts.map +0 -1
  68. package/dist/core/index.d.cts.map +0 -1
  69. package/dist/core/index.d.ts.map +0 -1
  70. package/dist/form/index.d.cts.map +0 -1
  71. package/dist/form/index.d.ts.map +0 -1
  72. package/dist/head/index.d.cts.map +0 -1
  73. package/dist/head/index.d.ts.map +0 -1
  74. package/dist/i18n/index.d.cts.map +0 -1
  75. package/dist/i18n/index.d.ts.map +0 -1
  76. package/dist/websocket/index.d.cts.map +0 -1
  77. package/dist/websocket/index.d.ts.map +0 -1
  78. package/src/auth/descriptors/$auth.ts +0 -436
  79. package/src/auth/descriptors/$authApple.ts +0 -8
  80. package/src/auth/descriptors/$authGithub.ts +0 -81
  81. package/src/auth/descriptors/$authGoogle.ts +0 -38
  82. package/src/auth/errors/SessionExpiredError.ts +0 -6
  83. package/src/auth/schemas/tokenResponseSchema.ts +0 -11
  84. package/src/auth/schemas/tokensSchema.ts +0 -21
  85. package/src/auth/schemas/userinfoResponseSchema.ts +0 -10
@@ -0,0 +1,28 @@
1
+ //#region rolldown:runtime
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __copyProps = (to, from, except, desc) => {
9
+ if (from && typeof from === "object" || typeof from === "function") {
10
+ for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) {
13
+ __defProp(to, key, {
14
+ get: ((k) => from[k]).bind(null, key),
15
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
16
+ });
17
+ }
18
+ }
19
+ }
20
+ return to;
21
+ };
22
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
23
+ value: mod,
24
+ enumerable: true
25
+ }) : target, mod));
26
+
27
+ //#endregion
28
+ export { };
@@ -1,12 +1,16 @@
1
1
  import { $atom, $env, $hook, $inject, $module, $use, Alepha, AlephaError, Atom, Descriptor, KIND, createDescriptor, t } from "alepha";
2
2
  import { AlephaDateTime, DateTimeProvider } from "alepha/datetime";
3
- import { AlephaServer } from "alepha/server";
4
- import { AlephaServerLinks, LinkProvider } from "alepha/server/links";
3
+ import { AlephaServer, ServerProvider, ServerRouterProvider, ServerTimingProvider } from "alepha/server";
4
+ import { AlephaServerCache } from "alepha/server/cache";
5
+ import { AlephaServerLinks, LinkProvider, ServerLinksProvider } from "alepha/server/links";
5
6
  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
- import { createRoot, hydrateRoot } from "react-dom/client";
9
+ import { existsSync } from "node:fs";
10
+ import { join } from "node:path";
11
+ import { ServerStaticProvider } from "alepha/server/static";
12
+ import { renderToString } from "react-dom/server";
13
+ import { RouterProvider } from "alepha/router";
10
14
 
11
15
  //#region src/core/services/ReactPageService.ts
12
16
  var ReactPageService = class {
@@ -66,7 +70,7 @@ var ReactPageService = class {
66
70
  * const userProfile = $page({
67
71
  * path: "/users/:id",
68
72
  * schema: {
69
- * params: t.object({ id: t.int() }),
73
+ * params: t.object({ id: t.integer() }),
70
74
  * query: t.object({ tab: t.optional(t.text()) })
71
75
  * },
72
76
  * resolve: async ({ params }) => {
@@ -143,31 +147,6 @@ var PageDescriptor = class extends Descriptor {
143
147
  };
144
148
  $page[KIND] = PageDescriptor;
145
149
 
146
- //#endregion
147
- //#region src/core/components/NotFound.tsx
148
- function NotFoundPage(props) {
149
- return /* @__PURE__ */ jsx("div", {
150
- style: {
151
- height: "100vh",
152
- display: "flex",
153
- flexDirection: "column",
154
- justifyContent: "center",
155
- alignItems: "center",
156
- textAlign: "center",
157
- fontFamily: "sans-serif",
158
- padding: "1rem",
159
- ...props.style
160
- },
161
- children: /* @__PURE__ */ jsx("h1", {
162
- style: {
163
- fontSize: "1rem",
164
- marginBottom: "0.5rem"
165
- },
166
- children: "404 - This page does not exist"
167
- })
168
- });
169
- }
170
-
171
150
  //#endregion
172
151
  //#region src/core/components/ClientOnly.tsx
173
152
  /**
@@ -597,12 +576,37 @@ function parseAnimation(animationLike, state, type = "enter") {
597
576
  }
598
577
  }
599
578
 
579
+ //#endregion
580
+ //#region src/core/components/NotFound.tsx
581
+ function NotFoundPage(props) {
582
+ return /* @__PURE__ */ jsx("div", {
583
+ style: {
584
+ height: "100vh",
585
+ display: "flex",
586
+ flexDirection: "column",
587
+ justifyContent: "center",
588
+ alignItems: "center",
589
+ textAlign: "center",
590
+ fontFamily: "sans-serif",
591
+ padding: "1rem",
592
+ ...props.style
593
+ },
594
+ children: /* @__PURE__ */ jsx("h1", {
595
+ style: {
596
+ fontSize: "1rem",
597
+ marginBottom: "0.5rem"
598
+ },
599
+ children: "404 - This page does not exist"
600
+ })
601
+ });
602
+ }
603
+
600
604
  //#endregion
601
605
  //#region src/core/providers/ReactPageProvider.ts
602
- const envSchema$1 = t.object({ REACT_STRICT_MODE: t.boolean({ default: true }) });
606
+ const envSchema$2 = t.object({ REACT_STRICT_MODE: t.boolean({ default: true }) });
603
607
  var ReactPageProvider = class {
604
608
  log = $logger();
605
- env = $env(envSchema$1);
609
+ env = $env(envSchema$2);
606
610
  alepha = $inject(Alepha);
607
611
  pages = [];
608
612
  getPages() {
@@ -920,6 +924,336 @@ const isPageRoute = (it) => {
920
924
  return it && typeof it === "object" && typeof it.path === "string" && typeof it.page === "object";
921
925
  };
922
926
 
927
+ //#endregion
928
+ //#region src/core/providers/ReactServerProvider.ts
929
+ const envSchema$1 = t.object({
930
+ REACT_SSR_ENABLED: t.optional(t.boolean()),
931
+ REACT_ROOT_ID: t.text({ default: "root" }),
932
+ REACT_SERVER_TEMPLATE: t.optional(t.text({ size: "rich" }))
933
+ });
934
+ /**
935
+ * React server provider configuration atom
936
+ */
937
+ const reactServerOptions = $atom({
938
+ name: "alepha.react.server.options",
939
+ schema: t.object({
940
+ publicDir: t.string(),
941
+ staticServer: t.object({
942
+ disabled: t.boolean(),
943
+ path: t.string({ description: "URL path where static files will be served." })
944
+ })
945
+ }),
946
+ default: {
947
+ publicDir: "public",
948
+ staticServer: {
949
+ disabled: false,
950
+ path: "/"
951
+ }
952
+ }
953
+ });
954
+ var ReactServerProvider = class {
955
+ log = $logger();
956
+ alepha = $inject(Alepha);
957
+ env = $env(envSchema$1);
958
+ pageApi = $inject(ReactPageProvider);
959
+ serverProvider = $inject(ServerProvider);
960
+ serverStaticProvider = $inject(ServerStaticProvider);
961
+ serverRouterProvider = $inject(ServerRouterProvider);
962
+ serverTimingProvider = $inject(ServerTimingProvider);
963
+ ROOT_DIV_REGEX = new RegExp(`<div([^>]*)\\s+id=["']${this.env.REACT_ROOT_ID}["']([^>]*)>(.*?)<\\/div>`, "is");
964
+ preprocessedTemplate = null;
965
+ options = $use(reactServerOptions);
966
+ /**
967
+ * Configure the React server provider.
968
+ */
969
+ onConfigure = $hook({
970
+ on: "configure",
971
+ handler: async () => {
972
+ const ssrEnabled = this.alepha.descriptors($page).length > 0 && this.env.REACT_SSR_ENABLED !== false;
973
+ this.alepha.state.set("alepha.react.server.ssr", ssrEnabled);
974
+ if (this.alepha.isViteDev()) {
975
+ await this.configureVite(ssrEnabled);
976
+ return;
977
+ }
978
+ let root = "";
979
+ if (!this.alepha.isServerless()) {
980
+ root = this.getPublicDirectory();
981
+ if (!root) this.log.warn("Missing static files, static file server will be disabled");
982
+ else {
983
+ this.log.debug(`Using static files from: ${root}`);
984
+ await this.configureStaticServer(root);
985
+ }
986
+ }
987
+ if (ssrEnabled) {
988
+ await this.registerPages(async () => this.template);
989
+ this.log.info("SSR OK");
990
+ return;
991
+ }
992
+ this.log.info("SSR is disabled, use History API fallback");
993
+ this.serverRouterProvider.createRoute({
994
+ path: "*",
995
+ handler: async ({ url, reply }) => {
996
+ if (url.pathname.includes(".")) {
997
+ reply.headers["content-type"] = "text/plain";
998
+ reply.body = "Not Found";
999
+ reply.status = 404;
1000
+ return;
1001
+ }
1002
+ reply.headers["content-type"] = "text/html";
1003
+ return this.template;
1004
+ }
1005
+ });
1006
+ }
1007
+ });
1008
+ get template() {
1009
+ return this.alepha.env.REACT_SERVER_TEMPLATE ?? "<!DOCTYPE html><html lang='en'><head></head><body></body></html>";
1010
+ }
1011
+ async registerPages(templateLoader) {
1012
+ const template = await templateLoader();
1013
+ if (template) this.preprocessedTemplate = this.preprocessTemplate(template);
1014
+ for (const page of this.pageApi.getPages()) {
1015
+ if (page.children?.length) continue;
1016
+ this.log.debug(`+ ${page.match} -> ${page.name}`);
1017
+ this.serverRouterProvider.createRoute({
1018
+ ...page,
1019
+ schema: void 0,
1020
+ method: "GET",
1021
+ path: page.match,
1022
+ handler: this.createHandler(page, templateLoader)
1023
+ });
1024
+ }
1025
+ }
1026
+ /**
1027
+ * Get the public directory path where static files are located.
1028
+ */
1029
+ getPublicDirectory() {
1030
+ const maybe = [join(process.cwd(), `dist/${this.options.publicDir}`), join(process.cwd(), this.options.publicDir)];
1031
+ for (const it of maybe) if (existsSync(it)) return it;
1032
+ return "";
1033
+ }
1034
+ /**
1035
+ * Configure the static file server to serve files from the given root directory.
1036
+ */
1037
+ async configureStaticServer(root) {
1038
+ await this.serverStaticProvider.createStaticServer({
1039
+ root,
1040
+ cacheControl: {
1041
+ maxAge: 3600,
1042
+ immutable: true
1043
+ },
1044
+ ...this.options.staticServer
1045
+ });
1046
+ }
1047
+ /**
1048
+ * Configure Vite for SSR.
1049
+ */
1050
+ async configureVite(ssrEnabled) {
1051
+ if (!ssrEnabled) return;
1052
+ this.log.info("SSR (dev) OK");
1053
+ const url = `http://${process.env.SERVER_HOST}:${process.env.SERVER_PORT}`;
1054
+ await this.registerPages(() => fetch(`${url}/index.html`).then((it) => it.text()).catch(() => void 0));
1055
+ }
1056
+ /**
1057
+ * For testing purposes, creates a render function that can be used.
1058
+ */
1059
+ async render(name, options = {}) {
1060
+ const page = this.pageApi.page(name);
1061
+ const url = new URL(this.pageApi.url(name, options));
1062
+ const state = {
1063
+ url,
1064
+ params: options.params ?? {},
1065
+ query: options.query ?? {},
1066
+ onError: () => null,
1067
+ layers: [],
1068
+ meta: {}
1069
+ };
1070
+ this.log.trace("Rendering", { url });
1071
+ await this.alepha.events.emit("react:server:render:begin", { state });
1072
+ const { redirect } = await this.pageApi.createLayers(page, state);
1073
+ if (redirect) return {
1074
+ state,
1075
+ html: "",
1076
+ redirect
1077
+ };
1078
+ if (!options.html) {
1079
+ this.alepha.state.set("alepha.react.router.state", state);
1080
+ return {
1081
+ state,
1082
+ html: renderToString(this.pageApi.root(state))
1083
+ };
1084
+ }
1085
+ const template = this.template ?? "";
1086
+ const html = this.renderToHtml(template, state, options.hydration);
1087
+ if (html instanceof Redirection) return {
1088
+ state,
1089
+ html: "",
1090
+ redirect
1091
+ };
1092
+ const result = {
1093
+ state,
1094
+ html
1095
+ };
1096
+ await this.alepha.events.emit("react:server:render:end", result);
1097
+ return result;
1098
+ }
1099
+ createHandler(route, templateLoader) {
1100
+ return async (serverRequest) => {
1101
+ const { url, reply, query, params } = serverRequest;
1102
+ const template = await templateLoader();
1103
+ if (!template) throw new AlephaError("Missing template for SSR rendering");
1104
+ this.log.trace("Rendering page", { name: route.name });
1105
+ const state = {
1106
+ url,
1107
+ params,
1108
+ query,
1109
+ onError: () => null,
1110
+ layers: []
1111
+ };
1112
+ if (this.alepha.has(ServerLinksProvider)) this.alepha.state.set("alepha.server.request.apiLinks", await this.alepha.inject(ServerLinksProvider).getUserApiLinks({
1113
+ user: serverRequest.user,
1114
+ authorization: serverRequest.headers.authorization
1115
+ }));
1116
+ let target = route;
1117
+ while (target) {
1118
+ if (route.can && !route.can()) {
1119
+ reply.status = 403;
1120
+ reply.headers["content-type"] = "text/plain";
1121
+ return "Forbidden";
1122
+ }
1123
+ target = target.parent;
1124
+ }
1125
+ await this.alepha.events.emit("react:server:render:begin", {
1126
+ request: serverRequest,
1127
+ state
1128
+ });
1129
+ this.serverTimingProvider.beginTiming("createLayers");
1130
+ const { redirect } = await this.pageApi.createLayers(route, state);
1131
+ this.serverTimingProvider.endTiming("createLayers");
1132
+ if (redirect) return reply.redirect(redirect);
1133
+ reply.headers["content-type"] = "text/html";
1134
+ reply.headers["cache-control"] = "no-store, no-cache, must-revalidate, proxy-revalidate";
1135
+ reply.headers.pragma = "no-cache";
1136
+ reply.headers.expires = "0";
1137
+ const html = this.renderToHtml(template, state);
1138
+ if (html instanceof Redirection) {
1139
+ reply.redirect(typeof html.redirect === "string" ? html.redirect : this.pageApi.href(html.redirect));
1140
+ return;
1141
+ }
1142
+ const event = {
1143
+ request: serverRequest,
1144
+ state,
1145
+ html
1146
+ };
1147
+ await this.alepha.events.emit("react:server:render:end", event);
1148
+ route.onServerResponse?.(serverRequest);
1149
+ this.log.trace("Page rendered", { name: route.name });
1150
+ return event.html;
1151
+ };
1152
+ }
1153
+ renderToHtml(template, state, hydration = true) {
1154
+ const element = this.pageApi.root(state);
1155
+ this.alepha.state.set("alepha.react.router.state", state);
1156
+ this.serverTimingProvider.beginTiming("renderToString");
1157
+ let app = "";
1158
+ try {
1159
+ app = renderToString(element);
1160
+ } catch (error) {
1161
+ this.log.error("renderToString has failed, fallback to error handler", error);
1162
+ const element$1 = state.onError(error, state);
1163
+ if (element$1 instanceof Redirection) return element$1;
1164
+ app = renderToString(element$1);
1165
+ this.log.debug("Error handled successfully with fallback");
1166
+ }
1167
+ this.serverTimingProvider.endTiming("renderToString");
1168
+ const response = { html: template };
1169
+ if (hydration) {
1170
+ const { request, context, ...store } = this.alepha.context.als?.getStore() ?? {};
1171
+ const hydrationData = {
1172
+ ...store,
1173
+ "alepha.react.router.state": void 0,
1174
+ layers: state.layers.map((it) => ({
1175
+ ...it,
1176
+ error: it.error ? {
1177
+ ...it.error,
1178
+ name: it.error.name,
1179
+ message: it.error.message,
1180
+ stack: !this.alepha.isProduction() ? it.error.stack : void 0
1181
+ } : void 0,
1182
+ index: void 0,
1183
+ path: void 0,
1184
+ element: void 0,
1185
+ route: void 0
1186
+ }))
1187
+ };
1188
+ const script = `<script>window.__ssr=${JSON.stringify(hydrationData)}<\/script>`;
1189
+ this.fillTemplate(response, app, script);
1190
+ }
1191
+ return response.html;
1192
+ }
1193
+ preprocessTemplate(template) {
1194
+ const bodyCloseIndex = template.match(/<\/body>/i)?.index ?? template.length;
1195
+ const beforeScript = template.substring(0, bodyCloseIndex);
1196
+ const afterScript = template.substring(bodyCloseIndex);
1197
+ const rootDivMatch = beforeScript.match(this.ROOT_DIV_REGEX);
1198
+ if (rootDivMatch) {
1199
+ const beforeDiv = beforeScript.substring(0, rootDivMatch.index);
1200
+ const afterDivStart = rootDivMatch.index + rootDivMatch[0].length;
1201
+ const afterDiv = beforeScript.substring(afterDivStart);
1202
+ return {
1203
+ beforeApp: `${beforeDiv}<div${rootDivMatch[1]} id="${this.env.REACT_ROOT_ID}"${rootDivMatch[2]}>`,
1204
+ afterApp: `</div>${afterDiv}`,
1205
+ beforeScript: "",
1206
+ afterScript
1207
+ };
1208
+ }
1209
+ const bodyMatch = beforeScript.match(/<body([^>]*)>/i);
1210
+ if (bodyMatch) {
1211
+ const beforeBody = beforeScript.substring(0, bodyMatch.index + bodyMatch[0].length);
1212
+ const afterBody = beforeScript.substring(bodyMatch.index + bodyMatch[0].length);
1213
+ return {
1214
+ beforeApp: `${beforeBody}<div id="${this.env.REACT_ROOT_ID}">`,
1215
+ afterApp: `</div>${afterBody}`,
1216
+ beforeScript: "",
1217
+ afterScript
1218
+ };
1219
+ }
1220
+ return {
1221
+ beforeApp: `<div id="${this.env.REACT_ROOT_ID}">`,
1222
+ afterApp: `</div>`,
1223
+ beforeScript,
1224
+ afterScript
1225
+ };
1226
+ }
1227
+ fillTemplate(response, app, script) {
1228
+ if (!this.preprocessedTemplate) this.preprocessedTemplate = this.preprocessTemplate(response.html);
1229
+ response.html = this.preprocessedTemplate.beforeApp + app + this.preprocessedTemplate.afterApp + script + this.preprocessedTemplate.afterScript;
1230
+ }
1231
+ };
1232
+
1233
+ //#endregion
1234
+ //#region src/core/services/ReactPageServerService.ts
1235
+ var ReactPageServerService = class extends ReactPageService {
1236
+ reactServerProvider = $inject(ReactServerProvider);
1237
+ serverProvider = $inject(ServerProvider);
1238
+ async render(name, options = {}) {
1239
+ return this.reactServerProvider.render(name, options);
1240
+ }
1241
+ async fetch(pathname, options = {}) {
1242
+ const response = await fetch(`${this.serverProvider.hostname}/${pathname}`);
1243
+ const html = await response.text();
1244
+ if (options?.html) return {
1245
+ html,
1246
+ response
1247
+ };
1248
+ const match = html.match(this.reactServerProvider.ROOT_DIV_REGEX);
1249
+ if (match) return {
1250
+ html: match[3],
1251
+ response
1252
+ };
1253
+ throw new AlephaError("Invalid HTML response");
1254
+ }
1255
+ };
1256
+
923
1257
  //#endregion
924
1258
  //#region src/core/providers/ReactBrowserRouterProvider.ts
925
1259
  var ReactBrowserRouterProvider = class extends RouterProvider {
@@ -1166,26 +1500,6 @@ var ReactBrowserProvider = class {
1166
1500
  });
1167
1501
  };
1168
1502
 
1169
- //#endregion
1170
- //#region src/core/providers/ReactBrowserRendererProvider.ts
1171
- var ReactBrowserRendererProvider = class {
1172
- log = $logger();
1173
- root;
1174
- onBrowserRender = $hook({
1175
- on: "react:browser:render",
1176
- handler: async ({ hydration, root, element }) => {
1177
- if (hydration?.layers) {
1178
- this.root = hydrateRoot(root, element);
1179
- this.log.info("Hydrated root element");
1180
- } else {
1181
- this.root ??= createRoot(root);
1182
- this.root.render(element);
1183
- this.log.info("Created root element");
1184
- }
1185
- }
1186
- });
1187
- };
1188
-
1189
1503
  //#endregion
1190
1504
  //#region src/core/services/ReactRouter.ts
1191
1505
  var ReactRouter = class {
@@ -1212,7 +1526,7 @@ var ReactRouter = class {
1212
1526
  path(name, config = {}) {
1213
1527
  return this.pageApi.pathname(name, {
1214
1528
  params: {
1215
- ...this.state.params,
1529
+ ...this.state?.params,
1216
1530
  ...config.params
1217
1531
  },
1218
1532
  query: config.query
@@ -1311,19 +1625,31 @@ const useInject = (service) => {
1311
1625
  };
1312
1626
 
1313
1627
  //#endregion
1314
- //#region src/core/index.browser.ts
1628
+ //#region src/core/index.ts
1629
+ /**
1630
+ * Provides full-stack React development with declarative routing, server-side rendering, and client-side hydration.
1631
+ *
1632
+ * The React module enables building modern React applications using the `$page` descriptor on class properties.
1633
+ * It delivers seamless server-side rendering, automatic code splitting, and client-side navigation with full
1634
+ * type safety and schema validation for route parameters and data.
1635
+ *
1636
+ * @see {@link $page}
1637
+ * @module alepha.react
1638
+ */
1315
1639
  const AlephaReact = $module({
1316
1640
  name: "alepha.react",
1317
1641
  descriptors: [$page],
1318
1642
  services: [
1643
+ ReactServerProvider,
1319
1644
  ReactPageProvider,
1320
- ReactBrowserRouterProvider,
1321
- ReactBrowserProvider,
1322
1645
  ReactRouter,
1323
- ReactBrowserRendererProvider,
1324
- ReactPageService
1646
+ ReactPageService,
1647
+ ReactPageServerService
1325
1648
  ],
1326
- register: (alepha) => alepha.with(AlephaDateTime).with(AlephaServer).with(AlephaServerLinks).with(ReactPageProvider).with(ReactBrowserProvider).with(ReactBrowserRouterProvider).with(ReactBrowserRendererProvider).with(ReactRouter)
1649
+ register: (alepha) => alepha.with(AlephaDateTime).with(AlephaServer).with(AlephaServerCache).with(AlephaServerLinks).with({
1650
+ provide: ReactPageService,
1651
+ use: ReactPageServerService
1652
+ }).with(ReactServerProvider).with(ReactPageProvider).with(ReactRouter)
1327
1653
  });
1328
1654
 
1329
1655
  //#endregion