@alepha/react 0.6.0 → 0.6.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.browser.cjs +1 -1
- package/dist/index.browser.js +2 -2
- package/dist/index.cjs +127 -98
- package/dist/index.d.ts +71 -45
- package/dist/index.js +129 -100
- package/dist/{useAuth-DOVx2kqa.cjs → useAuth-B9ypF48n.cjs} +112 -44
- package/dist/{useAuth-i7wbKVrt.js → useAuth-Ps01oe8e.js} +113 -45
- package/package.json +13 -12
- package/src/components/Link.tsx +0 -22
- package/src/components/NestedView.tsx +0 -36
- package/src/constants/SSID.ts +0 -1
- package/src/contexts/RouterContext.ts +0 -15
- package/src/contexts/RouterLayerContext.ts +0 -10
- package/src/descriptors/$auth.ts +0 -28
- package/src/descriptors/$page.ts +0 -144
- package/src/errors/RedirectionError.ts +0 -7
- package/src/hooks/RouterHookApi.ts +0 -154
- package/src/hooks/useActive.ts +0 -57
- package/src/hooks/useAuth.ts +0 -29
- package/src/hooks/useClient.ts +0 -6
- package/src/hooks/useInject.ts +0 -12
- package/src/hooks/useQueryParams.ts +0 -59
- package/src/hooks/useRouter.ts +0 -28
- package/src/hooks/useRouterEvents.ts +0 -43
- package/src/hooks/useRouterState.ts +0 -23
- package/src/index.browser.ts +0 -21
- package/src/index.shared.ts +0 -28
- package/src/index.ts +0 -46
- package/src/providers/PageDescriptorProvider.ts +0 -52
- package/src/providers/ReactAuthProvider.ts +0 -410
- package/src/providers/ReactBrowserProvider.ts +0 -250
- package/src/providers/ReactServerProvider.ts +0 -301
- package/src/services/Auth.ts +0 -45
- package/src/services/Router.ts +0 -855
package/dist/index.browser.cjs
CHANGED
package/dist/index.browser.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { autoInject, $inject, Alepha } from '@alepha/core';
|
|
2
|
-
import { a as $page, P as PageDescriptorProvider, l as ReactBrowserProvider, A as Auth } from './useAuth-
|
|
3
|
-
export { $ as $auth, L as Link, N as NestedView, R as Router, b as RouterContext, d as RouterHookApi, c as RouterLayerContext, p as pageDescriptorKey, j as useActive, k as useAuth, e as useClient, u as useInject, f as useQueryParams, g as useRouter, h as useRouterEvents, i as useRouterState } from './useAuth-
|
|
2
|
+
import { a as $page, P as PageDescriptorProvider, l as ReactBrowserProvider, A as Auth } from './useAuth-Ps01oe8e.js';
|
|
3
|
+
export { $ as $auth, L as Link, N as NestedView, R as Router, b as RouterContext, d as RouterHookApi, c as RouterLayerContext, p as pageDescriptorKey, j as useActive, k as useAuth, e as useClient, u as useInject, f as useQueryParams, g as useRouter, h as useRouterEvents, i as useRouterState } from './useAuth-Ps01oe8e.js';
|
|
4
4
|
import 'react/jsx-runtime';
|
|
5
5
|
import 'react';
|
|
6
6
|
import '@alepha/server';
|
package/dist/index.cjs
CHANGED
|
@@ -2,11 +2,12 @@
|
|
|
2
2
|
|
|
3
3
|
var core = require('@alepha/core');
|
|
4
4
|
var server = require('@alepha/server');
|
|
5
|
-
var useAuth = require('./useAuth-
|
|
5
|
+
var useAuth = require('./useAuth-B9ypF48n.cjs');
|
|
6
6
|
var openidClient = require('openid-client');
|
|
7
7
|
var node_fs = require('node:fs');
|
|
8
8
|
var promises = require('node:fs/promises');
|
|
9
9
|
var node_path = require('node:path');
|
|
10
|
+
var cheerio = require('cheerio');
|
|
10
11
|
var server$1 = require('react-dom/server');
|
|
11
12
|
require('react/jsx-runtime');
|
|
12
13
|
require('react');
|
|
@@ -16,7 +17,7 @@ require('path-to-regexp');
|
|
|
16
17
|
class ReactAuthProvider {
|
|
17
18
|
log = core.$logger();
|
|
18
19
|
alepha = core.$inject(core.Alepha);
|
|
19
|
-
|
|
20
|
+
serverCookieProvider = core.$inject(server.ServerCookieProvider);
|
|
20
21
|
authProviders = [];
|
|
21
22
|
authorizationCode = server.$cookie({
|
|
22
23
|
name: "authorizationCode",
|
|
@@ -54,23 +55,30 @@ class ReactAuthProvider {
|
|
|
54
55
|
name: "configure",
|
|
55
56
|
handler: async () => {
|
|
56
57
|
const auths = this.alepha.getDescriptorValues(useAuth.$auth);
|
|
57
|
-
for (const
|
|
58
|
-
const options =
|
|
58
|
+
for (const { value, key, instance } of auths) {
|
|
59
|
+
const options = value.options;
|
|
59
60
|
if (options.oidc) {
|
|
61
|
+
this.log.debug(
|
|
62
|
+
`Discover OIDC auth provider -> ${options.oidc.issuer}`
|
|
63
|
+
);
|
|
64
|
+
const client = await openidClient.discovery(
|
|
65
|
+
new URL(options.oidc.issuer),
|
|
66
|
+
options.oidc.clientId,
|
|
67
|
+
{
|
|
68
|
+
client_secret: options.oidc.clientSecret
|
|
69
|
+
},
|
|
70
|
+
void 0,
|
|
71
|
+
{
|
|
72
|
+
execute: [openidClient.allowInsecureRequests]
|
|
73
|
+
}
|
|
74
|
+
);
|
|
75
|
+
instance[key].jwks = () => {
|
|
76
|
+
return client.serverMetadata().jwks_uri;
|
|
77
|
+
};
|
|
60
78
|
this.authProviders.push({
|
|
61
|
-
name: options.name ??
|
|
79
|
+
name: options.name ?? key,
|
|
62
80
|
redirectUri: options.oidc.redirectUri ?? "/api/_oauth/callback",
|
|
63
|
-
client
|
|
64
|
-
new URL(options.oidc.issuer),
|
|
65
|
-
options.oidc.clientId,
|
|
66
|
-
{
|
|
67
|
-
client_secret: options.oidc.clientSecret
|
|
68
|
-
},
|
|
69
|
-
void 0,
|
|
70
|
-
{
|
|
71
|
-
execute: [openidClient.allowInsecureRequests]
|
|
72
|
-
}
|
|
73
|
-
)
|
|
81
|
+
client
|
|
74
82
|
});
|
|
75
83
|
}
|
|
76
84
|
}
|
|
@@ -79,21 +87,19 @@ class ReactAuthProvider {
|
|
|
79
87
|
/**
|
|
80
88
|
* Configure Fastify to forward Session Access Token to Header Authorization.
|
|
81
89
|
*/
|
|
82
|
-
|
|
83
|
-
name: "
|
|
84
|
-
after: this.
|
|
85
|
-
handler: async (
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
req.headers.authorization = `Bearer ${tokens.access_token}`;
|
|
91
|
-
}
|
|
92
|
-
if (this.user.get(req.cookies) && !this.tokens.get(req.cookies)) {
|
|
93
|
-
this.user.del(req.cookies);
|
|
94
|
-
}
|
|
90
|
+
onRequest = core.$hook({
|
|
91
|
+
name: "server:onRequest",
|
|
92
|
+
after: this.serverCookieProvider,
|
|
93
|
+
handler: async ({ request }) => {
|
|
94
|
+
if (request.cookies && !this.isViteFile(request.url.pathname) && !!this.authProviders.length) {
|
|
95
|
+
const tokens = await this.refresh(request.cookies);
|
|
96
|
+
if (tokens) {
|
|
97
|
+
request.headers.rep("authorization", `Bearer ${tokens.access_token}`);
|
|
95
98
|
}
|
|
96
|
-
|
|
99
|
+
if (this.user.get(request.cookies) && !this.tokens.get(request.cookies)) {
|
|
100
|
+
this.user.del(request.cookies);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
97
103
|
}
|
|
98
104
|
});
|
|
99
105
|
/**
|
|
@@ -122,7 +128,9 @@ class ReactAuthProvider {
|
|
|
122
128
|
});
|
|
123
129
|
return newTokens;
|
|
124
130
|
} catch (e) {
|
|
125
|
-
|
|
131
|
+
if (e instanceof Error) {
|
|
132
|
+
this.log.warn("Failed to refresh token", e.message);
|
|
133
|
+
}
|
|
126
134
|
}
|
|
127
135
|
}
|
|
128
136
|
this.tokens.del(cookies);
|
|
@@ -142,7 +150,7 @@ class ReactAuthProvider {
|
|
|
142
150
|
*/
|
|
143
151
|
login = server.$route({
|
|
144
152
|
security: false,
|
|
145
|
-
|
|
153
|
+
internal: true,
|
|
146
154
|
url: "/_oauth/login",
|
|
147
155
|
group: "auth",
|
|
148
156
|
method: "GET",
|
|
@@ -153,13 +161,13 @@ class ReactAuthProvider {
|
|
|
153
161
|
})
|
|
154
162
|
},
|
|
155
163
|
handler: async ({ query, cookies, url }) => {
|
|
156
|
-
const { client } = this.provider(query.provider);
|
|
164
|
+
const { client, redirectUri } = this.provider(query.provider);
|
|
157
165
|
const codeVerifier = openidClient.randomPKCECodeVerifier();
|
|
158
166
|
const codeChallenge = await openidClient.calculatePKCECodeChallenge(codeVerifier);
|
|
159
167
|
const scope = "openid profile email";
|
|
160
|
-
let redirect_uri =
|
|
168
|
+
let redirect_uri = redirectUri;
|
|
161
169
|
if (redirect_uri.startsWith("/")) {
|
|
162
|
-
redirect_uri = `${url.protocol}
|
|
170
|
+
redirect_uri = `${url.protocol}//${url.host}${redirect_uri}`;
|
|
163
171
|
}
|
|
164
172
|
const parameters = {
|
|
165
173
|
redirect_uri,
|
|
@@ -184,7 +192,7 @@ class ReactAuthProvider {
|
|
|
184
192
|
*/
|
|
185
193
|
callback = server.$route({
|
|
186
194
|
security: false,
|
|
187
|
-
|
|
195
|
+
internal: true,
|
|
188
196
|
url: "/_oauth/callback",
|
|
189
197
|
group: "auth",
|
|
190
198
|
method: "GET",
|
|
@@ -246,7 +254,7 @@ class ReactAuthProvider {
|
|
|
246
254
|
*/
|
|
247
255
|
logout = server.$route({
|
|
248
256
|
security: false,
|
|
249
|
-
|
|
257
|
+
internal: true,
|
|
250
258
|
url: "/_oauth/logout",
|
|
251
259
|
group: "auth",
|
|
252
260
|
method: "GET",
|
|
@@ -316,13 +324,13 @@ const envSchema$1 = core.t.object({
|
|
|
316
324
|
REACT_SERVER_DIST: core.t.string({ default: "client" }),
|
|
317
325
|
REACT_SERVER_PREFIX: core.t.string({ default: "" }),
|
|
318
326
|
REACT_SSR_ENABLED: core.t.boolean({ default: false }),
|
|
319
|
-
|
|
327
|
+
REACT_ROOT_ID: core.t.string({ default: "root" })
|
|
320
328
|
});
|
|
321
329
|
class ReactServerProvider {
|
|
322
330
|
log = core.$logger();
|
|
323
331
|
alepha = core.$inject(core.Alepha);
|
|
324
332
|
router = core.$inject(useAuth.Router);
|
|
325
|
-
|
|
333
|
+
serverProvider = core.$inject(server.ServerProvider);
|
|
326
334
|
env = core.$inject(envSchema$1);
|
|
327
335
|
configure = core.$hook({
|
|
328
336
|
name: "configure",
|
|
@@ -330,7 +338,9 @@ class ReactServerProvider {
|
|
|
330
338
|
await this.configureRoutes();
|
|
331
339
|
}
|
|
332
340
|
});
|
|
341
|
+
id = Math.random().toString(36).substring(2, 7);
|
|
333
342
|
async configureRoutes() {
|
|
343
|
+
this.alepha.state("ReactServerProvider.ssr", false);
|
|
334
344
|
if (this.alepha.isTest()) {
|
|
335
345
|
this.processDescriptors();
|
|
336
346
|
}
|
|
@@ -338,14 +348,15 @@ class ReactServerProvider {
|
|
|
338
348
|
return;
|
|
339
349
|
}
|
|
340
350
|
if (process.env.VITE_ALEPHA_DEV === "true") {
|
|
351
|
+
const url = `http://${process.env.SERVER_HOST}:${process.env.SERVER_PORT}`;
|
|
341
352
|
this.log.info("SSR (vite) OK");
|
|
342
|
-
|
|
343
|
-
|
|
353
|
+
this.alepha.state("ReactServerProvider.ssr", true);
|
|
354
|
+
const templateUrl = `${url}/index.html`;
|
|
344
355
|
const route2 = this.createHandler(
|
|
345
|
-
() => fetch(templateUrl).then((it) => it.text()).catch(() => void 0)
|
|
356
|
+
() => fetch(templateUrl).then((it) => it.text()).catch(() => void 0)
|
|
346
357
|
);
|
|
347
|
-
await this.
|
|
348
|
-
await this.
|
|
358
|
+
await this.serverProvider.route(route2);
|
|
359
|
+
await this.serverProvider.route({
|
|
349
360
|
...route2,
|
|
350
361
|
url: "*"
|
|
351
362
|
});
|
|
@@ -367,37 +378,16 @@ class ReactServerProvider {
|
|
|
367
378
|
this.log.warn("Missing static files, SSR will be disabled");
|
|
368
379
|
return;
|
|
369
380
|
}
|
|
370
|
-
await this.
|
|
381
|
+
await this.serverProvider.serve(this.createStaticHandler(root));
|
|
371
382
|
}
|
|
372
|
-
const template = this.
|
|
373
|
-
this.alepha.state("ReactServerProvider.template") ?? await promises.readFile(node_path.join(root, "index.html"), "utf-8")
|
|
374
|
-
);
|
|
383
|
+
const template = this.alepha.state("ReactServerProvider.template") ?? await promises.readFile(node_path.join(root, "index.html"), "utf-8");
|
|
375
384
|
const route = this.createHandler(async () => template);
|
|
376
|
-
await this.
|
|
377
|
-
await this.
|
|
385
|
+
await this.serverProvider.route(route);
|
|
386
|
+
await this.serverProvider.route({
|
|
378
387
|
...route,
|
|
379
388
|
url: "*"
|
|
380
389
|
});
|
|
381
|
-
|
|
382
|
-
/**
|
|
383
|
-
* Check if the template contains the outlet.
|
|
384
|
-
*
|
|
385
|
-
* @param template
|
|
386
|
-
* @protected
|
|
387
|
-
*/
|
|
388
|
-
checkTemplate(template) {
|
|
389
|
-
if (!template.includes(this.env.REACT_SSR_OUTLET)) {
|
|
390
|
-
if (!template.includes('<div id="root"></div>')) {
|
|
391
|
-
throw new Error(
|
|
392
|
-
`Missing React SSR outlet in index.html, please add ${this.env.REACT_SSR_OUTLET} to the index.html file`
|
|
393
|
-
);
|
|
394
|
-
}
|
|
395
|
-
return template.replace(
|
|
396
|
-
`<div id="root"></div>`,
|
|
397
|
-
`<div id="root">${this.env.REACT_SSR_OUTLET}</div>`
|
|
398
|
-
);
|
|
399
|
-
}
|
|
400
|
-
return template;
|
|
390
|
+
this.alepha.state("ReactServerProvider.ssr", true);
|
|
401
391
|
}
|
|
402
392
|
/**
|
|
403
393
|
*
|
|
@@ -434,13 +424,14 @@ class ReactServerProvider {
|
|
|
434
424
|
if (response) {
|
|
435
425
|
return response;
|
|
436
426
|
}
|
|
437
|
-
return await this.ssr(ctx.url, template,
|
|
438
|
-
user: ctx.user,
|
|
439
|
-
cookies: ctx.cookies
|
|
440
|
-
});
|
|
427
|
+
return await this.ssr(ctx.url, template, ctx);
|
|
441
428
|
}
|
|
442
429
|
};
|
|
443
430
|
}
|
|
431
|
+
/**
|
|
432
|
+
*
|
|
433
|
+
* @protected
|
|
434
|
+
*/
|
|
444
435
|
processDescriptors() {
|
|
445
436
|
const pages = this.alepha.getDescriptorValues(useAuth.$page);
|
|
446
437
|
for (const { key, instance, value } of pages) {
|
|
@@ -479,13 +470,26 @@ class ReactServerProvider {
|
|
|
479
470
|
*
|
|
480
471
|
* @param url
|
|
481
472
|
* @param template
|
|
482
|
-
* @param
|
|
473
|
+
* @param args
|
|
483
474
|
*/
|
|
484
|
-
async ssr(url, template
|
|
475
|
+
async ssr(url, template, args = {}) {
|
|
476
|
+
const hasAuth = this.alepha.has(ReactAuthProvider);
|
|
477
|
+
if (!args.user && args.cookies && hasAuth) {
|
|
478
|
+
const auth = this.alepha.get(ReactAuthProvider);
|
|
479
|
+
args.user = auth.user.get(args.cookies);
|
|
480
|
+
if (args.user) {
|
|
481
|
+
args.user.roles = [];
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
if (this.alepha.has(server.ServerLinksProvider) && hasAuth) {
|
|
485
|
+
const srv = this.alepha.get(server.ServerLinksProvider);
|
|
486
|
+
args.links = await srv.links();
|
|
487
|
+
this.alepha.als.set("links", args.links);
|
|
488
|
+
}
|
|
485
489
|
const { element, layers, redirect, context } = await this.router.render(
|
|
486
490
|
url.pathname + url.search,
|
|
487
491
|
{
|
|
488
|
-
args
|
|
492
|
+
args
|
|
489
493
|
}
|
|
490
494
|
);
|
|
491
495
|
if (redirect) {
|
|
@@ -496,42 +500,67 @@ class ReactServerProvider {
|
|
|
496
500
|
}
|
|
497
501
|
});
|
|
498
502
|
}
|
|
499
|
-
const
|
|
503
|
+
const html = server$1.renderToString(element);
|
|
504
|
+
const $ = cheerio.load(template);
|
|
500
505
|
const script = `<script>window.__ssr=${JSON.stringify({
|
|
506
|
+
links: args.links,
|
|
501
507
|
layers: layers.map((it) => ({
|
|
502
508
|
...it,
|
|
509
|
+
error: it.error ? {
|
|
510
|
+
...it.error,
|
|
511
|
+
name: it.error.name,
|
|
512
|
+
message: it.error.message,
|
|
513
|
+
stack: it.error.stack
|
|
514
|
+
// TODO: Hide stack in production ?
|
|
515
|
+
} : void 0,
|
|
503
516
|
index: void 0,
|
|
504
517
|
path: void 0,
|
|
505
518
|
element: void 0
|
|
506
519
|
}))
|
|
507
520
|
})}<\/script>`;
|
|
508
|
-
const
|
|
509
|
-
|
|
510
|
-
|
|
521
|
+
const body = $("body");
|
|
522
|
+
const root = body.find(`#${this.env.REACT_ROOT_ID}`);
|
|
523
|
+
if (root.length) {
|
|
524
|
+
root.html(html);
|
|
525
|
+
} else {
|
|
526
|
+
body.prepend(`<div id="${this.env.REACT_ROOT_ID}">${html}</div>`);
|
|
511
527
|
}
|
|
512
|
-
|
|
513
|
-
|
|
528
|
+
body.append(script);
|
|
529
|
+
if (context.head) {
|
|
530
|
+
this.renderHeadContext($, context.head);
|
|
514
531
|
}
|
|
515
|
-
|
|
516
|
-
return new Response(template, {
|
|
532
|
+
return new Response($.html(), {
|
|
517
533
|
headers: { "Content-Type": "text/html" }
|
|
518
534
|
});
|
|
519
535
|
}
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
536
|
+
renderHeadContext($, headContext) {
|
|
537
|
+
const head = $("head");
|
|
538
|
+
if (head) {
|
|
539
|
+
if (headContext.title) {
|
|
540
|
+
head.find("title").remove();
|
|
541
|
+
head.append(`<title>${headContext.title}</title>`);
|
|
542
|
+
}
|
|
543
|
+
if (headContext.meta) {
|
|
544
|
+
for (const it of headContext.meta) {
|
|
545
|
+
const meta = head.find(`meta[name="${it.name}"]`);
|
|
546
|
+
if (meta.length) {
|
|
547
|
+
meta.attr("content", it.content);
|
|
548
|
+
} else {
|
|
549
|
+
head.append(`<meta name="${it.name}" content="${it.content}" />`);
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
if (headContext.htmlAttributes) {
|
|
555
|
+
for (const [key, value] of Object.entries(headContext.htmlAttributes)) {
|
|
556
|
+
$("html").attr(key, value);
|
|
557
|
+
}
|
|
558
|
+
}
|
|
559
|
+
if (headContext.bodyAttributes) {
|
|
560
|
+
for (const [key, value] of Object.entries(headContext.bodyAttributes)) {
|
|
561
|
+
$("body").attr(key, value);
|
|
532
562
|
}
|
|
533
563
|
}
|
|
534
|
-
return template;
|
|
535
564
|
}
|
|
536
565
|
}
|
|
537
566
|
|