@alepha/react 0.9.1 → 0.9.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.
package/README.md CHANGED
@@ -15,6 +15,7 @@ Alternatively, you can install it individually:
15
15
  ```bash
16
16
  npm install @alepha/core @alepha/react
17
17
  ```
18
+
18
19
  ## Module
19
20
 
20
21
  Provides full-stack React development with declarative routing, server-side rendering, and client-side hydration.
@@ -30,3 +31,9 @@ type safety and schema validation for route parameters and data.
30
31
  #### $page()
31
32
 
32
33
  Main descriptor for defining a React route in the application.
34
+
35
+ ### Hooks
36
+
37
+ #### useStore()
38
+
39
+ Hook to access and mutate the Alepha state.
@@ -1,5 +1,5 @@
1
1
  import { $env, $hook, $inject, $logger, $module, Alepha, Descriptor, KIND, NotImplementedError, createDescriptor, t } from "@alepha/core";
2
- import { AlephaServer } from "@alepha/server";
2
+ import { AlephaServer, HttpClient } from "@alepha/server";
3
3
  import { AlephaServerLinks, LinkProvider } from "@alepha/server-links";
4
4
  import { RouterProvider } from "@alepha/router";
5
5
  import React, { StrictMode, createContext, createElement, useContext, useEffect, useMemo, useState } from "react";
@@ -227,20 +227,31 @@ const RouterContext = createContext(void 0);
227
227
  //#region src/contexts/RouterLayerContext.ts
228
228
  const RouterLayerContext = createContext(void 0);
229
229
 
230
+ //#endregion
231
+ //#region src/contexts/AlephaContext.ts
232
+ const AlephaContext = createContext(void 0);
233
+
234
+ //#endregion
235
+ //#region src/hooks/useAlepha.ts
236
+ const useAlepha = () => {
237
+ const alepha = useContext(AlephaContext);
238
+ if (!alepha) throw new Error("useAlepha must be used within an AlephaContext.Provider");
239
+ return alepha;
240
+ };
241
+
230
242
  //#endregion
231
243
  //#region src/hooks/useRouterEvents.ts
232
244
  const useRouterEvents = (opts = {}, deps = []) => {
233
- const ctx = useContext(RouterContext);
234
- if (!ctx) throw new Error("useRouter must be used within a RouterProvider");
245
+ const alepha = useAlepha();
235
246
  useEffect(() => {
236
- if (!ctx.alepha.isBrowser()) return;
247
+ if (!alepha.isBrowser()) return;
237
248
  const subs = [];
238
249
  const onBegin = opts.onBegin;
239
250
  const onEnd = opts.onEnd;
240
251
  const onError = opts.onError;
241
- if (onBegin) subs.push(ctx.alepha.on("react:transition:begin", { callback: onBegin }));
242
- if (onEnd) subs.push(ctx.alepha.on("react:transition:end", { callback: onEnd }));
243
- if (onError) subs.push(ctx.alepha.on("react:transition:error", { callback: onError }));
252
+ if (onBegin) subs.push(alepha.on("react:transition:begin", { callback: onBegin }));
253
+ if (onEnd) subs.push(alepha.on("react:transition:end", { callback: onEnd }));
254
+ if (onError) subs.push(alepha.on("react:transition:error", { callback: onError }));
244
255
  return () => {
245
256
  for (const sub of subs) sub();
246
257
  };
@@ -356,11 +367,10 @@ var PageDescriptorProvider = class {
356
367
  return new URL(url.replace(/\/\/+/g, "/") || "/", options.base ?? `http://localhost`);
357
368
  }
358
369
  root(state, context) {
359
- const root = createElement(RouterContext.Provider, { value: {
360
- alepha: this.alepha,
370
+ const root = createElement(AlephaContext.Provider, { value: this.alepha }, createElement(RouterContext.Provider, { value: {
361
371
  state,
362
372
  context
363
- } }, createElement(NestedView_default, {}, state.layers[0]?.element));
373
+ } }, createElement(NestedView_default, {}, state.layers[0]?.element)));
364
374
  if (this.env.REACT_STRICT_MODE) return createElement(StrictMode, {}, root);
365
375
  return root;
366
376
  }
@@ -943,13 +953,14 @@ var RouterHookApi = class {
943
953
  //#endregion
944
954
  //#region src/hooks/useRouter.ts
945
955
  const useRouter = () => {
956
+ const alepha = useAlepha();
946
957
  const ctx = useContext(RouterContext);
947
958
  const layer = useContext(RouterLayerContext);
948
959
  if (!ctx || !layer) throw new Error("useRouter must be used within a RouterProvider");
949
960
  const pages = useMemo(() => {
950
- return ctx.alepha.inject(PageDescriptorProvider).getPages();
961
+ return alepha.inject(PageDescriptorProvider).getPages();
951
962
  }, []);
952
- return useMemo(() => new RouterHookApi(pages, ctx.context, ctx.state, layer, ctx.alepha.isBrowser() ? ctx.alepha.inject(ReactBrowserProvider) : void 0), [layer]);
963
+ return useMemo(() => new RouterHookApi(pages, ctx.context, ctx.state, layer, alepha.isBrowser() ? alepha.inject(ReactBrowserProvider) : void 0), [layer]);
953
964
  };
954
965
 
955
966
  //#endregion
@@ -1008,20 +1019,11 @@ const useActive = (path) => {
1008
1019
  };
1009
1020
  };
1010
1021
 
1011
- //#endregion
1012
- //#region src/hooks/useAlepha.ts
1013
- const useAlepha = () => {
1014
- const routerContext = useContext(RouterContext);
1015
- if (!routerContext) throw new Error("useAlepha must be used within a RouterProvider");
1016
- return routerContext.alepha;
1017
- };
1018
-
1019
1022
  //#endregion
1020
1023
  //#region src/hooks/useInject.ts
1021
- const useInject = (clazz) => {
1022
- const ctx = useContext(RouterContext);
1023
- if (!ctx) throw new Error("useRouter must be used within a <RouterProvider>");
1024
- return useMemo(() => ctx.alepha.inject(clazz), []);
1024
+ const useInject = (service) => {
1025
+ const alepha = useAlepha();
1026
+ return useMemo(() => alepha.inject(service), []);
1025
1027
  };
1026
1028
 
1027
1029
  //#endregion
@@ -1033,21 +1035,20 @@ const useClient = (_scope) => {
1033
1035
  //#endregion
1034
1036
  //#region src/hooks/useQueryParams.ts
1035
1037
  const useQueryParams = (schema, options = {}) => {
1036
- const ctx = useContext(RouterContext);
1037
- if (!ctx) throw new Error("useQueryParams must be used within a RouterProvider");
1038
+ const alepha = useAlepha();
1038
1039
  const key = options.key ?? "q";
1039
1040
  const router = useRouter();
1040
1041
  const querystring = router.query[key];
1041
- const [queryParams, setQueryParams] = useState(decode(ctx.alepha, schema, router.query[key]));
1042
+ const [queryParams, setQueryParams] = useState(decode(alepha, schema, router.query[key]));
1042
1043
  useEffect(() => {
1043
- setQueryParams(decode(ctx.alepha, schema, querystring));
1044
+ setQueryParams(decode(alepha, schema, querystring));
1044
1045
  }, [querystring]);
1045
1046
  return [queryParams, (queryParams$1) => {
1046
1047
  setQueryParams(queryParams$1);
1047
1048
  router.setQueryParams((data) => {
1048
1049
  return {
1049
1050
  ...data,
1050
- [key]: encode(ctx.alepha, schema, queryParams$1)
1051
+ [key]: encode(alepha, schema, queryParams$1)
1051
1052
  };
1052
1053
  });
1053
1054
  }];
@@ -1066,14 +1067,73 @@ const decode = (alepha, schema, data) => {
1066
1067
  //#endregion
1067
1068
  //#region src/hooks/useRouterState.ts
1068
1069
  const useRouterState = () => {
1069
- const ctx = useContext(RouterContext);
1070
+ const router = useContext(RouterContext);
1070
1071
  const layer = useContext(RouterLayerContext);
1071
- if (!ctx || !layer) throw new Error("useRouter must be used within a RouterProvider");
1072
- const [state, setState] = useState(ctx.state);
1072
+ if (!router || !layer) throw new Error("useRouterState must be used within a RouterContext.Provider");
1073
+ const [state, setState] = useState(router.state);
1073
1074
  useRouterEvents({ onEnd: ({ state: state$1 }) => setState({ ...state$1 }) });
1074
1075
  return state;
1075
1076
  };
1076
1077
 
1078
+ //#endregion
1079
+ //#region src/hooks/useSchema.ts
1080
+ const useSchema = (action) => {
1081
+ const name = action.name;
1082
+ const alepha = useAlepha();
1083
+ const httpClient = useInject(HttpClient);
1084
+ const linkProvider = useInject(LinkProvider);
1085
+ const [schema, setSchema] = useState(ssrSchemaLoading(alepha, name));
1086
+ useEffect(() => {
1087
+ if (!schema.loading) return;
1088
+ const opts = { cache: true };
1089
+ httpClient.fetch(`${linkProvider.URL_LINKS}/${name}/schema`, {}, opts).then((it) => setSchema(it.data));
1090
+ }, [name]);
1091
+ return schema;
1092
+ };
1093
+ /**
1094
+ * Get an action schema during server-side rendering (SSR) or client-side rendering (CSR).
1095
+ */
1096
+ const ssrSchemaLoading = (alepha, name) => {
1097
+ if (!alepha.isBrowser()) {
1098
+ const links = alepha.context.get("links")?.links ?? [];
1099
+ const can = links.find((it) => it.name === name);
1100
+ if (can) {
1101
+ const schema$1 = alepha.inject(LinkProvider).links?.find((it) => it.name === name)?.schema;
1102
+ if (schema$1) {
1103
+ can.schema = schema$1;
1104
+ return schema$1;
1105
+ }
1106
+ }
1107
+ return { loading: true };
1108
+ }
1109
+ const schema = alepha.inject(LinkProvider).links?.find((it) => it.name === name)?.schema;
1110
+ if (schema) return schema;
1111
+ return { loading: true };
1112
+ };
1113
+
1114
+ //#endregion
1115
+ //#region src/hooks/useStore.ts
1116
+ /**
1117
+ * Hook to access and mutate the Alepha state.
1118
+ */
1119
+ const useStore = (key) => {
1120
+ const alepha = useAlepha();
1121
+ const [state, setState] = useState(alepha.state(key));
1122
+ useEffect(() => {
1123
+ if (!alepha.isBrowser()) return;
1124
+ return alepha.on("state:mutate", (ev) => {
1125
+ if (ev.key === key) setState(ev.value);
1126
+ });
1127
+ }, []);
1128
+ if (!alepha.isBrowser()) {
1129
+ const value = alepha.context.get(key);
1130
+ if (value !== null) return [value, (_) => {}];
1131
+ }
1132
+ return [state, (value) => {
1133
+ alepha.state(key, value);
1134
+ }];
1135
+ };
1136
+
1077
1137
  //#endregion
1078
1138
  //#region src/index.browser.ts
1079
1139
  const AlephaReact = $module({
@@ -1089,5 +1149,5 @@ const AlephaReact = $module({
1089
1149
  });
1090
1150
 
1091
1151
  //#endregion
1092
- export { $page, AlephaReact, BrowserRouterProvider, ClientOnly_default as ClientOnly, ErrorBoundary_default as ErrorBoundary, Link_default as Link, NestedView_default as NestedView, NotFoundPage as NotFound, PageDescriptor, PageDescriptorProvider, ReactBrowserProvider, RedirectionError, RouterContext, RouterHookApi, RouterLayerContext, isPageRoute, useActive, useAlepha, useClient, useInject, useQueryParams, useRouter, useRouterEvents, useRouterState };
1152
+ export { $page, AlephaContext, AlephaReact, BrowserRouterProvider, ClientOnly_default as ClientOnly, ErrorBoundary_default as ErrorBoundary, Link_default as Link, NestedView_default as NestedView, NotFoundPage as NotFound, PageDescriptor, PageDescriptorProvider, ReactBrowserProvider, RedirectionError, RouterContext, RouterHookApi, RouterLayerContext, isPageRoute, ssrSchemaLoading, useActive, useAlepha, useClient, useInject, useQueryParams, useRouter, useRouterEvents, useRouterState, useSchema, useStore };
1093
1153
  //# sourceMappingURL=index.browser.js.map