@alepha/react 0.5.1 → 0.6.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.browser.cjs +23 -19
- package/dist/index.browser.js +18 -0
- package/dist/index.cjs +419 -340
- package/dist/{index.d.cts → index.d.ts} +591 -420
- package/dist/index.js +554 -0
- package/dist/{useRouterState-BlKHWZwk.cjs → useAuth-DOVx2kqa.cjs} +243 -102
- package/dist/{useRouterState-CvFCmaq7.mjs → useAuth-i7wbKVrt.js} +214 -77
- package/package.json +21 -23
- package/src/components/Link.tsx +22 -0
- package/src/components/NestedView.tsx +2 -2
- package/src/constants/SSID.ts +1 -0
- package/src/contexts/RouterContext.ts +2 -2
- package/src/descriptors/$auth.ts +28 -0
- package/src/descriptors/$page.ts +57 -3
- package/src/errors/RedirectionError.ts +7 -0
- package/src/hooks/useAuth.ts +29 -0
- package/src/hooks/useInject.ts +3 -3
- package/src/index.browser.ts +3 -1
- package/src/index.shared.ts +14 -3
- package/src/index.ts +23 -6
- package/src/providers/ReactAuthProvider.ts +410 -0
- package/src/providers/ReactBrowserProvider.ts +41 -19
- package/src/providers/ReactServerProvider.ts +106 -49
- package/src/services/Auth.ts +45 -0
- package/src/services/Router.ts +154 -41
- package/dist/index.browser.mjs +0 -17
- package/dist/index.d.mts +0 -872
- package/dist/index.mjs +0 -477
- package/src/providers/ReactSessionProvider.ts +0 -363
package/src/services/Router.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import { $inject, $logger, Alepha, EventEmitter } from "@alepha/core";
|
|
2
|
-
import type { UserAccountInfo } from "@alepha/security";
|
|
3
2
|
import type { MatchFunction, ParamData } from "path-to-regexp";
|
|
4
3
|
import { compile, match } from "path-to-regexp";
|
|
5
4
|
import type { ReactNode } from "react";
|
|
@@ -7,14 +6,8 @@ import { createElement } from "react";
|
|
|
7
6
|
import NestedView from "../components/NestedView";
|
|
8
7
|
import { RouterContext } from "../contexts/RouterContext";
|
|
9
8
|
import { RouterLayerContext } from "../contexts/RouterLayerContext";
|
|
10
|
-
import type { PageDescriptorOptions } from "../descriptors/$page";
|
|
11
|
-
import
|
|
12
|
-
|
|
13
|
-
export class RedirectException extends Error {
|
|
14
|
-
constructor(public readonly page: HrefLike) {
|
|
15
|
-
super("Redirection");
|
|
16
|
-
}
|
|
17
|
-
}
|
|
9
|
+
import type { PageContext, PageDescriptorOptions } from "../descriptors/$page";
|
|
10
|
+
import { RedirectionError } from "../errors/RedirectionError";
|
|
18
11
|
|
|
19
12
|
export class Router extends EventEmitter<RouterEvents> {
|
|
20
13
|
protected readonly log = $logger();
|
|
@@ -40,10 +33,7 @@ export class Router extends EventEmitter<RouterEvents> {
|
|
|
40
33
|
/**
|
|
41
34
|
*
|
|
42
35
|
*/
|
|
43
|
-
public root(
|
|
44
|
-
state: RouterState,
|
|
45
|
-
opts: { user?: UserAccountInfo } = {},
|
|
46
|
-
): ReactNode {
|
|
36
|
+
public root(state: RouterState, context: PageContext = {}): ReactNode {
|
|
47
37
|
return createElement(
|
|
48
38
|
RouterContext.Provider,
|
|
49
39
|
{
|
|
@@ -51,7 +41,10 @@ export class Router extends EventEmitter<RouterEvents> {
|
|
|
51
41
|
state,
|
|
52
42
|
router: this,
|
|
53
43
|
alepha: this.alepha,
|
|
54
|
-
|
|
44
|
+
args: {
|
|
45
|
+
user: context.user,
|
|
46
|
+
cookies: context.cookies,
|
|
47
|
+
},
|
|
55
48
|
},
|
|
56
49
|
},
|
|
57
50
|
state.layers[0]?.element,
|
|
@@ -66,22 +59,19 @@ export class Router extends EventEmitter<RouterEvents> {
|
|
|
66
59
|
public async render(
|
|
67
60
|
url: string,
|
|
68
61
|
options: RouterRenderOptions = {},
|
|
69
|
-
): Promise<{
|
|
70
|
-
element: ReactNode;
|
|
71
|
-
layers: Layer[];
|
|
72
|
-
redirect?: string;
|
|
73
|
-
}> {
|
|
62
|
+
): Promise<RouterRenderResult> {
|
|
74
63
|
const [pathname, search = ""] = url.split("?");
|
|
75
64
|
const state: RouterState = {
|
|
76
65
|
pathname,
|
|
77
66
|
search,
|
|
78
67
|
layers: [],
|
|
68
|
+
context: {},
|
|
79
69
|
};
|
|
80
70
|
|
|
81
|
-
this.emit("begin", undefined);
|
|
71
|
+
await this.emit("begin", undefined);
|
|
82
72
|
|
|
83
73
|
try {
|
|
84
|
-
let layers = await this.match(url, options);
|
|
74
|
+
let layers = await this.match(url, options, state.context);
|
|
85
75
|
if (layers.length === 0) {
|
|
86
76
|
if (this.notFoundPageRoute) {
|
|
87
77
|
layers = await this.createLayers(url, this.notFoundPageRoute);
|
|
@@ -96,14 +86,15 @@ export class Router extends EventEmitter<RouterEvents> {
|
|
|
96
86
|
}
|
|
97
87
|
|
|
98
88
|
state.layers = layers;
|
|
99
|
-
this.emit("success", undefined);
|
|
89
|
+
await this.emit("success", undefined);
|
|
100
90
|
} catch (e) {
|
|
101
|
-
if (e instanceof
|
|
91
|
+
if (e instanceof RedirectionError) {
|
|
102
92
|
// redirect - stop processing
|
|
103
93
|
return {
|
|
104
94
|
element: null,
|
|
105
95
|
layers: [],
|
|
106
96
|
redirect: typeof e.page === "string" ? e.page : this.href(e.page),
|
|
97
|
+
context: state.context,
|
|
107
98
|
};
|
|
108
99
|
}
|
|
109
100
|
|
|
@@ -118,7 +109,7 @@ export class Router extends EventEmitter<RouterEvents> {
|
|
|
118
109
|
},
|
|
119
110
|
];
|
|
120
111
|
|
|
121
|
-
this.emit("error", e as Error);
|
|
112
|
+
await this.emit("error", e as Error);
|
|
122
113
|
}
|
|
123
114
|
|
|
124
115
|
if (options.state) {
|
|
@@ -126,20 +117,23 @@ export class Router extends EventEmitter<RouterEvents> {
|
|
|
126
117
|
options.state.layers = state.layers;
|
|
127
118
|
options.state.pathname = state.pathname;
|
|
128
119
|
options.state.search = state.search;
|
|
120
|
+
options.state.context = state.context;
|
|
129
121
|
|
|
130
|
-
this.emit("end", options.state);
|
|
122
|
+
await this.emit("end", options.state);
|
|
131
123
|
|
|
132
124
|
return {
|
|
133
|
-
element: this.root(options.state, options),
|
|
125
|
+
element: this.root(options.state, options.args),
|
|
134
126
|
layers: options.state.layers,
|
|
127
|
+
context: state.context,
|
|
135
128
|
};
|
|
136
129
|
}
|
|
137
130
|
|
|
138
131
|
// stateless (ssr)
|
|
139
|
-
this.emit("end", state);
|
|
132
|
+
await this.emit("end", state);
|
|
140
133
|
return {
|
|
141
|
-
element: this.root(state, options),
|
|
134
|
+
element: this.root(state, options.args),
|
|
142
135
|
layers: state.layers,
|
|
136
|
+
context: state.context,
|
|
143
137
|
};
|
|
144
138
|
}
|
|
145
139
|
|
|
@@ -147,11 +141,13 @@ export class Router extends EventEmitter<RouterEvents> {
|
|
|
147
141
|
*
|
|
148
142
|
* @param url
|
|
149
143
|
* @param options
|
|
144
|
+
* @param context
|
|
150
145
|
* @protected
|
|
151
146
|
*/
|
|
152
147
|
public async match(
|
|
153
148
|
url: string,
|
|
154
149
|
options: RouterMatchOptions = {},
|
|
150
|
+
context: RouterRenderContext = {},
|
|
155
151
|
): Promise<Layer[]> {
|
|
156
152
|
const pages = this.pages;
|
|
157
153
|
const previous = options.previous;
|
|
@@ -178,7 +174,8 @@ export class Router extends EventEmitter<RouterEvents> {
|
|
|
178
174
|
params,
|
|
179
175
|
query,
|
|
180
176
|
previous,
|
|
181
|
-
options.
|
|
177
|
+
options.args,
|
|
178
|
+
context,
|
|
182
179
|
);
|
|
183
180
|
}
|
|
184
181
|
}
|
|
@@ -189,11 +186,13 @@ export class Router extends EventEmitter<RouterEvents> {
|
|
|
189
186
|
/**
|
|
190
187
|
* Create layers for the given route.
|
|
191
188
|
*
|
|
189
|
+
* @param url
|
|
192
190
|
* @param route
|
|
193
191
|
* @param params
|
|
194
192
|
* @param query
|
|
195
193
|
* @param previous
|
|
196
|
-
* @param
|
|
194
|
+
* @param args
|
|
195
|
+
* @param renderContext
|
|
197
196
|
* @protected
|
|
198
197
|
*/
|
|
199
198
|
public async createLayers(
|
|
@@ -202,7 +201,8 @@ export class Router extends EventEmitter<RouterEvents> {
|
|
|
202
201
|
params: Record<string, any> = {},
|
|
203
202
|
query: Record<string, string> = {},
|
|
204
203
|
previous: PreviousLayerData[] = [],
|
|
205
|
-
|
|
204
|
+
args?: PageContext,
|
|
205
|
+
renderContext?: RouterRenderContext,
|
|
206
206
|
): Promise<Layer[]> {
|
|
207
207
|
const layers: Layer[] = [];
|
|
208
208
|
let context: Record<string, any> = {};
|
|
@@ -224,7 +224,7 @@ export class Router extends EventEmitter<RouterEvents> {
|
|
|
224
224
|
try {
|
|
225
225
|
config.query = route.schema?.query
|
|
226
226
|
? this.alepha.parse(route.schema.query, query)
|
|
227
|
-
:
|
|
227
|
+
: query;
|
|
228
228
|
} catch (e) {
|
|
229
229
|
it.error = e as Error;
|
|
230
230
|
break;
|
|
@@ -233,7 +233,7 @@ export class Router extends EventEmitter<RouterEvents> {
|
|
|
233
233
|
try {
|
|
234
234
|
config.params = route.schema?.params
|
|
235
235
|
? this.alepha.parse(route.schema.params, params)
|
|
236
|
-
:
|
|
236
|
+
: params;
|
|
237
237
|
} catch (e) {
|
|
238
238
|
it.error = e as Error;
|
|
239
239
|
break;
|
|
@@ -278,12 +278,15 @@ export class Router extends EventEmitter<RouterEvents> {
|
|
|
278
278
|
|
|
279
279
|
try {
|
|
280
280
|
const props =
|
|
281
|
-
(await route.resolve?.(
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
281
|
+
(await route.resolve?.(
|
|
282
|
+
{
|
|
283
|
+
...config,
|
|
284
|
+
...context,
|
|
285
|
+
context: args,
|
|
286
|
+
url,
|
|
287
|
+
} as any,
|
|
288
|
+
args ?? {},
|
|
289
|
+
)) ?? {};
|
|
287
290
|
|
|
288
291
|
// save props
|
|
289
292
|
it.props = {
|
|
@@ -297,7 +300,7 @@ export class Router extends EventEmitter<RouterEvents> {
|
|
|
297
300
|
};
|
|
298
301
|
} catch (e) {
|
|
299
302
|
// check if we need to redirect
|
|
300
|
-
if (e instanceof
|
|
303
|
+
if (e instanceof RedirectionError) {
|
|
301
304
|
throw e; // redirect - stop processing
|
|
302
305
|
}
|
|
303
306
|
|
|
@@ -318,6 +321,13 @@ export class Router extends EventEmitter<RouterEvents> {
|
|
|
318
321
|
params[key] = String(params[key]);
|
|
319
322
|
}
|
|
320
323
|
|
|
324
|
+
if (it.route.helmet && renderContext) {
|
|
325
|
+
this.mergeRenderContext(it.route, renderContext, {
|
|
326
|
+
...props,
|
|
327
|
+
...context,
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
|
|
321
331
|
acc += "/";
|
|
322
332
|
acc += it.route.path ? compile(it.route.path)(params) : "";
|
|
323
333
|
const path = acc.replace(/\/+/, "/");
|
|
@@ -402,6 +412,34 @@ export class Router extends EventEmitter<RouterEvents> {
|
|
|
402
412
|
return undefined;
|
|
403
413
|
}
|
|
404
414
|
|
|
415
|
+
/**
|
|
416
|
+
* Merge the render context with the page context.
|
|
417
|
+
*
|
|
418
|
+
* @param page
|
|
419
|
+
* @param ctx
|
|
420
|
+
* @param props
|
|
421
|
+
* @protected
|
|
422
|
+
*/
|
|
423
|
+
protected mergeRenderContext(
|
|
424
|
+
page: PageRoute,
|
|
425
|
+
ctx: RouterRenderContext,
|
|
426
|
+
props: Record<string, any>,
|
|
427
|
+
): void {
|
|
428
|
+
if (page.helmet) {
|
|
429
|
+
const helmet =
|
|
430
|
+
typeof page.helmet === "function" ? page.helmet(props) : page.helmet;
|
|
431
|
+
if (helmet.title) {
|
|
432
|
+
ctx.helmet ??= {};
|
|
433
|
+
|
|
434
|
+
if (ctx.helmet?.title) {
|
|
435
|
+
ctx.helmet.title = `${helmet.title} - ${ctx.helmet.title}`;
|
|
436
|
+
} else {
|
|
437
|
+
ctx.helmet.title = helmet.title;
|
|
438
|
+
}
|
|
439
|
+
}
|
|
440
|
+
}
|
|
441
|
+
}
|
|
442
|
+
|
|
405
443
|
/**
|
|
406
444
|
*
|
|
407
445
|
* @param e
|
|
@@ -685,7 +723,7 @@ export interface RouterMatchOptions {
|
|
|
685
723
|
/**
|
|
686
724
|
*
|
|
687
725
|
*/
|
|
688
|
-
|
|
726
|
+
args?: PageContext;
|
|
689
727
|
}
|
|
690
728
|
|
|
691
729
|
export interface RouterEvents {
|
|
@@ -725,6 +763,18 @@ export interface RouterState {
|
|
|
725
763
|
*
|
|
726
764
|
*/
|
|
727
765
|
layers: Array<Layer>;
|
|
766
|
+
|
|
767
|
+
/**
|
|
768
|
+
*
|
|
769
|
+
*/
|
|
770
|
+
context: RouterRenderContext;
|
|
771
|
+
}
|
|
772
|
+
|
|
773
|
+
export interface RouterRenderContext {
|
|
774
|
+
/**
|
|
775
|
+
*
|
|
776
|
+
*/
|
|
777
|
+
helmet?: RouterRenderHelmetContext;
|
|
728
778
|
}
|
|
729
779
|
|
|
730
780
|
export interface RouterRenderOptions extends RouterMatchOptions {
|
|
@@ -735,8 +785,71 @@ export interface RouterRenderOptions extends RouterMatchOptions {
|
|
|
735
785
|
}
|
|
736
786
|
|
|
737
787
|
export interface RouterStackItem {
|
|
788
|
+
/**
|
|
789
|
+
*
|
|
790
|
+
*/
|
|
738
791
|
route: PageRoute;
|
|
792
|
+
|
|
793
|
+
/**
|
|
794
|
+
*
|
|
795
|
+
*/
|
|
739
796
|
config?: Record<string, any>;
|
|
797
|
+
|
|
798
|
+
/**
|
|
799
|
+
*
|
|
800
|
+
*/
|
|
740
801
|
props?: Record<string, any>;
|
|
802
|
+
|
|
803
|
+
/**
|
|
804
|
+
*
|
|
805
|
+
*/
|
|
741
806
|
error?: Error;
|
|
742
807
|
}
|
|
808
|
+
|
|
809
|
+
export interface RouterRenderHelmetContext {
|
|
810
|
+
/**
|
|
811
|
+
*
|
|
812
|
+
*/
|
|
813
|
+
title?: string;
|
|
814
|
+
|
|
815
|
+
/**
|
|
816
|
+
*
|
|
817
|
+
*/
|
|
818
|
+
html?: {
|
|
819
|
+
attributes?: Record<string, string>;
|
|
820
|
+
};
|
|
821
|
+
|
|
822
|
+
/**
|
|
823
|
+
*
|
|
824
|
+
*/
|
|
825
|
+
body?: {
|
|
826
|
+
attributes?: Record<string, string>;
|
|
827
|
+
};
|
|
828
|
+
|
|
829
|
+
/**
|
|
830
|
+
*
|
|
831
|
+
*/
|
|
832
|
+
meta?: Array<{ name: string; content: string }>;
|
|
833
|
+
}
|
|
834
|
+
|
|
835
|
+
export interface RouterRenderResult {
|
|
836
|
+
/**
|
|
837
|
+
*
|
|
838
|
+
*/
|
|
839
|
+
element: ReactNode;
|
|
840
|
+
|
|
841
|
+
/**
|
|
842
|
+
*
|
|
843
|
+
*/
|
|
844
|
+
layers: Layer[];
|
|
845
|
+
|
|
846
|
+
/**
|
|
847
|
+
*
|
|
848
|
+
*/
|
|
849
|
+
redirect?: string;
|
|
850
|
+
|
|
851
|
+
/**
|
|
852
|
+
*
|
|
853
|
+
*/
|
|
854
|
+
context: RouterRenderContext;
|
|
855
|
+
}
|
package/dist/index.browser.mjs
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
import { autoInject, $inject, Alepha } from '@alepha/core';
|
|
2
|
-
import { $ as $page, P as PageDescriptorProvider, k as ReactBrowserProvider } from './useRouterState-CvFCmaq7.mjs';
|
|
3
|
-
export { N as NestedView, j as RedirectException, R as Router, a as RouterContext, f as RouterHookApi, b as RouterLayerContext, p as pageDescriptorKey, u as useActive, c as useClient, d as useInject, e as useQueryParams, g as useRouter, h as useRouterEvents, i as useRouterState } from './useRouterState-CvFCmaq7.mjs';
|
|
4
|
-
import 'react';
|
|
5
|
-
import '@alepha/server';
|
|
6
|
-
import 'react-dom/client';
|
|
7
|
-
import 'path-to-regexp';
|
|
8
|
-
|
|
9
|
-
class ReactModule {
|
|
10
|
-
alepha = $inject(Alepha);
|
|
11
|
-
constructor() {
|
|
12
|
-
this.alepha.with(PageDescriptorProvider).with(ReactBrowserProvider);
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
autoInject($page, ReactModule);
|
|
16
|
-
|
|
17
|
-
export { $page, ReactBrowserProvider, ReactModule };
|