@molecule/app-react 3.0.2 → 3.2.0
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 +180 -55
- package/dist/hooks/useAuth.d.ts +10 -4
- package/dist/hooks/useAuth.d.ts.map +1 -1
- package/dist/hooks/useAuth.js +10 -4
- package/dist/hooks/useAuth.js.map +1 -1
- package/dist/hooks/useChat.d.ts.map +1 -1
- package/dist/hooks/useChat.js +1 -0
- package/dist/hooks/useChat.js.map +1 -1
- package/dist/hooks/useForm.d.ts +6 -3
- package/dist/hooks/useForm.d.ts.map +1 -1
- package/dist/hooks/useForm.js +36 -5
- package/dist/hooks/useForm.js.map +1 -1
- package/dist/hooks/useHttp.d.ts +7 -3
- package/dist/hooks/useHttp.d.ts.map +1 -1
- package/dist/hooks/useHttp.js +14 -2
- package/dist/hooks/useHttp.js.map +1 -1
- package/dist/hooks/useProfileForm.d.ts.map +1 -1
- package/dist/hooks/useProfileForm.js +48 -11
- package/dist/hooks/useProfileForm.js.map +1 -1
- package/dist/index.d.ts +19 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +19 -3
- package/dist/index.js.map +1 -1
- package/dist/providers.d.ts +1 -1
- package/dist/providers.d.ts.map +1 -1
- package/dist/providers.js +5 -3
- package/dist/providers.js.map +1 -1
- package/dist/session-ready.d.ts +41 -0
- package/dist/session-ready.d.ts.map +1 -0
- package/dist/session-ready.js +43 -0
- package/dist/session-ready.js.map +1 -0
- package/dist/types.d.ts +30 -3
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -4
package/dist/hooks/useHttp.js
CHANGED
|
@@ -67,6 +67,11 @@ export function useHttp(method, url, options) {
|
|
|
67
67
|
mountedRef.current = false;
|
|
68
68
|
};
|
|
69
69
|
}, []);
|
|
70
|
+
// The newest request owns the state. Without this, an older request that
|
|
71
|
+
// answers after a newer one (a slow first load, then a refetch) overwrote the
|
|
72
|
+
// newer data and cleared `loading` while the newer request was still in flight.
|
|
73
|
+
// `reset` takes ownership too, so a request it superseded cannot repopulate.
|
|
74
|
+
const latestRequestRef = useRef(0);
|
|
70
75
|
// Publish options during commit, before consumers' layout effects. A render
|
|
71
76
|
// that suspends or is discarded must not change a mounted action's request.
|
|
72
77
|
// This only updates our own ref: no DOM access or state update is involved.
|
|
@@ -81,6 +86,10 @@ export function useHttp(method, url, options) {
|
|
|
81
86
|
// request being sent, while method/url/client decide WHICH request execute
|
|
82
87
|
// performs. Reading them here keeps this action stable for effect deps.
|
|
83
88
|
const { immediate: _immediate, onSuccess, onError, ...requestConfig } = optionsRef.current ?? {};
|
|
89
|
+
const request = ++latestRequestRef.current;
|
|
90
|
+
// Callbacks report THIS request's outcome (a submit's onSuccess still runs);
|
|
91
|
+
// only the newest request may change what the hook shows.
|
|
92
|
+
const ownsState = () => mountedRef.current && request === latestRequestRef.current;
|
|
84
93
|
setState((prev) => ({ ...prev, loading: true, error: null }));
|
|
85
94
|
try {
|
|
86
95
|
let response;
|
|
@@ -104,7 +113,8 @@ export function useHttp(method, url, options) {
|
|
|
104
113
|
throw new Error(t('react.error.unsupportedMethod', { method }, { defaultValue: `Unsupported method: ${method}` }));
|
|
105
114
|
}
|
|
106
115
|
if (mountedRef.current) {
|
|
107
|
-
|
|
116
|
+
if (ownsState())
|
|
117
|
+
setState({ data: response.data, loading: false, error: null });
|
|
108
118
|
onSuccess?.(response.data);
|
|
109
119
|
}
|
|
110
120
|
return response.data;
|
|
@@ -112,13 +122,15 @@ export function useHttp(method, url, options) {
|
|
|
112
122
|
catch (error) {
|
|
113
123
|
const err = error instanceof Error ? error : new Error(String(error));
|
|
114
124
|
if (mountedRef.current) {
|
|
115
|
-
|
|
125
|
+
if (ownsState())
|
|
126
|
+
setState({ data: null, loading: false, error: err });
|
|
116
127
|
onError?.(err);
|
|
117
128
|
}
|
|
118
129
|
return null;
|
|
119
130
|
}
|
|
120
131
|
}, [client, method, url]);
|
|
121
132
|
const reset = useCallback(() => {
|
|
133
|
+
latestRequestRef.current++;
|
|
122
134
|
setState({ data: null, loading: false, error: null });
|
|
123
135
|
}, []);
|
|
124
136
|
// Execute immediately if requested — mount only, as it has always been.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useHttp.js","sourceRoot":"","sources":["../../src/hooks/useHttp.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,SAAS,EAAE,kBAAkB,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAGhG,OAAO,EAAE,CAAC,EAAE,MAAM,oBAAoB,CAAA;AAEtC,OAAO,EAAE,WAAW,EAAE,MAAM,gBAAgB,CAAA;AAE5C;;;;;GAKG;AACH,MAAM,UAAU,aAAa;IAC3B,MAAM,MAAM,GAAG,UAAU,CAAC,WAAW,CAAC,CAAA;IACtC,IAAI,CAAC,MAAM,EAAE,CAAC;QACZ,MAAM,IAAI,KAAK,CACb,CAAC,CAAC,oCAAoC,EAAE,SAAS,EAAE;YACjD,YAAY,EAAE,mDAAmD;SAClE,CAAC,CACH,CAAA;IACH,CAAC;IACD,OAAO,MAAM,CAAA;AACf,CAAC;
|
|
1
|
+
{"version":3,"file":"useHttp.js","sourceRoot":"","sources":["../../src/hooks/useHttp.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useProfileForm.d.ts","sourceRoot":"","sources":["../../src/hooks/useProfileForm.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;
|
|
1
|
+
{"version":3,"file":"useProfileForm.d.ts","sourceRoot":"","sources":["../../src/hooks/useProfileForm.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,KAAK,EAAE,iBAAiB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAA;AAIlF,OAAO,EAAW,KAAK,cAAc,EAAE,KAAK,aAAa,EAAE,MAAM,cAAc,CAAA;AAgB/E;;;;;GAKG;AACH,MAAM,WAAW,qBAAqB,CACpC,KAAK,EACL,OAAO,SAAS,oBAAoB,CAAC,OAAO,CAAC,CAC7C,SAAQ,IAAI,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,eAAe,CAAC;IACtD,6EAA6E;IAC7E,aAAa,EAAE,OAAO,CAAA;IACtB;;;;;OAKG;IACH,WAAW,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,MAAM,CAAA;IACrC,oFAAoF;IACpF,QAAQ,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,OAAO,CAAA;IAClC;;;;OAIG;IACH,OAAO,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,OAAO,CAAC,KAAK,OAAO,CAAC,KAAK,CAAC,CAAA;IACjF;;;;OAIG;IACH,OAAO,CAAC,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAChD;AAED,8DAA8D;AAC9D,MAAM,WAAW,oBAAoB,CACnC,KAAK,EACL,OAAO,SAAS,oBAAoB,CAAC,OAAO,CAAC,CAC7C,SAAQ,IAAI,CAAC,aAAa,CAAC,OAAO,CAAC,EAAE,UAAU,GAAG,cAAc,GAAG,OAAO,CAAC;IAC3E,8CAA8C;IAC9C,IAAI,EAAE,KAAK,GAAG,IAAI,CAAA;IAClB,qEAAqE;IACrE,KAAK,EAAE,OAAO,CAAA;IACd;;;OAGG;IACH,KAAK,EAAE,OAAO,CAAA;IACd,2EAA2E;IAC3E,QAAQ,EAAE,CAAC,CAAC,SAAS,MAAM,OAAO,EAChC,IAAI,EAAE,CAAC,EACP,OAAO,CAAC,EAAE,UAAU,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,CAAC,KACxD,IAAI,CAAC,iBAAiB,EAAE,OAAO,CAAC,GAAG;QAAE,KAAK,EAAE,OAAO,CAAC,CAAC,CAAC,CAAA;KAAE,CAAA;IAC7D,sEAAsE;IACtE,SAAS,EAAE;QAAE,QAAQ,EAAE,CAAC,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,eAAe,CAAC,KAAK,IAAI,CAAA;KAAE,CAAA;IAC1E;;;OAGG;IACH,MAAM,EAAE,MAAM,OAAO,CAAC,KAAK,GAAG,SAAS,CAAC,CAAA;IACxC,4EAA4E;IAC5E,KAAK,EAAE,MAAM,IAAI,CAAA;CAClB;AAED;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,OAAO,SAAS,oBAAoB,CAAC,OAAO,CAAC,EACjF,OAAO,EAAE,qBAAqB,CAAC,KAAK,EAAE,OAAO,CAAC,GAC7C,oBAAoB,CAAC,KAAK,EAAE,OAAO,CAAC,CAiPtC"}
|
|
@@ -4,20 +4,21 @@
|
|
|
4
4
|
* @module
|
|
5
5
|
*/
|
|
6
6
|
import { useCallback, useInsertionEffect, useLayoutEffect, useRef, useState } from 'react';
|
|
7
|
+
import { formValuesEqual, snapshotFormValue } from '@molecule/app-forms';
|
|
7
8
|
import { useAuth, useAuthClient } from './useAuth.js';
|
|
8
9
|
import { useForm } from './useForm.js';
|
|
9
10
|
/**
|
|
10
|
-
*
|
|
11
|
+
* Detaches the value snapshot the hook keeps as its baseline.
|
|
11
12
|
*
|
|
12
13
|
* Form values are a plain string-keyed object at runtime; the caller's values
|
|
13
14
|
* TYPE may be an `interface`, which has no index signature to spread from, so
|
|
14
15
|
* the copy goes through the record view and comes back as `TValues`.
|
|
15
16
|
*
|
|
16
17
|
* @param values - The values object to snapshot.
|
|
17
|
-
* @returns A
|
|
18
|
+
* @returns A detached form-value snapshot.
|
|
18
19
|
*/
|
|
19
20
|
function copyValues(values) {
|
|
20
|
-
return
|
|
21
|
+
return snapshotFormValue(values);
|
|
21
22
|
}
|
|
22
23
|
/**
|
|
23
24
|
* Owns load → edit → submit → publish for the signed-in user's profile.
|
|
@@ -44,6 +45,9 @@ export function useProfileForm(options) {
|
|
|
44
45
|
const optionsRef = useRef(options);
|
|
45
46
|
const committedClient = useRef(client);
|
|
46
47
|
const baseline = useRef(copyValues(defaultValues));
|
|
48
|
+
// A late same-account record must not undo fields the person edited, even
|
|
49
|
+
// after those edits became the clean baseline of a completed save.
|
|
50
|
+
const editedFields = useRef(new Set());
|
|
47
51
|
const owner = useRef(null);
|
|
48
52
|
const activeSave = useRef(null);
|
|
49
53
|
const pendingPropagation = useRef(undefined);
|
|
@@ -68,20 +72,26 @@ export function useProfileForm(options) {
|
|
|
68
72
|
const rebase = useCallback((next, preserveAgainst) => {
|
|
69
73
|
const current = readValues();
|
|
70
74
|
baseline.current = copyValues(next);
|
|
71
|
-
form.reset(next);
|
|
72
75
|
if (preserveAgainst) {
|
|
76
|
+
form.updateValues(next, { keepDirtyValues: true });
|
|
73
77
|
for (const key of Object.keys(current)) {
|
|
74
|
-
if (
|
|
78
|
+
if (editedFields.current.has(key) ||
|
|
79
|
+
!formValuesEqual(current[key], preserveAgainst[key])) {
|
|
75
80
|
form.setValue(key, current[key]);
|
|
81
|
+
}
|
|
76
82
|
}
|
|
77
83
|
}
|
|
78
|
-
|
|
84
|
+
else {
|
|
85
|
+
form.reset(next);
|
|
86
|
+
}
|
|
87
|
+
}, [readValues, form.reset, form.updateValues, form.setValue]);
|
|
79
88
|
useLayoutEffect(() => {
|
|
80
89
|
const config = optionsRef.current;
|
|
81
90
|
if (!user) {
|
|
82
91
|
if (owner.current) {
|
|
83
92
|
owner.current = null;
|
|
84
93
|
pendingPropagation.current = undefined;
|
|
94
|
+
editedFields.current.clear();
|
|
85
95
|
rebase(copyValues(config.defaultValues));
|
|
86
96
|
setError(undefined);
|
|
87
97
|
}
|
|
@@ -100,8 +110,10 @@ export function useProfileForm(options) {
|
|
|
100
110
|
const previousBaseline = baseline.current;
|
|
101
111
|
// Keep this token across same-account hydration, but replace it when the
|
|
102
112
|
// account/client changes. A pending validation belongs to that exact owner.
|
|
103
|
-
if (!previous || !sameAccount)
|
|
113
|
+
if (!previous || !sameAccount) {
|
|
104
114
|
owner.current = { client, revision, identity };
|
|
115
|
+
editedFields.current.clear();
|
|
116
|
+
}
|
|
105
117
|
rebase(config.toValues(user), sameAccount ? previousBaseline : undefined);
|
|
106
118
|
if (!sameAccount) {
|
|
107
119
|
pendingPropagation.current = undefined;
|
|
@@ -131,7 +143,7 @@ export function useProfileForm(options) {
|
|
|
131
143
|
const before = copyValues(baseline.current);
|
|
132
144
|
const changes = {};
|
|
133
145
|
for (const key of Object.keys(snapshot)) {
|
|
134
|
-
if (!
|
|
146
|
+
if (!formValuesEqual(snapshot[key], before[key]))
|
|
135
147
|
changes[key] = snapshot[key];
|
|
136
148
|
}
|
|
137
149
|
setError(undefined);
|
|
@@ -154,8 +166,20 @@ export function useProfileForm(options) {
|
|
|
154
166
|
saved = undefined;
|
|
155
167
|
return;
|
|
156
168
|
}
|
|
157
|
-
if (mounted.current)
|
|
158
|
-
|
|
169
|
+
if (mounted.current) {
|
|
170
|
+
const savedValues = config.toValues(saved);
|
|
171
|
+
const current = readValues();
|
|
172
|
+
for (const key of editedFields.current) {
|
|
173
|
+
if (formValuesEqual(current[key], snapshot[key]) &&
|
|
174
|
+
!formValuesEqual(savedValues[key], snapshot[key])) {
|
|
175
|
+
// The accepted response intentionally transformed or cleared
|
|
176
|
+
// this field (for example, a write-only password).
|
|
177
|
+
editedFields.current.delete(key);
|
|
178
|
+
form.setValue(key, savedValues[key]);
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
rebase(savedValues, snapshot);
|
|
182
|
+
}
|
|
159
183
|
pendingPropagation.current = saved;
|
|
160
184
|
}
|
|
161
185
|
if (pendingPropagation.current !== undefined) {
|
|
@@ -186,9 +210,21 @@ export function useProfileForm(options) {
|
|
|
186
210
|
return activeSave.current;
|
|
187
211
|
}, [client, readValues, form.handleSubmit, rebase]);
|
|
188
212
|
const register = useCallback((name, registerOptions) => {
|
|
189
|
-
|
|
213
|
+
const registration = form.register(name, registerOptions);
|
|
214
|
+
return {
|
|
215
|
+
...registration,
|
|
216
|
+
onChange: (event) => {
|
|
217
|
+
editedFields.current.add(name);
|
|
218
|
+
registration.onChange(event);
|
|
219
|
+
},
|
|
220
|
+
};
|
|
190
221
|
}, [form.register]);
|
|
222
|
+
const setValue = useCallback((name, value) => {
|
|
223
|
+
editedFields.current.add(name);
|
|
224
|
+
form.setValue(name, value);
|
|
225
|
+
}, [form.setValue]);
|
|
191
226
|
const reset = useCallback(() => {
|
|
227
|
+
editedFields.current.clear();
|
|
192
228
|
form.reset(baseline.current);
|
|
193
229
|
setError(undefined);
|
|
194
230
|
}, [form.reset]);
|
|
@@ -204,6 +240,7 @@ export function useProfileForm(options) {
|
|
|
204
240
|
ready: user !== null,
|
|
205
241
|
error,
|
|
206
242
|
register,
|
|
243
|
+
setValue,
|
|
207
244
|
reset,
|
|
208
245
|
submit,
|
|
209
246
|
formProps: { onSubmit },
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useProfileForm.js","sourceRoot":"","sources":["../../src/hooks/useProfileForm.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"useProfileForm.js","sourceRoot":"","sources":["../../src/hooks/useProfileForm.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,WAAW,EAAE,kBAAkB,EAAE,eAAe,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAA;AAG1F,OAAO,EAAE,eAAe,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAA;AAExE,OAAO,EAAE,OAAO,EAAE,aAAa,EAAE,MAAM,cAAc,CAAA;AACrD,OAAO,EAAE,OAAO,EAA2C,MAAM,cAAc,CAAA;AAE/E;;;;;;;;;GASG;AACH,SAAS,UAAU,CAAoC,MAAS;IAC9D,OAAO,iBAAiB,CAAC,MAAiC,CAAM,CAAA;AAClE,CAAC;AAmED;;;;;;;;;;;;;;GAcG;AACH,MAAM,UAAU,cAAc,CAC5B,OAA8C;IAE9C,MAAM,EACJ,aAAa,EACb,WAAW,EAAE,SAAS,EACtB,QAAQ,EAAE,OAAO,EACjB,OAAO,EAAE,MAAM,EACf,OAAO,EAAE,MAAM,EACf,GAAG,WAAW,EACf,GAAG,OAAO,CAAA;IACX,MAAM,IAAI,GAAG,OAAO,CAAU,EAAE,GAAG,WAAW,EAAE,aAAa,EAAE,CAAC,CAAA;IAChE,MAAM,MAAM,GAAG,aAAa,EAAS,CAAA;IACrC,MAAM,EAAE,IAAI,EAAE,GAAG,OAAO,EAAS,CAAA;IACjC,MAAM,CAAC,KAAK,EAAE,QAAQ,CAAC,GAAG,QAAQ,EAAW,CAAA;IAC7C,MAAM,CAAC,QAAQ,EAAE,WAAW,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAA;IAC/C,MAAM,UAAU,GAAG,MAAM,CAAC,OAAO,CAAC,CAAA;IAClC,MAAM,eAAe,GAAG,MAAM,CAAC,MAAM,CAAC,CAAA;IACtC,MAAM,QAAQ,GAAG,MAAM,CAAC,UAAU,CAAC,aAAa,CAAC,CAAC,CAAA;IAClD,0EAA0E;IAC1E,mEAAmE;IACnE,MAAM,YAAY,GAAG,MAAM,CAAqB,IAAI,GAAG,EAAE,CAAC,CAAA;IAC1D,MAAM,KAAK,GAAG,MAAM,CAIV,IAAI,CAAC,CAAA;IACf,MAAM,UAAU,GAAG,MAAM,CAAoC,IAAI,CAAC,CAAA;IAClE,MAAM,kBAAkB,GAAG,MAAM,CAAoB,SAAS,CAAC,CAAA;IAC/D,MAAM,OAAO,GAAG,MAAM,CAAC,KAAK,CAAC,CAAA;IAE7B,6EAA6E;IAC7E,kBAAkB,CAAC,GAAG,EAAE;QACtB,UAAU,CAAC,OAAO,GAAG,OAAO,CAAA;QAC5B,eAAe,CAAC,OAAO,GAAG,MAAM,CAAA;IAClC,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,CAAC,CAAC,CAAA;IACrB,eAAe,CAAC,GAAG,EAAE;QACnB,OAAO,CAAC,OAAO,GAAG,IAAI,CAAA;QACtB,OAAO,GAAG,EAAE;YACV,OAAO,CAAC,OAAO,GAAG,KAAK,CAAA;QACzB,CAAC,CAAA;IACH,CAAC,EAAE,EAAE,CAAC,CAAA;IAEN,MAAM,UAAU,GAAG,WAAW,CAAC,GAAY,EAAE;QAC3C,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;QAC3C,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,MAAM,CAAsB;YAAE,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAA;QAC5F,OAAO,MAAM,CAAA;IACf,CAAC,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAA;IAEnB,MAAM,MAAM,GAAG,WAAW,CACxB,CAAC,IAAa,EAAE,eAAyB,EAAQ,EAAE;QACjD,MAAM,OAAO,GAAG,UAAU,EAAE,CAAA;QAC5B,QAAQ,CAAC,OAAO,GAAG,UAAU,CAAC,IAAI,CAAC,CAAA;QACnC,IAAI,eAAe,EAAE,CAAC;YACpB,IAAI,CAAC,YAAY,CAAC,IAAI,EAAE,EAAE,eAAe,EAAE,IAAI,EAAE,CAAC,CAAA;YAClD,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,OAAO,CAAsB,EAAE,CAAC;gBAC5D,IACE,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,GAAG,CAAC;oBAC7B,CAAC,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,eAAe,CAAC,GAAG,CAAC,CAAC,EACpD,CAAC;oBACD,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC,CAAA;gBAClC,CAAC;YACH,CAAC;QACH,CAAC;aAAM,CAAC;YACN,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,CAAA;QAClB,CAAC;IACH,CAAC,EACD,CAAC,UAAU,EAAE,IAAI,CAAC,KAAK,EAAE,IAAI,CAAC,YAAY,EAAE,IAAI,CAAC,QAAQ,CAAC,CAC3D,CAAA;IAED,eAAe,CAAC,GAAG,EAAE;QACnB,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAA;QACjC,IAAI,CAAC,IAAI,EAAE,CAAC;YACV,IAAI,KAAK,CAAC,OAAO,EAAE,CAAC;gBAClB,KAAK,CAAC,OAAO,GAAG,IAAI,CAAA;gBACpB,kBAAkB,CAAC,OAAO,GAAG,SAAS,CAAA;gBACtC,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,CAAA;gBAC5B,MAAM,CAAC,UAAU,CAAC,MAAM,CAAC,aAAa,CAAC,CAAC,CAAA;gBACxC,QAAQ,CAAC,SAAS,CAAC,CAAA;YACrB,CAAC;YACD,OAAM;QACR,CAAC;QACD,MAAM,QAAQ,GAAG,MAAM,CAAC,kBAAkB,EAAE,EAAE,CAAA;QAC9C,MAAM,QAAQ,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC,IAAI,CAAC,CAAA;QAC3C,IAAI,QAAQ,KAAK,SAAS,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC;YAClD,MAAM,IAAI,KAAK,CACb,kFAAkF,CACnF,CAAA;QACH,CAAC;QACD,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAA;QAC9B,MAAM,WAAW,GACf,CAAC,QAAQ;YACT,CAAC,QAAQ,CAAC,MAAM,KAAK,MAAM;gBACzB,QAAQ,CAAC,QAAQ,KAAK,QAAQ;gBAC9B,QAAQ,CAAC,QAAQ,KAAK,QAAQ,CAAC,CAAA;QACnC,MAAM,gBAAgB,GAAG,QAAQ,CAAC,OAAO,CAAA;QACzC,yEAAyE;QACzE,4EAA4E;QAC5E,IAAI,CAAC,QAAQ,IAAI,CAAC,WAAW,EAAE,CAAC;YAC9B,KAAK,CAAC,OAAO,GAAG,EAAE,MAAM,EAAE,QAAQ,EAAE,QAAQ,EAAE,CAAA;YAC9C,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,CAAA;QAC9B,CAAC;QACD,MAAM,CAAC,MAAM,CAAC,QAAQ,CAAC,IAAI,CAAC,EAAE,WAAW,CAAC,CAAC,CAAC,gBAAgB,CAAC,CAAC,CAAC,SAAS,CAAC,CAAA;QACzE,IAAI,CAAC,WAAW,EAAE,CAAC;YACjB,kBAAkB,CAAC,OAAO,GAAG,SAAS,CAAA;YACtC,QAAQ,CAAC,SAAS,CAAC,CAAA;QACrB,CAAC;IACH,CAAC,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC,CAAA;IAE1B,MAAM,MAAM,GAAG,WAAW,CAAC,GAA+B,EAAE;QAC1D,IAAI,UAAU,CAAC,OAAO;YAAE,OAAO,UAAU,CAAC,OAAO,CAAA;QACjD,MAAM,WAAW,GAAG,MAAM,CAAC,OAAO,EAAE,CAAA;QACpC,IAAI,CAAC,OAAO,CAAC,OAAO,IAAI,CAAC,WAAW;YAAE,OAAO,OAAO,CAAC,OAAO,CAAC,SAAS,CAAC,CAAA;QACvE,MAAM,MAAM,GAAG,UAAU,CAAC,OAAO,CAAA;QACjC,MAAM,QAAQ,GAAG,MAAM,CAAC,kBAAkB,EAAE,EAAE,CAAA;QAC9C,MAAM,QAAQ,GAAG,MAAM,CAAC,WAAW,EAAE,CAAC,WAAW,CAAC,CAAA;QAClD,MAAM,eAAe,GAAG,KAAK,CAAC,OAAO,CAAA;QACrC,MAAM,WAAW,GAAG,GAAY,EAAE;YAChC,MAAM,OAAO,GAAG,MAAM,CAAC,OAAO,EAAE,CAAA;YAChC,OAAO,CACL,OAAO,CAAC,OAAO;gBACf,eAAe,CAAC,OAAO,KAAK,MAAM;gBAClC,KAAK,CAAC,OAAO,KAAK,eAAe;gBACjC,OAAO,KAAK,IAAI;gBAChB,MAAM,CAAC,kBAAkB,EAAE,EAAE,KAAK,QAAQ;gBAC1C,MAAM,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,KAAK,QAAQ,CAC3C,CAAA;QACH,CAAC,CAAA;QACD,MAAM,QAAQ,GAAG,UAAU,EAAE,CAAA;QAC7B,MAAM,MAAM,GAAG,UAAU,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;QAC3C,MAAM,OAAO,GAAqB,EAAE,CAAA;QACpC,KAAK,MAAM,GAAG,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAsB,EAAE,CAAC;YAC7D,IAAI,CAAC,eAAe,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,MAAM,CAAC,GAAG,CAAC,CAAC;gBAAE,OAAO,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,GAAG,CAAC,CAAA;QAChF,CAAC;QACD,QAAQ,CAAC,SAAS,CAAC,CAAA;QACnB,WAAW,CAAC,IAAI,CAAC,CAAA;QACjB,IAAI,KAAwB,CAAA;QAC5B,MAAM,GAAG,GAAG,KAAK,IAAgC,EAAE;YACjD,IAAI,CAAC;gBACH,MAAM,IAAI,CAAC,YAAY,CAAC,KAAK,IAAI,EAAE;oBACjC,oEAAoE;oBACpE,qEAAqE;oBACrE,kEAAkE;oBAClE,IAAI,CAAC,WAAW,EAAE;wBAAE,OAAM;oBAC1B,MAAM,KAAK,GAAG,MAAM,CAAC,OAAO,CAAC,OAAO,EAAE,QAAQ,CAAC,CAAA;oBAC/C,IAAI,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;wBAClC,IAAI,CAAC,WAAW,EAAE;4BAAE,OAAM;wBAC1B,KAAK,GAAG,MAAM,MAAM,CAAC,aAAa,CAAC,KAAK,CAAC,CAAA;wBACzC,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;4BACnB,KAAK,GAAG,SAAS,CAAA;4BACjB,OAAM;wBACR,CAAC;wBACD,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;4BACpB,MAAM,WAAW,GAAG,MAAM,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;4BAC1C,MAAM,OAAO,GAAG,UAAU,EAAE,CAAA;4BAC5B,KAAK,MAAM,GAAG,IAAI,YAAY,CAAC,OAAO,EAAE,CAAC;gCACvC,IACE,eAAe,CAAC,OAAO,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC;oCAC5C,CAAC,eAAe,CAAC,WAAW,CAAC,GAAG,CAAC,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,EACjD,CAAC;oCACD,6DAA6D;oCAC7D,mDAAmD;oCACnD,YAAY,CAAC,OAAO,CAAC,MAAM,CAAC,GAAG,CAAC,CAAA;oCAChC,IAAI,CAAC,QAAQ,CAAC,GAAG,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAA;gCACtC,CAAC;4BACH,CAAC;4BACD,MAAM,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAA;wBAC/B,CAAC;wBACD,kBAAkB,CAAC,OAAO,GAAG,KAAK,CAAA;oBACpC,CAAC;oBACD,IAAI,kBAAkB,CAAC,OAAO,KAAK,SAAS,EAAE,CAAC;wBAC7C,IAAI,CAAC,WAAW,EAAE,EAAE,CAAC;4BACnB,KAAK,GAAG,SAAS,CAAA;4BACjB,OAAM;wBACR,CAAC;wBACD,MAAM,UAAU,GAAG,kBAAkB,CAAC,OAAO,CAAA;wBAC7C,MAAM,MAAM,CAAC,OAAO,EAAE,CAAC,UAAU,CAAC,CAAA;wBAClC,kBAAkB,CAAC,OAAO,GAAG,SAAS,CAAA;wBACtC,KAAK,GAAG,UAAU,CAAA;oBACpB,CAAC;gBACH,CAAC,CAAC,EAAE,CAAA;gBACJ,OAAO,KAAK,CAAA;YACd,CAAC;YAAC,OAAO,OAAO,EAAE,CAAC;gBACjB,IAAI,OAAO,CAAC,OAAO;oBAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;gBACtC,OAAO,SAAS,CAAA;YAClB,CAAC;oBAAS,CAAC;gBACT,UAAU,CAAC,OAAO,GAAG,IAAI,CAAA;gBACzB,IAAI,OAAO,CAAC,OAAO;oBAAE,WAAW,CAAC,KAAK,CAAC,CAAA;YACzC,CAAC;QACH,CAAC,CAAA;QACD,UAAU,CAAC,OAAO,GAAG,GAAG,EAAE,CAAA;QAC1B,OAAO,UAAU,CAAC,OAAO,CAAA;IAC3B,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,EAAE,IAAI,CAAC,YAAY,EAAE,MAAM,CAAC,CAAC,CAAA;IAEnD,MAAM,QAAQ,GAAG,WAAW,CAC1B,CACE,IAAO,EACP,eAAmE,EACT,EAAE;QAC5D,MAAM,YAAY,GAAG,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC,CAAA;QACzD,OAAO;YACL,GAAG,YAAY;YACf,QAAQ,EAAE,CAAC,KAAmD,EAAE,EAAE;gBAChE,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;gBAC9B,YAAY,CAAC,QAAQ,CAAC,KAAK,CAAC,CAAA;YAC9B,CAAC;SAC0D,CAAA;IAC/D,CAAC,EACD,CAAC,IAAI,CAAC,QAAQ,CAAC,CAChB,CAAA;IACD,MAAM,QAAQ,GAAG,WAAW,CAC1B,CAA0B,IAAO,EAAE,KAAiB,EAAQ,EAAE;QAC5D,YAAY,CAAC,OAAO,CAAC,GAAG,CAAC,IAAI,CAAC,CAAA;QAC9B,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,KAAK,CAAC,CAAA;IAC5B,CAAC,EACD,CAAC,IAAI,CAAC,QAAQ,CAAC,CAChB,CAAA;IACD,MAAM,KAAK,GAAG,WAAW,CAAC,GAAG,EAAE;QAC7B,YAAY,CAAC,OAAO,CAAC,KAAK,EAAE,CAAA;QAC5B,IAAI,CAAC,KAAK,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAA;QAC5B,QAAQ,CAAC,SAAS,CAAC,CAAA;IACrB,CAAC,EAAE,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAA;IAChB,MAAM,QAAQ,GAAG,WAAW,CAC1B,CAAC,KAAuC,EAAQ,EAAE;QAChD,KAAK,CAAC,cAAc,EAAE,CAAA;QACtB,KAAK,MAAM,EAAE,CAAA;IACf,CAAC,EACD,CAAC,MAAM,CAAC,CACT,CAAA;IAED,OAAO;QACL,GAAG,IAAI;QACP,SAAS,EAAE,EAAE,GAAG,IAAI,CAAC,SAAS,EAAE,YAAY,EAAE,QAAQ,IAAI,IAAI,CAAC,YAAY,EAAE;QAC7E,YAAY,EAAE,QAAQ,IAAI,IAAI,CAAC,YAAY;QAC3C,IAAI;QACJ,KAAK,EAAE,IAAI,KAAK,IAAI;QACpB,KAAK;QACL,QAAQ;QACR,QAAQ;QACR,KAAK;QACL,MAAM;QACN,SAAS,EAAE,EAAE,QAAQ,EAAE;KACxB,CAAA;AACH,CAAC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -15,6 +15,18 @@
|
|
|
15
15
|
* from that response before navigating. Configure the endpoint, method and JSON
|
|
16
16
|
* envelope once in `AuthClientConfig`; no page-local session store is needed.
|
|
17
17
|
*
|
|
18
|
+
* **Session restore:** `useAuth().status === 'loading'` is undecided: wait for
|
|
19
|
+
* it and redirect only on `status === 'unauthenticated'`. `'unavailable'` keeps
|
|
20
|
+
* the session candidate for retry, but `isAuthenticated` can be false and `user`
|
|
21
|
+
* can be null. A restore failure other than 401/403 never signs out.
|
|
22
|
+
* `MoleculeProvider auth={authClient} awaitSession` (or `SessionReady` inside
|
|
23
|
+
* `AuthProvider`) holds public pages until that initial loading status ends,
|
|
24
|
+
* so their first render reads the restored user when restoration succeeds.
|
|
25
|
+
* `sessionFallback` remains inside the service providers. Unavailable sessions
|
|
26
|
+
* also render and can still have a null user. Synchronous SSR renders the
|
|
27
|
+
* fallback for a loading client: initialize a request-scoped client first,
|
|
28
|
+
* or leave the opt-in hold disabled for SSR.
|
|
29
|
+
*
|
|
18
30
|
* @example
|
|
19
31
|
* ```tsx
|
|
20
32
|
* const profile = useProfileForm<User, { name: string; email: string }>({
|
|
@@ -44,16 +56,19 @@
|
|
|
44
56
|
* const authClient = createJWTAuthClient({ baseURL: '/api' })
|
|
45
57
|
*
|
|
46
58
|
* function Dashboard() {
|
|
47
|
-
* const { user,
|
|
59
|
+
* const { user, status, logout } = useAuth<{ name?: string }>()
|
|
48
60
|
* const { t } = useTranslation()
|
|
49
61
|
* const { theme, toggleTheme } = useTheme()
|
|
50
62
|
*
|
|
51
|
-
* if (
|
|
63
|
+
* if (status === 'loading') {
|
|
64
|
+
* return <p>{t('auth.restoring', undefined, { defaultValue: 'Restoring session...' })}</p>
|
|
65
|
+
* }
|
|
66
|
+
* if (status === 'unauthenticated') {
|
|
52
67
|
* return <p>{t('auth.required', undefined, { defaultValue: 'Please log in.' })}</p>
|
|
53
68
|
* }
|
|
54
69
|
* return (
|
|
55
70
|
* <div style={{ background: theme.colors.background }}>
|
|
56
|
-
* <h1>{t('greeting.welcome', { name: user?.name }, { defaultValue: 'Welcome, {{name}}!' })}</h1>
|
|
71
|
+
* <h1>{t('greeting.welcome', { name: user?.name ?? '' }, { defaultValue: 'Welcome, {{name}}!' })}</h1>
|
|
57
72
|
* <button onClick={toggleTheme}>{t('theme.toggle', undefined, { defaultValue: 'Toggle theme' })}</button>
|
|
58
73
|
* <button onClick={() => logout()}>{t('auth.logout', undefined, { defaultValue: 'Log out' })}</button>
|
|
59
74
|
* </div>
|
|
@@ -162,5 +177,6 @@ export * from './contexts.js';
|
|
|
162
177
|
export * from './hooks/index.js';
|
|
163
178
|
export * from './hooks/useProfileForm.js';
|
|
164
179
|
export * from './providers.js';
|
|
180
|
+
export * from './session-ready.js';
|
|
165
181
|
export * from './types.js';
|
|
166
182
|
//# sourceMappingURL=index.d.ts.map
|
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;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6KG;AAEH,cAAc,qBAAqB,CAAA;AACnC,cAAc,eAAe,CAAA;AAC7B,cAAc,kBAAkB,CAAA;AAChC,cAAc,2BAA2B,CAAA;AACzC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,oBAAoB,CAAA;AAClC,cAAc,YAAY,CAAA"}
|
package/dist/index.js
CHANGED
|
@@ -15,6 +15,18 @@
|
|
|
15
15
|
* from that response before navigating. Configure the endpoint, method and JSON
|
|
16
16
|
* envelope once in `AuthClientConfig`; no page-local session store is needed.
|
|
17
17
|
*
|
|
18
|
+
* **Session restore:** `useAuth().status === 'loading'` is undecided: wait for
|
|
19
|
+
* it and redirect only on `status === 'unauthenticated'`. `'unavailable'` keeps
|
|
20
|
+
* the session candidate for retry, but `isAuthenticated` can be false and `user`
|
|
21
|
+
* can be null. A restore failure other than 401/403 never signs out.
|
|
22
|
+
* `MoleculeProvider auth={authClient} awaitSession` (or `SessionReady` inside
|
|
23
|
+
* `AuthProvider`) holds public pages until that initial loading status ends,
|
|
24
|
+
* so their first render reads the restored user when restoration succeeds.
|
|
25
|
+
* `sessionFallback` remains inside the service providers. Unavailable sessions
|
|
26
|
+
* also render and can still have a null user. Synchronous SSR renders the
|
|
27
|
+
* fallback for a loading client: initialize a request-scoped client first,
|
|
28
|
+
* or leave the opt-in hold disabled for SSR.
|
|
29
|
+
*
|
|
18
30
|
* @example
|
|
19
31
|
* ```tsx
|
|
20
32
|
* const profile = useProfileForm<User, { name: string; email: string }>({
|
|
@@ -44,16 +56,19 @@
|
|
|
44
56
|
* const authClient = createJWTAuthClient({ baseURL: '/api' })
|
|
45
57
|
*
|
|
46
58
|
* function Dashboard() {
|
|
47
|
-
* const { user,
|
|
59
|
+
* const { user, status, logout } = useAuth<{ name?: string }>()
|
|
48
60
|
* const { t } = useTranslation()
|
|
49
61
|
* const { theme, toggleTheme } = useTheme()
|
|
50
62
|
*
|
|
51
|
-
* if (
|
|
63
|
+
* if (status === 'loading') {
|
|
64
|
+
* return <p>{t('auth.restoring', undefined, { defaultValue: 'Restoring session...' })}</p>
|
|
65
|
+
* }
|
|
66
|
+
* if (status === 'unauthenticated') {
|
|
52
67
|
* return <p>{t('auth.required', undefined, { defaultValue: 'Please log in.' })}</p>
|
|
53
68
|
* }
|
|
54
69
|
* return (
|
|
55
70
|
* <div style={{ background: theme.colors.background }}>
|
|
56
|
-
* <h1>{t('greeting.welcome', { name: user?.name }, { defaultValue: 'Welcome, {{name}}!' })}</h1>
|
|
71
|
+
* <h1>{t('greeting.welcome', { name: user?.name ?? '' }, { defaultValue: 'Welcome, {{name}}!' })}</h1>
|
|
57
72
|
* <button onClick={toggleTheme}>{t('theme.toggle', undefined, { defaultValue: 'Toggle theme' })}</button>
|
|
58
73
|
* <button onClick={() => logout()}>{t('auth.logout', undefined, { defaultValue: 'Log out' })}</button>
|
|
59
74
|
* </div>
|
|
@@ -162,5 +177,6 @@ export * from './contexts.js';
|
|
|
162
177
|
export * from './hooks/index.js';
|
|
163
178
|
export * from './hooks/useProfileForm.js';
|
|
164
179
|
export * from './providers.js';
|
|
180
|
+
export * from './session-ready.js';
|
|
165
181
|
export * from './types.js';
|
|
166
182
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6KG;AAEH,cAAc,qBAAqB,CAAA;AACnC,cAAc,eAAe,CAAA;AAC7B,cAAc,kBAAkB,CAAA;AAChC,cAAc,2BAA2B,CAAA;AACzC,cAAc,gBAAgB,CAAA;AAC9B,cAAc,oBAAoB,CAAA;AAClC,cAAc,YAAY,CAAA"}
|
package/dist/providers.d.ts
CHANGED
|
@@ -188,5 +188,5 @@ export declare function PreviewProvider({ provider, children }: PreviewProviderP
|
|
|
188
188
|
* </MoleculeProvider>
|
|
189
189
|
* ```
|
|
190
190
|
*/
|
|
191
|
-
export declare function MoleculeProvider({ children, state, auth, theme, router, i18n, http, storage, logger, chat, workspace, editor, preview, }: MoleculeProviderProps): React.ReactElement;
|
|
191
|
+
export declare function MoleculeProvider({ children, state, auth, theme, router, i18n, http, storage, logger, chat, workspace, editor, preview, awaitSession, sessionFallback, }: MoleculeProviderProps): React.ReactElement;
|
|
192
192
|
//# sourceMappingURL=providers.d.ts.map
|
package/dist/providers.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../src/providers.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,MAAM,OAAO,CAAA;
|
|
1
|
+
{"version":3,"file":"providers.d.ts","sourceRoot":"","sources":["../src/providers.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAGH,OAAO,KAAK,MAAM,OAAO,CAAA;AAuBzB,OAAO,KAAK,EACV,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,iBAAiB,EACjB,iBAAiB,EACjB,mBAAmB,EACnB,qBAAqB,EACrB,oBAAoB,EACpB,mBAAmB,EACnB,kBAAkB,EAClB,oBAAoB,EACpB,kBAAkB,EAClB,sBAAsB,EACvB,MAAM,YAAY,CAAA;AAEnB;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,kBAAkB,GAAG,KAAK,CAAC,YAAY,CAE5F;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,CAAC,GAAG,OAAO,EAAE,EACxC,MAAM,EACN,QAAQ,GACT,EAAE,iBAAiB,CAAC,CAAC,CAAC,GAAG,KAAK,CAAC,YAAY,CAI3C;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,kBAAkB,GAAG,KAAK,CAAC,YAAY,CAE5F;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,mBAAmB,GAAG,KAAK,CAAC,YAAY,CAE5F;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,iBAAiB,GAAG,KAAK,CAAC,YAAY,CAE1F;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,YAAY,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,iBAAiB,GAAG,KAAK,CAAC,YAAY,CAExF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,oBAAoB,GAAG,KAAK,CAAC,YAAY,CAEhG;AAED;;;;;;;;;;;;;;;GAeG;AACH,wBAAgB,cAAc,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,mBAAmB,GAAG,KAAK,CAAC,YAAY,CAE9F;AAED;;;;GAIG;AACH,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,iBAAiB,GAAG,KAAK,CAAC,YAAY,CAE1F;AAED;;;;GAIG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,QAAQ,EACR,QAAQ,GACT,EAAE,sBAAsB,GAAG,KAAK,CAAC,YAAY,CAE7C;AAED;;;;GAIG;AACH,wBAAgB,cAAc,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,mBAAmB,GAAG,KAAK,CAAC,YAAY,CAE9F;AAED;;;;GAIG;AACH,wBAAgB,eAAe,CAAC,EAAE,QAAQ,EAAE,QAAQ,EAAE,EAAE,oBAAoB,GAAG,KAAK,CAAC,YAAY,CAEhG;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4BG;AACH,wBAAgB,gBAAgB,CAAC,EAC/B,QAAQ,EACR,KAAK,EACL,IAAI,EACJ,KAAK,EACL,MAAM,EACN,IAAI,EACJ,IAAI,EACJ,OAAO,EACP,MAAM,EACN,IAAI,EACJ,SAAS,EACT,MAAM,EACN,OAAO,EACP,YAAoB,EACpB,eAAe,GAChB,EAAE,qBAAqB,GAAG,KAAK,CAAC,YAAY,CAkE5C"}
|
package/dist/providers.js
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { jsx as _jsx, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
2
|
import { getProvider as getStateProvider, hasProvider as hasStateProvider, } from '@molecule/app-state';
|
|
3
3
|
import { AuthContext, ChatContext, EditorContext, HttpContext, I18nContext, LoggerContext, PreviewContext, RouterContext, StateContext, StorageContext, ThemeContext, WorkspaceContext, } from './contexts.js';
|
|
4
|
+
import { SessionReady } from './session-ready.js';
|
|
4
5
|
/**
|
|
5
6
|
* Provider for state management.
|
|
6
7
|
*
|
|
@@ -208,9 +209,10 @@ export function PreviewProvider({ provider, children }) {
|
|
|
208
209
|
* </MoleculeProvider>
|
|
209
210
|
* ```
|
|
210
211
|
*/
|
|
211
|
-
export function MoleculeProvider({ children, state, auth, theme, router, i18n, http, storage, logger, chat, workspace, editor, preview, }) {
|
|
212
|
-
// Build provider tree from inside out
|
|
213
|
-
|
|
212
|
+
export function MoleculeProvider({ children, state, auth, theme, router, i18n, http, storage, logger, chat, workspace, editor, preview, awaitSession = false, sessionFallback, }) {
|
|
213
|
+
// Build provider tree from inside out. The session hold is innermost: its fallback still sees
|
|
214
|
+
// every service, and it reads the auth context provided below.
|
|
215
|
+
let result = awaitSession && auth ? (_jsx(SessionReady, { fallback: sessionFallback, children: children })) : (children);
|
|
214
216
|
if (preview) {
|
|
215
217
|
result = _jsx(PreviewContext.Provider, { value: preview, children: result });
|
|
216
218
|
}
|
package/dist/providers.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"providers.js","sourceRoot":"","sources":["../src/providers.tsx"],"names":[],"mappings":";AAUA,OAAO,EACL,WAAW,IAAI,gBAAgB,EAC/B,WAAW,IAAI,gBAAgB,GAChC,MAAM,qBAAqB,CAAA;AAE5B,OAAO,EACL,WAAW,EACX,WAAW,EACX,aAAa,EACb,WAAW,EACX,WAAW,EACX,aAAa,EACb,cAAc,EACd,aAAa,EACb,YAAY,EACZ,cAAc,EACd,YAAY,EACZ,gBAAgB,GACjB,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"providers.js","sourceRoot":"","sources":["../src/providers.tsx"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hold rendering until the auth client has decided the session.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import type { ReactNode } from 'react';
|
|
7
|
+
import type { SessionReadyProps } from './types.js';
|
|
8
|
+
/**
|
|
9
|
+
* Renders its children once `useAuth().status` has left `'loading'` (signed in, signed out,
|
|
10
|
+
* or `'unavailable'`), and `fallback` until then. It never redirects. An unavailable session
|
|
11
|
+
* is unconfirmed and may still have `user === null`; handle that status separately from sign-out.
|
|
12
|
+
*
|
|
13
|
+
* After a full page load a returning visitor's session is restored by one request. Until it
|
|
14
|
+
* answers, `useAuth().user` is `null` everywhere, so a public page that renders a signed-in action
|
|
15
|
+
* at once lets that action run against "signed out": a Follow button whose handler starts with
|
|
16
|
+
* `if (!user) return` ignores the click, one that redirects to `/login` signs the visitor out of
|
|
17
|
+
* the flow. Wrapping the routes (or passing `awaitSession` to `MoleculeProvider`) makes every
|
|
18
|
+
* page's first render read the restored user when restoration succeeds.
|
|
19
|
+
*
|
|
20
|
+
* A visitor with no session to restore settles in microtasks without a request. Must render
|
|
21
|
+
* inside the auth context (`AuthProvider`, or `MoleculeProvider` with `auth`). A client that
|
|
22
|
+
* nobody initialized starts its restore on this first read.
|
|
23
|
+
* The bundled JWT client bounds each auth request (default 10 seconds) and settles a timeout
|
|
24
|
+
* as `'unavailable'`. Custom clients must also settle their initial session status.
|
|
25
|
+
*
|
|
26
|
+
* Synchronous SSR renders the fallback while a client is loading. Initialize a request-scoped
|
|
27
|
+
* client before rendering to include its decided content, or leave the hold disabled for SSR.
|
|
28
|
+
*
|
|
29
|
+
* Route-level redirects for signed-in-only pages stay with `AuthGuard` (`@molecule/app-ui-react`).
|
|
30
|
+
*
|
|
31
|
+
* @param props - {@link SessionReadyProps}.
|
|
32
|
+
* @returns The children once the session is decided, otherwise the fallback.
|
|
33
|
+
* @example
|
|
34
|
+
* ```tsx
|
|
35
|
+
* <MoleculeProvider auth={authClient} awaitSession>
|
|
36
|
+
* <Routes>…</Routes>
|
|
37
|
+
* </MoleculeProvider>
|
|
38
|
+
* ```
|
|
39
|
+
*/
|
|
40
|
+
export declare function SessionReady({ children, fallback }: SessionReadyProps): ReactNode;
|
|
41
|
+
//# sourceMappingURL=session-ready.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-ready.d.ts","sourceRoot":"","sources":["../src/session-ready.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAGtC,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,YAAY,CAAA;AAEnD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,wBAAgB,YAAY,CAAC,EAAE,QAAQ,EAAE,QAAe,EAAE,EAAE,iBAAiB,GAAG,SAAS,CAGxF"}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Hold rendering until the auth client has decided the session.
|
|
3
|
+
*
|
|
4
|
+
* @module
|
|
5
|
+
*/
|
|
6
|
+
import { useAuth } from './hooks/useAuth.js';
|
|
7
|
+
/**
|
|
8
|
+
* Renders its children once `useAuth().status` has left `'loading'` (signed in, signed out,
|
|
9
|
+
* or `'unavailable'`), and `fallback` until then. It never redirects. An unavailable session
|
|
10
|
+
* is unconfirmed and may still have `user === null`; handle that status separately from sign-out.
|
|
11
|
+
*
|
|
12
|
+
* After a full page load a returning visitor's session is restored by one request. Until it
|
|
13
|
+
* answers, `useAuth().user` is `null` everywhere, so a public page that renders a signed-in action
|
|
14
|
+
* at once lets that action run against "signed out": a Follow button whose handler starts with
|
|
15
|
+
* `if (!user) return` ignores the click, one that redirects to `/login` signs the visitor out of
|
|
16
|
+
* the flow. Wrapping the routes (or passing `awaitSession` to `MoleculeProvider`) makes every
|
|
17
|
+
* page's first render read the restored user when restoration succeeds.
|
|
18
|
+
*
|
|
19
|
+
* A visitor with no session to restore settles in microtasks without a request. Must render
|
|
20
|
+
* inside the auth context (`AuthProvider`, or `MoleculeProvider` with `auth`). A client that
|
|
21
|
+
* nobody initialized starts its restore on this first read.
|
|
22
|
+
* The bundled JWT client bounds each auth request (default 10 seconds) and settles a timeout
|
|
23
|
+
* as `'unavailable'`. Custom clients must also settle their initial session status.
|
|
24
|
+
*
|
|
25
|
+
* Synchronous SSR renders the fallback while a client is loading. Initialize a request-scoped
|
|
26
|
+
* client before rendering to include its decided content, or leave the hold disabled for SSR.
|
|
27
|
+
*
|
|
28
|
+
* Route-level redirects for signed-in-only pages stay with `AuthGuard` (`@molecule/app-ui-react`).
|
|
29
|
+
*
|
|
30
|
+
* @param props - {@link SessionReadyProps}.
|
|
31
|
+
* @returns The children once the session is decided, otherwise the fallback.
|
|
32
|
+
* @example
|
|
33
|
+
* ```tsx
|
|
34
|
+
* <MoleculeProvider auth={authClient} awaitSession>
|
|
35
|
+
* <Routes>…</Routes>
|
|
36
|
+
* </MoleculeProvider>
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export function SessionReady({ children, fallback = null }) {
|
|
40
|
+
const { status } = useAuth();
|
|
41
|
+
return status === 'loading' ? fallback : children;
|
|
42
|
+
}
|
|
43
|
+
//# sourceMappingURL=session-ready.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session-ready.js","sourceRoot":"","sources":["../src/session-ready.tsx"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAAE,OAAO,EAAE,MAAM,oBAAoB,CAAA;AAG5C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,MAAM,UAAU,YAAY,CAAC,EAAE,QAAQ,EAAE,QAAQ,GAAG,IAAI,EAAqB;IAC3E,MAAM,EAAE,MAAM,EAAE,GAAG,OAAO,EAAE,CAAA;IAC5B,OAAO,MAAM,KAAK,SAAS,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAA;AACnD,CAAC"}
|
package/dist/types.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ import type { ReactNode } from 'react';
|
|
|
7
7
|
import type { BackgroundTaskState, ChatAttachment, ChatMessage, ChatProvider, ChatStreamEvent } from '@molecule/app-ai-chat';
|
|
8
8
|
import type { AuthClient, AuthState, RegisterExtraShape } from '@molecule/app-auth';
|
|
9
9
|
import type { DiffFile, EditorFile, EditorProvider, EditorTab, FixWithAIRequest } from '@molecule/app-code-editor';
|
|
10
|
-
import type { FormController, FormOptions } from '@molecule/app-forms';
|
|
10
|
+
import type { FormController, FormOptions, FormResetOptions } from '@molecule/app-forms';
|
|
11
11
|
import type { HttpClient } from '@molecule/app-http';
|
|
12
12
|
import type { I18nProvider, TranslateOptions } from '@molecule/app-i18n';
|
|
13
13
|
import type { PanelId, WorkspaceLayout, WorkspaceProvider } from '@molecule/app-ide';
|
|
@@ -23,6 +23,15 @@ import type { Theme, ThemeProvider } from '@molecule/app-theme';
|
|
|
23
23
|
export interface ProviderProps {
|
|
24
24
|
children: ReactNode;
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Props for the `SessionReady` component.
|
|
28
|
+
*/
|
|
29
|
+
export interface SessionReadyProps {
|
|
30
|
+
/** What renders once the initial restore settles — signed in, signed out, or unavailable. */
|
|
31
|
+
children: ReactNode;
|
|
32
|
+
/** What renders while the session restore is still undecided; default nothing. */
|
|
33
|
+
fallback?: ReactNode;
|
|
34
|
+
}
|
|
26
35
|
/**
|
|
27
36
|
* Props for state provider component.
|
|
28
37
|
*/
|
|
@@ -102,6 +111,20 @@ export interface PreviewProviderProps extends ProviderProps {
|
|
|
102
111
|
export interface MoleculeProviderProps extends ProviderProps {
|
|
103
112
|
state?: StateProvider;
|
|
104
113
|
auth?: AuthClient<unknown>;
|
|
114
|
+
/**
|
|
115
|
+
* Render the app only once `auth` leaves its initial loading status ({@link SessionReadyProps}).
|
|
116
|
+
* After a full page load a returning visitor's session is restored by one request, and until
|
|
117
|
+
* it answers `useAuth().user` is `null` on every page — a public page's signed-in action
|
|
118
|
+
* (`if (!user) return`, a redirect to `/login`) then treats a signed-in visitor as signed out.
|
|
119
|
+
* When restoration succeeds, every page's first render reads the restored user. An
|
|
120
|
+
* `'unavailable'` session also renders, but remains unconfirmed and may have a null user.
|
|
121
|
+
* A visitor with no session to restore settles in microtasks without a request.
|
|
122
|
+
* Ignored without `auth`. Synchronous SSR renders the fallback for a loading client:
|
|
123
|
+
* initialize a request-scoped client first, or leave this disabled for SSR.
|
|
124
|
+
*/
|
|
125
|
+
awaitSession?: boolean;
|
|
126
|
+
/** Rendered instead of the app while {@link MoleculeProviderProps.awaitSession} waits; default nothing. */
|
|
127
|
+
sessionFallback?: ReactNode;
|
|
105
128
|
theme?: ThemeProvider;
|
|
106
129
|
router?: Router;
|
|
107
130
|
i18n?: I18nProvider;
|
|
@@ -144,7 +167,9 @@ export interface UseAuthResult<T = unknown, TRegister extends RegisterExtraShape
|
|
|
144
167
|
* The session state as one value — render from this instead of combining
|
|
145
168
|
* `initialized`/`authenticated`/`loading`. A stored token AND user make it
|
|
146
169
|
* `'authenticated'` on the first render; only `'loading'` means "no decision
|
|
147
|
-
* yet", so a route guard waits on that and nothing else.
|
|
170
|
+
* yet", so a route guard waits on that and nothing else. `'unavailable'` keeps
|
|
171
|
+
* a session candidate for retry and can have a null profile; redirect only on
|
|
172
|
+
* `'unauthenticated'`.
|
|
148
173
|
*/
|
|
149
174
|
status: AuthState<T>['status'];
|
|
150
175
|
state: AuthState<T>;
|
|
@@ -164,8 +189,10 @@ export interface UseAuthResult<T = unknown, TRegister extends RegisterExtraShape
|
|
|
164
189
|
* `authenticated` read signed out.
|
|
165
190
|
*/
|
|
166
191
|
setSession: AuthClient<T>['setSession'];
|
|
192
|
+
/** Whether a session is authenticated locally; this can be false while unavailable. */
|
|
167
193
|
isAuthenticated: boolean;
|
|
168
194
|
isLoading: boolean;
|
|
195
|
+
/** The cached profile, which can be null while an unavailable candidate is retained. */
|
|
169
196
|
user: T | null;
|
|
170
197
|
}
|
|
171
198
|
/**
|
|
@@ -495,5 +522,5 @@ export interface UsePreviewResult {
|
|
|
495
522
|
/** Navigates the preview to the next navigation-history entry (Forward). */
|
|
496
523
|
forward: () => void;
|
|
497
524
|
}
|
|
498
|
-
export type { AuthClient, AuthState, FormController, FormOptions, HttpClient, Router, RouterConfig, Store, StoreConfig, Theme, };
|
|
525
|
+
export type { AuthClient, AuthState, FormController, FormOptions, FormResetOptions, HttpClient, Router, RouterConfig, Store, StoreConfig, Theme, };
|
|
499
526
|
//# sourceMappingURL=types.d.ts.map
|