@alepha/react 0.6.0 → 0.6.1

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.
File without changes
File without changes
package/dist/index.cjs CHANGED
@@ -142,7 +142,7 @@ class ReactAuthProvider {
142
142
  */
143
143
  login = server.$route({
144
144
  security: false,
145
- private: true,
145
+ internal: true,
146
146
  url: "/_oauth/login",
147
147
  group: "auth",
148
148
  method: "GET",
@@ -153,13 +153,13 @@ class ReactAuthProvider {
153
153
  })
154
154
  },
155
155
  handler: async ({ query, cookies, url }) => {
156
- const { client } = this.provider(query.provider);
156
+ const { client, redirectUri } = this.provider(query.provider);
157
157
  const codeVerifier = openidClient.randomPKCECodeVerifier();
158
158
  const codeChallenge = await openidClient.calculatePKCECodeChallenge(codeVerifier);
159
159
  const scope = "openid profile email";
160
- let redirect_uri = this.authProviders[0].redirectUri;
160
+ let redirect_uri = redirectUri;
161
161
  if (redirect_uri.startsWith("/")) {
162
- redirect_uri = `${url.protocol}://${url.host}${redirect_uri}`;
162
+ redirect_uri = `${url.protocol}//${url.host}${redirect_uri}`;
163
163
  }
164
164
  const parameters = {
165
165
  redirect_uri,
@@ -184,7 +184,7 @@ class ReactAuthProvider {
184
184
  */
185
185
  callback = server.$route({
186
186
  security: false,
187
- private: true,
187
+ internal: true,
188
188
  url: "/_oauth/callback",
189
189
  group: "auth",
190
190
  method: "GET",
@@ -246,7 +246,7 @@ class ReactAuthProvider {
246
246
  */
247
247
  logout = server.$route({
248
248
  security: false,
249
- private: true,
249
+ internal: true,
250
250
  url: "/_oauth/logout",
251
251
  group: "auth",
252
252
  method: "GET",
@@ -339,7 +339,7 @@ class ReactServerProvider {
339
339
  }
340
340
  if (process.env.VITE_ALEPHA_DEV === "true") {
341
341
  this.log.info("SSR (vite) OK");
342
- const templateUrl = "http://127.0.0.1:5173/index.html";
342
+ const templateUrl = `http://${process.env.SERVER_HOST}:${process.env.SERVER_PORT}/index.html`;
343
343
  this.log.debug(`Fetch template from ${templateUrl}`);
344
344
  const route2 = this.createHandler(
345
345
  () => fetch(templateUrl).then((it) => it.text()).catch(() => void 0).then((it) => it ? this.checkTemplate(it) : void 0)
@@ -434,10 +434,7 @@ class ReactServerProvider {
434
434
  if (response) {
435
435
  return response;
436
436
  }
437
- return await this.ssr(ctx.url, template, {
438
- user: ctx.user,
439
- cookies: ctx.cookies
440
- });
437
+ return await this.ssr(ctx.url, template, ctx);
441
438
  }
442
439
  };
443
440
  }
@@ -479,13 +476,13 @@ class ReactServerProvider {
479
476
  *
480
477
  * @param url
481
478
  * @param template
482
- * @param page
479
+ * @param args
483
480
  */
484
- async ssr(url, template = this.env.REACT_SSR_OUTLET, page = {}) {
481
+ async ssr(url, template = this.env.REACT_SSR_OUTLET, args = {}) {
485
482
  const { element, layers, redirect, context } = await this.router.render(
486
483
  url.pathname + url.search,
487
484
  {
488
- args: page
485
+ args
489
486
  }
490
487
  );
491
488
  if (redirect) {
package/dist/index.d.ts CHANGED
@@ -1014,9 +1014,9 @@ declare class ReactServerProvider {
1014
1014
  *
1015
1015
  * @param url
1016
1016
  * @param template
1017
- * @param page
1017
+ * @param args
1018
1018
  */
1019
- ssr(url: URL, template?: string, page?: PageContext): Promise<Response>;
1019
+ ssr(url: URL, template?: string, args?: PageContext): Promise<Response>;
1020
1020
  protected renderHelmetContext(template: string, helmetContext: RouterRenderHelmetContext): string;
1021
1021
  }
1022
1022
 
package/dist/index.js CHANGED
@@ -141,7 +141,7 @@ class ReactAuthProvider {
141
141
  */
142
142
  login = $route({
143
143
  security: false,
144
- private: true,
144
+ internal: true,
145
145
  url: "/_oauth/login",
146
146
  group: "auth",
147
147
  method: "GET",
@@ -152,13 +152,13 @@ class ReactAuthProvider {
152
152
  })
153
153
  },
154
154
  handler: async ({ query, cookies, url }) => {
155
- const { client } = this.provider(query.provider);
155
+ const { client, redirectUri } = this.provider(query.provider);
156
156
  const codeVerifier = randomPKCECodeVerifier();
157
157
  const codeChallenge = await calculatePKCECodeChallenge(codeVerifier);
158
158
  const scope = "openid profile email";
159
- let redirect_uri = this.authProviders[0].redirectUri;
159
+ let redirect_uri = redirectUri;
160
160
  if (redirect_uri.startsWith("/")) {
161
- redirect_uri = `${url.protocol}://${url.host}${redirect_uri}`;
161
+ redirect_uri = `${url.protocol}//${url.host}${redirect_uri}`;
162
162
  }
163
163
  const parameters = {
164
164
  redirect_uri,
@@ -183,7 +183,7 @@ class ReactAuthProvider {
183
183
  */
184
184
  callback = $route({
185
185
  security: false,
186
- private: true,
186
+ internal: true,
187
187
  url: "/_oauth/callback",
188
188
  group: "auth",
189
189
  method: "GET",
@@ -245,7 +245,7 @@ class ReactAuthProvider {
245
245
  */
246
246
  logout = $route({
247
247
  security: false,
248
- private: true,
248
+ internal: true,
249
249
  url: "/_oauth/logout",
250
250
  group: "auth",
251
251
  method: "GET",
@@ -338,7 +338,7 @@ class ReactServerProvider {
338
338
  }
339
339
  if (process.env.VITE_ALEPHA_DEV === "true") {
340
340
  this.log.info("SSR (vite) OK");
341
- const templateUrl = "http://127.0.0.1:5173/index.html";
341
+ const templateUrl = `http://${process.env.SERVER_HOST}:${process.env.SERVER_PORT}/index.html`;
342
342
  this.log.debug(`Fetch template from ${templateUrl}`);
343
343
  const route2 = this.createHandler(
344
344
  () => fetch(templateUrl).then((it) => it.text()).catch(() => void 0).then((it) => it ? this.checkTemplate(it) : void 0)
@@ -433,10 +433,7 @@ class ReactServerProvider {
433
433
  if (response) {
434
434
  return response;
435
435
  }
436
- return await this.ssr(ctx.url, template, {
437
- user: ctx.user,
438
- cookies: ctx.cookies
439
- });
436
+ return await this.ssr(ctx.url, template, ctx);
440
437
  }
441
438
  };
442
439
  }
@@ -478,13 +475,13 @@ class ReactServerProvider {
478
475
  *
479
476
  * @param url
480
477
  * @param template
481
- * @param page
478
+ * @param args
482
479
  */
483
- async ssr(url, template = this.env.REACT_SSR_OUTLET, page = {}) {
480
+ async ssr(url, template = this.env.REACT_SSR_OUTLET, args = {}) {
484
481
  const { element, layers, redirect, context } = await this.router.render(
485
482
  url.pathname + url.search,
486
483
  {
487
- args: page
484
+ args
488
485
  }
489
486
  );
490
487
  if (redirect) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@alepha/react",
3
- "version": "0.6.0",
3
+ "version": "0.6.1",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "main": "./dist/index.js",
@@ -9,9 +9,9 @@
9
9
  "./dist/index.js": "./dist/index.browser.js"
10
10
  },
11
11
  "dependencies": {
12
- "@alepha/core": "0.6.0",
13
- "@alepha/security": "0.6.0",
14
- "@alepha/server": "0.6.0",
12
+ "@alepha/core": "0.6.1",
13
+ "@alepha/security": "0.6.1",
14
+ "@alepha/server": "0.6.1",
15
15
  "openid-client": "^6.4.2",
16
16
  "path-to-regexp": "^8.2.0",
17
17
  "react-dom": "^18.3.1"
@@ -1,22 +0,0 @@
1
- import React from "react";
2
- import type { AnchorHTMLAttributes } from "react";
3
- import { RouterContext } from "../contexts/RouterContext";
4
- import { useRouter } from "../hooks/useRouter";
5
-
6
- export interface LinkProps extends AnchorHTMLAttributes<HTMLAnchorElement> {
7
- to: string;
8
- children?: React.ReactNode;
9
- }
10
-
11
- const Link = (props: LinkProps) => {
12
- React.useContext(RouterContext);
13
-
14
- const router = useRouter();
15
- return (
16
- <a {...router.createAnchorProps(props.to)} {...props}>
17
- {props.children}
18
- </a>
19
- );
20
- };
21
-
22
- export default Link;
@@ -1,36 +0,0 @@
1
- import type { ReactNode } from "react";
2
- import { useContext, useEffect, useState } from "react";
3
- import { RouterContext } from "../contexts/RouterContext";
4
- import { RouterLayerContext } from "../contexts/RouterLayerContext";
5
-
6
- export interface NestedViewProps {
7
- children?: ReactNode;
8
- }
9
-
10
- /**
11
- * Nested view component
12
- *
13
- * @param props
14
- * @constructor
15
- */
16
- const NestedView = (props: NestedViewProps) => {
17
- const app = useContext(RouterContext);
18
- const layer = useContext(RouterLayerContext);
19
- const index = layer?.index ?? 0;
20
-
21
- const [view, setView] = useState<ReactNode | undefined>(
22
- app?.state.layers[index]?.element,
23
- );
24
-
25
- useEffect(() => {
26
- if (app?.alepha.isBrowser()) {
27
- return app?.router.on("end", (state) => {
28
- setView(state.layers[index]?.element);
29
- });
30
- }
31
- }, [app]);
32
-
33
- return view ?? props.children ?? null;
34
- };
35
-
36
- export default NestedView;
@@ -1 +0,0 @@
1
- export const SSID = "ssid";
@@ -1,15 +0,0 @@
1
- import type { Alepha } from "@alepha/core";
2
- import { createContext } from "react";
3
- import type { PageContext } from "../descriptors/$page";
4
- import type { Router, RouterState } from "../services/Router";
5
-
6
- export interface RouterContextValue {
7
- router: Router;
8
- alepha: Alepha;
9
- state: RouterState;
10
- args: PageContext;
11
- }
12
-
13
- export const RouterContext = createContext<RouterContextValue | undefined>(
14
- undefined,
15
- );
@@ -1,10 +0,0 @@
1
- import { createContext } from "react";
2
-
3
- export interface RouterLayerContextValue {
4
- index: number;
5
- path: string;
6
- }
7
-
8
- export const RouterLayerContext = createContext<
9
- RouterLayerContextValue | undefined
10
- >(undefined);
@@ -1,28 +0,0 @@
1
- import { KIND, __descriptor } from "@alepha/core";
2
-
3
- const KEY = "AUTH";
4
-
5
- export interface AuthDescriptorOptions {
6
- name?: string;
7
- oidc?: {
8
- issuer: string;
9
- clientId: string;
10
- clientSecret?: string;
11
- redirectUri?: string;
12
- };
13
- }
14
-
15
- export interface AuthDescriptor {
16
- [KIND]: typeof KEY;
17
- options: AuthDescriptorOptions;
18
- }
19
-
20
- export const $auth = (options: AuthDescriptorOptions): AuthDescriptor => {
21
- __descriptor(KEY);
22
- return {
23
- [KIND]: KEY,
24
- options,
25
- };
26
- };
27
-
28
- $auth[KIND] = KEY;
@@ -1,144 +0,0 @@
1
- import type { Async, Static, TSchema } from "@alepha/core";
2
- import { KIND, NotImplementedError, __descriptor } from "@alepha/core";
3
- import type { UserAccountToken } from "@alepha/security";
4
- import type { CookieManager } from "@alepha/server";
5
- import type { FC } from "react";
6
- import type { RouterHookApi } from "../hooks/RouterHookApi";
7
- import {} from "../services/Router";
8
- import type { RouterRenderHelmetContext } from "../services/Router";
9
-
10
- export const pageDescriptorKey = "PAGE";
11
-
12
- export interface PageDescriptorConfigSchema {
13
- query?: TSchema;
14
- params?: TSchema;
15
- }
16
- export type TPropsDefault = any;
17
- export type TPropsParentDefault = object;
18
-
19
- export interface PageDescriptorOptions<
20
- TConfig extends PageDescriptorConfigSchema = PageDescriptorConfigSchema,
21
- TProps extends object = TPropsDefault,
22
- TPropsParent extends object = TPropsParentDefault,
23
- > {
24
- /**
25
- *
26
- */
27
- name?: string;
28
-
29
- /**
30
- *
31
- */
32
- path?: string;
33
-
34
- /**
35
- *
36
- */
37
- schema?: TConfig;
38
-
39
- /**
40
- * Function to call when the page is loaded.
41
- */
42
- resolve?: (
43
- config: PageDescriptorConfigValue<TConfig> &
44
- TPropsParent & { context: PageContext },
45
- context: PageContext,
46
- ) => Async<TProps>;
47
-
48
- /**
49
- * Component to render when the page is loaded.
50
- */
51
- component?: FC<TProps & TPropsParent>;
52
-
53
- /**
54
- * Component to render when the page is loaded. (like .component)
55
- */
56
- lazy?: () => Promise<{ default: FC<TProps & TPropsParent> }>;
57
-
58
- /**
59
- *
60
- */
61
- children?: () => Array<{ options: PageDescriptorOptions }>;
62
-
63
- /**
64
- *
65
- */
66
- parent?: { options: PageDescriptorOptions<any, TPropsParent> };
67
-
68
- /**
69
- *
70
- */
71
- helmet?:
72
- | RouterRenderHelmetContext
73
- | ((props: TProps) => RouterRenderHelmetContext);
74
-
75
- /**
76
- *
77
- */
78
- notFoundHandler?: FC<{ error: Error }>;
79
-
80
- /**
81
- *
82
- */
83
- errorHandler?: FC<{ error: Error; url: string }>;
84
- }
85
-
86
- export interface PageContext {
87
- user?: UserAccountToken;
88
- cookies?: CookieManager;
89
- }
90
-
91
- export interface PageDescriptorConfigValue<
92
- TConfig extends PageDescriptorConfigSchema = PageDescriptorConfigSchema,
93
- > {
94
- query: TConfig["query"] extends TSchema
95
- ? Static<TConfig["query"]>
96
- : Record<string, string>;
97
- params: TConfig["params"] extends TSchema
98
- ? Static<TConfig["params"]>
99
- : Record<string, string>;
100
- pathname: string;
101
- }
102
-
103
- export interface PageDescriptor<
104
- TConfig extends PageDescriptorConfigSchema = PageDescriptorConfigSchema,
105
- TProps extends object = TPropsDefault,
106
- TPropsParent extends object = TPropsParentDefault,
107
- > {
108
- [KIND]: typeof pageDescriptorKey;
109
- render: (options?: {
110
- params?: Record<string, string>;
111
- query?: Record<string, string>;
112
- }) => Promise<string>;
113
- go: () => void;
114
- createAnchorProps: (routerHook: RouterHookApi) => {
115
- href: string;
116
- onClick: () => void;
117
- };
118
- options: PageDescriptorOptions<TConfig, TProps, TPropsParent>;
119
- }
120
-
121
- export const $page = <
122
- TConfig extends PageDescriptorConfigSchema = PageDescriptorConfigSchema,
123
- TProps extends object = TPropsDefault,
124
- TPropsParent extends object = TPropsParentDefault,
125
- >(
126
- options: PageDescriptorOptions<TConfig, TProps, TPropsParent>,
127
- ): PageDescriptor<TConfig, TProps, TPropsParent> => {
128
- __descriptor(pageDescriptorKey);
129
- return {
130
- [KIND]: pageDescriptorKey,
131
- options,
132
- render: () => {
133
- throw new NotImplementedError(pageDescriptorKey);
134
- },
135
- go: () => {
136
- throw new NotImplementedError(pageDescriptorKey);
137
- },
138
- createAnchorProps: () => {
139
- throw new NotImplementedError(pageDescriptorKey);
140
- },
141
- };
142
- };
143
-
144
- $page[KIND] = pageDescriptorKey;
@@ -1,7 +0,0 @@
1
- import type { HrefLike } from "../hooks/RouterHookApi";
2
-
3
- export class RedirectionError extends Error {
4
- constructor(public readonly page: HrefLike) {
5
- super("Redirection");
6
- }
7
- }
@@ -1,154 +0,0 @@
1
- import {} from "react";
2
- import type {
3
- ReactBrowserProvider,
4
- RouterGoOptions,
5
- } from "../providers/ReactBrowserProvider";
6
- import type { AnchorProps, RouterState } from "../services/Router";
7
-
8
- export class RouterHookApi {
9
- constructor(
10
- private readonly state: RouterState,
11
- private readonly layer: {
12
- path: string;
13
- },
14
- private readonly browser?: ReactBrowserProvider,
15
- ) {}
16
-
17
- /**
18
- *
19
- */
20
- public get current(): RouterState {
21
- return this.state;
22
- }
23
-
24
- /**
25
- *
26
- */
27
- public get pathname(): string {
28
- return this.state.pathname;
29
- }
30
-
31
- /**
32
- *
33
- */
34
- public get query(): Record<string, string> {
35
- const query: Record<string, string> = {};
36
-
37
- for (const [key, value] of new URLSearchParams(
38
- this.state.search,
39
- ).entries()) {
40
- query[key] = String(value);
41
- }
42
-
43
- return query;
44
- }
45
-
46
- /**
47
- *
48
- */
49
- public async back() {
50
- this.browser?.history.back();
51
- }
52
-
53
- /**
54
- *
55
- */
56
- public async forward() {
57
- this.browser?.history.forward();
58
- }
59
-
60
- /**
61
- *
62
- * @param props
63
- */
64
- public async invalidate(props?: Record<string, any>) {
65
- await this.browser?.invalidate(props);
66
- }
67
-
68
- /**
69
- * Create a valid href for the given pathname.
70
- *
71
- * @param pathname
72
- * @param layer
73
- */
74
- public createHref(pathname: HrefLike, layer: { path: string } = this.layer) {
75
- if (typeof pathname === "object") {
76
- pathname = pathname.options.path ?? "";
77
- }
78
-
79
- return pathname.startsWith("/")
80
- ? pathname
81
- : `${layer.path}/${pathname}`.replace(/\/\/+/g, "/");
82
- }
83
-
84
- /**
85
- *
86
- * @param path
87
- * @param options
88
- */
89
- public async go(
90
- path: HrefLike,
91
- options: RouterGoOptions = {},
92
- ): Promise<void> {
93
- return await this.browser?.go(this.createHref(path, this.layer), options);
94
- }
95
-
96
- /**
97
- *
98
- * @param path
99
- */
100
- public createAnchorProps(path: string): AnchorProps {
101
- const href = this.createHref(path, this.layer);
102
- return {
103
- href,
104
- onClick: (ev: any) => {
105
- ev.stopPropagation();
106
- ev.preventDefault();
107
-
108
- this.go(path).catch(console.error);
109
- },
110
- };
111
- }
112
-
113
- /**
114
- * Set query params.
115
- *
116
- * @param record
117
- * @param options
118
- */
119
- public setQueryParams(
120
- record: Record<string, any>,
121
- options: {
122
- /**
123
- * If true, this will merge current query params with the new ones.
124
- */
125
- merge?: boolean;
126
-
127
- /**
128
- * If true, this will add a new entry to the history stack.
129
- */
130
- push?: boolean;
131
- } = {},
132
- ) {
133
- const search = new URLSearchParams(
134
- options.merge
135
- ? {
136
- ...this.query,
137
- ...record,
138
- }
139
- : {
140
- ...record,
141
- },
142
- ).toString();
143
-
144
- const state = search ? `${this.pathname}?${search}` : this.pathname;
145
-
146
- if (options.push) {
147
- window.history.pushState({}, "", state);
148
- } else {
149
- window.history.replaceState({}, "", state);
150
- }
151
- }
152
- }
153
-
154
- export type HrefLike = string | { options: { path?: string; name?: string } };
@@ -1,57 +0,0 @@
1
- import { useContext, useEffect, useMemo, useState } from "react";
2
- import { RouterContext } from "../contexts/RouterContext";
3
- import { RouterLayerContext } from "../contexts/RouterLayerContext";
4
- import type { AnchorProps } from "../services/Router";
5
- import type { HrefLike } from "./RouterHookApi";
6
- import { useRouter } from "./useRouter";
7
-
8
- export const useActive = (path: HrefLike): UseActiveHook => {
9
- const router = useRouter();
10
- const ctx = useContext(RouterContext);
11
- const layer = useContext(RouterLayerContext);
12
- if (!ctx || !layer) {
13
- throw new Error("useRouter must be used within a RouterProvider");
14
- }
15
-
16
- let name: string | undefined;
17
- if (typeof path === "object" && path.options.name) {
18
- name = path.options.name;
19
- }
20
-
21
- const [current, setCurrent] = useState(ctx.state.pathname);
22
- const href = useMemo(() => router.createHref(path, layer), [path, layer]);
23
- const [isPending, setPending] = useState(false);
24
- const isActive = current === href;
25
-
26
- useEffect(
27
- () => ctx.router.on("end", ({ pathname }) => setCurrent(pathname)),
28
- [],
29
- );
30
-
31
- return {
32
- name,
33
- isPending,
34
- isActive,
35
- anchorProps: {
36
- href,
37
- onClick: (ev: any) => {
38
- ev.stopPropagation();
39
- ev.preventDefault();
40
- if (isActive) return;
41
- if (isPending) return;
42
-
43
- setPending(true);
44
- router.go(href).then(() => {
45
- setPending(false);
46
- });
47
- },
48
- },
49
- };
50
- };
51
-
52
- export interface UseActiveHook {
53
- isActive: boolean;
54
- anchorProps: AnchorProps;
55
- isPending: boolean;
56
- name?: string;
57
- }