@alepha/react 0.9.1 → 0.9.3
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 +202 -84
- package/dist/index.browser.js.map +1 -1
- package/dist/index.cjs +228 -91
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +262 -165
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +261 -164
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +226 -93
- package/dist/index.js.map +1 -1
- package/package.json +12 -12
- package/src/components/ErrorViewer.tsx +1 -1
- package/src/components/Link.tsx +4 -24
- package/src/components/NestedView.tsx +11 -2
- package/src/components/NotFound.tsx +4 -1
- package/src/contexts/AlephaContext.ts +4 -0
- package/src/contexts/RouterContext.ts +0 -2
- package/src/descriptors/$page.ts +71 -9
- package/src/errors/{RedirectionError.ts → Redirection.ts} +1 -1
- package/src/hooks/RouterHookApi.ts +35 -11
- package/src/hooks/useActive.ts +22 -15
- package/src/hooks/useAlepha.ts +5 -5
- package/src/hooks/useClient.ts +2 -0
- package/src/hooks/useInject.ts +5 -8
- package/src/hooks/useQueryParams.ts +6 -9
- package/src/hooks/useRouter.ts +6 -5
- package/src/hooks/useRouterEvents.ts +12 -12
- package/src/hooks/useRouterState.ts +6 -4
- package/src/hooks/useSchema.ts +93 -0
- package/src/hooks/useStore.ts +47 -0
- package/src/index.shared.ts +5 -2
- package/src/providers/BrowserRouterProvider.ts +9 -0
- package/src/providers/PageDescriptorProvider.ts +123 -39
- package/src/providers/ReactBrowserProvider.ts +17 -11
- package/src/providers/ReactServerProvider.ts +47 -10
package/dist/index.cjs
CHANGED
|
@@ -41,6 +41,12 @@ const $page = (options) => {
|
|
|
41
41
|
return (0, __alepha_core.createDescriptor)(PageDescriptor, options);
|
|
42
42
|
};
|
|
43
43
|
var PageDescriptor = class extends __alepha_core.Descriptor {
|
|
44
|
+
onInit() {
|
|
45
|
+
if (this.options.static) this.options.cache ??= {
|
|
46
|
+
provider: "memory",
|
|
47
|
+
ttl: [1, "week"]
|
|
48
|
+
};
|
|
49
|
+
}
|
|
44
50
|
get name() {
|
|
45
51
|
return this.options.name ?? this.config.propertyKey;
|
|
46
52
|
}
|
|
@@ -49,7 +55,7 @@ var PageDescriptor = class extends __alepha_core.Descriptor {
|
|
|
49
55
|
* Only valid for server-side rendering, it will throw an error if called on the client-side.
|
|
50
56
|
*/
|
|
51
57
|
async render(options) {
|
|
52
|
-
throw new
|
|
58
|
+
throw new Error("render method is not implemented in this environment");
|
|
53
59
|
}
|
|
54
60
|
};
|
|
55
61
|
$page[__alepha_core.KIND] = PageDescriptor;
|
|
@@ -103,7 +109,7 @@ const ErrorViewer = ({ error, alepha }) => {
|
|
|
103
109
|
heading: {
|
|
104
110
|
fontSize: "20px",
|
|
105
111
|
fontWeight: "bold",
|
|
106
|
-
marginBottom: "
|
|
112
|
+
marginBottom: "10px"
|
|
107
113
|
},
|
|
108
114
|
name: {
|
|
109
115
|
fontSize: "16px",
|
|
@@ -230,20 +236,31 @@ const RouterContext = (0, react.createContext)(void 0);
|
|
|
230
236
|
//#region src/contexts/RouterLayerContext.ts
|
|
231
237
|
const RouterLayerContext = (0, react.createContext)(void 0);
|
|
232
238
|
|
|
239
|
+
//#endregion
|
|
240
|
+
//#region src/contexts/AlephaContext.ts
|
|
241
|
+
const AlephaContext = (0, react.createContext)(void 0);
|
|
242
|
+
|
|
243
|
+
//#endregion
|
|
244
|
+
//#region src/hooks/useAlepha.ts
|
|
245
|
+
const useAlepha = () => {
|
|
246
|
+
const alepha = (0, react.useContext)(AlephaContext);
|
|
247
|
+
if (!alepha) throw new Error("useAlepha must be used within an AlephaContext.Provider");
|
|
248
|
+
return alepha;
|
|
249
|
+
};
|
|
250
|
+
|
|
233
251
|
//#endregion
|
|
234
252
|
//#region src/hooks/useRouterEvents.ts
|
|
235
253
|
const useRouterEvents = (opts = {}, deps = []) => {
|
|
236
|
-
const
|
|
237
|
-
if (!ctx) throw new Error("useRouter must be used within a RouterProvider");
|
|
254
|
+
const alepha = useAlepha();
|
|
238
255
|
(0, react.useEffect)(() => {
|
|
239
|
-
if (!
|
|
256
|
+
if (!alepha.isBrowser()) return;
|
|
240
257
|
const subs = [];
|
|
241
258
|
const onBegin = opts.onBegin;
|
|
242
259
|
const onEnd = opts.onEnd;
|
|
243
260
|
const onError = opts.onError;
|
|
244
|
-
if (onBegin) subs.push(
|
|
245
|
-
if (onEnd) subs.push(
|
|
246
|
-
if (onError) subs.push(
|
|
261
|
+
if (onBegin) subs.push(alepha.on("react:transition:begin", { callback: onBegin }));
|
|
262
|
+
if (onEnd) subs.push(alepha.on("react:transition:end", { callback: onEnd }));
|
|
263
|
+
if (onError) subs.push(alepha.on("react:transition:error", { callback: onError }));
|
|
247
264
|
return () => {
|
|
248
265
|
for (const sub of subs) sub();
|
|
249
266
|
};
|
|
@@ -309,13 +326,16 @@ const NestedView = (props) => {
|
|
|
309
326
|
const layer = (0, react.useContext)(RouterLayerContext);
|
|
310
327
|
const index = layer?.index ?? 0;
|
|
311
328
|
const [view, setView] = (0, react.useState)(app?.state.layers[index]?.element);
|
|
312
|
-
useRouterEvents({ onEnd: ({ state }) => {
|
|
329
|
+
useRouterEvents({ onEnd: ({ state, context }) => {
|
|
330
|
+
if (app) app.context = context;
|
|
313
331
|
if (!state.layers[index]?.cache) setView(state.layers[index]?.element);
|
|
314
332
|
} }, [app]);
|
|
315
333
|
if (!app) throw new Error("NestedView must be used within a RouterContext.");
|
|
316
334
|
const element = view ?? props.children ?? null;
|
|
317
335
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ErrorBoundary_default, {
|
|
318
|
-
fallback:
|
|
336
|
+
fallback: (error) => {
|
|
337
|
+
return app.context.onError?.(error, app.context);
|
|
338
|
+
},
|
|
319
339
|
children: element
|
|
320
340
|
});
|
|
321
341
|
};
|
|
@@ -323,7 +343,7 @@ var NestedView_default = NestedView;
|
|
|
323
343
|
|
|
324
344
|
//#endregion
|
|
325
345
|
//#region src/components/NotFound.tsx
|
|
326
|
-
function NotFoundPage() {
|
|
346
|
+
function NotFoundPage(props) {
|
|
327
347
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
|
|
328
348
|
style: {
|
|
329
349
|
height: "100vh",
|
|
@@ -333,7 +353,8 @@ function NotFoundPage() {
|
|
|
333
353
|
alignItems: "center",
|
|
334
354
|
textAlign: "center",
|
|
335
355
|
fontFamily: "sans-serif",
|
|
336
|
-
padding: "1rem"
|
|
356
|
+
padding: "1rem",
|
|
357
|
+
...props.style
|
|
337
358
|
},
|
|
338
359
|
children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)("h1", {
|
|
339
360
|
style: {
|
|
@@ -346,8 +367,8 @@ function NotFoundPage() {
|
|
|
346
367
|
}
|
|
347
368
|
|
|
348
369
|
//#endregion
|
|
349
|
-
//#region src/errors/
|
|
350
|
-
var
|
|
370
|
+
//#region src/errors/Redirection.ts
|
|
371
|
+
var Redirection = class extends Error {
|
|
351
372
|
page;
|
|
352
373
|
constructor(page) {
|
|
353
374
|
super("Redirection");
|
|
@@ -370,7 +391,7 @@ var PageDescriptorProvider = class {
|
|
|
370
391
|
for (const page of this.pages) if (page.name === name) return page;
|
|
371
392
|
throw new Error(`Page ${name} not found`);
|
|
372
393
|
}
|
|
373
|
-
|
|
394
|
+
pathname(name, options = {}) {
|
|
374
395
|
const page = this.page(name);
|
|
375
396
|
if (!page) throw new Error(`Page ${name} not found`);
|
|
376
397
|
let url = page.path ?? "";
|
|
@@ -380,14 +401,20 @@ var PageDescriptorProvider = class {
|
|
|
380
401
|
parent = parent.parent;
|
|
381
402
|
}
|
|
382
403
|
url = this.compile(url, options.params ?? {});
|
|
383
|
-
|
|
404
|
+
if (options.query) {
|
|
405
|
+
const query = new URLSearchParams(options.query);
|
|
406
|
+
if (query.toString()) url += `?${query.toString()}`;
|
|
407
|
+
}
|
|
408
|
+
return url.replace(/\/\/+/g, "/") || "/";
|
|
409
|
+
}
|
|
410
|
+
url(name, options = {}) {
|
|
411
|
+
return new URL(this.pathname(name, options), options.base ?? `http://localhost`);
|
|
384
412
|
}
|
|
385
413
|
root(state, context) {
|
|
386
|
-
const root = (0, react.createElement)(RouterContext.Provider, { value: {
|
|
387
|
-
alepha: this.alepha,
|
|
414
|
+
const root = (0, react.createElement)(AlephaContext.Provider, { value: this.alepha }, (0, react.createElement)(RouterContext.Provider, { value: {
|
|
388
415
|
state,
|
|
389
416
|
context
|
|
390
|
-
} }, (0, react.createElement)(NestedView_default, {}, state.layers[0]?.element));
|
|
417
|
+
} }, (0, react.createElement)(NestedView_default, {}, state.layers[0]?.element)));
|
|
391
418
|
if (this.env.REACT_STRICT_MODE) return (0, react.createElement)(react.StrictMode, {}, root);
|
|
392
419
|
return root;
|
|
393
420
|
}
|
|
@@ -456,12 +483,10 @@ var PageDescriptorProvider = class {
|
|
|
456
483
|
...props
|
|
457
484
|
};
|
|
458
485
|
} catch (e) {
|
|
459
|
-
if (e instanceof
|
|
460
|
-
layers: [],
|
|
461
|
-
redirect: typeof e.page === "string" ? e.page : this.href(e.page),
|
|
486
|
+
if (e instanceof Redirection) return this.createRedirectionLayer(e.page, {
|
|
462
487
|
pathname,
|
|
463
488
|
search
|
|
464
|
-
};
|
|
489
|
+
});
|
|
465
490
|
this.log.error(e);
|
|
466
491
|
it.error = e;
|
|
467
492
|
break;
|
|
@@ -477,9 +502,21 @@ var PageDescriptorProvider = class {
|
|
|
477
502
|
acc += it.route.path ? this.compile(it.route.path, params) : "";
|
|
478
503
|
const path = acc.replace(/\/+/, "/");
|
|
479
504
|
const localErrorHandler = this.getErrorHandler(it.route);
|
|
480
|
-
if (localErrorHandler)
|
|
481
|
-
|
|
482
|
-
|
|
505
|
+
if (localErrorHandler) {
|
|
506
|
+
const onErrorParent = request.onError;
|
|
507
|
+
request.onError = (error, context$1) => {
|
|
508
|
+
const result = localErrorHandler(error, context$1);
|
|
509
|
+
if (result === void 0) return onErrorParent(error, context$1);
|
|
510
|
+
return result;
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
if (it.error) try {
|
|
514
|
+
let element$1 = await request.onError(it.error, request);
|
|
515
|
+
if (element$1 === void 0) throw it.error;
|
|
516
|
+
if (element$1 instanceof Redirection) return this.createRedirectionLayer(element$1.page, {
|
|
517
|
+
pathname,
|
|
518
|
+
search
|
|
519
|
+
});
|
|
483
520
|
if (element$1 === null) element$1 = this.renderError(it.error);
|
|
484
521
|
layers.push({
|
|
485
522
|
props,
|
|
@@ -493,6 +530,12 @@ var PageDescriptorProvider = class {
|
|
|
493
530
|
route: it.route
|
|
494
531
|
});
|
|
495
532
|
break;
|
|
533
|
+
} catch (e) {
|
|
534
|
+
if (e instanceof Redirection) return this.createRedirectionLayer(e.page, {
|
|
535
|
+
pathname,
|
|
536
|
+
search
|
|
537
|
+
});
|
|
538
|
+
throw e;
|
|
496
539
|
}
|
|
497
540
|
const element = await this.createElement(it.route, {
|
|
498
541
|
...props,
|
|
@@ -516,6 +559,14 @@ var PageDescriptorProvider = class {
|
|
|
516
559
|
search
|
|
517
560
|
};
|
|
518
561
|
}
|
|
562
|
+
createRedirectionLayer(href, context) {
|
|
563
|
+
return {
|
|
564
|
+
layers: [],
|
|
565
|
+
redirect: typeof href === "string" ? href : this.href(href),
|
|
566
|
+
pathname: context.pathname,
|
|
567
|
+
search: context.search
|
|
568
|
+
};
|
|
569
|
+
}
|
|
519
570
|
getErrorHandler(route) {
|
|
520
571
|
if (route.errorHandler) return route.errorHandler;
|
|
521
572
|
let parent = route.parent;
|
|
@@ -571,6 +622,7 @@ var PageDescriptorProvider = class {
|
|
|
571
622
|
let hasNotFoundHandler = false;
|
|
572
623
|
const pages = this.alepha.descriptors($page);
|
|
573
624
|
const hasParent = (it) => {
|
|
625
|
+
if (it.options.parent) return true;
|
|
574
626
|
for (const page of pages) {
|
|
575
627
|
const children = page.options.children ? Array.isArray(page.options.children) ? page.options.children : page.options.children() : [];
|
|
576
628
|
if (children.includes(it)) return true;
|
|
@@ -586,7 +638,7 @@ var PageDescriptorProvider = class {
|
|
|
586
638
|
name: "notFound",
|
|
587
639
|
cache: true,
|
|
588
640
|
component: NotFoundPage,
|
|
589
|
-
|
|
641
|
+
onServerResponse: ({ reply }) => {
|
|
590
642
|
reply.status = 404;
|
|
591
643
|
}
|
|
592
644
|
});
|
|
@@ -594,6 +646,12 @@ var PageDescriptorProvider = class {
|
|
|
594
646
|
});
|
|
595
647
|
map(pages, target) {
|
|
596
648
|
const children = target.options.children ? Array.isArray(target.options.children) ? target.options.children : target.options.children() : [];
|
|
649
|
+
const getChildrenFromParent = (it) => {
|
|
650
|
+
const children$1 = [];
|
|
651
|
+
for (const page of pages) if (page.options.parent === it) children$1.push(page);
|
|
652
|
+
return children$1;
|
|
653
|
+
};
|
|
654
|
+
children.push(...getChildrenFromParent(target));
|
|
597
655
|
return {
|
|
598
656
|
...target.options,
|
|
599
657
|
name: target.name,
|
|
@@ -715,6 +773,10 @@ var BrowserRouterProvider = class extends __alepha_router.RouterProvider {
|
|
|
715
773
|
options.state.pathname = state.pathname;
|
|
716
774
|
options.state.search = state.search;
|
|
717
775
|
}
|
|
776
|
+
if (options.previous) for (let i = 0; i < options.previous.length; i++) {
|
|
777
|
+
const layer = options.previous[i];
|
|
778
|
+
if (state.layers[i]?.name !== layer.name) this.pageDescriptorProvider.page(layer.name)?.onLeave?.();
|
|
779
|
+
}
|
|
718
780
|
await this.alepha.emit("react:transition:end", {
|
|
719
781
|
state: options.state,
|
|
720
782
|
context
|
|
@@ -784,15 +846,11 @@ var ReactBrowserProvider = class {
|
|
|
784
846
|
}
|
|
785
847
|
async go(url, options = {}) {
|
|
786
848
|
const result = await this.render({ url });
|
|
787
|
-
if (result.context.url.pathname !== url) {
|
|
788
|
-
this.pushState(result.context.url.pathname);
|
|
789
|
-
return;
|
|
790
|
-
}
|
|
791
|
-
if (options.replace) {
|
|
792
|
-
this.pushState(url);
|
|
849
|
+
if (result.context.url.pathname + result.context.url.search !== url) {
|
|
850
|
+
this.pushState(result.context.url.pathname + result.context.url.search);
|
|
793
851
|
return;
|
|
794
852
|
}
|
|
795
|
-
this.pushState(url);
|
|
853
|
+
this.pushState(url, options.replace);
|
|
796
854
|
}
|
|
797
855
|
async render(options = {}) {
|
|
798
856
|
const previous = options.previous ?? this.state.layers;
|
|
@@ -821,7 +879,13 @@ var ReactBrowserProvider = class {
|
|
|
821
879
|
handler: async () => {
|
|
822
880
|
const hydration = this.getHydrationState();
|
|
823
881
|
const previous = hydration?.layers ?? [];
|
|
824
|
-
if (hydration
|
|
882
|
+
if (hydration) {
|
|
883
|
+
for (const [key, value] of Object.entries(hydration)) if (key !== "layers" && key !== "links") this.alepha.state(key, value);
|
|
884
|
+
}
|
|
885
|
+
if (hydration?.links) for (const link of hydration.links.links) this.client.pushLink({
|
|
886
|
+
...link,
|
|
887
|
+
prefix: hydration.links.prefix
|
|
888
|
+
});
|
|
825
889
|
const { context } = await this.render({ previous });
|
|
826
890
|
await this.alepha.emit("react:browser:render", {
|
|
827
891
|
state: this.state,
|
|
@@ -949,6 +1013,7 @@ var ReactServerProvider = class {
|
|
|
949
1013
|
html: (0, react_dom_server.renderToString)(this.pageDescriptorProvider.root(state, context))
|
|
950
1014
|
};
|
|
951
1015
|
const html = this.renderToHtml(this.template ?? "", state, context, options.hydration);
|
|
1016
|
+
if (html instanceof Redirection) throw new Error("Redirection is not supported in this context");
|
|
952
1017
|
const result = {
|
|
953
1018
|
context,
|
|
954
1019
|
state,
|
|
@@ -963,6 +1028,7 @@ var ReactServerProvider = class {
|
|
|
963
1028
|
const { url, reply, query, params } = serverRequest;
|
|
964
1029
|
const template = await templateLoader();
|
|
965
1030
|
if (!template) throw new Error("Template not found");
|
|
1031
|
+
this.log.trace("Rendering page", { name: page.name });
|
|
966
1032
|
const context = {
|
|
967
1033
|
url,
|
|
968
1034
|
params,
|
|
@@ -988,6 +1054,10 @@ var ReactServerProvider = class {
|
|
|
988
1054
|
}
|
|
989
1055
|
target = target.parent;
|
|
990
1056
|
}
|
|
1057
|
+
await this.alepha.emit("react:transition:begin", {
|
|
1058
|
+
request: serverRequest,
|
|
1059
|
+
context
|
|
1060
|
+
});
|
|
991
1061
|
await this.alepha.emit("react:server:render:begin", {
|
|
992
1062
|
request: serverRequest,
|
|
993
1063
|
context
|
|
@@ -1002,14 +1072,20 @@ var ReactServerProvider = class {
|
|
|
1002
1072
|
reply.headers.expires = "0";
|
|
1003
1073
|
if (page.cache && serverRequest.user) delete context.links;
|
|
1004
1074
|
const html = this.renderToHtml(template, state, context);
|
|
1005
|
-
|
|
1075
|
+
if (html instanceof Redirection) {
|
|
1076
|
+
reply.redirect(typeof html.page === "string" ? html.page : this.pageDescriptorProvider.href(html.page));
|
|
1077
|
+
return;
|
|
1078
|
+
}
|
|
1079
|
+
const event = {
|
|
1006
1080
|
request: serverRequest,
|
|
1007
1081
|
context,
|
|
1008
1082
|
state,
|
|
1009
1083
|
html
|
|
1010
|
-
}
|
|
1011
|
-
|
|
1012
|
-
|
|
1084
|
+
};
|
|
1085
|
+
await this.alepha.emit("react:server:render:end", event);
|
|
1086
|
+
page.onServerResponse?.(serverRequest);
|
|
1087
|
+
this.log.trace("Page rendered", { name: page.name });
|
|
1088
|
+
return event.html;
|
|
1013
1089
|
};
|
|
1014
1090
|
}
|
|
1015
1091
|
renderToHtml(template, state, context, hydration = true) {
|
|
@@ -1020,20 +1096,23 @@ var ReactServerProvider = class {
|
|
|
1020
1096
|
app = (0, react_dom_server.renderToString)(element);
|
|
1021
1097
|
} catch (error) {
|
|
1022
1098
|
this.log.error("Error during SSR", error);
|
|
1023
|
-
|
|
1099
|
+
const element$1 = context.onError(error, context);
|
|
1100
|
+
if (element$1 instanceof Redirection) return element$1;
|
|
1101
|
+
app = (0, react_dom_server.renderToString)(element$1);
|
|
1024
1102
|
}
|
|
1025
1103
|
this.serverTimingProvider.endTiming("renderToString");
|
|
1026
1104
|
const response = { html: template };
|
|
1027
1105
|
if (hydration) {
|
|
1106
|
+
const { request, context: context$1,...rest } = this.alepha.context.als?.getStore() ?? {};
|
|
1028
1107
|
const hydrationData = {
|
|
1029
|
-
|
|
1108
|
+
...rest,
|
|
1030
1109
|
layers: state.layers.map((it) => ({
|
|
1031
1110
|
...it,
|
|
1032
1111
|
error: it.error ? {
|
|
1033
1112
|
...it.error,
|
|
1034
1113
|
name: it.error.name,
|
|
1035
1114
|
message: it.error.message,
|
|
1036
|
-
stack: it.error.stack
|
|
1115
|
+
stack: !this.alepha.isProduction() ? it.error.stack : void 0
|
|
1037
1116
|
} : void 0,
|
|
1038
1117
|
index: void 0,
|
|
1039
1118
|
path: void 0,
|
|
@@ -1053,24 +1132,34 @@ var ReactServerProvider = class {
|
|
|
1053
1132
|
else {
|
|
1054
1133
|
const bodyOpenTag = /<body([^>]*)>/i;
|
|
1055
1134
|
if (bodyOpenTag.test(response.html)) response.html = response.html.replace(bodyOpenTag, (match) => {
|
|
1056
|
-
return `${match}
|
|
1135
|
+
return `${match}<div id="${this.env.REACT_ROOT_ID}">${app}</div>`;
|
|
1057
1136
|
});
|
|
1058
1137
|
}
|
|
1059
1138
|
const bodyCloseTagRegex = /<\/body>/i;
|
|
1060
|
-
if (bodyCloseTagRegex.test(response.html)) response.html = response.html.replace(bodyCloseTagRegex, `${script}
|
|
1139
|
+
if (bodyCloseTagRegex.test(response.html)) response.html = response.html.replace(bodyCloseTagRegex, `${script}</body>`);
|
|
1061
1140
|
}
|
|
1062
1141
|
};
|
|
1063
1142
|
|
|
1064
1143
|
//#endregion
|
|
1065
1144
|
//#region src/hooks/RouterHookApi.ts
|
|
1066
1145
|
var RouterHookApi = class {
|
|
1067
|
-
constructor(pages, context, state, layer, browser) {
|
|
1146
|
+
constructor(pages, context, state, layer, pageApi, browser) {
|
|
1068
1147
|
this.pages = pages;
|
|
1069
1148
|
this.context = context;
|
|
1070
1149
|
this.state = state;
|
|
1071
1150
|
this.layer = layer;
|
|
1151
|
+
this.pageApi = pageApi;
|
|
1072
1152
|
this.browser = browser;
|
|
1073
1153
|
}
|
|
1154
|
+
path(name, config = {}) {
|
|
1155
|
+
return this.pageApi.pathname(name, {
|
|
1156
|
+
params: {
|
|
1157
|
+
...this.context.params,
|
|
1158
|
+
...config.params
|
|
1159
|
+
},
|
|
1160
|
+
query: config.query
|
|
1161
|
+
});
|
|
1162
|
+
}
|
|
1074
1163
|
getURL() {
|
|
1075
1164
|
if (!this.browser) return this.context.url;
|
|
1076
1165
|
return new URL(this.location.href);
|
|
@@ -1112,23 +1201,23 @@ var RouterHookApi = class {
|
|
|
1112
1201
|
}
|
|
1113
1202
|
async go(path, options) {
|
|
1114
1203
|
for (const page of this.pages) if (page.name === path) {
|
|
1115
|
-
|
|
1116
|
-
|
|
1204
|
+
await this.browser?.go(this.path(path, options), options);
|
|
1205
|
+
return;
|
|
1117
1206
|
}
|
|
1118
|
-
await this.browser?.go(
|
|
1207
|
+
await this.browser?.go(path, options);
|
|
1119
1208
|
}
|
|
1120
1209
|
anchor(path, options = {}) {
|
|
1210
|
+
let href = path;
|
|
1121
1211
|
for (const page of this.pages) if (page.name === path) {
|
|
1122
|
-
|
|
1212
|
+
href = this.path(path, options);
|
|
1123
1213
|
break;
|
|
1124
1214
|
}
|
|
1125
|
-
const href = this.createHref(path, this.layer, options);
|
|
1126
1215
|
return {
|
|
1127
1216
|
href,
|
|
1128
1217
|
onClick: (ev) => {
|
|
1129
1218
|
ev.stopPropagation();
|
|
1130
1219
|
ev.preventDefault();
|
|
1131
|
-
this.go(
|
|
1220
|
+
this.go(href, options).catch(console.error);
|
|
1132
1221
|
}
|
|
1133
1222
|
};
|
|
1134
1223
|
}
|
|
@@ -1150,33 +1239,25 @@ var RouterHookApi = class {
|
|
|
1150
1239
|
//#endregion
|
|
1151
1240
|
//#region src/hooks/useRouter.ts
|
|
1152
1241
|
const useRouter = () => {
|
|
1242
|
+
const alepha = useAlepha();
|
|
1153
1243
|
const ctx = (0, react.useContext)(RouterContext);
|
|
1154
1244
|
const layer = (0, react.useContext)(RouterLayerContext);
|
|
1155
1245
|
if (!ctx || !layer) throw new Error("useRouter must be used within a RouterProvider");
|
|
1156
1246
|
const pages = (0, react.useMemo)(() => {
|
|
1157
|
-
return
|
|
1247
|
+
return alepha.inject(PageDescriptorProvider).getPages();
|
|
1158
1248
|
}, []);
|
|
1159
|
-
return (0, react.useMemo)(() => new RouterHookApi(pages, ctx.context, ctx.state, layer,
|
|
1249
|
+
return (0, react.useMemo)(() => new RouterHookApi(pages, ctx.context, ctx.state, layer, alepha.inject(PageDescriptorProvider), alepha.isBrowser() ? alepha.inject(ReactBrowserProvider) : void 0), [layer]);
|
|
1160
1250
|
};
|
|
1161
1251
|
|
|
1162
1252
|
//#endregion
|
|
1163
1253
|
//#region src/components/Link.tsx
|
|
1164
1254
|
const Link = (props) => {
|
|
1165
|
-
react.default.useContext(RouterContext);
|
|
1166
1255
|
const router = useRouter();
|
|
1167
|
-
const
|
|
1168
|
-
if (!to) return null;
|
|
1169
|
-
const can = typeof props.to === "string" ? void 0 : props.to.options.can;
|
|
1170
|
-
if (can && !can()) return null;
|
|
1171
|
-
const name = typeof props.to === "string" ? void 0 : props.to.options.name;
|
|
1172
|
-
const anchorProps = {
|
|
1173
|
-
...props,
|
|
1174
|
-
to: void 0
|
|
1175
|
-
};
|
|
1256
|
+
const { to,...anchorProps } = props;
|
|
1176
1257
|
return /* @__PURE__ */ (0, react_jsx_runtime.jsx)("a", {
|
|
1177
1258
|
...router.anchor(to),
|
|
1178
1259
|
...anchorProps,
|
|
1179
|
-
children: props.children
|
|
1260
|
+
children: props.children
|
|
1180
1261
|
});
|
|
1181
1262
|
};
|
|
1182
1263
|
var Link_default = Link;
|
|
@@ -1188,22 +1269,21 @@ const useActive = (path) => {
|
|
|
1188
1269
|
const ctx = (0, react.useContext)(RouterContext);
|
|
1189
1270
|
const layer = (0, react.useContext)(RouterLayerContext);
|
|
1190
1271
|
if (!ctx || !layer) throw new Error("useRouter must be used within a RouterProvider");
|
|
1191
|
-
let name;
|
|
1192
|
-
if (typeof path === "object" && path.options.name) name = path.options.name;
|
|
1193
1272
|
const [current, setCurrent] = (0, react.useState)(ctx.state.pathname);
|
|
1194
|
-
const href = (0, react.useMemo)(() => router.createHref(path, layer), [path, layer]);
|
|
1273
|
+
const href = (0, react.useMemo)(() => router.createHref(path ?? "", layer), [path, layer]);
|
|
1195
1274
|
const [isPending, setPending] = (0, react.useState)(false);
|
|
1196
|
-
const isActive = current === href;
|
|
1197
|
-
useRouterEvents({ onEnd: ({ state }) =>
|
|
1275
|
+
const isActive = current === href || current === `${href}/` || `${current}/` === href;
|
|
1276
|
+
useRouterEvents({ onEnd: ({ state }) => {
|
|
1277
|
+
path && setCurrent(state.pathname);
|
|
1278
|
+
} }, [path]);
|
|
1198
1279
|
return {
|
|
1199
|
-
name,
|
|
1200
1280
|
isPending,
|
|
1201
1281
|
isActive,
|
|
1202
1282
|
anchorProps: {
|
|
1203
1283
|
href,
|
|
1204
1284
|
onClick: (ev) => {
|
|
1205
|
-
ev
|
|
1206
|
-
ev
|
|
1285
|
+
ev?.stopPropagation();
|
|
1286
|
+
ev?.preventDefault();
|
|
1207
1287
|
if (isActive) return;
|
|
1208
1288
|
if (isPending) return;
|
|
1209
1289
|
setPending(true);
|
|
@@ -1216,45 +1296,62 @@ const useActive = (path) => {
|
|
|
1216
1296
|
};
|
|
1217
1297
|
|
|
1218
1298
|
//#endregion
|
|
1219
|
-
//#region src/hooks/
|
|
1220
|
-
const
|
|
1221
|
-
const
|
|
1222
|
-
|
|
1223
|
-
return routerContext.alepha;
|
|
1299
|
+
//#region src/hooks/useInject.ts
|
|
1300
|
+
const useInject = (service) => {
|
|
1301
|
+
const alepha = useAlepha();
|
|
1302
|
+
return (0, react.useMemo)(() => alepha.inject(service), []);
|
|
1224
1303
|
};
|
|
1225
1304
|
|
|
1226
1305
|
//#endregion
|
|
1227
|
-
//#region src/hooks/
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1306
|
+
//#region src/hooks/useStore.ts
|
|
1307
|
+
/**
|
|
1308
|
+
* Hook to access and mutate the Alepha state.
|
|
1309
|
+
*/
|
|
1310
|
+
const useStore = (key, defaultValue) => {
|
|
1311
|
+
const alepha = useAlepha();
|
|
1312
|
+
(0, react.useMemo)(() => {
|
|
1313
|
+
if (defaultValue != null && alepha.state(key) == null) alepha.state(key, defaultValue);
|
|
1314
|
+
}, [defaultValue]);
|
|
1315
|
+
const [state, setState] = (0, react.useState)(alepha.state(key));
|
|
1316
|
+
(0, react.useEffect)(() => {
|
|
1317
|
+
if (!alepha.isBrowser()) return;
|
|
1318
|
+
return alepha.on("state:mutate", (ev) => {
|
|
1319
|
+
if (ev.key === key) setState(ev.value);
|
|
1320
|
+
});
|
|
1321
|
+
}, []);
|
|
1322
|
+
if (!alepha.isBrowser()) {
|
|
1323
|
+
const value = alepha.context.get(key);
|
|
1324
|
+
if (value !== null) return [value, (_) => {}];
|
|
1325
|
+
}
|
|
1326
|
+
return [state, (value) => {
|
|
1327
|
+
alepha.state(key, value);
|
|
1328
|
+
}];
|
|
1232
1329
|
};
|
|
1233
1330
|
|
|
1234
1331
|
//#endregion
|
|
1235
1332
|
//#region src/hooks/useClient.ts
|
|
1236
1333
|
const useClient = (_scope) => {
|
|
1334
|
+
useStore("user");
|
|
1237
1335
|
return useInject(__alepha_server_links.LinkProvider).client();
|
|
1238
1336
|
};
|
|
1239
1337
|
|
|
1240
1338
|
//#endregion
|
|
1241
1339
|
//#region src/hooks/useQueryParams.ts
|
|
1242
1340
|
const useQueryParams = (schema, options = {}) => {
|
|
1243
|
-
const
|
|
1244
|
-
if (!ctx) throw new Error("useQueryParams must be used within a RouterProvider");
|
|
1341
|
+
const alepha = useAlepha();
|
|
1245
1342
|
const key = options.key ?? "q";
|
|
1246
1343
|
const router = useRouter();
|
|
1247
1344
|
const querystring = router.query[key];
|
|
1248
|
-
const [queryParams, setQueryParams] = (0, react.useState)(decode(
|
|
1345
|
+
const [queryParams, setQueryParams] = (0, react.useState)(decode(alepha, schema, router.query[key]));
|
|
1249
1346
|
(0, react.useEffect)(() => {
|
|
1250
|
-
setQueryParams(decode(
|
|
1347
|
+
setQueryParams(decode(alepha, schema, querystring));
|
|
1251
1348
|
}, [querystring]);
|
|
1252
1349
|
return [queryParams, (queryParams$1) => {
|
|
1253
1350
|
setQueryParams(queryParams$1);
|
|
1254
1351
|
router.setQueryParams((data) => {
|
|
1255
1352
|
return {
|
|
1256
1353
|
...data,
|
|
1257
|
-
[key]: encode(
|
|
1354
|
+
[key]: encode(alepha, schema, queryParams$1)
|
|
1258
1355
|
};
|
|
1259
1356
|
});
|
|
1260
1357
|
}];
|
|
@@ -1273,14 +1370,50 @@ const decode = (alepha, schema, data) => {
|
|
|
1273
1370
|
//#endregion
|
|
1274
1371
|
//#region src/hooks/useRouterState.ts
|
|
1275
1372
|
const useRouterState = () => {
|
|
1276
|
-
const
|
|
1373
|
+
const router = (0, react.useContext)(RouterContext);
|
|
1277
1374
|
const layer = (0, react.useContext)(RouterLayerContext);
|
|
1278
|
-
if (!
|
|
1279
|
-
const [state, setState] = (0, react.useState)(
|
|
1375
|
+
if (!router || !layer) throw new Error("useRouterState must be used within a RouterContext.Provider");
|
|
1376
|
+
const [state, setState] = (0, react.useState)(router.state);
|
|
1280
1377
|
useRouterEvents({ onEnd: ({ state: state$1 }) => setState({ ...state$1 }) });
|
|
1281
1378
|
return state;
|
|
1282
1379
|
};
|
|
1283
1380
|
|
|
1381
|
+
//#endregion
|
|
1382
|
+
//#region src/hooks/useSchema.ts
|
|
1383
|
+
const useSchema = (action) => {
|
|
1384
|
+
const name = action.name;
|
|
1385
|
+
const alepha = useAlepha();
|
|
1386
|
+
const httpClient = useInject(__alepha_server.HttpClient);
|
|
1387
|
+
const linkProvider = useInject(__alepha_server_links.LinkProvider);
|
|
1388
|
+
const [schema, setSchema] = (0, react.useState)(ssrSchemaLoading(alepha, name));
|
|
1389
|
+
(0, react.useEffect)(() => {
|
|
1390
|
+
if (!schema.loading) return;
|
|
1391
|
+
const opts = { cache: true };
|
|
1392
|
+
httpClient.fetch(`${linkProvider.URL_LINKS}/${name}/schema`, {}, opts).then((it) => setSchema(it.data));
|
|
1393
|
+
}, [name]);
|
|
1394
|
+
return schema;
|
|
1395
|
+
};
|
|
1396
|
+
/**
|
|
1397
|
+
* Get an action schema during server-side rendering (SSR) or client-side rendering (CSR).
|
|
1398
|
+
*/
|
|
1399
|
+
const ssrSchemaLoading = (alepha, name) => {
|
|
1400
|
+
if (!alepha.isBrowser()) {
|
|
1401
|
+
const links = alepha.context.get("links")?.links ?? [];
|
|
1402
|
+
const can = links.find((it) => it.name === name);
|
|
1403
|
+
if (can) {
|
|
1404
|
+
const schema$1 = alepha.inject(__alepha_server_links.LinkProvider).links?.find((it) => it.name === name)?.schema;
|
|
1405
|
+
if (schema$1) {
|
|
1406
|
+
can.schema = schema$1;
|
|
1407
|
+
return schema$1;
|
|
1408
|
+
}
|
|
1409
|
+
}
|
|
1410
|
+
return { loading: true };
|
|
1411
|
+
}
|
|
1412
|
+
const schema = alepha.inject(__alepha_server_links.LinkProvider).links?.find((it) => it.name === name)?.schema;
|
|
1413
|
+
if (schema) return schema;
|
|
1414
|
+
return { loading: true };
|
|
1415
|
+
};
|
|
1416
|
+
|
|
1284
1417
|
//#endregion
|
|
1285
1418
|
//#region src/index.ts
|
|
1286
1419
|
/**
|
|
@@ -1306,6 +1439,7 @@ const AlephaReact = (0, __alepha_core.$module)({
|
|
|
1306
1439
|
|
|
1307
1440
|
//#endregion
|
|
1308
1441
|
exports.$page = $page;
|
|
1442
|
+
exports.AlephaContext = AlephaContext;
|
|
1309
1443
|
exports.AlephaReact = AlephaReact;
|
|
1310
1444
|
exports.ClientOnly = ClientOnly_default;
|
|
1311
1445
|
exports.ErrorBoundary = ErrorBoundary_default;
|
|
@@ -1316,11 +1450,12 @@ exports.PageDescriptor = PageDescriptor;
|
|
|
1316
1450
|
exports.PageDescriptorProvider = PageDescriptorProvider;
|
|
1317
1451
|
exports.ReactBrowserProvider = ReactBrowserProvider;
|
|
1318
1452
|
exports.ReactServerProvider = ReactServerProvider;
|
|
1319
|
-
exports.
|
|
1453
|
+
exports.Redirection = Redirection;
|
|
1320
1454
|
exports.RouterContext = RouterContext;
|
|
1321
1455
|
exports.RouterHookApi = RouterHookApi;
|
|
1322
1456
|
exports.RouterLayerContext = RouterLayerContext;
|
|
1323
1457
|
exports.isPageRoute = isPageRoute;
|
|
1458
|
+
exports.ssrSchemaLoading = ssrSchemaLoading;
|
|
1324
1459
|
exports.useActive = useActive;
|
|
1325
1460
|
exports.useAlepha = useAlepha;
|
|
1326
1461
|
exports.useClient = useClient;
|
|
@@ -1329,4 +1464,6 @@ exports.useQueryParams = useQueryParams;
|
|
|
1329
1464
|
exports.useRouter = useRouter;
|
|
1330
1465
|
exports.useRouterEvents = useRouterEvents;
|
|
1331
1466
|
exports.useRouterState = useRouterState;
|
|
1467
|
+
exports.useSchema = useSchema;
|
|
1468
|
+
exports.useStore = useStore;
|
|
1332
1469
|
//# sourceMappingURL=index.cjs.map
|