@alepha/react 0.9.0 → 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 +7 -0
- package/dist/index.browser.js +104 -41
- package/dist/index.browser.js.map +1 -1
- package/dist/index.cjs +109 -41
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +55 -31
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +56 -32
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +107 -43
- package/dist/index.js.map +1 -1
- package/package.json +7 -7
- package/src/components/ErrorViewer.tsx +4 -3
- package/src/contexts/AlephaContext.ts +4 -0
- package/src/contexts/RouterContext.ts +0 -2
- package/src/hooks/useAlepha.ts +5 -5
- package/src/hooks/useInject.ts +5 -8
- package/src/hooks/useQueryParams.ts +6 -9
- package/src/hooks/useRouter.ts +4 -4
- package/src/hooks/useRouterEvents.ts +7 -10
- package/src/hooks/useRouterState.ts +6 -4
- package/src/hooks/useSchema.ts +93 -0
- package/src/hooks/useStore.ts +39 -0
- package/src/index.shared.ts +3 -0
- package/src/providers/PageDescriptorProvider.ts +13 -9
- package/src/providers/ReactBrowserProvider.ts +1 -1
- package/src/providers/ReactServerProvider.ts +6 -2
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.
|
package/dist/index.browser.js
CHANGED
|
@@ -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";
|
|
@@ -71,23 +71,11 @@ const ClientOnly = (props) => {
|
|
|
71
71
|
};
|
|
72
72
|
var ClientOnly_default = ClientOnly;
|
|
73
73
|
|
|
74
|
-
//#endregion
|
|
75
|
-
//#region src/contexts/RouterContext.ts
|
|
76
|
-
const RouterContext = createContext(void 0);
|
|
77
|
-
|
|
78
|
-
//#endregion
|
|
79
|
-
//#region src/hooks/useAlepha.ts
|
|
80
|
-
const useAlepha = () => {
|
|
81
|
-
const routerContext = useContext(RouterContext);
|
|
82
|
-
if (!routerContext) throw new Error("useAlepha must be used within a RouterProvider");
|
|
83
|
-
return routerContext.alepha;
|
|
84
|
-
};
|
|
85
|
-
|
|
86
74
|
//#endregion
|
|
87
75
|
//#region src/components/ErrorViewer.tsx
|
|
88
|
-
const ErrorViewer = ({ error }) => {
|
|
76
|
+
const ErrorViewer = ({ error, alepha }) => {
|
|
89
77
|
const [expanded, setExpanded] = useState(false);
|
|
90
|
-
const isProduction =
|
|
78
|
+
const isProduction = alepha.isProduction();
|
|
91
79
|
if (isProduction) return /* @__PURE__ */ jsx(ErrorViewerProduction, {});
|
|
92
80
|
const stackLines = error.stack?.split("\n") ?? [];
|
|
93
81
|
const previewLines = stackLines.slice(0, 5);
|
|
@@ -231,24 +219,39 @@ const ErrorViewerProduction = () => {
|
|
|
231
219
|
});
|
|
232
220
|
};
|
|
233
221
|
|
|
222
|
+
//#endregion
|
|
223
|
+
//#region src/contexts/RouterContext.ts
|
|
224
|
+
const RouterContext = createContext(void 0);
|
|
225
|
+
|
|
234
226
|
//#endregion
|
|
235
227
|
//#region src/contexts/RouterLayerContext.ts
|
|
236
228
|
const RouterLayerContext = createContext(void 0);
|
|
237
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
|
+
|
|
238
242
|
//#endregion
|
|
239
243
|
//#region src/hooks/useRouterEvents.ts
|
|
240
244
|
const useRouterEvents = (opts = {}, deps = []) => {
|
|
241
|
-
const
|
|
242
|
-
if (!ctx) throw new Error("useRouter must be used within a RouterProvider");
|
|
245
|
+
const alepha = useAlepha();
|
|
243
246
|
useEffect(() => {
|
|
244
|
-
if (!
|
|
247
|
+
if (!alepha.isBrowser()) return;
|
|
245
248
|
const subs = [];
|
|
246
249
|
const onBegin = opts.onBegin;
|
|
247
250
|
const onEnd = opts.onEnd;
|
|
248
251
|
const onError = opts.onError;
|
|
249
|
-
if (onBegin) subs.push(
|
|
250
|
-
if (onEnd) subs.push(
|
|
251
|
-
if (onError) subs.push(
|
|
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 }));
|
|
252
255
|
return () => {
|
|
253
256
|
for (const sub of subs) sub();
|
|
254
257
|
};
|
|
@@ -364,11 +367,10 @@ var PageDescriptorProvider = class {
|
|
|
364
367
|
return new URL(url.replace(/\/\/+/g, "/") || "/", options.base ?? `http://localhost`);
|
|
365
368
|
}
|
|
366
369
|
root(state, context) {
|
|
367
|
-
const root = createElement(RouterContext.Provider, { value: {
|
|
368
|
-
alepha: this.alepha,
|
|
370
|
+
const root = createElement(AlephaContext.Provider, { value: this.alepha }, createElement(RouterContext.Provider, { value: {
|
|
369
371
|
state,
|
|
370
372
|
context
|
|
371
|
-
} }, createElement(NestedView_default, {}, state.layers[0]?.element));
|
|
373
|
+
} }, createElement(NestedView_default, {}, state.layers[0]?.element)));
|
|
372
374
|
if (this.env.REACT_STRICT_MODE) return createElement(StrictMode, {}, root);
|
|
373
375
|
return root;
|
|
374
376
|
}
|
|
@@ -514,7 +516,10 @@ var PageDescriptorProvider = class {
|
|
|
514
516
|
return void 0;
|
|
515
517
|
}
|
|
516
518
|
renderError(error) {
|
|
517
|
-
return createElement(ErrorViewer_default, {
|
|
519
|
+
return createElement(ErrorViewer_default, {
|
|
520
|
+
error,
|
|
521
|
+
alepha: this.alepha
|
|
522
|
+
});
|
|
518
523
|
}
|
|
519
524
|
renderEmptyView() {
|
|
520
525
|
return createElement(NestedView_default, {});
|
|
@@ -812,7 +817,7 @@ var ReactBrowserProvider = class {
|
|
|
812
817
|
hydration
|
|
813
818
|
});
|
|
814
819
|
window.addEventListener("popstate", () => {
|
|
815
|
-
if (this.state.pathname ===
|
|
820
|
+
if (this.state.pathname === this.url) return;
|
|
816
821
|
this.render();
|
|
817
822
|
});
|
|
818
823
|
}
|
|
@@ -948,13 +953,14 @@ var RouterHookApi = class {
|
|
|
948
953
|
//#endregion
|
|
949
954
|
//#region src/hooks/useRouter.ts
|
|
950
955
|
const useRouter = () => {
|
|
956
|
+
const alepha = useAlepha();
|
|
951
957
|
const ctx = useContext(RouterContext);
|
|
952
958
|
const layer = useContext(RouterLayerContext);
|
|
953
959
|
if (!ctx || !layer) throw new Error("useRouter must be used within a RouterProvider");
|
|
954
960
|
const pages = useMemo(() => {
|
|
955
|
-
return
|
|
961
|
+
return alepha.inject(PageDescriptorProvider).getPages();
|
|
956
962
|
}, []);
|
|
957
|
-
return useMemo(() => new RouterHookApi(pages, ctx.context, ctx.state, layer,
|
|
963
|
+
return useMemo(() => new RouterHookApi(pages, ctx.context, ctx.state, layer, alepha.isBrowser() ? alepha.inject(ReactBrowserProvider) : void 0), [layer]);
|
|
958
964
|
};
|
|
959
965
|
|
|
960
966
|
//#endregion
|
|
@@ -1015,10 +1021,9 @@ const useActive = (path) => {
|
|
|
1015
1021
|
|
|
1016
1022
|
//#endregion
|
|
1017
1023
|
//#region src/hooks/useInject.ts
|
|
1018
|
-
const useInject = (
|
|
1019
|
-
const
|
|
1020
|
-
|
|
1021
|
-
return useMemo(() => ctx.alepha.inject(clazz), []);
|
|
1024
|
+
const useInject = (service) => {
|
|
1025
|
+
const alepha = useAlepha();
|
|
1026
|
+
return useMemo(() => alepha.inject(service), []);
|
|
1022
1027
|
};
|
|
1023
1028
|
|
|
1024
1029
|
//#endregion
|
|
@@ -1030,21 +1035,20 @@ const useClient = (_scope) => {
|
|
|
1030
1035
|
//#endregion
|
|
1031
1036
|
//#region src/hooks/useQueryParams.ts
|
|
1032
1037
|
const useQueryParams = (schema, options = {}) => {
|
|
1033
|
-
const
|
|
1034
|
-
if (!ctx) throw new Error("useQueryParams must be used within a RouterProvider");
|
|
1038
|
+
const alepha = useAlepha();
|
|
1035
1039
|
const key = options.key ?? "q";
|
|
1036
1040
|
const router = useRouter();
|
|
1037
1041
|
const querystring = router.query[key];
|
|
1038
|
-
const [queryParams, setQueryParams] = useState(decode(
|
|
1042
|
+
const [queryParams, setQueryParams] = useState(decode(alepha, schema, router.query[key]));
|
|
1039
1043
|
useEffect(() => {
|
|
1040
|
-
setQueryParams(decode(
|
|
1044
|
+
setQueryParams(decode(alepha, schema, querystring));
|
|
1041
1045
|
}, [querystring]);
|
|
1042
1046
|
return [queryParams, (queryParams$1) => {
|
|
1043
1047
|
setQueryParams(queryParams$1);
|
|
1044
1048
|
router.setQueryParams((data) => {
|
|
1045
1049
|
return {
|
|
1046
1050
|
...data,
|
|
1047
|
-
[key]: encode(
|
|
1051
|
+
[key]: encode(alepha, schema, queryParams$1)
|
|
1048
1052
|
};
|
|
1049
1053
|
});
|
|
1050
1054
|
}];
|
|
@@ -1063,14 +1067,73 @@ const decode = (alepha, schema, data) => {
|
|
|
1063
1067
|
//#endregion
|
|
1064
1068
|
//#region src/hooks/useRouterState.ts
|
|
1065
1069
|
const useRouterState = () => {
|
|
1066
|
-
const
|
|
1070
|
+
const router = useContext(RouterContext);
|
|
1067
1071
|
const layer = useContext(RouterLayerContext);
|
|
1068
|
-
if (!
|
|
1069
|
-
const [state, setState] = useState(
|
|
1072
|
+
if (!router || !layer) throw new Error("useRouterState must be used within a RouterContext.Provider");
|
|
1073
|
+
const [state, setState] = useState(router.state);
|
|
1070
1074
|
useRouterEvents({ onEnd: ({ state: state$1 }) => setState({ ...state$1 }) });
|
|
1071
1075
|
return state;
|
|
1072
1076
|
};
|
|
1073
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
|
+
|
|
1074
1137
|
//#endregion
|
|
1075
1138
|
//#region src/index.browser.ts
|
|
1076
1139
|
const AlephaReact = $module({
|
|
@@ -1086,5 +1149,5 @@ const AlephaReact = $module({
|
|
|
1086
1149
|
});
|
|
1087
1150
|
|
|
1088
1151
|
//#endregion
|
|
1089
|
-
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 };
|
|
1090
1153
|
//# sourceMappingURL=index.browser.js.map
|