@molecule/app-bonds-default-react 2.0.0 → 3.0.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 +103 -42
- package/dist/auth.d.ts +38 -33
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +63 -42
- package/dist/index.d.ts +27 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +27 -0
- package/dist/setup.d.ts +1 -1
- package/dist/setup.js +1 -1
- package/package.json +18 -17
package/README.md
CHANGED
|
@@ -3,7 +3,7 @@ AUTO-GENERATED — DO NOT EDIT THIS FILE.
|
|
|
3
3
|
Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
|
|
4
4
|
Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
|
|
5
5
|
To change this document, edit the module-level JSDoc in src/index.ts.
|
|
6
|
-
Generated: 2026-10-
|
|
6
|
+
Generated: 2026-10-31T10:35:03.226Z
|
|
7
7
|
-->
|
|
8
8
|
|
|
9
9
|
# @molecule/app-bonds-default-react
|
|
@@ -39,6 +39,17 @@ React fleet stack, replacing the byte-identical per-app files:
|
|
|
39
39
|
argument is given. The profile is never inferred from `authConfig`, so an
|
|
40
40
|
untyped call keeps the default contract.
|
|
41
41
|
|
|
42
|
+
**What this client already handles**
|
|
43
|
+
|
|
44
|
+
- `getAccessToken()` returns a stored token during restore; only 401/403 ends
|
|
45
|
+
the session, while network/5xx failures retain it.
|
|
46
|
+
- `tokenKey`/`storagePrefix`, `authScheme`, `mapAuthResponse`,
|
|
47
|
+
`currentUserEndpoint`, and `profileEndpoint`/`profileUpdateMethod` adapt the API.
|
|
48
|
+
- `logout()` settles local sign-out; `refresh()` uses a stored refresh token.
|
|
49
|
+
|
|
50
|
+
This factory adds no render gate: `AuthProvider` renders children immediately.
|
|
51
|
+
The HTTP-sync variants gate requests during restore, not rendering.
|
|
52
|
+
|
|
42
53
|
> **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
|
|
43
54
|
> It is written to be read by coding agents as much as by people, and is generated from this
|
|
44
55
|
> package's source — edit `src/index.ts` JSDoc, not this file.
|
|
@@ -72,6 +83,24 @@ bootstrapApp({
|
|
|
72
83
|
})
|
|
73
84
|
```
|
|
74
85
|
|
|
86
|
+
Adapt a nested token and `Token` header without another session store.
|
|
87
|
+
|
|
88
|
+
```ts
|
|
89
|
+
import { createDefaultAuthClient } from '@molecule/app-bonds-default-react'
|
|
90
|
+
|
|
91
|
+
const { authClient, setupAuthDefault } = createDefaultAuthClient({
|
|
92
|
+
tokenKey: 'app.jwt',
|
|
93
|
+
authScheme: 'Token',
|
|
94
|
+
currentUserEndpoint: '/user',
|
|
95
|
+
profileEndpoint: '/user',
|
|
96
|
+
profileUpdateMethod: 'PUT',
|
|
97
|
+
mapAuthResponse: (data) => {
|
|
98
|
+
const { token, ...user } = (data as { user: { token: string; id: string } }).user
|
|
99
|
+
return { accessToken: token, user }
|
|
100
|
+
},
|
|
101
|
+
})
|
|
102
|
+
```
|
|
103
|
+
|
|
75
104
|
## Type
|
|
76
105
|
|
|
77
106
|
`feature`
|
|
@@ -113,7 +142,9 @@ this same wiring. Pass `stripApiPrefix: true` when the app uses
|
|
|
113
142
|
when the API reads another scheme than `Bearer`: it is an option of THIS
|
|
114
143
|
constructor (`createDefaultHttpClientWithAuthBearer({ authScheme: 'Token', … })`,
|
|
115
144
|
or `createFetchClient({ baseURL, authScheme: 'Token' })` directly), never a
|
|
116
|
-
property of the client it returns.
|
|
145
|
+
property of the client it returns. The client is built with the same scheme, so
|
|
146
|
+
a token the client also holds — the one a default auth bond's token sync gives
|
|
147
|
+
it — is sent with that scheme too, not as `Bearer`.
|
|
117
148
|
|
|
118
149
|
```typescript
|
|
119
150
|
function createDefaultHttpClientWithAuthBearer(opts: {
|
|
@@ -121,26 +152,33 @@ function createDefaultHttpClientWithAuthBearer(opts: {
|
|
|
121
152
|
withCredentials?: boolean
|
|
122
153
|
stripApiPrefix?: boolean
|
|
123
154
|
getToken: () => string | null | undefined
|
|
155
|
+
/**
|
|
156
|
+
* The scheme of the `Authorization` header the token from `getToken()` is sent
|
|
157
|
+
* with: `Bearer` (default), or what the API expects, e.g. `Token` for an API
|
|
158
|
+
* that reads `Authorization: Token <jwt>`. Pass it to this constructor — the
|
|
159
|
+
* same option `@molecule/app-http`'s client config calls `authScheme`, and not
|
|
160
|
+
* a property of the client that comes back.
|
|
161
|
+
*/
|
|
124
162
|
authScheme?: string
|
|
125
|
-
}): {
|
|
163
|
+
}): {
|
|
164
|
+
httpClient: HttpClient
|
|
165
|
+
setupHttpDefault: () => void
|
|
166
|
+
}
|
|
126
167
|
```
|
|
127
168
|
|
|
128
169
|
#### `createDefaultAuthClient(authConfig)`
|
|
129
170
|
|
|
130
171
|
Builds the default JWT auth client + wires it into `@molecule/app-auth`.
|
|
131
|
-
|
|
132
|
-
Replaces the 16-line per-app `bonds/auth-default.ts` that 93 fleet
|
|
133
|
-
apps shipped byte-identically. Apps pass their own `authConfig`
|
|
134
|
-
(which lives in `src/config.ts`).
|
|
172
|
+
Apps pass their own `authConfig` (which lives in `src/config.ts`).
|
|
135
173
|
|
|
136
174
|
`TUser` is the profile the app's pages read, and may be any JSON object type —
|
|
137
|
-
including a profile with NO `id` (`{ username, email, bio, image }`),
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
`
|
|
142
|
-
|
|
143
|
-
|
|
175
|
+
including a profile with NO `id` (`{ username, email, bio, image }`), as in the
|
|
176
|
+
factory it wraps. Never add a synthetic `id` to satisfy a type: nothing in the
|
|
177
|
+
client reads it, and the fabricated value then travels into storage, `getUser()`
|
|
178
|
+
and every page. Omit the type parameter and the default is `UserProfile`, whose
|
|
179
|
+
`id: string` stays required — the profile is never inferred from `authConfig`
|
|
180
|
+
(not even from an annotated config variable), so an untyped wrapper call keeps
|
|
181
|
+
the default contract.
|
|
144
182
|
|
|
145
183
|
Type the config with the SAME type (`const authConfig: AuthClientConfig<MyUser> = { … }`)
|
|
146
184
|
so its `mapAuthResponse` is checked against `MyUser`, and name that type at the
|
|
@@ -150,12 +188,19 @@ page that reads `undefined`. An annotated config alone does not supply the type
|
|
|
150
188
|
argument — that is a 1.8 caller shape this major changes (see
|
|
151
189
|
`AuthClientConfig`'s migration note in `@molecule/app-auth`).
|
|
152
190
|
|
|
153
|
-
`TRegister` is the app's own registration fields (
|
|
191
|
+
`TRegister` is the app's own extra registration fields (for example
|
|
192
|
+
`{ username: string }`), passed on to `@molecule/app-auth`'s factory so
|
|
193
|
+
`authClient.register({ username, email, password })` type-checks with no cast.
|
|
154
194
|
|
|
155
195
|
_(description truncated — read the full text in `node_modules/@molecule/app-bonds-default-react/dist/auth.d.ts`.)_
|
|
156
196
|
|
|
157
197
|
```typescript
|
|
158
|
-
function createDefaultAuthClient
|
|
198
|
+
function createDefaultAuthClient<
|
|
199
|
+
TUser extends AuthProfileConstraint<TUser> = UserProfile,
|
|
200
|
+
TRegister extends RegisterExtraShape<TRegister> = Record<never, never>,
|
|
201
|
+
>(
|
|
202
|
+
authConfig: NoInfer<AuthClientConfigOptions<TUser>>,
|
|
203
|
+
): {
|
|
159
204
|
authClient: AuthClient<TUser, TRegister>
|
|
160
205
|
setupAuthDefault: () => void
|
|
161
206
|
}
|
|
@@ -164,28 +209,35 @@ function createDefaultAuthClient(authConfig: NoInfer<AuthClientConfigOptions<TUs
|
|
|
164
209
|
#### `createDefaultAuthClientWithHttpSync(authConfig)`
|
|
165
210
|
|
|
166
211
|
Variant of `createDefaultAuthClient` that also keeps the bonded
|
|
167
|
-
`@molecule/app-http` client's bearer token in sync with auth
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
212
|
+
`@molecule/app-http` client's bearer token in sync with the auth client's session.
|
|
213
|
+
Required by apps whose http client must carry the JWT on every request — without
|
|
214
|
+
this wiring, authed endpoints return 401 after page reloads or token refresh.
|
|
215
|
+
|
|
216
|
+
The sync is a LIVE read: `@molecule/app-http`'s `setAuthTokenSource` registers the auth
|
|
217
|
+
client's token, and every client the app bonds — one bonded after this setup too (the
|
|
218
|
+
scaffold wires auth BEFORE http, so a token copied into the client present at setup was
|
|
219
|
+
never sent) — reads it on each request. A session adopted with no
|
|
220
|
+
auth event (`useAuth().setSession()`, an OAuth callback's `setAccessToken()`, the same
|
|
221
|
+
profile under a new token) is therefore sent by the next request, not the previous
|
|
222
|
+
account's bearer. A transport without `setAuthTokenResolver` is followed through a request
|
|
223
|
+
interceptor (see `setAuthTokenSource` for which request first carries the change), and
|
|
224
|
+
auth events still push the token to it.
|
|
225
|
+
|
|
226
|
+
A cookie session (no body token) resolves to `null`, so the request carries NO
|
|
227
|
+
`Authorization` header — a kept bearer would answer as the PRIOR session.
|
|
228
|
+
Requests also wait for the session restore, so a page that fetches viewer data on mount
|
|
229
|
+
cannot race it (see {@link registerAuthGate}).
|
|
171
230
|
|
|
172
231
|
The profile type is `createDefaultAuthClient`'s: a JSON object type of named
|
|
173
232
|
fields (see `AuthProfileShape`), including an app profile with no `id`.
|
|
174
233
|
|
|
175
|
-
Hydrates the HTTP client's token from the persisted auth state
|
|
176
|
-
on setup, then listens for auth events to keep them aligned, and holds
|
|
177
|
-
requests until the session restore settles so a page effect that fetches
|
|
178
|
-
viewer-dependent data on mount cannot race it (see {@link registerAuthGate}).
|
|
179
|
-
|
|
180
|
-
The hydrated token is delivered through `@molecule/app-http`'s token source, not
|
|
181
|
-
through a client read at setup: the scaffold wires auth BEFORE http, so a client
|
|
182
|
-
read here is the unbonded fallback and the restored session's token would land on a
|
|
183
|
-
client nothing sends through (xr-molecule-flash-m156 — a reload sent every request
|
|
184
|
-
with no `Authorization` header). The source is applied to the client WHENEVER it is
|
|
185
|
-
bonded, so setup order no longer decides whether the first request is authorized.
|
|
186
|
-
|
|
187
234
|
```typescript
|
|
188
|
-
function createDefaultAuthClientWithHttpSync
|
|
235
|
+
function createDefaultAuthClientWithHttpSync<
|
|
236
|
+
TUser extends AuthProfileConstraint<TUser> = UserProfile,
|
|
237
|
+
TRegister extends RegisterExtraShape<TRegister> = Record<never, never>,
|
|
238
|
+
>(
|
|
239
|
+
authConfig: NoInfer<AuthClientConfigOptions<TUser>>,
|
|
240
|
+
): {
|
|
189
241
|
authClient: AuthClient<TUser, TRegister>
|
|
190
242
|
setupAuthDefault: () => void
|
|
191
243
|
}
|
|
@@ -196,15 +248,18 @@ function createDefaultAuthClientWithHttpSync(authConfig: NoInfer<AuthClientConfi
|
|
|
196
248
|
Most aggressive variant — bonds a fetch-based HTTP client with the
|
|
197
249
|
given `baseURL` (so molecule pkg `useGet('/path')` calls hit
|
|
198
250
|
`${baseURL}/path` instead of the SPA's catch-all route), AND keeps
|
|
199
|
-
its bearer token in sync with auth
|
|
251
|
+
its bearer token in sync with the auth client's session, read on every
|
|
252
|
+
request: a session adopted with no auth event (`setSession()`,
|
|
253
|
+
`setAccessToken()`) is sent by the next request. The token uses the auth
|
|
254
|
+
config's `authScheme` (default `Bearer`); a client bonded after this setup
|
|
255
|
+
gets the same source and auth events.
|
|
200
256
|
|
|
201
257
|
The auth client restores a stored token AND user SYNCHRONOUSLY, so the first
|
|
202
258
|
render is the signed-in UI — do not gate a protected page (or a provider of
|
|
203
259
|
yours) on a current-user request. This bond registers `whenRestored()` as the
|
|
204
260
|
HTTP gate, so requests still wait for the background restore/verification to
|
|
205
261
|
settle; only `useAuth().status === 'loading'` means "no decision yet"
|
|
206
|
-
(
|
|
207
|
-
on every navigation).
|
|
262
|
+
(a page that waits on its own `/me` pays a round trip on every navigation).
|
|
208
263
|
|
|
209
264
|
Used by apps where molecule packages render pricing / billing /
|
|
210
265
|
other authed JSON-fetching screens that need both behaviors.
|
|
@@ -217,10 +272,16 @@ The profile type is `createDefaultAuthClient`'s: a JSON object type of named
|
|
|
217
272
|
fields (see `AuthProfileShape`), including an app profile with no `id`.
|
|
218
273
|
|
|
219
274
|
```typescript
|
|
220
|
-
function createDefaultAuthClientWithFetchClient
|
|
275
|
+
function createDefaultAuthClientWithFetchClient<
|
|
276
|
+
TUser extends AuthProfileConstraint<TUser> = UserProfile,
|
|
277
|
+
TRegister extends RegisterExtraShape<TRegister> = Record<never, never>,
|
|
278
|
+
>(
|
|
221
279
|
authConfig: NoInfer<AuthClientConfigOptions<TUser>>,
|
|
222
280
|
fetchClientOptions: { baseURL: string; withCredentials?: boolean },
|
|
223
|
-
): {
|
|
281
|
+
): {
|
|
282
|
+
authClient: AuthClient<TUser, TRegister>
|
|
283
|
+
setupAuthDefault: () => void
|
|
284
|
+
}
|
|
224
285
|
```
|
|
225
286
|
|
|
226
287
|
#### `bootstrapApp(opts)`
|
|
@@ -246,7 +307,7 @@ function bootstrapApp(opts: {
|
|
|
246
307
|
Returns (and lazily constructs) the shared default CSS-variables theme provider.
|
|
247
308
|
|
|
248
309
|
```typescript
|
|
249
|
-
function getDefaultThemeProvider():
|
|
310
|
+
function getDefaultThemeProvider(): ReturnType<typeof createCSSVariablesThemeProvider>
|
|
250
311
|
```
|
|
251
312
|
|
|
252
313
|
#### `setupAppFontsArimo()`
|
|
@@ -282,7 +343,7 @@ WITHOUT this, `createStore()` throws
|
|
|
282
343
|
`@molecule/app-state: No provider set. Call setProvider() first` — nothing
|
|
283
344
|
else in a scaffolded app wires the `state` bond, so the documented
|
|
284
345
|
`createStore()` + `useStore()` pattern failed at the first render and every
|
|
285
|
-
page came up blank while `tsc` passed
|
|
346
|
+
page came up blank while `tsc` passed.
|
|
286
347
|
|
|
287
348
|
```typescript
|
|
288
349
|
function setupAppStateSimple(): void
|
|
@@ -343,7 +404,7 @@ function setupAllDefaultBonds(): void
|
|
|
343
404
|
|
|
344
405
|
Peer dependencies:
|
|
345
406
|
|
|
346
|
-
- `@molecule/app-auth` ^
|
|
407
|
+
- `@molecule/app-auth` ^3.0.0
|
|
347
408
|
- `@molecule/app-charts` ^1.0.1
|
|
348
409
|
- `@molecule/app-charts-chartjs` ^1.0.1
|
|
349
410
|
- `@molecule/app-client-search` ^1.0.0
|
|
@@ -356,7 +417,7 @@ Peer dependencies:
|
|
|
356
417
|
- `@molecule/app-drag-drop-dndkit` ^1.0.1
|
|
357
418
|
- `@molecule/app-fonts` ^1.0.1
|
|
358
419
|
- `@molecule/app-fonts-arimo` ^1.0.1
|
|
359
|
-
- `@molecule/app-http` ^1.0
|
|
420
|
+
- `@molecule/app-http` ^1.5.0
|
|
360
421
|
- `@molecule/app-icons` ^1.0.1
|
|
361
422
|
- `@molecule/app-icons-molecule` ^1.0.1
|
|
362
423
|
- `@molecule/app-keyboard-shortcuts` ^1.0.1
|
package/dist/auth.d.ts
CHANGED
|
@@ -30,7 +30,9 @@ export declare function createDefaultHttpClient(baseURL: string): {
|
|
|
30
30
|
* when the API reads another scheme than `Bearer`: it is an option of THIS
|
|
31
31
|
* constructor (`createDefaultHttpClientWithAuthBearer({ authScheme: 'Token', … })`,
|
|
32
32
|
* or `createFetchClient({ baseURL, authScheme: 'Token' })` directly), never a
|
|
33
|
-
* property of the client it returns.
|
|
33
|
+
* property of the client it returns. The client is built with the same scheme, so
|
|
34
|
+
* a token the client also holds — the one a default auth bond's token sync gives
|
|
35
|
+
* it — is sent with that scheme too, not as `Bearer`.
|
|
34
36
|
*/
|
|
35
37
|
export declare function createDefaultHttpClientWithAuthBearer(opts: {
|
|
36
38
|
baseURL: string;
|
|
@@ -51,19 +53,16 @@ export declare function createDefaultHttpClientWithAuthBearer(opts: {
|
|
|
51
53
|
};
|
|
52
54
|
/**
|
|
53
55
|
* Builds the default JWT auth client + wires it into `@molecule/app-auth`.
|
|
54
|
-
*
|
|
55
|
-
* Replaces the 16-line per-app `bonds/auth-default.ts` that 93 fleet
|
|
56
|
-
* apps shipped byte-identically. Apps pass their own `authConfig`
|
|
57
|
-
* (which lives in `src/config.ts`).
|
|
56
|
+
* Apps pass their own `authConfig` (which lives in `src/config.ts`).
|
|
58
57
|
*
|
|
59
58
|
* `TUser` is the profile the app's pages read, and may be any JSON object type —
|
|
60
|
-
* including a profile with NO `id` (`{ username, email, bio, image }`),
|
|
61
|
-
*
|
|
62
|
-
*
|
|
63
|
-
*
|
|
64
|
-
* `
|
|
65
|
-
*
|
|
66
|
-
*
|
|
59
|
+
* including a profile with NO `id` (`{ username, email, bio, image }`), as in the
|
|
60
|
+
* factory it wraps. Never add a synthetic `id` to satisfy a type: nothing in the
|
|
61
|
+
* client reads it, and the fabricated value then travels into storage, `getUser()`
|
|
62
|
+
* and every page. Omit the type parameter and the default is `UserProfile`, whose
|
|
63
|
+
* `id: string` stays required — the profile is never inferred from `authConfig`
|
|
64
|
+
* (not even from an annotated config variable), so an untyped wrapper call keeps
|
|
65
|
+
* the default contract.
|
|
67
66
|
*
|
|
68
67
|
* Type the config with the SAME type (`const authConfig: AuthClientConfig<MyUser> = { … }`)
|
|
69
68
|
* so its `mapAuthResponse` is checked against `MyUser`, and name that type at the
|
|
@@ -73,9 +72,10 @@ export declare function createDefaultHttpClientWithAuthBearer(opts: {
|
|
|
73
72
|
* argument — that is a 1.8 caller shape this major changes (see
|
|
74
73
|
* `AuthClientConfig`'s migration note in `@molecule/app-auth`).
|
|
75
74
|
*
|
|
76
|
-
* `TRegister` is the app's own registration fields (
|
|
77
|
-
*
|
|
75
|
+
* `TRegister` is the app's own extra registration fields (for example
|
|
76
|
+
* `{ username: string }`), passed on to `@molecule/app-auth`'s factory so
|
|
78
77
|
* `authClient.register({ username, email, password })` type-checks with no cast.
|
|
78
|
+
* See the module guidance for the lifecycle and API adaptations already included.
|
|
79
79
|
*
|
|
80
80
|
* @example
|
|
81
81
|
* ```ts
|
|
@@ -93,25 +93,27 @@ export declare function createDefaultAuthClient<TUser extends AuthProfileConstra
|
|
|
93
93
|
};
|
|
94
94
|
/**
|
|
95
95
|
* Variant of `createDefaultAuthClient` that also keeps the bonded
|
|
96
|
-
* `@molecule/app-http` client's bearer token in sync with auth
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
96
|
+
* `@molecule/app-http` client's bearer token in sync with the auth client's session.
|
|
97
|
+
* Required by apps whose http client must carry the JWT on every request — without
|
|
98
|
+
* this wiring, authed endpoints return 401 after page reloads or token refresh.
|
|
99
|
+
*
|
|
100
|
+
* The sync is a LIVE read: `@molecule/app-http`'s `setAuthTokenSource` registers the auth
|
|
101
|
+
* client's token, and every client the app bonds — one bonded after this setup too (the
|
|
102
|
+
* scaffold wires auth BEFORE http, so a token copied into the client present at setup was
|
|
103
|
+
* never sent) — reads it on each request. A session adopted with no
|
|
104
|
+
* auth event (`useAuth().setSession()`, an OAuth callback's `setAccessToken()`, the same
|
|
105
|
+
* profile under a new token) is therefore sent by the next request, not the previous
|
|
106
|
+
* account's bearer. A transport without `setAuthTokenResolver` is followed through a request
|
|
107
|
+
* interceptor (see `setAuthTokenSource` for which request first carries the change), and
|
|
108
|
+
* auth events still push the token to it.
|
|
109
|
+
*
|
|
110
|
+
* A cookie session (no body token) resolves to `null`, so the request carries NO
|
|
111
|
+
* `Authorization` header — a kept bearer would answer as the PRIOR session.
|
|
112
|
+
* Requests also wait for the session restore, so a page that fetches viewer data on mount
|
|
113
|
+
* cannot race it (see {@link registerAuthGate}).
|
|
100
114
|
*
|
|
101
115
|
* The profile type is `createDefaultAuthClient`'s: a JSON object type of named
|
|
102
116
|
* fields (see `AuthProfileShape`), including an app profile with no `id`.
|
|
103
|
-
*
|
|
104
|
-
* Hydrates the HTTP client's token from the persisted auth state
|
|
105
|
-
* on setup, then listens for auth events to keep them aligned, and holds
|
|
106
|
-
* requests until the session restore settles so a page effect that fetches
|
|
107
|
-
* viewer-dependent data on mount cannot race it (see {@link registerAuthGate}).
|
|
108
|
-
*
|
|
109
|
-
* The hydrated token is delivered through `@molecule/app-http`'s token source, not
|
|
110
|
-
* through a client read at setup: the scaffold wires auth BEFORE http, so a client
|
|
111
|
-
* read here is the unbonded fallback and the restored session's token would land on a
|
|
112
|
-
* client nothing sends through (xr-molecule-flash-m156 — a reload sent every request
|
|
113
|
-
* with no `Authorization` header). The source is applied to the client WHENEVER it is
|
|
114
|
-
* bonded, so setup order no longer decides whether the first request is authorized.
|
|
115
117
|
*/
|
|
116
118
|
export declare function createDefaultAuthClientWithHttpSync<TUser extends AuthProfileConstraint<TUser> = UserProfile, TRegister extends RegisterExtraShape<TRegister> = Record<never, never>>(authConfig: NoInfer<AuthClientConfigOptions<TUser>>): {
|
|
117
119
|
authClient: AuthClient<TUser, TRegister>;
|
|
@@ -121,15 +123,18 @@ export declare function createDefaultAuthClientWithHttpSync<TUser extends AuthPr
|
|
|
121
123
|
* Most aggressive variant — bonds a fetch-based HTTP client with the
|
|
122
124
|
* given `baseURL` (so molecule pkg `useGet('/path')` calls hit
|
|
123
125
|
* `${baseURL}/path` instead of the SPA's catch-all route), AND keeps
|
|
124
|
-
* its bearer token in sync with auth
|
|
126
|
+
* its bearer token in sync with the auth client's session, read on every
|
|
127
|
+
* request: a session adopted with no auth event (`setSession()`,
|
|
128
|
+
* `setAccessToken()`) is sent by the next request. The token uses the auth
|
|
129
|
+
* config's `authScheme` (default `Bearer`); a client bonded after this setup
|
|
130
|
+
* gets the same source and auth events.
|
|
125
131
|
*
|
|
126
132
|
* The auth client restores a stored token AND user SYNCHRONOUSLY, so the first
|
|
127
133
|
* render is the signed-in UI — do not gate a protected page (or a provider of
|
|
128
134
|
* yours) on a current-user request. This bond registers `whenRestored()` as the
|
|
129
135
|
* HTTP gate, so requests still wait for the background restore/verification to
|
|
130
136
|
* settle; only `useAuth().status === 'loading'` means "no decision yet"
|
|
131
|
-
* (
|
|
132
|
-
* on every navigation).
|
|
137
|
+
* (a page that waits on its own `/me` pays a round trip on every navigation).
|
|
133
138
|
*
|
|
134
139
|
* Used by apps where molecule packages render pricing / billing /
|
|
135
140
|
* other authed JSON-fetching screens that need both behaviors.
|
package/dist/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,uBAAuB,EAEvB,qBAAqB,EACrB,kBAAkB,EAClB,WAAW,EACZ,MAAM,oBAAoB,CAAA;AAE3B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,uBAAuB,EAEvB,qBAAqB,EACrB,kBAAkB,EAClB,WAAW,EACZ,MAAM,oBAAoB,CAAA;AAE3B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAsCpD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG;IACxD,UAAU,EAAE,UAAU,CAAA;IACtB,gBAAgB,EAAE,MAAM,IAAI,CAAA;CAC7B,CAMA;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,qCAAqC,CAAC,IAAI,EAAE;IAC1D,OAAO,EAAE,MAAM,CAAA;IACf,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB,QAAQ,EAAE,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IACzC;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;CACpB,GAAG;IACF,UAAU,EAAE,UAAU,CAAA;IACtB,gBAAgB,EAAE,MAAM,IAAI,CAAA;CAC7B,CAuBA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAmCG;AACH,wBAAgB,uBAAuB,CACrC,KAAK,SAAS,qBAAqB,CAAC,KAAK,CAAC,GAAG,WAAW,EACxD,SAAS,SAAS,kBAAkB,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAEtE,UAAU,EAAE,OAAO,CAAC,uBAAuB,CAAC,KAAK,CAAC,CAAC,GAClD;IACD,UAAU,EAAE,UAAU,CAAC,KAAK,EAAE,SAAS,CAAC,CAAA;IACxC,gBAAgB,EAAE,MAAM,IAAI,CAAA;CAC7B,CAMA;AAED;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AACH,wBAAgB,mCAAmC,CACjD,KAAK,SAAS,qBAAqB,CAAC,KAAK,CAAC,GAAG,WAAW,EACxD,SAAS,SAAS,kBAAkB,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAEtE,UAAU,EAAE,OAAO,CAAC,uBAAuB,CAAC,KAAK,CAAC,CAAC,GAClD;IACD,UAAU,EAAE,UAAU,CAAC,KAAK,EAAE,SAAS,CAAC,CAAA;IACxC,gBAAgB,EAAE,MAAM,IAAI,CAAA;CAC7B,CAkCA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,wBAAgB,sCAAsC,CACpD,KAAK,SAAS,qBAAqB,CAAC,KAAK,CAAC,GAAG,WAAW,EACxD,SAAS,SAAS,kBAAkB,CAAC,SAAS,CAAC,GAAG,MAAM,CAAC,KAAK,EAAE,KAAK,CAAC,EAEtE,UAAU,EAAE,OAAO,CAAC,uBAAuB,CAAC,KAAK,CAAC,CAAC,EACnD,kBAAkB,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,OAAO,CAAA;CAAE,GACjE;IACD,UAAU,EAAE,UAAU,CAAC,KAAK,EAAE,SAAS,CAAC,CAAA;IACxC,gBAAgB,EAAE,MAAM,IAAI,CAAA;CAC7B,CAmCA"}
|
package/dist/auth.js
CHANGED
|
@@ -3,13 +3,12 @@ import { createFetchClient, getClient as getHttpClient, setAuthGate, setAuthToke
|
|
|
3
3
|
/**
|
|
4
4
|
* Holds every HTTP request until this auth client's session restore has settled.
|
|
5
5
|
*
|
|
6
|
-
* Without it a page effect that fires on mount (fetching
|
|
7
|
-
*
|
|
6
|
+
* Without it a page effect that fires on mount (fetching account settings, a
|
|
7
|
+
* dashboard, or a saved-item state) sends its request before `initialize()` has
|
|
8
8
|
* restored the session, so it leaves with no `Authorization` header and the server
|
|
9
9
|
* answers for an anonymous viewer. Localhost usually hides it — the restore wins
|
|
10
|
-
* the race against the first effect there — and the deployed URL exposes it,
|
|
11
|
-
*
|
|
12
|
-
* followed user as database leftovers.
|
|
10
|
+
* the race against the first effect there — and the deployed URL exposes it, where a
|
|
11
|
+
* saved item reads as unsaved because the request lacked its session.
|
|
13
12
|
*
|
|
14
13
|
* `whenRestored()` resolves immediately when no restore is in flight, so a request
|
|
15
14
|
* made after boot never waits at all; it holds only while the restore is actually
|
|
@@ -62,13 +61,18 @@ export function createDefaultHttpClient(baseURL) {
|
|
|
62
61
|
* when the API reads another scheme than `Bearer`: it is an option of THIS
|
|
63
62
|
* constructor (`createDefaultHttpClientWithAuthBearer({ authScheme: 'Token', … })`,
|
|
64
63
|
* or `createFetchClient({ baseURL, authScheme: 'Token' })` directly), never a
|
|
65
|
-
* property of the client it returns.
|
|
64
|
+
* property of the client it returns. The client is built with the same scheme, so
|
|
65
|
+
* a token the client also holds — the one a default auth bond's token sync gives
|
|
66
|
+
* it — is sent with that scheme too, not as `Bearer`.
|
|
66
67
|
*/
|
|
67
68
|
export function createDefaultHttpClientWithAuthBearer(opts) {
|
|
68
69
|
const authScheme = opts.authScheme ?? 'Bearer';
|
|
69
70
|
const httpClient = createFetchClient({
|
|
70
71
|
baseURL: opts.baseURL,
|
|
71
72
|
withCredentials: opts.withCredentials,
|
|
73
|
+
// The client's own token (a default auth bond's token sync) overrides the interceptor's
|
|
74
|
+
// header, so it must carry the same scheme.
|
|
75
|
+
authScheme,
|
|
72
76
|
});
|
|
73
77
|
httpClient.addRequestInterceptor((config) => {
|
|
74
78
|
if (opts.stripApiPrefix && typeof config.url === 'string' && config.url.startsWith('/api/')) {
|
|
@@ -87,19 +91,16 @@ export function createDefaultHttpClientWithAuthBearer(opts) {
|
|
|
87
91
|
}
|
|
88
92
|
/**
|
|
89
93
|
* Builds the default JWT auth client + wires it into `@molecule/app-auth`.
|
|
90
|
-
*
|
|
91
|
-
* Replaces the 16-line per-app `bonds/auth-default.ts` that 93 fleet
|
|
92
|
-
* apps shipped byte-identically. Apps pass their own `authConfig`
|
|
93
|
-
* (which lives in `src/config.ts`).
|
|
94
|
+
* Apps pass their own `authConfig` (which lives in `src/config.ts`).
|
|
94
95
|
*
|
|
95
96
|
* `TUser` is the profile the app's pages read, and may be any JSON object type —
|
|
96
|
-
* including a profile with NO `id` (`{ username, email, bio, image }`),
|
|
97
|
-
*
|
|
98
|
-
*
|
|
99
|
-
*
|
|
100
|
-
* `
|
|
101
|
-
*
|
|
102
|
-
*
|
|
97
|
+
* including a profile with NO `id` (`{ username, email, bio, image }`), as in the
|
|
98
|
+
* factory it wraps. Never add a synthetic `id` to satisfy a type: nothing in the
|
|
99
|
+
* client reads it, and the fabricated value then travels into storage, `getUser()`
|
|
100
|
+
* and every page. Omit the type parameter and the default is `UserProfile`, whose
|
|
101
|
+
* `id: string` stays required — the profile is never inferred from `authConfig`
|
|
102
|
+
* (not even from an annotated config variable), so an untyped wrapper call keeps
|
|
103
|
+
* the default contract.
|
|
103
104
|
*
|
|
104
105
|
* Type the config with the SAME type (`const authConfig: AuthClientConfig<MyUser> = { … }`)
|
|
105
106
|
* so its `mapAuthResponse` is checked against `MyUser`, and name that type at the
|
|
@@ -109,9 +110,10 @@ export function createDefaultHttpClientWithAuthBearer(opts) {
|
|
|
109
110
|
* argument — that is a 1.8 caller shape this major changes (see
|
|
110
111
|
* `AuthClientConfig`'s migration note in `@molecule/app-auth`).
|
|
111
112
|
*
|
|
112
|
-
* `TRegister` is the app's own registration fields (
|
|
113
|
-
*
|
|
113
|
+
* `TRegister` is the app's own extra registration fields (for example
|
|
114
|
+
* `{ username: string }`), passed on to `@molecule/app-auth`'s factory so
|
|
114
115
|
* `authClient.register({ username, email, password })` type-checks with no cast.
|
|
116
|
+
* See the module guidance for the lifecycle and API adaptations already included.
|
|
115
117
|
*
|
|
116
118
|
* @example
|
|
117
119
|
* ```ts
|
|
@@ -132,25 +134,27 @@ export function createDefaultAuthClient(authConfig) {
|
|
|
132
134
|
}
|
|
133
135
|
/**
|
|
134
136
|
* Variant of `createDefaultAuthClient` that also keeps the bonded
|
|
135
|
-
* `@molecule/app-http` client's bearer token in sync with auth
|
|
136
|
-
*
|
|
137
|
-
*
|
|
138
|
-
*
|
|
137
|
+
* `@molecule/app-http` client's bearer token in sync with the auth client's session.
|
|
138
|
+
* Required by apps whose http client must carry the JWT on every request — without
|
|
139
|
+
* this wiring, authed endpoints return 401 after page reloads or token refresh.
|
|
140
|
+
*
|
|
141
|
+
* The sync is a LIVE read: `@molecule/app-http`'s `setAuthTokenSource` registers the auth
|
|
142
|
+
* client's token, and every client the app bonds — one bonded after this setup too (the
|
|
143
|
+
* scaffold wires auth BEFORE http, so a token copied into the client present at setup was
|
|
144
|
+
* never sent) — reads it on each request. A session adopted with no
|
|
145
|
+
* auth event (`useAuth().setSession()`, an OAuth callback's `setAccessToken()`, the same
|
|
146
|
+
* profile under a new token) is therefore sent by the next request, not the previous
|
|
147
|
+
* account's bearer. A transport without `setAuthTokenResolver` is followed through a request
|
|
148
|
+
* interceptor (see `setAuthTokenSource` for which request first carries the change), and
|
|
149
|
+
* auth events still push the token to it.
|
|
150
|
+
*
|
|
151
|
+
* A cookie session (no body token) resolves to `null`, so the request carries NO
|
|
152
|
+
* `Authorization` header — a kept bearer would answer as the PRIOR session.
|
|
153
|
+
* Requests also wait for the session restore, so a page that fetches viewer data on mount
|
|
154
|
+
* cannot race it (see {@link registerAuthGate}).
|
|
139
155
|
*
|
|
140
156
|
* The profile type is `createDefaultAuthClient`'s: a JSON object type of named
|
|
141
157
|
* fields (see `AuthProfileShape`), including an app profile with no `id`.
|
|
142
|
-
*
|
|
143
|
-
* Hydrates the HTTP client's token from the persisted auth state
|
|
144
|
-
* on setup, then listens for auth events to keep them aligned, and holds
|
|
145
|
-
* requests until the session restore settles so a page effect that fetches
|
|
146
|
-
* viewer-dependent data on mount cannot race it (see {@link registerAuthGate}).
|
|
147
|
-
*
|
|
148
|
-
* The hydrated token is delivered through `@molecule/app-http`'s token source, not
|
|
149
|
-
* through a client read at setup: the scaffold wires auth BEFORE http, so a client
|
|
150
|
-
* read here is the unbonded fallback and the restored session's token would land on a
|
|
151
|
-
* client nothing sends through (xr-molecule-flash-m156 — a reload sent every request
|
|
152
|
-
* with no `Authorization` header). The source is applied to the client WHENEVER it is
|
|
153
|
-
* bonded, so setup order no longer decides whether the first request is authorized.
|
|
154
158
|
*/
|
|
155
159
|
export function createDefaultAuthClientWithHttpSync(authConfig) {
|
|
156
160
|
const authClient = createJWTAuthClient(authConfig);
|
|
@@ -164,7 +168,9 @@ export function createDefaultAuthClientWithHttpSync(authConfig) {
|
|
|
164
168
|
// Resolve the HTTP client per sync, never once here. `setClient` runs this
|
|
165
169
|
// source, so the token reaches the client the scaffold bonds later as well as
|
|
166
170
|
// one already bonded; a client kept from this moment would be the throwaway
|
|
167
|
-
// fallback the later `setHttpDefault()` replaces.
|
|
171
|
+
// fallback the later `setHttpDefault()` replaces. The client reads the source
|
|
172
|
+
// itself on every request, so a session adopted by `setSession()` (no auth
|
|
173
|
+
// event) or re-adopted for the same profile (no state change) is sent at once.
|
|
168
174
|
setHttpAuthTokenSource(() => authClient.getAccessToken());
|
|
169
175
|
authClient.addEventListener((event) => {
|
|
170
176
|
if (event.type === 'login' || event.type === 'register' || event.type === 'refresh') {
|
|
@@ -173,6 +179,10 @@ export function createDefaultAuthClientWithHttpSync(authConfig) {
|
|
|
173
179
|
// keeping a previously-set bearer here would pin the http client to the
|
|
174
180
|
// PRIOR session (e.g. a guest's) — the server prefers the Authorization
|
|
175
181
|
// header over the fresh cookie, so every request 401s as the old user.
|
|
182
|
+
//
|
|
183
|
+
// A client that implements `setAuthTokenResolver` already resolves the source
|
|
184
|
+
// per request; this push is what carries the same change to a transport that
|
|
185
|
+
// does not (and keeps `getAuthToken()` current there).
|
|
176
186
|
getHttpClient().setAuthToken(authClient.getAccessToken());
|
|
177
187
|
}
|
|
178
188
|
else if (event.type === 'logout') {
|
|
@@ -186,15 +196,18 @@ export function createDefaultAuthClientWithHttpSync(authConfig) {
|
|
|
186
196
|
* Most aggressive variant — bonds a fetch-based HTTP client with the
|
|
187
197
|
* given `baseURL` (so molecule pkg `useGet('/path')` calls hit
|
|
188
198
|
* `${baseURL}/path` instead of the SPA's catch-all route), AND keeps
|
|
189
|
-
* its bearer token in sync with auth
|
|
199
|
+
* its bearer token in sync with the auth client's session, read on every
|
|
200
|
+
* request: a session adopted with no auth event (`setSession()`,
|
|
201
|
+
* `setAccessToken()`) is sent by the next request. The token uses the auth
|
|
202
|
+
* config's `authScheme` (default `Bearer`); a client bonded after this setup
|
|
203
|
+
* gets the same source and auth events.
|
|
190
204
|
*
|
|
191
205
|
* The auth client restores a stored token AND user SYNCHRONOUSLY, so the first
|
|
192
206
|
* render is the signed-in UI — do not gate a protected page (or a provider of
|
|
193
207
|
* yours) on a current-user request. This bond registers `whenRestored()` as the
|
|
194
208
|
* HTTP gate, so requests still wait for the background restore/verification to
|
|
195
209
|
* settle; only `useAuth().status === 'loading'` means "no decision yet"
|
|
196
|
-
* (
|
|
197
|
-
* on every navigation).
|
|
210
|
+
* (a page that waits on its own `/me` pays a round trip on every navigation).
|
|
198
211
|
*
|
|
199
212
|
* Used by apps where molecule packages render pricing / billing /
|
|
200
213
|
* other authed JSON-fetching screens that need both behaviors.
|
|
@@ -214,22 +227,30 @@ export function createDefaultAuthClientWithFetchClient(authConfig, fetchClientOp
|
|
|
214
227
|
const httpClient = createFetchClient({
|
|
215
228
|
baseURL: fetchClientOptions.baseURL,
|
|
216
229
|
withCredentials: fetchClientOptions.withCredentials ?? true,
|
|
230
|
+
// The scheme the auth config names for this token (`Authorization: Token <jwt>`).
|
|
231
|
+
authScheme: authConfig.authScheme,
|
|
217
232
|
});
|
|
218
233
|
// Registered BEFORE the bond: `setHttpClient` applies the restored session's
|
|
219
234
|
// token, and the same source reaches any client an app bonds later instead of
|
|
220
|
-
// this one
|
|
235
|
+
// this one. A client captured at setup cannot track later replacements. The
|
|
236
|
+
// client resolves the source on every request, so a session adopted by
|
|
237
|
+
// `setSession()` or `setAccessToken()` after this point is sent immediately.
|
|
221
238
|
setHttpAuthTokenSource(() => authClient.getAccessToken());
|
|
222
239
|
setHttpClient(httpClient);
|
|
223
240
|
setClient(authClient);
|
|
224
241
|
registerAuthGate(authClient);
|
|
225
242
|
authClient.addEventListener((event) => {
|
|
243
|
+
// The client bonded NOW, resolved per event: an app that bonds its own client after
|
|
244
|
+
// this setup sends through that one, never through the client created above.
|
|
226
245
|
if (event.type === 'login' || event.type === 'register' || event.type === 'refresh') {
|
|
227
246
|
// Mirror the token exactly, including null — see the identical note in
|
|
228
247
|
// createDefaultAuthClientWithHttpSync (stale bearer = stuck prior session).
|
|
229
|
-
|
|
248
|
+
// The per-request read of the source above is the guarantee; this push keeps
|
|
249
|
+
// `getAuthToken()` current on a transport without `setAuthTokenResolver`.
|
|
250
|
+
getHttpClient().setAuthToken(authClient.getAccessToken());
|
|
230
251
|
}
|
|
231
252
|
else if (event.type === 'logout') {
|
|
232
|
-
|
|
253
|
+
getHttpClient().setAuthToken(null);
|
|
233
254
|
}
|
|
234
255
|
});
|
|
235
256
|
},
|
package/dist/index.d.ts
CHANGED
|
@@ -30,6 +30,16 @@
|
|
|
30
30
|
* argument is given. The profile is never inferred from `authConfig`, so an
|
|
31
31
|
* untyped call keeps the default contract.
|
|
32
32
|
*
|
|
33
|
+
* **What this client already handles**
|
|
34
|
+
* - `getAccessToken()` returns a stored token during restore; only 401/403 ends
|
|
35
|
+
* the session, while network/5xx failures retain it.
|
|
36
|
+
* - `tokenKey`/`storagePrefix`, `authScheme`, `mapAuthResponse`,
|
|
37
|
+
* `currentUserEndpoint`, and `profileEndpoint`/`profileUpdateMethod` adapt the API.
|
|
38
|
+
* - `logout()` settles local sign-out; `refresh()` uses a stored refresh token.
|
|
39
|
+
*
|
|
40
|
+
* This factory adds no render gate: `AuthProvider` renders children immediately.
|
|
41
|
+
* The HTTP-sync variants gate requests during restore, not rendering.
|
|
42
|
+
*
|
|
33
43
|
* @example
|
|
34
44
|
* ```tsx
|
|
35
45
|
* // src/main.tsx
|
|
@@ -59,6 +69,23 @@
|
|
|
59
69
|
* })
|
|
60
70
|
* ```
|
|
61
71
|
*
|
|
72
|
+
* @example Adapt a nested token and `Token` header without another session store.
|
|
73
|
+
* ```ts
|
|
74
|
+
* import { createDefaultAuthClient } from '@molecule/app-bonds-default-react'
|
|
75
|
+
*
|
|
76
|
+
* const { authClient, setupAuthDefault } = createDefaultAuthClient({
|
|
77
|
+
* tokenKey: 'app.jwt',
|
|
78
|
+
* authScheme: 'Token',
|
|
79
|
+
* currentUserEndpoint: '/user',
|
|
80
|
+
* profileEndpoint: '/user',
|
|
81
|
+
* profileUpdateMethod: 'PUT',
|
|
82
|
+
* mapAuthResponse: (data) => {
|
|
83
|
+
* const { token, ...user } = (data as { user: { token: string; id: string } }).user
|
|
84
|
+
* return { accessToken: token, user }
|
|
85
|
+
* },
|
|
86
|
+
* })
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
62
89
|
* @remarks
|
|
63
90
|
* - **Optional setups are NOT exported from the package root.** Import each from
|
|
64
91
|
* its own subpath — `@molecule/app-bonds-default-react/optional/maps-leaflet.js`,
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAsHG;AAEH,cAAc,WAAW,CAAA;AACzB,cAAc,gBAAgB,CAAA;AAC9B,cAAc,YAAY,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -30,6 +30,16 @@
|
|
|
30
30
|
* argument is given. The profile is never inferred from `authConfig`, so an
|
|
31
31
|
* untyped call keeps the default contract.
|
|
32
32
|
*
|
|
33
|
+
* **What this client already handles**
|
|
34
|
+
* - `getAccessToken()` returns a stored token during restore; only 401/403 ends
|
|
35
|
+
* the session, while network/5xx failures retain it.
|
|
36
|
+
* - `tokenKey`/`storagePrefix`, `authScheme`, `mapAuthResponse`,
|
|
37
|
+
* `currentUserEndpoint`, and `profileEndpoint`/`profileUpdateMethod` adapt the API.
|
|
38
|
+
* - `logout()` settles local sign-out; `refresh()` uses a stored refresh token.
|
|
39
|
+
*
|
|
40
|
+
* This factory adds no render gate: `AuthProvider` renders children immediately.
|
|
41
|
+
* The HTTP-sync variants gate requests during restore, not rendering.
|
|
42
|
+
*
|
|
33
43
|
* @example
|
|
34
44
|
* ```tsx
|
|
35
45
|
* // src/main.tsx
|
|
@@ -59,6 +69,23 @@
|
|
|
59
69
|
* })
|
|
60
70
|
* ```
|
|
61
71
|
*
|
|
72
|
+
* @example Adapt a nested token and `Token` header without another session store.
|
|
73
|
+
* ```ts
|
|
74
|
+
* import { createDefaultAuthClient } from '@molecule/app-bonds-default-react'
|
|
75
|
+
*
|
|
76
|
+
* const { authClient, setupAuthDefault } = createDefaultAuthClient({
|
|
77
|
+
* tokenKey: 'app.jwt',
|
|
78
|
+
* authScheme: 'Token',
|
|
79
|
+
* currentUserEndpoint: '/user',
|
|
80
|
+
* profileEndpoint: '/user',
|
|
81
|
+
* profileUpdateMethod: 'PUT',
|
|
82
|
+
* mapAuthResponse: (data) => {
|
|
83
|
+
* const { token, ...user } = (data as { user: { token: string; id: string } }).user
|
|
84
|
+
* return { accessToken: token, user }
|
|
85
|
+
* },
|
|
86
|
+
* })
|
|
87
|
+
* ```
|
|
88
|
+
*
|
|
62
89
|
* @remarks
|
|
63
90
|
* - **Optional setups are NOT exported from the package root.** Import each from
|
|
64
91
|
* its own subpath — `@molecule/app-bonds-default-react/optional/maps-leaflet.js`,
|
package/dist/setup.d.ts
CHANGED
|
@@ -29,7 +29,7 @@ export declare function setupAppRoutingReactRouter(): void;
|
|
|
29
29
|
* `@molecule/app-state: No provider set. Call setProvider() first` — nothing
|
|
30
30
|
* else in a scaffolded app wires the `state` bond, so the documented
|
|
31
31
|
* `createStore()` + `useStore()` pattern failed at the first render and every
|
|
32
|
-
* page came up blank while `tsc` passed
|
|
32
|
+
* page came up blank while `tsc` passed.
|
|
33
33
|
*/
|
|
34
34
|
export declare function setupAppStateSimple(): void;
|
|
35
35
|
/** Wires `@molecule/app-storage-localstorage` to `@molecule/app-storage`. */
|
package/dist/setup.js
CHANGED
|
@@ -68,7 +68,7 @@ export function setupAppRoutingReactRouter() {
|
|
|
68
68
|
* `@molecule/app-state: No provider set. Call setProvider() first` — nothing
|
|
69
69
|
* else in a scaffolded app wires the `state` bond, so the documented
|
|
70
70
|
* `createStore()` + `useStore()` pattern failed at the first render and every
|
|
71
|
-
* page came up blank while `tsc` passed
|
|
71
|
+
* page came up blank while `tsc` passed.
|
|
72
72
|
*/
|
|
73
73
|
export function setupAppStateSimple() {
|
|
74
74
|
setState(defaultStateProvider);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@molecule/app-bonds-default-react",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.0.1",
|
|
4
4
|
"description": "Default app-side bond wirings for React+Tailwind+react-router+localStorage+Arimo font+molecule icons. 5 setup functions consolidating 665 byte-identical per-app bond wiring files across the fleet.",
|
|
5
5
|
"homepage": "https://www.molecule.dev/packages/app-bonds-default-react",
|
|
6
6
|
"type": "module",
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"scripts": {
|
|
10
10
|
"build": "tsc",
|
|
11
11
|
"test": "vitest run",
|
|
12
|
-
"test:watch": "vitest"
|
|
12
|
+
"test:watch": "vitest",
|
|
13
|
+
"typecheck:tests": "tsc -p tsconfig.tests.json"
|
|
13
14
|
},
|
|
14
15
|
"exports": {
|
|
15
16
|
".": {
|
|
@@ -38,37 +39,37 @@
|
|
|
38
39
|
"directory": "packages/app/infrastructure/bonds-default-react"
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|
|
41
|
-
"@molecule/app-auth": "2.0
|
|
42
|
+
"@molecule/app-auth": "3.2.0",
|
|
42
43
|
"@molecule/app-charts-chartjs": "1.0.3",
|
|
43
|
-
"@molecule/app-client-search": "1.0.
|
|
44
|
+
"@molecule/app-client-search": "1.0.4",
|
|
44
45
|
"@molecule/app-client-search-minisearch": "1.0.4",
|
|
45
46
|
"@molecule/app-code-editor": "1.0.3",
|
|
46
47
|
"@molecule/app-code-editor-monaco": "1.0.4",
|
|
47
48
|
"@molecule/app-command-palette": "1.0.3",
|
|
48
49
|
"@molecule/app-command-palette-cmdk": "1.0.3",
|
|
49
|
-
"@molecule/app-drag-drop": "1.0.
|
|
50
|
-
"@molecule/app-drag-drop-dndkit": "1.0.
|
|
50
|
+
"@molecule/app-drag-drop": "1.0.4",
|
|
51
|
+
"@molecule/app-drag-drop-dndkit": "1.0.4",
|
|
51
52
|
"@molecule/app-fonts": "1.2.2",
|
|
52
53
|
"@molecule/app-fonts-arimo": "1.0.5",
|
|
53
|
-
"@molecule/app-http": "1.
|
|
54
|
+
"@molecule/app-http": "1.5.3",
|
|
54
55
|
"@molecule/app-icons": "1.0.3",
|
|
55
56
|
"@molecule/app-icons-molecule": "1.0.3",
|
|
56
57
|
"@molecule/app-keyboard-shortcuts": "1.0.3",
|
|
57
58
|
"@molecule/app-keyboard-shortcuts-hotkeys": "1.0.3",
|
|
58
59
|
"@molecule/app-maps-leaflet": "1.0.3",
|
|
59
60
|
"@molecule/app-realtime": "1.0.4",
|
|
60
|
-
"@molecule/app-realtime-socketio": "1.1.
|
|
61
|
-
"@molecule/app-routing": "1.0.
|
|
62
|
-
"@molecule/app-routing-react-router": "1.
|
|
63
|
-
"@molecule/app-state": "1.0.
|
|
64
|
-
"@molecule/app-storage": "1.0
|
|
65
|
-
"@molecule/app-storage-localstorage": "1.0
|
|
61
|
+
"@molecule/app-realtime-socketio": "1.1.2",
|
|
62
|
+
"@molecule/app-routing": "1.0.5",
|
|
63
|
+
"@molecule/app-routing-react-router": "1.2.1",
|
|
64
|
+
"@molecule/app-state": "1.0.5",
|
|
65
|
+
"@molecule/app-storage": "1.1.0",
|
|
66
|
+
"@molecule/app-storage-localstorage": "1.1.0",
|
|
66
67
|
"@molecule/app-theme": "1.0.4",
|
|
67
68
|
"@molecule/app-theme-css-variables": "1.0.4",
|
|
68
69
|
"@molecule/app-ui": "1.3.8",
|
|
69
|
-
"@molecule/app-ui-tailwind": "1.2.
|
|
70
|
+
"@molecule/app-ui-tailwind": "1.2.8",
|
|
70
71
|
"@molecule/app-video-hls": "1.0.3",
|
|
71
|
-
"@molecule/app-virtual-scroll": "1.0.
|
|
72
|
+
"@molecule/app-virtual-scroll": "1.0.4",
|
|
72
73
|
"@molecule/app-virtual-scroll-tanstack": "1.0.3",
|
|
73
74
|
"@types/node": "26.6.5",
|
|
74
75
|
"@types/react": "19.3.0",
|
|
@@ -80,7 +81,7 @@
|
|
|
80
81
|
"vitest": "4.1.12"
|
|
81
82
|
},
|
|
82
83
|
"peerDependencies": {
|
|
83
|
-
"@molecule/app-auth": "^
|
|
84
|
+
"@molecule/app-auth": "^3.0.0",
|
|
84
85
|
"@molecule/app-charts": "^1.0.1",
|
|
85
86
|
"@molecule/app-charts-chartjs": "^1.0.1",
|
|
86
87
|
"@molecule/app-client-search": "^1.0.0",
|
|
@@ -93,7 +94,7 @@
|
|
|
93
94
|
"@molecule/app-drag-drop-dndkit": "^1.0.1",
|
|
94
95
|
"@molecule/app-fonts": "^1.0.1",
|
|
95
96
|
"@molecule/app-fonts-arimo": "^1.0.1",
|
|
96
|
-
"@molecule/app-http": "^1.0
|
|
97
|
+
"@molecule/app-http": "^1.5.0",
|
|
97
98
|
"@molecule/app-icons": "^1.0.1",
|
|
98
99
|
"@molecule/app-icons-molecule": "^1.0.1",
|
|
99
100
|
"@molecule/app-keyboard-shortcuts": "^1.0.1",
|