@alepha/react 0.14.4 → 0.15.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.
- package/README.md +10 -0
- package/dist/auth/index.browser.js +603 -242
- package/dist/auth/index.browser.js.map +1 -1
- package/dist/auth/index.d.ts +2 -2
- package/dist/auth/index.d.ts.map +1 -1
- package/dist/auth/index.js +1317 -952
- package/dist/auth/index.js.map +1 -1
- package/dist/core/index.d.ts +17 -17
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +20 -20
- package/dist/core/index.js.map +1 -1
- package/dist/form/index.d.ts +9 -10
- package/dist/form/index.d.ts.map +1 -1
- package/dist/form/index.js +15 -15
- package/dist/form/index.js.map +1 -1
- package/dist/head/index.browser.js +20 -0
- package/dist/head/index.browser.js.map +1 -1
- package/dist/head/index.d.ts +62 -64
- package/dist/head/index.d.ts.map +1 -1
- package/dist/head/index.js +20 -0
- package/dist/head/index.js.map +1 -1
- package/dist/i18n/index.d.ts +9 -9
- package/dist/i18n/index.d.ts.map +1 -1
- package/dist/i18n/index.js.map +1 -1
- package/dist/router/index.browser.js +605 -244
- package/dist/router/index.browser.js.map +1 -1
- package/dist/router/index.d.ts +100 -111
- package/dist/router/index.d.ts.map +1 -1
- package/dist/router/index.js +1317 -952
- package/dist/router/index.js.map +1 -1
- package/dist/websocket/index.d.ts +0 -1
- package/dist/websocket/index.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/auth/__tests__/$auth.spec.ts +164 -150
- package/src/auth/index.ts +9 -3
- package/src/auth/services/ReactAuth.ts +15 -5
- package/src/core/hooks/useAction.ts +1 -2
- package/src/core/index.ts +4 -4
- package/src/form/errors/FormValidationError.ts +4 -6
- package/src/form/hooks/useFormState.ts +1 -1
- package/src/form/index.ts +1 -1
- package/src/form/services/FormModel.ts +31 -25
- package/src/head/helpers/SeoExpander.ts +2 -1
- package/src/head/hooks/useHead.spec.tsx +2 -2
- package/src/head/index.browser.ts +2 -2
- package/src/head/index.ts +4 -4
- package/src/head/interfaces/Head.ts +15 -3
- package/src/head/primitives/$head.ts +2 -5
- package/src/head/providers/BrowserHeadProvider.ts +55 -0
- package/src/head/providers/HeadProvider.ts +4 -1
- package/src/i18n/__tests__/integration.spec.tsx +1 -1
- package/src/i18n/components/Localize.spec.tsx +2 -2
- package/src/i18n/hooks/useI18n.browser.spec.tsx +2 -2
- package/src/i18n/index.ts +1 -1
- package/src/i18n/primitives/$dictionary.ts +1 -1
- package/src/i18n/providers/I18nProvider.spec.ts +1 -1
- package/src/i18n/providers/I18nProvider.ts +1 -1
- package/src/router/__tests__/page-head-browser.browser.spec.ts +5 -1
- package/src/router/__tests__/page-head.spec.ts +11 -7
- package/src/router/__tests__/seo-head.spec.ts +7 -3
- package/src/router/atoms/ssrManifestAtom.ts +2 -11
- package/src/router/components/ErrorViewer.tsx +626 -167
- package/src/router/components/Link.tsx +4 -2
- package/src/router/components/NestedView.tsx +7 -9
- package/src/router/components/NotFound.tsx +2 -2
- package/src/router/hooks/useQueryParams.ts +1 -1
- package/src/router/hooks/useRouter.ts +1 -1
- package/src/router/hooks/useRouterState.ts +1 -1
- package/src/router/index.browser.ts +10 -11
- package/src/router/index.shared.ts +7 -7
- package/src/router/index.ts +10 -7
- package/src/router/primitives/$page.browser.spec.tsx +6 -1
- package/src/router/primitives/$page.spec.tsx +7 -1
- package/src/router/primitives/$page.ts +5 -9
- package/src/router/providers/ReactBrowserProvider.ts +17 -6
- package/src/router/providers/ReactBrowserRouterProvider.ts +1 -1
- package/src/router/providers/ReactPageProvider.ts +4 -3
- package/src/router/providers/ReactServerProvider.ts +32 -50
- package/src/router/providers/ReactServerTemplateProvider.ts +336 -155
- package/src/router/providers/SSRManifestProvider.ts +17 -60
- package/src/router/services/ReactPageService.ts +4 -1
- package/src/router/services/ReactRouter.ts +6 -5
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/websocket/hooks/useRoom.tsx"],"
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../src/websocket/hooks/useRoom.tsx"],"mappings":";;;;;;AASA;UAAiB,cAAA,iBACC,SAAA,kBACA,SAAA;EAFa;;;EAO7B,MAAA;EAKmC;;;EAAnC,OAAA,EAAS,gBAAA,CAAiB,OAAA,EAAS,OAAA;EA4CjB;;;EAvClB,OAAA,GAAU,OAAA,EAAS,MAAA,CAAO,OAAA;EAhBV;;;;EAsBhB,GAAA;EAXS;;;;EAiBT,aAAA;EAZ0B;;;;EAkB1B,iBAAA;EAMA;;;;EAAA,oBAAA;EAeW;;;EAVX,SAAA;EAgB4B;;;EAX5B,YAAA;EAegB;;;EAVhB,OAAA,IAAW,KAAA,EAAO,KAAA;AAAA;;;;UAMH,aAAA,iBAA8B,SAAA;EAItB;;;EAAvB,IAAA,GAAO,OAAA,EAAS,MAAA,CAAO,OAAA,MAAa,OAAA;EAUpC;;;EALA,WAAA;EAoBA;;;EAfA,YAAA;EAkDW;;;EA7CX,OAAA;EA6CiE;;;EAxCjE,KAAA,GAAQ,KAAA;EA2CO;;;EAtCf,SAAA;EAmCsB;;;EA9BtB,UAAA;AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;cA8BW,OAAA,mBAA2B,SAAA,kBAA2B,SAAA,EACjE,OAAA,EAAS,cAAA,CAAe,OAAA,EAAS,OAAA,GACjC,IAAA,gBACC,aAAA,CAAc,OAAA"}
|
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@alepha/react",
|
|
3
3
|
"description": "React components and hooks for building Alepha applications.",
|
|
4
4
|
"author": "Nicolas Foures",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.15.1",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"engines": {
|
|
8
8
|
"node": ">=22.0.0"
|
|
@@ -20,19 +20,19 @@
|
|
|
20
20
|
"react-dom": "^19"
|
|
21
21
|
},
|
|
22
22
|
"devDependencies": {
|
|
23
|
-
"@biomejs/biome": "^2.3.
|
|
23
|
+
"@biomejs/biome": "^2.3.12",
|
|
24
24
|
"@testing-library/dom": "^10.4.1",
|
|
25
|
-
"@testing-library/react": "^16.3.
|
|
25
|
+
"@testing-library/react": "^16.3.2",
|
|
26
26
|
"@types/react": "^19",
|
|
27
27
|
"@types/react-dom": "^19",
|
|
28
|
-
"alepha": "0.
|
|
28
|
+
"alepha": "0.15.1",
|
|
29
29
|
"jsdom": "^27.4.0",
|
|
30
30
|
"react": "^19.2.3",
|
|
31
31
|
"typescript": "^5.9.3",
|
|
32
|
-
"vitest": "^4.0.
|
|
32
|
+
"vitest": "^4.0.18"
|
|
33
33
|
},
|
|
34
34
|
"peerDependencies": {
|
|
35
|
-
"alepha": "0.
|
|
35
|
+
"alepha": "0.15.1",
|
|
36
36
|
"react": "^19"
|
|
37
37
|
},
|
|
38
38
|
"scripts": {
|
|
@@ -1,188 +1,202 @@
|
|
|
1
1
|
import { randomUUID } from "node:crypto";
|
|
2
2
|
import { Alepha } from "alepha";
|
|
3
3
|
import { DateTimeProvider } from "alepha/datetime";
|
|
4
|
-
import { $
|
|
5
|
-
import { HttpClient, ServerProvider } from "alepha/server";
|
|
6
|
-
import { $client } from "alepha/server/links";
|
|
7
|
-
import { AlephaServerSecurity } from "alepha/server/security";
|
|
8
|
-
import { describe, test } from "vitest";
|
|
4
|
+
import { $issuer, AlephaSecurity } from "alepha/security";
|
|
5
|
+
import { AlephaServer, HttpClient, ServerProvider } from "alepha/server";
|
|
9
6
|
import {
|
|
10
7
|
$auth,
|
|
11
8
|
alephaServerAuthRoutes,
|
|
12
9
|
type TokenResponse,
|
|
13
10
|
tokenResponseSchema,
|
|
14
|
-
tokensSchema
|
|
11
|
+
tokensSchema,
|
|
15
12
|
} from "alepha/server/auth";
|
|
16
|
-
import {
|
|
13
|
+
import { $client } from "alepha/server/links";
|
|
14
|
+
import { describe, test } from "vitest";
|
|
15
|
+
import { ReactAuth, type ReactAuthProvider } from "../index.ts";
|
|
17
16
|
|
|
18
17
|
describe("$auth", () => {
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
},
|
|
35
|
-
],
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
auth = $auth({
|
|
39
|
-
realm: this.realm,
|
|
40
|
-
credentials: {
|
|
41
|
-
account: () => user,
|
|
18
|
+
const user = {
|
|
19
|
+
id: randomUUID(),
|
|
20
|
+
name: "John Doe",
|
|
21
|
+
username: "john",
|
|
22
|
+
password: "***",
|
|
23
|
+
roles: ["admin"],
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
class App {
|
|
27
|
+
issuer = $issuer({
|
|
28
|
+
secret: "my-secret-key",
|
|
29
|
+
roles: [
|
|
30
|
+
{
|
|
31
|
+
name: "admin",
|
|
32
|
+
permissions: [{ name: "*" }],
|
|
42
33
|
},
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
34
|
+
],
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
auth = $auth({
|
|
38
|
+
issuer: this.issuer,
|
|
39
|
+
credentials: {
|
|
40
|
+
account: () => user,
|
|
41
|
+
},
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
api = $client<ReactAuthProvider>();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const userinfo = (alepha: Alepha, token?: string) =>
|
|
48
|
+
alepha
|
|
49
|
+
.inject(HttpClient)
|
|
50
|
+
.fetch(
|
|
51
|
+
`${alepha.inject(ServerProvider).hostname}${alephaServerAuthRoutes.userinfo}`,
|
|
52
|
+
{
|
|
53
|
+
method: "GET",
|
|
54
|
+
headers: {
|
|
55
|
+
authorization: `Bearer ${token}`,
|
|
58
56
|
},
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
57
|
+
},
|
|
58
|
+
)
|
|
59
|
+
.then((it) => it.data);
|
|
60
|
+
|
|
61
|
+
const login = (alepha: Alepha) =>
|
|
62
|
+
alepha
|
|
63
|
+
.inject(HttpClient)
|
|
64
|
+
.fetch(
|
|
65
|
+
`${alepha.inject(ServerProvider).hostname}${alephaServerAuthRoutes.token}?provider=auth`,
|
|
66
|
+
{
|
|
67
|
+
method: "POST",
|
|
68
|
+
body: JSON.stringify({
|
|
69
|
+
username: user.username,
|
|
70
|
+
password: user.password,
|
|
71
|
+
}),
|
|
72
|
+
schema: {
|
|
73
|
+
response: tokenResponseSchema,
|
|
76
74
|
},
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
75
|
+
},
|
|
76
|
+
);
|
|
77
|
+
|
|
78
|
+
const refresh = (alepha: Alepha, tokens: TokenResponse) =>
|
|
79
|
+
alepha
|
|
80
|
+
.inject(HttpClient)
|
|
81
|
+
.fetch(
|
|
82
|
+
`${alepha.inject(ServerProvider).hostname}${alephaServerAuthRoutes.refresh}?provider=auth`,
|
|
83
|
+
{
|
|
84
|
+
method: "POST",
|
|
85
|
+
body: JSON.stringify({
|
|
86
|
+
refresh_token: tokens.refresh_token!,
|
|
87
|
+
access_token: tokens.access_token,
|
|
88
|
+
}),
|
|
89
|
+
schema: {
|
|
90
|
+
response: tokensSchema,
|
|
93
91
|
},
|
|
94
|
-
|
|
92
|
+
},
|
|
93
|
+
);
|
|
94
|
+
|
|
95
|
+
test("should login with credentials", async ({ expect }) => {
|
|
96
|
+
const alepha = Alepha.create()
|
|
97
|
+
.with(AlephaServer)
|
|
98
|
+
.with(AlephaSecurity)
|
|
99
|
+
.with(App);
|
|
100
|
+
const auth = alepha.inject(ReactAuth);
|
|
101
|
+
await alepha.start();
|
|
102
|
+
|
|
103
|
+
expect(auth.user).toBeUndefined();
|
|
104
|
+
await auth.login("auth", {
|
|
105
|
+
username: user.username,
|
|
106
|
+
password: user.password,
|
|
107
|
+
hostname: alepha.inject(ServerProvider).hostname,
|
|
108
|
+
});
|
|
109
|
+
expect(auth.user).toEqual({
|
|
110
|
+
id: user.id,
|
|
111
|
+
name: user.name,
|
|
112
|
+
roles: user.roles,
|
|
113
|
+
username: user.username,
|
|
114
|
+
});
|
|
115
|
+
});
|
|
95
116
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
117
|
+
test("should get userinfo", async ({ expect }) => {
|
|
118
|
+
const alepha = Alepha.create()
|
|
119
|
+
.with(AlephaServer)
|
|
120
|
+
.with(AlephaSecurity)
|
|
121
|
+
.with(App);
|
|
122
|
+
await alepha.start();
|
|
100
123
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
hostname: alepha.inject(ServerProvider).hostname,
|
|
106
|
-
});
|
|
107
|
-
expect(auth.user).toEqual({
|
|
124
|
+
const { data: tokens } = await login(alepha);
|
|
125
|
+
|
|
126
|
+
expect(await userinfo(alepha, tokens.access_token)).toEqual({
|
|
127
|
+
user: {
|
|
108
128
|
id: user.id,
|
|
109
129
|
name: user.name,
|
|
110
130
|
roles: user.roles,
|
|
111
131
|
username: user.username,
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
const { data: tokens } = await login(alepha);
|
|
120
|
-
|
|
121
|
-
expect(await userinfo(alepha, tokens.access_token)).toEqual({
|
|
122
|
-
user: {
|
|
123
|
-
id: user.id,
|
|
124
|
-
name: user.name,
|
|
125
|
-
roles: user.roles,
|
|
126
|
-
username: user.username,
|
|
127
|
-
sessionId: expect.any(String),
|
|
128
|
-
},
|
|
129
|
-
api: {
|
|
130
|
-
prefix: "/api",
|
|
131
|
-
links: [],
|
|
132
|
-
},
|
|
133
|
-
});
|
|
132
|
+
sessionId: expect.any(String),
|
|
133
|
+
},
|
|
134
|
+
api: {
|
|
135
|
+
prefix: "/api",
|
|
136
|
+
links: [],
|
|
137
|
+
},
|
|
134
138
|
});
|
|
139
|
+
});
|
|
135
140
|
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
141
|
+
test("should reject expired token", async ({ expect }) => {
|
|
142
|
+
const alepha = Alepha.create()
|
|
143
|
+
.with(AlephaServer)
|
|
144
|
+
.with(AlephaSecurity)
|
|
145
|
+
.with(App);
|
|
146
|
+
await alepha.start();
|
|
139
147
|
|
|
140
|
-
|
|
148
|
+
const { data: tokens } = await login(alepha);
|
|
141
149
|
|
|
142
|
-
|
|
150
|
+
await alepha.inject(DateTimeProvider).travel(1, "hour");
|
|
143
151
|
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
});
|
|
152
|
+
expect(await userinfo(alepha, tokens.access_token)).toEqual({
|
|
153
|
+
api: {
|
|
154
|
+
prefix: "/api",
|
|
155
|
+
links: [],
|
|
156
|
+
},
|
|
150
157
|
});
|
|
158
|
+
});
|
|
151
159
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
160
|
+
test("should refresh expired token", async ({ expect }) => {
|
|
161
|
+
const alepha = Alepha.create()
|
|
162
|
+
.with(AlephaServer)
|
|
163
|
+
.with(AlephaSecurity)
|
|
164
|
+
.with(App);
|
|
165
|
+
await alepha.start();
|
|
155
166
|
|
|
156
|
-
|
|
167
|
+
const { data: tokens } = await login(alepha);
|
|
157
168
|
|
|
158
|
-
|
|
169
|
+
await alepha.inject(DateTimeProvider).travel(1, "hour");
|
|
159
170
|
|
|
160
|
-
|
|
171
|
+
const { data: tokens2 } = await refresh(alepha, tokens);
|
|
161
172
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
});
|
|
173
|
+
expect(await userinfo(alepha, tokens2.access_token)).toEqual({
|
|
174
|
+
user: {
|
|
175
|
+
id: user.id,
|
|
176
|
+
name: user.name,
|
|
177
|
+
roles: user.roles,
|
|
178
|
+
username: user.username,
|
|
179
|
+
},
|
|
180
|
+
api: {
|
|
181
|
+
prefix: "/api",
|
|
182
|
+
links: [],
|
|
183
|
+
},
|
|
174
184
|
});
|
|
185
|
+
});
|
|
175
186
|
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
187
|
+
test("should reject expired refresh token", async ({ expect }) => {
|
|
188
|
+
const alepha = Alepha.create()
|
|
189
|
+
.with(AlephaServer)
|
|
190
|
+
.with(AlephaSecurity)
|
|
191
|
+
.with(App);
|
|
192
|
+
await alepha.start();
|
|
179
193
|
|
|
180
|
-
|
|
194
|
+
const { data: tokens } = await login(alepha);
|
|
181
195
|
|
|
182
|
-
|
|
196
|
+
await alepha.inject(DateTimeProvider).travel(40, "days");
|
|
183
197
|
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
198
|
+
await expect(refresh(alepha, tokens)).rejects.toThrowError(
|
|
199
|
+
"Failed to refresh access token using the refresh token (issuer)",
|
|
200
|
+
);
|
|
201
|
+
});
|
|
188
202
|
});
|
package/src/auth/index.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
import { AlephaReact } from "@alepha/react";
|
|
2
2
|
import { $module } from "alepha";
|
|
3
3
|
import type { UserAccount } from "alepha/security";
|
|
4
|
-
import { ReactAuthProvider } from "./providers/ReactAuthProvider.ts";
|
|
5
|
-
import { ReactAuth } from "./services/ReactAuth.ts";
|
|
6
4
|
import { $auth, AlephaServerAuth } from "alepha/server/auth";
|
|
7
5
|
import { AlephaServerLinks } from "alepha/server/links";
|
|
6
|
+
import { ReactAuthProvider } from "./providers/ReactAuthProvider.ts";
|
|
7
|
+
import { ReactAuth } from "./services/ReactAuth.ts";
|
|
8
8
|
|
|
9
9
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
10
10
|
|
|
@@ -30,5 +30,11 @@ declare module "@alepha/react/router" {
|
|
|
30
30
|
export const AlephaReactAuth = $module({
|
|
31
31
|
name: "alepha.react.auth",
|
|
32
32
|
primitives: [$auth],
|
|
33
|
-
services: [
|
|
33
|
+
services: [
|
|
34
|
+
AlephaReact,
|
|
35
|
+
AlephaServerLinks,
|
|
36
|
+
AlephaServerAuth,
|
|
37
|
+
ReactAuthProvider,
|
|
38
|
+
ReactAuth,
|
|
39
|
+
],
|
|
34
40
|
});
|
|
@@ -3,7 +3,12 @@ import { $hook, $inject, Alepha } from "alepha";
|
|
|
3
3
|
import { $logger } from "alepha/logger";
|
|
4
4
|
import type { UserAccountToken } from "alepha/security";
|
|
5
5
|
import { HttpClient } from "alepha/server";
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
alephaServerAuthRoutes,
|
|
8
|
+
type Tokens,
|
|
9
|
+
tokenResponseSchema,
|
|
10
|
+
userinfoResponseSchema,
|
|
11
|
+
} from "alepha/server/auth";
|
|
7
12
|
import { LinkProvider } from "alepha/server/links";
|
|
8
13
|
|
|
9
14
|
/**
|
|
@@ -46,9 +51,12 @@ export class ReactAuth {
|
|
|
46
51
|
}
|
|
47
52
|
|
|
48
53
|
public async ping() {
|
|
49
|
-
const { data } = await this.httpClient.fetch(
|
|
50
|
-
|
|
51
|
-
|
|
54
|
+
const { data } = await this.httpClient.fetch(
|
|
55
|
+
alephaServerAuthRoutes.userinfo,
|
|
56
|
+
{
|
|
57
|
+
schema: { response: userinfoResponseSchema },
|
|
58
|
+
},
|
|
59
|
+
);
|
|
52
60
|
|
|
53
61
|
this.alepha.store.set("alepha.server.request.apiLinks", data.api);
|
|
54
62
|
this.alepha.store.set("alepha.server.request.user", data.user);
|
|
@@ -75,7 +83,9 @@ export class ReactAuth {
|
|
|
75
83
|
[extra: string]: any;
|
|
76
84
|
},
|
|
77
85
|
): Promise<Tokens> {
|
|
78
|
-
const realmParam = options.realm
|
|
86
|
+
const realmParam = options.realm
|
|
87
|
+
? `&realm=${encodeURIComponent(options.realm)}`
|
|
88
|
+
: "";
|
|
79
89
|
|
|
80
90
|
if (options.username || options.password) {
|
|
81
91
|
const { data } = await this.httpClient.fetch(
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { Async } from "alepha";
|
|
1
2
|
import {
|
|
2
3
|
DateTimeProvider,
|
|
3
4
|
type DurationLike,
|
|
@@ -13,7 +14,6 @@ import {
|
|
|
13
14
|
} from "react";
|
|
14
15
|
import { useAlepha } from "./useAlepha.ts";
|
|
15
16
|
import { useInject } from "./useInject.ts";
|
|
16
|
-
import type { Async } from "alepha";
|
|
17
17
|
|
|
18
18
|
/**
|
|
19
19
|
* Hook for handling async actions with automatic error handling and event emission.
|
|
@@ -207,7 +207,6 @@ export function useAction<Args extends any[], Result = void>(
|
|
|
207
207
|
await options.onSuccess(result);
|
|
208
208
|
}
|
|
209
209
|
|
|
210
|
-
|
|
211
210
|
return result;
|
|
212
211
|
} catch (err) {
|
|
213
212
|
// Ignore abort errors
|
package/src/core/index.ts
CHANGED
|
@@ -2,17 +2,17 @@ import { $module } from "alepha";
|
|
|
2
2
|
|
|
3
3
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
4
4
|
|
|
5
|
-
export { default as ClientOnly } from "./components/ClientOnly.tsx";
|
|
6
5
|
export type * from "./components/ClientOnly.tsx";
|
|
7
|
-
export { default as
|
|
6
|
+
export { default as ClientOnly } from "./components/ClientOnly.tsx";
|
|
8
7
|
export type * from "./components/ErrorBoundary.tsx";
|
|
9
|
-
export
|
|
8
|
+
export { default as ErrorBoundary } from "./components/ErrorBoundary.tsx";
|
|
10
9
|
export * from "./contexts/AlephaContext.ts";
|
|
10
|
+
export * from "./contexts/AlephaProvider.tsx";
|
|
11
11
|
export * from "./hooks/useAction.ts";
|
|
12
12
|
export * from "./hooks/useAlepha.ts";
|
|
13
|
+
export * from "./hooks/useClient.ts";
|
|
13
14
|
export * from "./hooks/useEvents.ts";
|
|
14
15
|
export * from "./hooks/useInject.ts";
|
|
15
|
-
export * from "./hooks/useClient.ts";
|
|
16
16
|
export * from "./hooks/useStore.ts";
|
|
17
17
|
|
|
18
18
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
@@ -3,12 +3,10 @@ import { TypeBoxError } from "alepha";
|
|
|
3
3
|
export class FormValidationError extends TypeBoxError {
|
|
4
4
|
readonly name = "ValidationError";
|
|
5
5
|
|
|
6
|
-
constructor(
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
}
|
|
11
|
-
) {
|
|
6
|
+
constructor(options: {
|
|
7
|
+
message: string;
|
|
8
|
+
path: string;
|
|
9
|
+
}) {
|
|
12
10
|
super({
|
|
13
11
|
message: options.message,
|
|
14
12
|
instancePath: options.path,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { useAlepha } from "@alepha/react";
|
|
2
|
-
import type { FormModel } from "../services/FormModel.ts";
|
|
3
2
|
import { type TObject, TypeBoxError } from "alepha";
|
|
4
3
|
import { useEffect, useState } from "react";
|
|
4
|
+
import type { FormModel } from "../services/FormModel.ts";
|
|
5
5
|
|
|
6
6
|
export interface UseFormStateReturn {
|
|
7
7
|
loading: boolean;
|
package/src/form/index.ts
CHANGED
|
@@ -3,10 +3,10 @@ import { $module } from "alepha";
|
|
|
3
3
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
4
4
|
|
|
5
5
|
export { default as FormState } from "./components/FormState.tsx";
|
|
6
|
+
export * from "./errors/FormValidationError.ts";
|
|
6
7
|
export * from "./hooks/useForm.ts";
|
|
7
8
|
export * from "./hooks/useFormState.ts";
|
|
8
9
|
export * from "./services/FormModel.ts";
|
|
9
|
-
export * from "./errors/FormValidationError.ts";
|
|
10
10
|
|
|
11
11
|
// ---------------------------------------------------------------------------------------------------------------------
|
|
12
12
|
|
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
import type { TArray } from "alepha";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
$inject,
|
|
4
|
+
Alepha,
|
|
5
|
+
type Static,
|
|
6
|
+
type TObject,
|
|
7
|
+
type TSchema,
|
|
8
|
+
t,
|
|
9
|
+
} from "alepha";
|
|
3
10
|
import { $logger } from "alepha/logger";
|
|
4
11
|
import type { ChangeEvent, InputHTMLAttributes } from "react";
|
|
5
12
|
|
|
@@ -217,7 +224,6 @@ export class FormModel<T extends TObject> {
|
|
|
217
224
|
}
|
|
218
225
|
|
|
219
226
|
if (prop in schema.properties) {
|
|
220
|
-
|
|
221
227
|
// // it's a nested object, create another proxy
|
|
222
228
|
// if (t.schema.isObject(schema.properties[prop])) {
|
|
223
229
|
// return this.createProxyFromSchema(
|
|
@@ -284,13 +290,17 @@ export class FormModel<T extends TObject> {
|
|
|
284
290
|
options.onChange(key, typedValue, context.store);
|
|
285
291
|
}
|
|
286
292
|
|
|
287
|
-
this.alepha.events.emit(
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
293
|
+
this.alepha.events.emit(
|
|
294
|
+
"form:change",
|
|
295
|
+
{
|
|
296
|
+
id: this.id,
|
|
297
|
+
path: path,
|
|
298
|
+
value: typedValue,
|
|
299
|
+
},
|
|
300
|
+
{
|
|
301
|
+
catch: true,
|
|
302
|
+
},
|
|
303
|
+
);
|
|
294
304
|
|
|
295
305
|
if (sync) {
|
|
296
306
|
const inputElement = window.document.querySelector(
|
|
@@ -408,14 +418,10 @@ export class FormModel<T extends TObject> {
|
|
|
408
418
|
set,
|
|
409
419
|
form: this,
|
|
410
420
|
required,
|
|
411
|
-
items: this.createProxyFromSchema(
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
parent: key,
|
|
416
|
-
store: context.store,
|
|
417
|
-
},
|
|
418
|
-
)
|
|
421
|
+
items: this.createProxyFromSchema(options, field, {
|
|
422
|
+
parent: key,
|
|
423
|
+
store: context.store,
|
|
424
|
+
}),
|
|
419
425
|
} as ObjectInputField<any>;
|
|
420
426
|
}
|
|
421
427
|
|
|
@@ -460,7 +466,8 @@ export class FormModel<T extends TObject> {
|
|
|
460
466
|
// Handle string representations from Select components (Yes/No dropdown)
|
|
461
467
|
if (input === "true") return true;
|
|
462
468
|
if (input === "false") return false;
|
|
463
|
-
if (input === "" || input === null || input === undefined)
|
|
469
|
+
if (input === "" || input === null || input === undefined)
|
|
470
|
+
return undefined;
|
|
464
471
|
// Handle actual boolean values
|
|
465
472
|
return !!input;
|
|
466
473
|
}
|
|
@@ -520,12 +527,11 @@ export interface FormEventLike {
|
|
|
520
527
|
stopPropagation?: () => void;
|
|
521
528
|
}
|
|
522
529
|
|
|
523
|
-
export type InputField<T extends TSchema> =
|
|
524
|
-
T
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
: BaseInputField;
|
|
530
|
+
export type InputField<T extends TSchema> = T extends TObject
|
|
531
|
+
? ObjectInputField<T>
|
|
532
|
+
: T extends TArray<infer U>
|
|
533
|
+
? ArrayInputField<U>
|
|
534
|
+
: BaseInputField;
|
|
529
535
|
|
|
530
536
|
export interface BaseInputField {
|
|
531
537
|
path: string;
|
|
@@ -542,7 +548,7 @@ export interface ObjectInputField<T extends TObject> extends BaseInputField {
|
|
|
542
548
|
}
|
|
543
549
|
|
|
544
550
|
export interface ArrayInputField<T extends TSchema> extends BaseInputField {
|
|
545
|
-
items: Array<InputField<T
|
|
551
|
+
items: Array<InputField<T>>;
|
|
546
552
|
}
|
|
547
553
|
|
|
548
554
|
export type InputHTMLAttributesLike = Pick<
|
|
@@ -113,7 +113,8 @@ export class SeoExpander {
|
|
|
113
113
|
meta.push({
|
|
114
114
|
name: "twitter:card",
|
|
115
115
|
content:
|
|
116
|
-
head.twitter?.card ??
|
|
116
|
+
head.twitter?.card ??
|
|
117
|
+
(twitterImage ? "summary_large_image" : "summary"),
|
|
117
118
|
});
|
|
118
119
|
}
|
|
119
120
|
if (head.url) {
|