@alepha/react 0.8.1 → 0.9.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.
package/dist/index.js CHANGED
@@ -1,31 +1,35 @@
1
- import { $hook, $inject, $logger, Alepha, KIND, NotImplementedError, OPTIONS, __bind, __descriptor, t } from "@alepha/core";
1
+ import { $env, $hook, $inject, $logger, $module, Alepha, Descriptor, KIND, NotImplementedError, createDescriptor, t } from "@alepha/core";
2
2
  import { AlephaServer, ServerRouterProvider, ServerTimingProvider, apiLinksResponseSchema } from "@alepha/server";
3
3
  import { AlephaServerCache } from "@alepha/server-cache";
4
4
  import { AlephaServerLinks, LinkProvider, ServerLinksProvider } from "@alepha/server-links";
5
5
  import React, { StrictMode, createContext, createElement, useContext, useEffect, useMemo, useState } from "react";
6
6
  import { jsx, jsxs } from "react/jsx-runtime";
7
+ import { RouterProvider } from "@alepha/router";
7
8
  import { existsSync } from "node:fs";
8
9
  import { join } from "node:path";
9
10
  import { ServerStaticProvider } from "@alepha/server-static";
10
11
  import { renderToString } from "react-dom/server";
11
- import { RouterProvider } from "@alepha/router";
12
12
 
13
13
  //#region src/descriptors/$page.ts
14
- const KEY = "PAGE";
15
14
  /**
16
15
  * Main descriptor for defining a React route in the application.
17
16
  */
18
17
  const $page = (options) => {
19
- __descriptor(KEY);
20
- return {
21
- [KIND]: KEY,
22
- [OPTIONS]: options,
23
- render: () => {
24
- throw new NotImplementedError(KEY);
25
- }
26
- };
18
+ return createDescriptor(PageDescriptor, options);
27
19
  };
28
- $page[KIND] = KEY;
20
+ var PageDescriptor = class extends Descriptor {
21
+ get name() {
22
+ return this.options.name ?? this.config.propertyKey;
23
+ }
24
+ /**
25
+ * For testing or build purposes, this will render the page (with or without the HTML layout) and return the HTML and context.
26
+ * Only valid for server-side rendering, it will throw an error if called on the client-side.
27
+ */
28
+ async render(options) {
29
+ throw new NotImplementedError("");
30
+ }
31
+ };
32
+ $page[KIND] = PageDescriptor;
29
33
 
30
34
  //#endregion
31
35
  //#region src/components/ClientOnly.tsx
@@ -341,7 +345,7 @@ var RedirectionError = class extends Error {
341
345
  const envSchema$1 = t.object({ REACT_STRICT_MODE: t.boolean({ default: true }) });
342
346
  var PageDescriptorProvider = class {
343
347
  log = $logger();
344
- env = $inject(envSchema$1);
348
+ env = $env(envSchema$1);
345
349
  alepha = $inject(Alepha);
346
350
  pages = [];
347
351
  getPages() {
@@ -547,18 +551,17 @@ var PageDescriptorProvider = class {
547
551
  on: "configure",
548
552
  handler: () => {
549
553
  let hasNotFoundHandler = false;
550
- const pages = this.alepha.getDescriptorValues($page);
554
+ const pages = this.alepha.descriptors($page);
551
555
  const hasParent = (it) => {
552
556
  for (const page of pages) {
553
- const children = page.value[OPTIONS].children ? Array.isArray(page.value[OPTIONS].children) ? page.value[OPTIONS].children : page.value[OPTIONS].children() : [];
557
+ const children = page.options.children ? Array.isArray(page.options.children) ? page.options.children : page.options.children() : [];
554
558
  if (children.includes(it)) return true;
555
559
  }
556
560
  };
557
- for (const { value, key } of pages) value[OPTIONS].name ??= key;
558
- for (const { value } of pages) {
559
- if (value[OPTIONS].path === "/*") hasNotFoundHandler = true;
560
- if (hasParent(value)) continue;
561
- this.add(this.map(pages, value));
561
+ for (const page of pages) {
562
+ if (page.options.path === "/*") hasNotFoundHandler = true;
563
+ if (hasParent(page)) continue;
564
+ this.add(this.map(pages, page));
562
565
  }
563
566
  if (!hasNotFoundHandler && pages.length > 0) this.add({
564
567
  path: "/*",
@@ -572,9 +575,10 @@ var PageDescriptorProvider = class {
572
575
  }
573
576
  });
574
577
  map(pages, target) {
575
- const children = target[OPTIONS].children ? Array.isArray(target[OPTIONS].children) ? target[OPTIONS].children : target[OPTIONS].children() : [];
578
+ const children = target.options.children ? Array.isArray(target.options.children) ? target.options.children : target.options.children() : [];
576
579
  return {
577
- ...target[OPTIONS],
580
+ ...target.options,
581
+ name: target.name,
578
582
  parent: void 0,
579
583
  children: children.map((it) => this.map(pages, it))
580
584
  };
@@ -611,6 +615,214 @@ const isPageRoute = (it) => {
611
615
  return it && typeof it === "object" && typeof it.path === "string" && typeof it.page === "object";
612
616
  };
613
617
 
618
+ //#endregion
619
+ //#region src/providers/BrowserRouterProvider.ts
620
+ var BrowserRouterProvider = class extends RouterProvider {
621
+ log = $logger();
622
+ alepha = $inject(Alepha);
623
+ pageDescriptorProvider = $inject(PageDescriptorProvider);
624
+ add(entry) {
625
+ this.pageDescriptorProvider.add(entry);
626
+ }
627
+ configure = $hook({
628
+ on: "configure",
629
+ handler: async () => {
630
+ for (const page of this.pageDescriptorProvider.getPages()) if (page.component || page.lazy) this.push({
631
+ path: page.match,
632
+ page
633
+ });
634
+ }
635
+ });
636
+ async transition(url, options = {}) {
637
+ const { pathname, search } = url;
638
+ const state = {
639
+ pathname,
640
+ search,
641
+ layers: []
642
+ };
643
+ const context = {
644
+ url,
645
+ query: {},
646
+ params: {},
647
+ onError: () => null,
648
+ ...options.context ?? {}
649
+ };
650
+ await this.alepha.emit("react:transition:begin", {
651
+ state,
652
+ context
653
+ });
654
+ try {
655
+ const previous = options.previous;
656
+ const { route, params } = this.match(pathname);
657
+ const query = {};
658
+ if (search) for (const [key, value] of new URLSearchParams(search).entries()) query[key] = String(value);
659
+ context.query = query;
660
+ context.params = params ?? {};
661
+ context.previous = previous;
662
+ if (isPageRoute(route)) {
663
+ const result = await this.pageDescriptorProvider.createLayers(route.page, context);
664
+ if (result.redirect) return {
665
+ redirect: result.redirect,
666
+ state,
667
+ context
668
+ };
669
+ state.layers = result.layers;
670
+ }
671
+ if (state.layers.length === 0) state.layers.push({
672
+ name: "not-found",
673
+ element: createElement(NotFoundPage),
674
+ index: 0,
675
+ path: "/"
676
+ });
677
+ await this.alepha.emit("react:transition:success", {
678
+ state,
679
+ context
680
+ });
681
+ } catch (e) {
682
+ this.log.error(e);
683
+ state.layers = [{
684
+ name: "error",
685
+ element: this.pageDescriptorProvider.renderError(e),
686
+ index: 0,
687
+ path: "/"
688
+ }];
689
+ await this.alepha.emit("react:transition:error", {
690
+ error: e,
691
+ state,
692
+ context
693
+ });
694
+ }
695
+ if (options.state) {
696
+ options.state.layers = state.layers;
697
+ options.state.pathname = state.pathname;
698
+ options.state.search = state.search;
699
+ }
700
+ await this.alepha.emit("react:transition:end", {
701
+ state: options.state,
702
+ context
703
+ });
704
+ return {
705
+ context,
706
+ state
707
+ };
708
+ }
709
+ root(state, context) {
710
+ return this.pageDescriptorProvider.root(state, context);
711
+ }
712
+ };
713
+
714
+ //#endregion
715
+ //#region src/providers/ReactBrowserProvider.ts
716
+ var ReactBrowserProvider = class {
717
+ log = $logger();
718
+ client = $inject(LinkProvider);
719
+ alepha = $inject(Alepha);
720
+ router = $inject(BrowserRouterProvider);
721
+ root;
722
+ transitioning;
723
+ state = {
724
+ layers: [],
725
+ pathname: "",
726
+ search: ""
727
+ };
728
+ get document() {
729
+ return window.document;
730
+ }
731
+ get history() {
732
+ return window.history;
733
+ }
734
+ get location() {
735
+ return window.location;
736
+ }
737
+ get url() {
738
+ let url = this.location.pathname + this.location.search;
739
+ if (import.meta?.env?.BASE_URL) {
740
+ url = url.replace(import.meta.env?.BASE_URL, "");
741
+ if (!url.startsWith("/")) url = `/${url}`;
742
+ }
743
+ return url;
744
+ }
745
+ pushState(url, replace) {
746
+ let path = url;
747
+ if (import.meta?.env?.BASE_URL) path = (import.meta.env?.BASE_URL + path).replaceAll("//", "/");
748
+ if (replace) this.history.replaceState({}, "", path);
749
+ else this.history.pushState({}, "", path);
750
+ }
751
+ async invalidate(props) {
752
+ const previous = [];
753
+ if (props) {
754
+ const [key] = Object.keys(props);
755
+ const value = props[key];
756
+ for (const layer of this.state.layers) {
757
+ if (layer.props?.[key]) {
758
+ previous.push({
759
+ ...layer,
760
+ props: {
761
+ ...layer.props,
762
+ [key]: value
763
+ }
764
+ });
765
+ break;
766
+ }
767
+ previous.push(layer);
768
+ }
769
+ }
770
+ await this.render({ previous });
771
+ }
772
+ async go(url, options = {}) {
773
+ const result = await this.render({ url });
774
+ if (result.context.url.pathname !== url) {
775
+ this.pushState(result.context.url.pathname);
776
+ return;
777
+ }
778
+ if (options.replace) {
779
+ this.pushState(url);
780
+ return;
781
+ }
782
+ this.pushState(url);
783
+ }
784
+ async render(options = {}) {
785
+ const previous = options.previous ?? this.state.layers;
786
+ const url = options.url ?? this.url;
787
+ this.transitioning = { to: url };
788
+ const result = await this.router.transition(new URL(`http://localhost${url}`), {
789
+ previous,
790
+ state: this.state
791
+ });
792
+ if (result.redirect) return await this.render({ url: result.redirect });
793
+ this.transitioning = void 0;
794
+ return result;
795
+ }
796
+ /**
797
+ * Get embedded layers from the server.
798
+ */
799
+ getHydrationState() {
800
+ try {
801
+ if ("__ssr" in window && typeof window.__ssr === "object") return window.__ssr;
802
+ } catch (error) {
803
+ console.error(error);
804
+ }
805
+ }
806
+ ready = $hook({
807
+ on: "ready",
808
+ handler: async () => {
809
+ const hydration = this.getHydrationState();
810
+ const previous = hydration?.layers ?? [];
811
+ if (hydration?.links) for (const link of hydration.links.links) this.client.pushLink(link);
812
+ const { context } = await this.render({ previous });
813
+ await this.alepha.emit("react:browser:render", {
814
+ state: this.state,
815
+ context,
816
+ hydration
817
+ });
818
+ window.addEventListener("popstate", () => {
819
+ if (this.state.pathname === location.pathname) return;
820
+ this.render();
821
+ });
822
+ }
823
+ });
824
+ };
825
+
614
826
  //#endregion
615
827
  //#region src/providers/ReactServerProvider.ts
616
828
  const envSchema = t.object({
@@ -626,18 +838,15 @@ var ReactServerProvider = class {
626
838
  serverStaticProvider = $inject(ServerStaticProvider);
627
839
  serverRouterProvider = $inject(ServerRouterProvider);
628
840
  serverTimingProvider = $inject(ServerTimingProvider);
629
- env = $inject(envSchema);
841
+ env = $env(envSchema);
630
842
  ROOT_DIV_REGEX = new RegExp(`<div([^>]*)\\s+id=["']${this.env.REACT_ROOT_ID}["']([^>]*)>(.*?)<\\/div>`, "is");
631
843
  onConfigure = $hook({
632
844
  on: "configure",
633
845
  handler: async () => {
634
- const pages = this.alepha.getDescriptorValues($page);
846
+ const pages = this.alepha.descriptors($page);
635
847
  const ssrEnabled = pages.length > 0 && this.env.REACT_SSR_ENABLED !== false;
636
848
  this.alepha.state("react.server.ssr", ssrEnabled);
637
- for (const { key, instance, value } of pages) {
638
- const name = value[OPTIONS].name ?? key;
639
- instance[key].render = this.createRenderFunction(name);
640
- }
849
+ for (const page of pages) page.render = this.createRenderFunction(page.name);
641
850
  if (this.alepha.isServerless() === "vite") {
642
851
  await this.configureVite(ssrEnabled);
643
852
  return;
@@ -657,7 +866,7 @@ var ReactServerProvider = class {
657
866
  return;
658
867
  }
659
868
  this.log.info("SSR is disabled, use History API fallback");
660
- await this.serverRouterProvider.route({
869
+ this.serverRouterProvider.createRoute({
661
870
  path: "*",
662
871
  handler: async ({ url, reply }) => {
663
872
  if (url.pathname.includes(".")) {
@@ -679,7 +888,7 @@ var ReactServerProvider = class {
679
888
  for (const page of this.pageDescriptorProvider.getPages()) {
680
889
  if (page.children?.length) continue;
681
890
  this.log.debug(`+ ${page.match} -> ${page.name}`);
682
- await this.serverRouterProvider.route({
891
+ this.serverRouterProvider.createRoute({
683
892
  ...page,
684
893
  schema: void 0,
685
894
  method: "GET",
@@ -694,7 +903,7 @@ var ReactServerProvider = class {
694
903
  return "";
695
904
  }
696
905
  async configureStaticServer(root) {
697
- await this.serverStaticProvider.serve({
906
+ await this.serverStaticProvider.createStaticServer({
698
907
  root,
699
908
  path: this.env.REACT_SERVER_PREFIX
700
909
  });
@@ -748,7 +957,7 @@ var ReactServerProvider = class {
748
957
  onError: () => null
749
958
  };
750
959
  if (this.alepha.has(ServerLinksProvider)) {
751
- const srv = this.alepha.get(ServerLinksProvider);
960
+ const srv = this.alepha.inject(ServerLinksProvider);
752
961
  const schema = apiLinksResponseSchema;
753
962
  context.links = this.alepha.parse(schema, await srv.getLinks({
754
963
  user: serverRequest.user,
@@ -838,214 +1047,6 @@ var ReactServerProvider = class {
838
1047
  }
839
1048
  };
840
1049
 
841
- //#endregion
842
- //#region src/providers/BrowserRouterProvider.ts
843
- var BrowserRouterProvider = class extends RouterProvider {
844
- log = $logger();
845
- alepha = $inject(Alepha);
846
- pageDescriptorProvider = $inject(PageDescriptorProvider);
847
- add(entry) {
848
- this.pageDescriptorProvider.add(entry);
849
- }
850
- configure = $hook({
851
- on: "configure",
852
- handler: async () => {
853
- for (const page of this.pageDescriptorProvider.getPages()) if (page.component || page.lazy) this.push({
854
- path: page.match,
855
- page
856
- });
857
- }
858
- });
859
- async transition(url, options = {}) {
860
- const { pathname, search } = url;
861
- const state = {
862
- pathname,
863
- search,
864
- layers: []
865
- };
866
- const context = {
867
- url,
868
- query: {},
869
- params: {},
870
- onError: () => null,
871
- ...options.context ?? {}
872
- };
873
- await this.alepha.emit("react:transition:begin", {
874
- state,
875
- context
876
- });
877
- try {
878
- const previous = options.previous;
879
- const { route, params } = this.match(pathname);
880
- const query = {};
881
- if (search) for (const [key, value] of new URLSearchParams(search).entries()) query[key] = String(value);
882
- context.query = query;
883
- context.params = params ?? {};
884
- context.previous = previous;
885
- if (isPageRoute(route)) {
886
- const result = await this.pageDescriptorProvider.createLayers(route.page, context);
887
- if (result.redirect) return {
888
- redirect: result.redirect,
889
- state,
890
- context
891
- };
892
- state.layers = result.layers;
893
- }
894
- if (state.layers.length === 0) state.layers.push({
895
- name: "not-found",
896
- element: createElement(NotFoundPage),
897
- index: 0,
898
- path: "/"
899
- });
900
- await this.alepha.emit("react:transition:success", {
901
- state,
902
- context
903
- });
904
- } catch (e) {
905
- this.log.error(e);
906
- state.layers = [{
907
- name: "error",
908
- element: this.pageDescriptorProvider.renderError(e),
909
- index: 0,
910
- path: "/"
911
- }];
912
- await this.alepha.emit("react:transition:error", {
913
- error: e,
914
- state,
915
- context
916
- });
917
- }
918
- if (options.state) {
919
- options.state.layers = state.layers;
920
- options.state.pathname = state.pathname;
921
- options.state.search = state.search;
922
- }
923
- await this.alepha.emit("react:transition:end", {
924
- state: options.state,
925
- context
926
- });
927
- return {
928
- context,
929
- state
930
- };
931
- }
932
- root(state, context) {
933
- return this.pageDescriptorProvider.root(state, context);
934
- }
935
- };
936
-
937
- //#endregion
938
- //#region src/providers/ReactBrowserProvider.ts
939
- var ReactBrowserProvider = class {
940
- log = $logger();
941
- client = $inject(LinkProvider);
942
- alepha = $inject(Alepha);
943
- router = $inject(BrowserRouterProvider);
944
- root;
945
- transitioning;
946
- state = {
947
- layers: [],
948
- pathname: "",
949
- search: ""
950
- };
951
- get document() {
952
- return window.document;
953
- }
954
- get history() {
955
- return window.history;
956
- }
957
- get location() {
958
- return window.location;
959
- }
960
- get url() {
961
- let url = this.location.pathname + this.location.search;
962
- if (import.meta?.env?.BASE_URL) {
963
- url = url.replace(import.meta.env?.BASE_URL, "");
964
- if (!url.startsWith("/")) url = `/${url}`;
965
- }
966
- return url;
967
- }
968
- pushState(url, replace) {
969
- let path = url;
970
- if (import.meta?.env?.BASE_URL) path = (import.meta.env?.BASE_URL + path).replaceAll("//", "/");
971
- if (replace) this.history.replaceState({}, "", path);
972
- else this.history.pushState({}, "", path);
973
- }
974
- async invalidate(props) {
975
- const previous = [];
976
- if (props) {
977
- const [key] = Object.keys(props);
978
- const value = props[key];
979
- for (const layer of this.state.layers) {
980
- if (layer.props?.[key]) {
981
- previous.push({
982
- ...layer,
983
- props: {
984
- ...layer.props,
985
- [key]: value
986
- }
987
- });
988
- break;
989
- }
990
- previous.push(layer);
991
- }
992
- }
993
- await this.render({ previous });
994
- }
995
- async go(url, options = {}) {
996
- const result = await this.render({ url });
997
- if (result.context.url.pathname !== url) {
998
- this.pushState(result.context.url.pathname);
999
- return;
1000
- }
1001
- if (options.replace) {
1002
- this.pushState(url);
1003
- return;
1004
- }
1005
- this.pushState(url);
1006
- }
1007
- async render(options = {}) {
1008
- const previous = options.previous ?? this.state.layers;
1009
- const url = options.url ?? this.url;
1010
- this.transitioning = { to: url };
1011
- const result = await this.router.transition(new URL(`http://localhost${url}`), {
1012
- previous,
1013
- state: this.state
1014
- });
1015
- if (result.redirect) return await this.render({ url: result.redirect });
1016
- this.transitioning = void 0;
1017
- return result;
1018
- }
1019
- /**
1020
- * Get embedded layers from the server.
1021
- */
1022
- getHydrationState() {
1023
- try {
1024
- if ("__ssr" in window && typeof window.__ssr === "object") return window.__ssr;
1025
- } catch (error) {
1026
- console.error(error);
1027
- }
1028
- }
1029
- ready = $hook({
1030
- on: "ready",
1031
- handler: async () => {
1032
- const hydration = this.getHydrationState();
1033
- const previous = hydration?.layers ?? [];
1034
- if (hydration?.links) for (const link of hydration.links.links) this.client.pushLink(link);
1035
- const { context } = await this.render({ previous });
1036
- await this.alepha.emit("react:browser:render", {
1037
- state: this.state,
1038
- context,
1039
- hydration
1040
- });
1041
- window.addEventListener("popstate", () => {
1042
- if (this.state.pathname === location.pathname) return;
1043
- this.render();
1044
- });
1045
- }
1046
- });
1047
- };
1048
-
1049
1050
  //#endregion
1050
1051
  //#region src/hooks/RouterHookApi.ts
1051
1052
  var RouterHookApi = class {
@@ -1139,9 +1140,9 @@ const useRouter = () => {
1139
1140
  const layer = useContext(RouterLayerContext);
1140
1141
  if (!ctx || !layer) throw new Error("useRouter must be used within a RouterProvider");
1141
1142
  const pages = useMemo(() => {
1142
- return ctx.alepha.get(PageDescriptorProvider).getPages();
1143
+ return ctx.alepha.inject(PageDescriptorProvider).getPages();
1143
1144
  }, []);
1144
- return useMemo(() => new RouterHookApi(pages, ctx.context, ctx.state, layer, ctx.alepha.isBrowser() ? ctx.alepha.get(ReactBrowserProvider) : void 0), [layer]);
1145
+ return useMemo(() => new RouterHookApi(pages, ctx.context, ctx.state, layer, ctx.alepha.isBrowser() ? ctx.alepha.inject(ReactBrowserProvider) : void 0), [layer]);
1145
1146
  };
1146
1147
 
1147
1148
  //#endregion
@@ -1149,11 +1150,11 @@ const useRouter = () => {
1149
1150
  const Link = (props) => {
1150
1151
  React.useContext(RouterContext);
1151
1152
  const router = useRouter();
1152
- const to = typeof props.to === "string" ? props.to : props.to[OPTIONS].path;
1153
+ const to = typeof props.to === "string" ? props.to : props.to.options.path;
1153
1154
  if (!to) return null;
1154
- const can = typeof props.to === "string" ? void 0 : props.to[OPTIONS].can;
1155
+ const can = typeof props.to === "string" ? void 0 : props.to.options.can;
1155
1156
  if (can && !can()) return null;
1156
- const name = typeof props.to === "string" ? void 0 : props.to[OPTIONS].name;
1157
+ const name = typeof props.to === "string" ? void 0 : props.to.options.name;
1157
1158
  const anchorProps = {
1158
1159
  ...props,
1159
1160
  to: void 0
@@ -1205,7 +1206,7 @@ const useActive = (path) => {
1205
1206
  const useInject = (clazz) => {
1206
1207
  const ctx = useContext(RouterContext);
1207
1208
  if (!ctx) throw new Error("useRouter must be used within a <RouterProvider>");
1208
- return useMemo(() => ctx.alepha.get(clazz), []);
1209
+ return useMemo(() => ctx.alepha.inject(clazz), []);
1209
1210
  };
1210
1211
 
1211
1212
  //#endregion
@@ -1267,136 +1268,20 @@ const useRouterState = () => {
1267
1268
  * It delivers seamless server-side rendering, automatic code splitting, and client-side navigation with full
1268
1269
  * type safety and schema validation for route parameters and data.
1269
1270
  *
1270
- * **Key Features:**
1271
- * - Declarative page definition with `$page` descriptor
1272
- * - Server-side rendering (SSR) with automatic hydration
1273
- * - Type-safe routing with parameter validation
1274
- * - Schema-based data resolution and validation
1275
- * - SEO-friendly meta tag management
1276
- * - Automatic code splitting and lazy loading
1277
- * - Client-side navigation with browser history
1278
- *
1279
- * **Basic Usage:**
1280
- * ```ts
1281
- * import { Alepha, run, t } from "alepha";
1282
- * import { AlephaReact, $page } from "alepha/react";
1283
- *
1284
- * class AppRoutes {
1285
- * // Home page
1286
- * home = $page({
1287
- * path: "/",
1288
- * component: () => (
1289
- * <div>
1290
- * <h1>Welcome to Alepha</h1>
1291
- * <p>Build amazing React applications!</p>
1292
- * </div>
1293
- * ),
1294
- * });
1295
- *
1296
- * // About page with meta tags
1297
- * about = $page({
1298
- * path: "/about",
1299
- * head: {
1300
- * title: "About Us",
1301
- * description: "Learn more about our mission",
1302
- * },
1303
- * component: () => (
1304
- * <div>
1305
- * <h1>About Us</h1>
1306
- * <p>Learn more about our mission.</p>
1307
- * </div>
1308
- * ),
1309
- * });
1310
- * }
1311
- *
1312
- * const alepha = Alepha.create()
1313
- * .with(AlephaReact)
1314
- * .with(AppRoutes);
1315
- *
1316
- * run(alepha);
1317
- * ```
1318
- *
1319
- * **Dynamic Routes with Parameters:**
1320
- * ```tsx
1321
- * class UserRoutes {
1322
- * userProfile = $page({
1323
- * path: "/users/:id",
1324
- * schema: {
1325
- * params: t.object({
1326
- * id: t.string(),
1327
- * }),
1328
- * },
1329
- * resolve: async ({ params }) => {
1330
- * // Fetch user data server-side
1331
- * const user = await getUserById(params.id);
1332
- * return { user };
1333
- * },
1334
- * head: ({ user }) => ({
1335
- * title: `${user.name} - Profile`,
1336
- * description: `View ${user.name}'s profile`,
1337
- * }),
1338
- * component: ({ user }) => (
1339
- * <div>
1340
- * <h1>{user.name}</h1>
1341
- * <p>Email: {user.email}</p>
1342
- * </div>
1343
- * ),
1344
- * });
1345
- *
1346
- * userSettings = $page({
1347
- * path: "/users/:id/settings",
1348
- * schema: {
1349
- * params: t.object({
1350
- * id: t.string(),
1351
- * }),
1352
- * },
1353
- * component: ({ params }) => (
1354
- * <UserSettings userId={params.id} />
1355
- * ),
1356
- * });
1357
- * }
1358
- * ```
1359
- *
1360
- * **Static Generation:**
1361
- * ```tsx
1362
- * class BlogRoutes {
1363
- * blogPost = $page({
1364
- * path: "/blog/:slug",
1365
- * schema: {
1366
- * params: t.object({
1367
- * slug: t.string(),
1368
- * }),
1369
- * },
1370
- * static: {
1371
- * entries: [
1372
- * { params: { slug: "getting-started" } },
1373
- * { params: { slug: "advanced-features" } },
1374
- * { params: { slug: "deployment" } },
1375
- * ],
1376
- * },
1377
- * resolve: ({ params }) => {
1378
- * const post = getBlogPost(params.slug);
1379
- * return { post };
1380
- * },
1381
- * component: ({ post }) => (
1382
- * <article>
1383
- * <h1>{post.title}</h1>
1384
- * <div>{post.content}</div>
1385
- * </article>
1386
- * ),
1387
- * });
1388
- * }
1389
- * ```
1390
- *
1391
1271
  * @see {@link $page}
1392
1272
  * @module alepha.react
1393
1273
  */
1394
- var AlephaReact = class {
1395
- name = "alepha.react";
1396
- $services = (alepha) => alepha.with(AlephaServer).with(AlephaServerCache).with(AlephaServerLinks).with(ReactServerProvider).with(PageDescriptorProvider);
1397
- };
1398
- __bind($page, AlephaReact);
1274
+ const AlephaReact = $module({
1275
+ name: "alepha.react",
1276
+ descriptors: [$page],
1277
+ services: [
1278
+ ReactServerProvider,
1279
+ PageDescriptorProvider,
1280
+ ReactBrowserProvider
1281
+ ],
1282
+ register: (alepha) => alepha.with(AlephaServer).with(AlephaServerCache).with(AlephaServerLinks).with(ReactServerProvider).with(PageDescriptorProvider)
1283
+ });
1399
1284
 
1400
1285
  //#endregion
1401
- export { $page, AlephaReact, ClientOnly_default as ClientOnly, ErrorBoundary_default as ErrorBoundary, Link_default as Link, NestedView_default as NestedView, NotFoundPage as NotFound, PageDescriptorProvider, ReactBrowserProvider, ReactServerProvider, RedirectionError, RouterContext, RouterHookApi, RouterLayerContext, isPageRoute, useActive, useAlepha, useClient, useInject, useQueryParams, useRouter, useRouterEvents, useRouterState };
1286
+ export { $page, AlephaReact, ClientOnly_default as ClientOnly, ErrorBoundary_default as ErrorBoundary, Link_default as Link, NestedView_default as NestedView, NotFoundPage as NotFound, PageDescriptor, PageDescriptorProvider, ReactBrowserProvider, ReactServerProvider, RedirectionError, RouterContext, RouterHookApi, RouterLayerContext, isPageRoute, useActive, useAlepha, useClient, useInject, useQueryParams, useRouter, useRouterEvents, useRouterState };
1402
1287
  //# sourceMappingURL=index.js.map