@molecule/app-bonds-default-react 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -3,15 +3,11 @@ 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-16T08:11:11.584Z
6
+ Generated: 2026-10-16T23:54:35.164Z
7
7
  -->
8
8
 
9
9
  # @molecule/app-bonds-default-react
10
10
 
11
- > **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
12
- > It is written to be read by coding agents as much as by people, and is generated from this
13
- > package's source — edit `src/index.ts` JSDoc, not this file.
14
-
15
11
  `@molecule/app-bonds-default-react` — default app-side bond wirings for the
16
12
  React fleet stack, replacing the byte-identical per-app files:
17
13
 
@@ -39,6 +35,10 @@ React fleet stack, replacing the byte-identical per-app files:
39
35
  `...WithFetchClient` variants also keep the bonded http client's bearer
40
36
  token in sync with auth events.
41
37
 
38
+ > **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
39
+ > It is written to be read by coding agents as much as by people, and is generated from this
40
+ > package's source — edit `src/index.ts` JSDoc, not this file.
41
+
42
42
  ## Quick Start
43
43
 
44
44
  ```tsx
@@ -105,7 +105,11 @@ stripping a leading `/api/` from request URLs.
105
105
  Replaces ~20 per-app `bonds/http-default.ts` files that hand-roll
106
106
  this same wiring. Pass `stripApiPrefix: true` when the app uses
107
107
  `baseURL: '/api'` to handle pages that pass `/api/`-prefixed paths
108
- (would otherwise resolve to `/api/api/...` and 404).
108
+ (would otherwise resolve to `/api/api/...` and 404). Pass `authScheme`
109
+ when the API reads another scheme than `Bearer`: it is an option of THIS
110
+ constructor (`createDefaultHttpClientWithAuthBearer({ authScheme: 'Token', … })`,
111
+ or `createFetchClient({ baseURL, authScheme: 'Token' })` directly), never a
112
+ property of the client it returns.
109
113
 
110
114
  ```typescript
111
115
  function createDefaultHttpClientWithAuthBearer(opts: {
@@ -113,6 +117,7 @@ function createDefaultHttpClientWithAuthBearer(opts: {
113
117
  withCredentials?: boolean
114
118
  stripApiPrefix?: boolean
115
119
  getToken: () => string | null | undefined
120
+ authScheme?: string
116
121
  }): { httpClient: HttpClient; setupHttpDefault: () => void }
117
122
  ```
118
123
 
@@ -140,7 +145,9 @@ client must carry the JWT on every request — without this wiring,
140
145
  authed endpoints return 401 after page reloads or token refresh.
141
146
 
142
147
  Hydrates the HTTP client's token from the persisted auth state
143
- on setup, then listens for auth events to keep them aligned.
148
+ on setup, then listens for auth events to keep them aligned, and holds
149
+ requests until the session restore settles so a page effect that fetches
150
+ viewer-dependent data on mount cannot race it (see {@link registerAuthGate}).
144
151
 
145
152
  ```typescript
146
153
  function createDefaultAuthClientWithHttpSync(authConfig: AuthClientConfig): {
@@ -159,6 +166,10 @@ its bearer token in sync with auth events.
159
166
  Used by apps where molecule packages render pricing / billing /
160
167
  other authed JSON-fetching screens that need both behaviors.
161
168
 
169
+ Requests are also held until the session restore settles (see
170
+ {@link registerAuthGate}), so a page that fetches viewer-dependent data on
171
+ mount cannot race it and answer for an anonymous viewer.
172
+
162
173
  ```typescript
163
174
  function createDefaultAuthClientWithFetchClient(
164
175
  authConfig: AuthClientConfig,
package/dist/auth.d.ts CHANGED
@@ -26,13 +26,25 @@ export declare function createDefaultHttpClient(baseURL: string): {
26
26
  * Replaces ~20 per-app `bonds/http-default.ts` files that hand-roll
27
27
  * this same wiring. Pass `stripApiPrefix: true` when the app uses
28
28
  * `baseURL: '/api'` to handle pages that pass `/api/`-prefixed paths
29
- * (would otherwise resolve to `/api/api/...` and 404).
29
+ * (would otherwise resolve to `/api/api/...` and 404). Pass `authScheme`
30
+ * when the API reads another scheme than `Bearer`: it is an option of THIS
31
+ * constructor (`createDefaultHttpClientWithAuthBearer({ authScheme: 'Token', … })`,
32
+ * or `createFetchClient({ baseURL, authScheme: 'Token' })` directly), never a
33
+ * property of the client it returns.
30
34
  */
31
35
  export declare function createDefaultHttpClientWithAuthBearer(opts: {
32
36
  baseURL: string;
33
37
  withCredentials?: boolean;
34
38
  stripApiPrefix?: boolean;
35
39
  getToken: () => string | null | undefined;
40
+ /**
41
+ * The scheme of the `Authorization` header the token from `getToken()` is sent
42
+ * with: `Bearer` (default), or what the API expects, e.g. `Token` for an API
43
+ * that reads `Authorization: Token <jwt>`. Pass it to this constructor — the
44
+ * same option `@molecule/app-http`'s client config calls `authScheme`, and not
45
+ * a property of the client that comes back.
46
+ */
47
+ authScheme?: string;
36
48
  }): {
37
49
  httpClient: HttpClient;
38
50
  setupHttpDefault: () => void;
@@ -66,7 +78,9 @@ export declare function createDefaultAuthClient<TUser extends UserProfile = User
66
78
  * authed endpoints return 401 after page reloads or token refresh.
67
79
  *
68
80
  * Hydrates the HTTP client's token from the persisted auth state
69
- * on setup, then listens for auth events to keep them aligned.
81
+ * on setup, then listens for auth events to keep them aligned, and holds
82
+ * requests until the session restore settles so a page effect that fetches
83
+ * viewer-dependent data on mount cannot race it (see {@link registerAuthGate}).
70
84
  */
71
85
  export declare function createDefaultAuthClientWithHttpSync<TUser extends UserProfile = UserProfile>(authConfig: AuthClientConfig): {
72
86
  authClient: AuthClient<TUser>;
@@ -80,6 +94,10 @@ export declare function createDefaultAuthClientWithHttpSync<TUser extends UserPr
80
94
  *
81
95
  * Used by apps where molecule packages render pricing / billing /
82
96
  * other authed JSON-fetching screens that need both behaviors.
97
+ *
98
+ * Requests are also held until the session restore settles (see
99
+ * {@link registerAuthGate}), so a page that fetches viewer-dependent data on
100
+ * mount cannot race it and answer for an anonymous viewer.
83
101
  */
84
102
  export declare function createDefaultAuthClientWithFetchClient<TUser extends UserProfile = UserProfile>(authConfig: AuthClientConfig, fetchClientOptions: {
85
103
  baseURL: string;
@@ -1 +1 @@
1
- {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAEnF,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAA;AAOpD;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CAAC,OAAO,EAAE,MAAM,GAAG;IACxD,UAAU,EAAE,UAAU,CAAA;IACtB,gBAAgB,EAAE,MAAM,IAAI,CAAA;CAC7B,CAMA;AAED;;;;;;;;;GASG;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;CAC1C,GAAG;IACF,UAAU,EAAE,UAAU,CAAA;IACtB,gBAAgB,EAAE,MAAM,IAAI,CAAA;CAC7B,CAmBA;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,SAAS,WAAW,GAAG,WAAW,EAC7E,UAAU,EAAE,gBAAgB,GAC3B;IACD,UAAU,EAAE,UAAU,CAAC,KAAK,CAAC,CAAA;IAC7B,gBAAgB,EAAE,MAAM,IAAI,CAAA;CAC7B,CAMA;AAED;;;;;;;;;GASG;AACH,wBAAgB,mCAAmC,CAAC,KAAK,SAAS,WAAW,GAAG,WAAW,EACzF,UAAU,EAAE,gBAAgB,GAC3B;IACD,UAAU,EAAE,UAAU,CAAC,KAAK,CAAC,CAAA;IAC7B,gBAAgB,EAAE,MAAM,IAAI,CAAA;CAC7B,CAwBA;AAED;;;;;;;;GAQG;AACH,wBAAgB,sCAAsC,CAAC,KAAK,SAAS,WAAW,GAAG,WAAW,EAC5F,UAAU,EAAE,gBAAgB,EAC5B,kBAAkB,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,OAAO,CAAA;CAAE,GACjE;IACD,UAAU,EAAE,UAAU,CAAC,KAAK,CAAC,CAAA;IAC7B,gBAAgB,EAAE,MAAM,IAAI,CAAA;CAC7B,CA0BA"}
1
+ {"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAE,gBAAgB,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAEnF,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;;;;;;;;;;;;;GAaG;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,CAoBA;AAED;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,uBAAuB,CAAC,KAAK,SAAS,WAAW,GAAG,WAAW,EAC7E,UAAU,EAAE,gBAAgB,GAC3B;IACD,UAAU,EAAE,UAAU,CAAC,KAAK,CAAC,CAAA;IAC7B,gBAAgB,EAAE,MAAM,IAAI,CAAA;CAC7B,CAMA;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,mCAAmC,CAAC,KAAK,SAAS,WAAW,GAAG,WAAW,EACzF,UAAU,EAAE,gBAAgB,GAC3B;IACD,UAAU,EAAE,UAAU,CAAC,KAAK,CAAC,CAAA;IAC7B,gBAAgB,EAAE,MAAM,IAAI,CAAA;CAC7B,CA2BA;AAED;;;;;;;;;;;;GAYG;AACH,wBAAgB,sCAAsC,CAAC,KAAK,SAAS,WAAW,GAAG,WAAW,EAC5F,UAAU,EAAE,gBAAgB,EAC5B,kBAAkB,EAAE;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,eAAe,CAAC,EAAE,OAAO,CAAA;CAAE,GACjE;IACD,UAAU,EAAE,UAAU,CAAC,KAAK,CAAC,CAAA;IAC7B,gBAAgB,EAAE,MAAM,IAAI,CAAA;CAC7B,CA2BA"}
package/dist/auth.js CHANGED
@@ -1,5 +1,34 @@
1
1
  import { createJWTAuthClient, setClient } from '@molecule/app-auth';
2
- import { createFetchClient, getClient as getHttpClient, setClient as setHttpClient, } from '@molecule/app-http';
2
+ import { createFetchClient, getClient as getHttpClient, setAuthGate, setClient as setHttpClient, } from '@molecule/app-http';
3
+ /**
4
+ * Holds every HTTP request until this auth client's session restore has settled.
5
+ *
6
+ * Without it a page effect that fires on mount (fetching a profile's `following`
7
+ * flag, a feed, a `favorited` state) sends its request before `initialize()` has
8
+ * restored the session, so it leaves with no `Authorization` header and the server
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, which
11
+ * is how X1 xr-molecule-flash-m95 spent ~25 min treating a "Follow" button on a
12
+ * followed user as database leftovers.
13
+ *
14
+ * `whenRestored()` resolves immediately when no restore is in flight, so a request
15
+ * made after boot never waits at all; it holds only while the restore is actually
16
+ * running. A provider without `whenRestored` (a bond that restores nothing) leaves
17
+ * requests ungated.
18
+ *
19
+ * The restore this gate waits for does NOT go through the gated client — both default auth
20
+ * bonds' `createJWTAuthClient` restores with `fetch` — so the scaffold's own wiring cannot
21
+ * deadlock. An app that registers its own gate over a restore made through the bonded client
22
+ * must exempt that request with `skipAuthGate: true`
23
+ * (`setAuthGate(() => store.whenSettled())` + `http.get(currentUserEndpoint, { skipAuthGate: true })`
24
+ * in the store); otherwise the gate holds the request it is waiting for and every request it
25
+ * holds stalls until the gate times out. See `RequestConfig.skipAuthGate` in `@molecule/app-http`.
26
+ *
27
+ * @param authClient - The bonded auth client.
28
+ */
29
+ function registerAuthGate(authClient) {
30
+ setAuthGate(() => authClient.whenRestored?.() ?? Promise.resolve());
31
+ }
3
32
  /**
4
33
  * Builds the default fetch-based HTTP client + wires it into
5
34
  * `@molecule/app-http`. Replaces the per-app `bonds/http-default.ts`
@@ -29,9 +58,14 @@ export function createDefaultHttpClient(baseURL) {
29
58
  * Replaces ~20 per-app `bonds/http-default.ts` files that hand-roll
30
59
  * this same wiring. Pass `stripApiPrefix: true` when the app uses
31
60
  * `baseURL: '/api'` to handle pages that pass `/api/`-prefixed paths
32
- * (would otherwise resolve to `/api/api/...` and 404).
61
+ * (would otherwise resolve to `/api/api/...` and 404). Pass `authScheme`
62
+ * when the API reads another scheme than `Bearer`: it is an option of THIS
63
+ * constructor (`createDefaultHttpClientWithAuthBearer({ authScheme: 'Token', … })`,
64
+ * or `createFetchClient({ baseURL, authScheme: 'Token' })` directly), never a
65
+ * property of the client it returns.
33
66
  */
34
67
  export function createDefaultHttpClientWithAuthBearer(opts) {
68
+ const authScheme = opts.authScheme ?? 'Bearer';
35
69
  const httpClient = createFetchClient({
36
70
  baseURL: opts.baseURL,
37
71
  withCredentials: opts.withCredentials,
@@ -42,7 +76,7 @@ export function createDefaultHttpClientWithAuthBearer(opts) {
42
76
  }
43
77
  const token = opts.getToken();
44
78
  if (token) {
45
- config.headers = { ...config.headers, Authorization: `Bearer ${token}` };
79
+ config.headers = { ...config.headers, Authorization: `${authScheme} ${token}` };
46
80
  }
47
81
  return config;
48
82
  });
@@ -83,7 +117,9 @@ export function createDefaultAuthClient(authConfig) {
83
117
  * authed endpoints return 401 after page reloads or token refresh.
84
118
  *
85
119
  * Hydrates the HTTP client's token from the persisted auth state
86
- * on setup, then listens for auth events to keep them aligned.
120
+ * on setup, then listens for auth events to keep them aligned, and holds
121
+ * requests until the session restore settles so a page effect that fetches
122
+ * viewer-dependent data on mount cannot race it (see {@link registerAuthGate}).
87
123
  */
88
124
  export function createDefaultAuthClientWithHttpSync(authConfig) {
89
125
  const authClient = createJWTAuthClient(authConfig);
@@ -91,6 +127,9 @@ export function createDefaultAuthClientWithHttpSync(authConfig) {
91
127
  authClient,
92
128
  setupAuthDefault: () => {
93
129
  setClient(authClient);
130
+ // Requests made while the session is still restoring wait for it, so a page
131
+ // effect that fetches viewer-dependent data on mount cannot race the restore.
132
+ registerAuthGate(authClient);
94
133
  const initialToken = authClient.getAccessToken();
95
134
  if (initialToken) {
96
135
  getHttpClient().setAuthToken(initialToken);
@@ -119,6 +158,10 @@ export function createDefaultAuthClientWithHttpSync(authConfig) {
119
158
  *
120
159
  * Used by apps where molecule packages render pricing / billing /
121
160
  * other authed JSON-fetching screens that need both behaviors.
161
+ *
162
+ * Requests are also held until the session restore settles (see
163
+ * {@link registerAuthGate}), so a page that fetches viewer-dependent data on
164
+ * mount cannot race it and answer for an anonymous viewer.
122
165
  */
123
166
  export function createDefaultAuthClientWithFetchClient(authConfig, fetchClientOptions) {
124
167
  const authClient = createJWTAuthClient(authConfig);
@@ -131,6 +174,7 @@ export function createDefaultAuthClientWithFetchClient(authConfig, fetchClientOp
131
174
  });
132
175
  setHttpClient(httpClient);
133
176
  setClient(authClient);
177
+ registerAuthGate(authClient);
134
178
  const initialToken = authClient.getAccessToken();
135
179
  if (initialToken) {
136
180
  httpClient.setAuthToken(initialToken);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@molecule/app-bonds-default-react",
3
- "version": "1.1.0",
3
+ "version": "1.1.2",
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",
@@ -38,7 +38,7 @@
38
38
  "directory": "packages/app/infrastructure/bonds-default-react"
39
39
  },
40
40
  "devDependencies": {
41
- "@molecule/app-auth": "1.2.1",
41
+ "@molecule/app-auth": "1.3.0",
42
42
  "@molecule/app-charts-chartjs": "1.0.2",
43
43
  "@molecule/app-client-search": "1.0.2",
44
44
  "@molecule/app-client-search-minisearch": "1.0.3",
@@ -50,7 +50,7 @@
50
50
  "@molecule/app-drag-drop-dndkit": "1.0.2",
51
51
  "@molecule/app-fonts": "1.1.0",
52
52
  "@molecule/app-fonts-arimo": "1.0.2",
53
- "@molecule/app-http": "1.1.1",
53
+ "@molecule/app-http": "1.1.3",
54
54
  "@molecule/app-icons": "1.0.2",
55
55
  "@molecule/app-icons-molecule": "1.0.2",
56
56
  "@molecule/app-keyboard-shortcuts": "1.0.2",