@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/dist/index.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, ServerRouterProvider, ServerTimingProvider, apiLinksResponseSchema } from "@alepha/server";
2
+ import { AlephaServer, HttpClient, 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";
@@ -51,23 +51,11 @@ const ClientOnly = (props) => {
51
51
  };
52
52
  var ClientOnly_default = ClientOnly;
53
53
 
54
- //#endregion
55
- //#region src/contexts/RouterContext.ts
56
- const RouterContext = createContext(void 0);
57
-
58
- //#endregion
59
- //#region src/hooks/useAlepha.ts
60
- const useAlepha = () => {
61
- const routerContext = useContext(RouterContext);
62
- if (!routerContext) throw new Error("useAlepha must be used within a RouterProvider");
63
- return routerContext.alepha;
64
- };
65
-
66
54
  //#endregion
67
55
  //#region src/components/ErrorViewer.tsx
68
- const ErrorViewer = ({ error }) => {
56
+ const ErrorViewer = ({ error, alepha }) => {
69
57
  const [expanded, setExpanded] = useState(false);
70
- const isProduction = useAlepha().isProduction();
58
+ const isProduction = alepha.isProduction();
71
59
  if (isProduction) return /* @__PURE__ */ jsx(ErrorViewerProduction, {});
72
60
  const stackLines = error.stack?.split("\n") ?? [];
73
61
  const previewLines = stackLines.slice(0, 5);
@@ -211,24 +199,39 @@ const ErrorViewerProduction = () => {
211
199
  });
212
200
  };
213
201
 
202
+ //#endregion
203
+ //#region src/contexts/RouterContext.ts
204
+ const RouterContext = createContext(void 0);
205
+
214
206
  //#endregion
215
207
  //#region src/contexts/RouterLayerContext.ts
216
208
  const RouterLayerContext = createContext(void 0);
217
209
 
210
+ //#endregion
211
+ //#region src/contexts/AlephaContext.ts
212
+ const AlephaContext = createContext(void 0);
213
+
214
+ //#endregion
215
+ //#region src/hooks/useAlepha.ts
216
+ const useAlepha = () => {
217
+ const alepha = useContext(AlephaContext);
218
+ if (!alepha) throw new Error("useAlepha must be used within an AlephaContext.Provider");
219
+ return alepha;
220
+ };
221
+
218
222
  //#endregion
219
223
  //#region src/hooks/useRouterEvents.ts
220
224
  const useRouterEvents = (opts = {}, deps = []) => {
221
- const ctx = useContext(RouterContext);
222
- if (!ctx) throw new Error("useRouter must be used within a RouterProvider");
225
+ const alepha = useAlepha();
223
226
  useEffect(() => {
224
- if (!ctx.alepha.isBrowser()) return;
227
+ if (!alepha.isBrowser()) return;
225
228
  const subs = [];
226
229
  const onBegin = opts.onBegin;
227
230
  const onEnd = opts.onEnd;
228
231
  const onError = opts.onError;
229
- if (onBegin) subs.push(ctx.alepha.on("react:transition:begin", { callback: onBegin }));
230
- if (onEnd) subs.push(ctx.alepha.on("react:transition:end", { callback: onEnd }));
231
- if (onError) subs.push(ctx.alepha.on("react:transition:error", { callback: onError }));
232
+ if (onBegin) subs.push(alepha.on("react:transition:begin", { callback: onBegin }));
233
+ if (onEnd) subs.push(alepha.on("react:transition:end", { callback: onEnd }));
234
+ if (onError) subs.push(alepha.on("react:transition:error", { callback: onError }));
232
235
  return () => {
233
236
  for (const sub of subs) sub();
234
237
  };
@@ -368,11 +371,10 @@ var PageDescriptorProvider = class {
368
371
  return new URL(url.replace(/\/\/+/g, "/") || "/", options.base ?? `http://localhost`);
369
372
  }
370
373
  root(state, context) {
371
- const root = createElement(RouterContext.Provider, { value: {
372
- alepha: this.alepha,
374
+ const root = createElement(AlephaContext.Provider, { value: this.alepha }, createElement(RouterContext.Provider, { value: {
373
375
  state,
374
376
  context
375
- } }, createElement(NestedView_default, {}, state.layers[0]?.element));
377
+ } }, createElement(NestedView_default, {}, state.layers[0]?.element)));
376
378
  if (this.env.REACT_STRICT_MODE) return createElement(StrictMode, {}, root);
377
379
  return root;
378
380
  }
@@ -518,7 +520,10 @@ var PageDescriptorProvider = class {
518
520
  return void 0;
519
521
  }
520
522
  renderError(error) {
521
- return createElement(ErrorViewer_default, { error });
523
+ return createElement(ErrorViewer_default, {
524
+ error,
525
+ alepha: this.alepha
526
+ });
522
527
  }
523
528
  renderEmptyView() {
524
529
  return createElement(NestedView_default, {});
@@ -816,7 +821,7 @@ var ReactBrowserProvider = class {
816
821
  hydration
817
822
  });
818
823
  window.addEventListener("popstate", () => {
819
- if (this.state.pathname === location.pathname) return;
824
+ if (this.state.pathname === this.url) return;
820
825
  this.render();
821
826
  });
822
827
  }
@@ -829,7 +834,8 @@ const envSchema = t.object({
829
834
  REACT_SERVER_DIST: t.string({ default: "public" }),
830
835
  REACT_SERVER_PREFIX: t.string({ default: "" }),
831
836
  REACT_SSR_ENABLED: t.optional(t.boolean()),
832
- REACT_ROOT_ID: t.string({ default: "root" })
837
+ REACT_ROOT_ID: t.string({ default: "root" }),
838
+ REACT_SERVER_TEMPLATE: t.optional(t.string({ size: "rich" }))
833
839
  });
834
840
  var ReactServerProvider = class {
835
841
  log = $logger();
@@ -882,7 +888,7 @@ var ReactServerProvider = class {
882
888
  }
883
889
  });
884
890
  get template() {
885
- return this.alepha.state("react.server.template") ?? "<!DOCTYPE html><html lang='en'><head></head><body></body></html>";
891
+ return this.alepha.env.REACT_SERVER_TEMPLATE ?? "<!DOCTYPE html><html lang='en'><head></head><body></body></html>";
886
892
  }
887
893
  async registerPages(templateLoader) {
888
894
  for (const page of this.pageDescriptorProvider.getPages()) {
@@ -1136,13 +1142,14 @@ var RouterHookApi = class {
1136
1142
  //#endregion
1137
1143
  //#region src/hooks/useRouter.ts
1138
1144
  const useRouter = () => {
1145
+ const alepha = useAlepha();
1139
1146
  const ctx = useContext(RouterContext);
1140
1147
  const layer = useContext(RouterLayerContext);
1141
1148
  if (!ctx || !layer) throw new Error("useRouter must be used within a RouterProvider");
1142
1149
  const pages = useMemo(() => {
1143
- return ctx.alepha.inject(PageDescriptorProvider).getPages();
1150
+ return alepha.inject(PageDescriptorProvider).getPages();
1144
1151
  }, []);
1145
- return useMemo(() => new RouterHookApi(pages, ctx.context, ctx.state, layer, ctx.alepha.isBrowser() ? ctx.alepha.inject(ReactBrowserProvider) : void 0), [layer]);
1152
+ return useMemo(() => new RouterHookApi(pages, ctx.context, ctx.state, layer, alepha.isBrowser() ? alepha.inject(ReactBrowserProvider) : void 0), [layer]);
1146
1153
  };
1147
1154
 
1148
1155
  //#endregion
@@ -1203,10 +1210,9 @@ const useActive = (path) => {
1203
1210
 
1204
1211
  //#endregion
1205
1212
  //#region src/hooks/useInject.ts
1206
- const useInject = (clazz) => {
1207
- const ctx = useContext(RouterContext);
1208
- if (!ctx) throw new Error("useRouter must be used within a <RouterProvider>");
1209
- return useMemo(() => ctx.alepha.inject(clazz), []);
1213
+ const useInject = (service) => {
1214
+ const alepha = useAlepha();
1215
+ return useMemo(() => alepha.inject(service), []);
1210
1216
  };
1211
1217
 
1212
1218
  //#endregion
@@ -1218,21 +1224,20 @@ const useClient = (_scope) => {
1218
1224
  //#endregion
1219
1225
  //#region src/hooks/useQueryParams.ts
1220
1226
  const useQueryParams = (schema, options = {}) => {
1221
- const ctx = useContext(RouterContext);
1222
- if (!ctx) throw new Error("useQueryParams must be used within a RouterProvider");
1227
+ const alepha = useAlepha();
1223
1228
  const key = options.key ?? "q";
1224
1229
  const router = useRouter();
1225
1230
  const querystring = router.query[key];
1226
- const [queryParams, setQueryParams] = useState(decode(ctx.alepha, schema, router.query[key]));
1231
+ const [queryParams, setQueryParams] = useState(decode(alepha, schema, router.query[key]));
1227
1232
  useEffect(() => {
1228
- setQueryParams(decode(ctx.alepha, schema, querystring));
1233
+ setQueryParams(decode(alepha, schema, querystring));
1229
1234
  }, [querystring]);
1230
1235
  return [queryParams, (queryParams$1) => {
1231
1236
  setQueryParams(queryParams$1);
1232
1237
  router.setQueryParams((data) => {
1233
1238
  return {
1234
1239
  ...data,
1235
- [key]: encode(ctx.alepha, schema, queryParams$1)
1240
+ [key]: encode(alepha, schema, queryParams$1)
1236
1241
  };
1237
1242
  });
1238
1243
  }];
@@ -1251,14 +1256,73 @@ const decode = (alepha, schema, data) => {
1251
1256
  //#endregion
1252
1257
  //#region src/hooks/useRouterState.ts
1253
1258
  const useRouterState = () => {
1254
- const ctx = useContext(RouterContext);
1259
+ const router = useContext(RouterContext);
1255
1260
  const layer = useContext(RouterLayerContext);
1256
- if (!ctx || !layer) throw new Error("useRouter must be used within a RouterProvider");
1257
- const [state, setState] = useState(ctx.state);
1261
+ if (!router || !layer) throw new Error("useRouterState must be used within a RouterContext.Provider");
1262
+ const [state, setState] = useState(router.state);
1258
1263
  useRouterEvents({ onEnd: ({ state: state$1 }) => setState({ ...state$1 }) });
1259
1264
  return state;
1260
1265
  };
1261
1266
 
1267
+ //#endregion
1268
+ //#region src/hooks/useSchema.ts
1269
+ const useSchema = (action) => {
1270
+ const name = action.name;
1271
+ const alepha = useAlepha();
1272
+ const httpClient = useInject(HttpClient);
1273
+ const linkProvider = useInject(LinkProvider);
1274
+ const [schema, setSchema] = useState(ssrSchemaLoading(alepha, name));
1275
+ useEffect(() => {
1276
+ if (!schema.loading) return;
1277
+ const opts = { cache: true };
1278
+ httpClient.fetch(`${linkProvider.URL_LINKS}/${name}/schema`, {}, opts).then((it) => setSchema(it.data));
1279
+ }, [name]);
1280
+ return schema;
1281
+ };
1282
+ /**
1283
+ * Get an action schema during server-side rendering (SSR) or client-side rendering (CSR).
1284
+ */
1285
+ const ssrSchemaLoading = (alepha, name) => {
1286
+ if (!alepha.isBrowser()) {
1287
+ const links = alepha.context.get("links")?.links ?? [];
1288
+ const can = links.find((it) => it.name === name);
1289
+ if (can) {
1290
+ const schema$1 = alepha.inject(LinkProvider).links?.find((it) => it.name === name)?.schema;
1291
+ if (schema$1) {
1292
+ can.schema = schema$1;
1293
+ return schema$1;
1294
+ }
1295
+ }
1296
+ return { loading: true };
1297
+ }
1298
+ const schema = alepha.inject(LinkProvider).links?.find((it) => it.name === name)?.schema;
1299
+ if (schema) return schema;
1300
+ return { loading: true };
1301
+ };
1302
+
1303
+ //#endregion
1304
+ //#region src/hooks/useStore.ts
1305
+ /**
1306
+ * Hook to access and mutate the Alepha state.
1307
+ */
1308
+ const useStore = (key) => {
1309
+ const alepha = useAlepha();
1310
+ const [state, setState] = useState(alepha.state(key));
1311
+ useEffect(() => {
1312
+ if (!alepha.isBrowser()) return;
1313
+ return alepha.on("state:mutate", (ev) => {
1314
+ if (ev.key === key) setState(ev.value);
1315
+ });
1316
+ }, []);
1317
+ if (!alepha.isBrowser()) {
1318
+ const value = alepha.context.get(key);
1319
+ if (value !== null) return [value, (_) => {}];
1320
+ }
1321
+ return [state, (value) => {
1322
+ alepha.state(key, value);
1323
+ }];
1324
+ };
1325
+
1262
1326
  //#endregion
1263
1327
  //#region src/index.ts
1264
1328
  /**
@@ -1283,5 +1347,5 @@ const AlephaReact = $module({
1283
1347
  });
1284
1348
 
1285
1349
  //#endregion
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 };
1350
+ export { $page, AlephaContext, 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, ssrSchemaLoading, useActive, useAlepha, useClient, useInject, useQueryParams, useRouter, useRouterEvents, useRouterState, useSchema, useStore };
1287
1351
  //# sourceMappingURL=index.js.map