@quiltt/react 4.5.1 → 5.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/CHANGELOG.md +32 -0
- package/README.md +15 -3
- package/dist/components/QuilttProviderRender-12s-pvnc98Lj.cjs +6 -0
- package/dist/components/QuilttSettings-12s-cLPT_GLC.cjs +6 -0
- package/dist/components/index.cjs +211 -0
- package/dist/components/index.d.ts +55 -0
- package/dist/components/index.js +208 -0
- package/dist/components/useQuilttConnector-12s-BLTzI3gE.cjs +174 -0
- package/dist/components/useQuilttConnector-12s-UQQcvQrO.js +174 -0
- package/dist/components/useQuilttRenderGuard-12s-D9WbRzZO.cjs +36 -0
- package/dist/components/useQuilttSession-12s-BjyJL1qZ.cjs +133 -0
- package/dist/components/useQuilttSession-12s-VOH0S5zh.js +133 -0
- package/dist/components/useQuilttSettings-12s-aDLXY6z3.cjs +10 -0
- package/dist/components/useScript-12s-CMIDUHrx.cjs +92 -0
- package/dist/{useSession-12s-7GOn4sUn.js → components/useSession-12s-BNPsfKY-.js} +28 -7
- package/dist/components/useSession-12s-BNotXj5T.cjs +90 -0
- package/dist/{useStorage-12s-DHcq3Kuh.js → components/useStorage-12s-CpG6X57D.js} +7 -17
- package/dist/components/useStorage-12s-D7nllsrI.cjs +61 -0
- package/dist/contexts/QuilttSettings-12s-BK-0SQME.js +6 -0
- package/dist/contexts/QuilttSettings-12s-cLPT_GLC.cjs +6 -0
- package/dist/contexts/index.cjs +7 -0
- package/dist/contexts/index.d.ts +8 -0
- package/dist/contexts/index.js +1 -0
- package/dist/hooks/QuilttProviderRender-12s-DtQtubjL.js +6 -0
- package/dist/hooks/QuilttProviderRender-12s-pvnc98Lj.cjs +6 -0
- package/dist/hooks/QuilttSettings-12s-BK-0SQME.js +6 -0
- package/dist/hooks/QuilttSettings-12s-cLPT_GLC.cjs +6 -0
- package/dist/hooks/index.cjs +31 -0
- package/dist/hooks/index.d.ts +142 -0
- package/dist/hooks/index.js +11 -0
- package/dist/hooks/useEventListener-12s-CV8cLxWu.cjs +34 -0
- package/dist/hooks/useIsomorphicLayoutEffect-12s-B8KjaroI.cjs +9 -0
- package/dist/{useQuilttClient-12s-CAAUait1.js → hooks/useQuilttClient-12s-Dj_MtYTU.js} +1 -1
- package/dist/hooks/useQuilttClient-12s-flQYSVdG.cjs +6 -0
- package/dist/{useQuilttConnector-12s-D8VedbrT.js → hooks/useQuilttConnector-12s-C0KJHuZN.js} +5 -4
- package/dist/hooks/useQuilttConnector-12s-D51PZPgm.cjs +175 -0
- package/dist/hooks/useQuilttInstitutions-12s-D_Kr43s3.cjs +82 -0
- package/dist/{useQuilttInstitutions-12s-DExzPnfb.js → hooks/useQuilttInstitutions-12s-rgLHD9ye.js} +6 -16
- package/dist/hooks/useQuilttRenderGuard-12s-CsS2Ma6Q.js +36 -0
- package/dist/hooks/useQuilttRenderGuard-12s-D9WbRzZO.cjs +36 -0
- package/dist/{useQuilttResolvable-12s-C0BV3AvR.js → hooks/useQuilttResolvable-12s-BUDeH696.js} +6 -16
- package/dist/hooks/useQuilttResolvable-12s-D8wnfNBt.cjs +76 -0
- package/dist/hooks/useQuilttSession-12s-C8kq5S2Y.cjs +139 -0
- package/dist/hooks/useQuilttSession-12s-DQFfjmob.js +135 -0
- package/dist/hooks/useQuilttSettings-12s--rCJoNHD.js +10 -0
- package/dist/hooks/useQuilttSettings-12s-aDLXY6z3.cjs +10 -0
- package/dist/hooks/useScript-12s-CMIDUHrx.cjs +92 -0
- package/dist/hooks/useScript-12s-JCgaTW9n.js +92 -0
- package/dist/hooks/useSession-12s-BNPsfKY-.js +90 -0
- package/dist/hooks/useSession-12s-BNotXj5T.cjs +90 -0
- package/dist/hooks/useStorage-12s-CpG6X57D.js +61 -0
- package/dist/hooks/useStorage-12s-D7nllsrI.cjs +61 -0
- package/dist/index.cjs +149 -0
- package/dist/index.d.ts +7 -216
- package/dist/index.js +7 -191
- package/dist/providers/QuilttAuthProvider-12s-CDgQbarX.js +60 -0
- package/dist/providers/QuilttAuthProvider-12s-D__FY1Qn.cjs +60 -0
- package/dist/providers/QuilttProviderRender-12s-DtQtubjL.js +6 -0
- package/dist/providers/QuilttProviderRender-12s-pvnc98Lj.cjs +6 -0
- package/dist/providers/QuilttSettings-12s-BK-0SQME.js +6 -0
- package/dist/providers/QuilttSettings-12s-cLPT_GLC.cjs +6 -0
- package/dist/providers/QuilttSettingsProvider-12s-D7e8dsOE.cjs +19 -0
- package/dist/{QuilttSettingsProvider-12s-ZcmFmOiZ.js → providers/QuilttSettingsProvider-12s-DND2gPQm.js} +8 -5
- package/dist/providers/index.cjs +34 -0
- package/dist/providers/index.d.ts +28 -0
- package/dist/providers/index.js +30 -0
- package/dist/utils/index.cjs +89 -0
- package/dist/utils/index.d.ts +31 -0
- package/dist/utils/index.js +77 -0
- package/package.json +38 -12
- package/src/hooks/useQuilttClient.ts +1 -1
- package/src/hooks/useQuilttConnector.ts +3 -2
- package/src/hooks/useQuilttInstitutions.ts +8 -23
- package/src/hooks/useQuilttResolvable.ts +8 -23
- package/src/hooks/useSession.ts +32 -6
- package/src/hooks/useStorage.ts +7 -15
- package/src/index.ts +61 -0
- package/src/providers/QuilttAuthProvider.tsx +12 -5
- package/src/providers/QuilttProvider.tsx +5 -1
- package/src/providers/QuilttSettingsProvider.tsx +4 -5
- package/src/utils/index.ts +1 -0
- package/src/utils/telemetry.ts +33 -0
- package/dist/QuilttAuthProvider-12s-DE-ePRo_.js +0 -205
- package/dist/useQuilttSession-12s-sRvULtJv.js +0 -39
- /package/dist/{QuilttProviderRender-12s-DtQtubjL.js → components/QuilttProviderRender-12s-DtQtubjL.js} +0 -0
- /package/dist/{QuilttSettings-12s-BK-0SQME.js → components/QuilttSettings-12s-BK-0SQME.js} +0 -0
- /package/dist/{useQuilttRenderGuard-12s-CsS2Ma6Q.js → components/useQuilttRenderGuard-12s-CsS2Ma6Q.js} +0 -0
- /package/dist/{useQuilttSettings-12s--rCJoNHD.js → components/useQuilttSettings-12s--rCJoNHD.js} +0 -0
- /package/dist/{useScript-12s-JCgaTW9n.js → components/useScript-12s-JCgaTW9n.js} +0 -0
- /package/dist/{useEventListener-12s-D_-6QIXa.js → hooks/useEventListener-12s-D_-6QIXa.js} +0 -0
- /package/dist/{useIsomorphicLayoutEffect-12s-DeTHOKz1.js → hooks/useIsomorphicLayoutEffect-12s-DeTHOKz1.js} +0 -0
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useCallback, useMemo } from 'react';
|
|
3
|
+
import { JsonWebTokenParse, AuthAPI } from '@quiltt/core';
|
|
4
|
+
import { u as useQuilttSettings } from './useQuilttSettings-12s--rCJoNHD.js';
|
|
5
|
+
import { u as useSession } from './useSession-12s-BNPsfKY-.js';
|
|
6
|
+
|
|
7
|
+
const useAuthenticateSession = (auth, setSession)=>{
|
|
8
|
+
const authenticateSession = useCallback(async (payload, callbacks)=>{
|
|
9
|
+
const response = await auth.authenticate(payload);
|
|
10
|
+
switch(response.status){
|
|
11
|
+
case 201:
|
|
12
|
+
setSession(response.data.token);
|
|
13
|
+
if (callbacks.onSuccess) return callbacks.onSuccess();
|
|
14
|
+
break;
|
|
15
|
+
case 401:
|
|
16
|
+
if (callbacks.onFailure) return callbacks.onFailure();
|
|
17
|
+
break;
|
|
18
|
+
case 422:
|
|
19
|
+
if (callbacks.onError) return callbacks.onError(response.data);
|
|
20
|
+
break;
|
|
21
|
+
default:
|
|
22
|
+
throw new Error(`AuthAPI.authenticate: Unexpected response status ${response.status}`);
|
|
23
|
+
}
|
|
24
|
+
}, [
|
|
25
|
+
auth,
|
|
26
|
+
setSession
|
|
27
|
+
]);
|
|
28
|
+
return authenticateSession;
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const useIdentifySession = (auth, setSession)=>{
|
|
32
|
+
const identifySession = useCallback(async (payload, callbacks)=>{
|
|
33
|
+
const response = await auth.identify(payload);
|
|
34
|
+
switch(response.status){
|
|
35
|
+
case 201:
|
|
36
|
+
setSession(response.data.token);
|
|
37
|
+
if (callbacks.onSuccess) return callbacks.onSuccess();
|
|
38
|
+
break;
|
|
39
|
+
case 202:
|
|
40
|
+
if (callbacks.onChallenged) return callbacks.onChallenged();
|
|
41
|
+
break;
|
|
42
|
+
case 403:
|
|
43
|
+
if (callbacks.onForbidden) return callbacks.onForbidden();
|
|
44
|
+
break;
|
|
45
|
+
case 422:
|
|
46
|
+
if (callbacks.onError) return callbacks.onError(response.data);
|
|
47
|
+
break;
|
|
48
|
+
default:
|
|
49
|
+
throw new Error(`AuthAPI.identify: Unexpected response status ${response.status}`);
|
|
50
|
+
}
|
|
51
|
+
}, [
|
|
52
|
+
auth,
|
|
53
|
+
setSession
|
|
54
|
+
]);
|
|
55
|
+
return identifySession;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Optionally Accepts environmentId to validate session is from with your desired environment
|
|
60
|
+
*/ const useImportSession = (auth, session, setSession, environmentId)=>{
|
|
61
|
+
const importSession = useCallback(async (token)=>{
|
|
62
|
+
// Is there a token?
|
|
63
|
+
if (!token) return !!session;
|
|
64
|
+
// Is this token already imported?
|
|
65
|
+
if (session && session.token === token) return true;
|
|
66
|
+
const jwt = JsonWebTokenParse(token);
|
|
67
|
+
// Is this token a valid JWT?
|
|
68
|
+
if (!jwt) return false;
|
|
69
|
+
// Is this token within the expected environment?
|
|
70
|
+
if (environmentId && jwt.claims.eid !== environmentId) return false;
|
|
71
|
+
// Is this token active?
|
|
72
|
+
const response = await auth.ping(token);
|
|
73
|
+
switch(response.status){
|
|
74
|
+
case 200:
|
|
75
|
+
setSession(token);
|
|
76
|
+
return true;
|
|
77
|
+
case 401:
|
|
78
|
+
return false;
|
|
79
|
+
default:
|
|
80
|
+
throw new Error(`AuthAPI.ping: Unexpected response status ${response.status}`);
|
|
81
|
+
}
|
|
82
|
+
}, [
|
|
83
|
+
auth,
|
|
84
|
+
session,
|
|
85
|
+
setSession,
|
|
86
|
+
environmentId
|
|
87
|
+
]);
|
|
88
|
+
return importSession;
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
const useRevokeSession = (auth, session, setSession)=>{
|
|
92
|
+
const revokeSession = useCallback(async ()=>{
|
|
93
|
+
if (!session) return;
|
|
94
|
+
await auth.revoke(session.token);
|
|
95
|
+
setSession(null);
|
|
96
|
+
}, [
|
|
97
|
+
auth,
|
|
98
|
+
session,
|
|
99
|
+
setSession
|
|
100
|
+
]);
|
|
101
|
+
return revokeSession;
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
const useQuilttSession = (environmentId)=>{
|
|
105
|
+
const { clientId } = useQuilttSettings();
|
|
106
|
+
const [session, setSession] = useSession();
|
|
107
|
+
const auth = useMemo(()=>new AuthAPI(clientId), [
|
|
108
|
+
clientId
|
|
109
|
+
]);
|
|
110
|
+
const importSession = useImportSession(auth, session, setSession, environmentId);
|
|
111
|
+
const identifySession = useIdentifySession(auth, setSession);
|
|
112
|
+
const authenticateSession = useAuthenticateSession(auth, setSession);
|
|
113
|
+
const revokeSession = useRevokeSession(auth, session, setSession);
|
|
114
|
+
/**
|
|
115
|
+
* Forget current session.
|
|
116
|
+
* @param token specific token to forget, to help guard against async processes clearing the wrong session
|
|
117
|
+
*/ const forgetSession = useCallback(async (token)=>{
|
|
118
|
+
if (!token || session && token && token === session.token) {
|
|
119
|
+
setSession(null);
|
|
120
|
+
}
|
|
121
|
+
}, [
|
|
122
|
+
session,
|
|
123
|
+
setSession
|
|
124
|
+
]);
|
|
125
|
+
return {
|
|
126
|
+
session,
|
|
127
|
+
importSession,
|
|
128
|
+
identifySession,
|
|
129
|
+
authenticateSession,
|
|
130
|
+
revokeSession,
|
|
131
|
+
forgetSession
|
|
132
|
+
};
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
export { useAuthenticateSession as a, useIdentifySession as b, useImportSession as c, useRevokeSession as d, useQuilttSession as u };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useContext } from 'react';
|
|
3
|
+
import { Q as QuilttSettings } from './QuilttSettings-12s-BK-0SQME.js';
|
|
4
|
+
|
|
5
|
+
const useQuilttSettings = ()=>{
|
|
6
|
+
const settings = useContext(QuilttSettings);
|
|
7
|
+
return settings;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
export { useQuilttSettings as u };
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
var react = require('react');
|
|
3
|
+
var QuilttSettings12s = require('./QuilttSettings-12s-cLPT_GLC.cjs');
|
|
4
|
+
|
|
5
|
+
const useQuilttSettings = ()=>{
|
|
6
|
+
const settings = react.useContext(QuilttSettings12s.QuilttSettings);
|
|
7
|
+
return settings;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
exports.useQuilttSettings = useQuilttSettings;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
var react = require('react');
|
|
3
|
+
|
|
4
|
+
// Cached script statuses
|
|
5
|
+
const cachedScriptStatuses = {};
|
|
6
|
+
function getScriptNode(src) {
|
|
7
|
+
const node = document.querySelector(`script[src="${src}"]`);
|
|
8
|
+
const status = node?.getAttribute('data-status');
|
|
9
|
+
return {
|
|
10
|
+
node,
|
|
11
|
+
status
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
// @see https://usehooks-ts.com/react-hook/use-script
|
|
15
|
+
function useScript(src, options) {
|
|
16
|
+
const [status, setStatus] = react.useState(()=>{
|
|
17
|
+
if (!src || options?.shouldPreventLoad) {
|
|
18
|
+
return 'idle';
|
|
19
|
+
}
|
|
20
|
+
if (typeof window === 'undefined') {
|
|
21
|
+
// SSR Handling - always return 'loading'
|
|
22
|
+
return 'loading';
|
|
23
|
+
}
|
|
24
|
+
return cachedScriptStatuses[src] ?? 'loading';
|
|
25
|
+
});
|
|
26
|
+
react.useEffect(()=>{
|
|
27
|
+
if (!src || options?.shouldPreventLoad) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const cachedScriptStatus = cachedScriptStatuses[src];
|
|
31
|
+
if (cachedScriptStatus === 'ready' || cachedScriptStatus === 'error') {
|
|
32
|
+
// If the script is already cached, set its status immediately
|
|
33
|
+
setStatus(cachedScriptStatus);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
// Fetch existing script element by src
|
|
37
|
+
// It may have been added by another instance of this hook
|
|
38
|
+
const script = getScriptNode(src);
|
|
39
|
+
let scriptNode = script.node;
|
|
40
|
+
if (!scriptNode) {
|
|
41
|
+
// Create script element and add it to document body
|
|
42
|
+
scriptNode = document.createElement('script');
|
|
43
|
+
scriptNode.src = src;
|
|
44
|
+
scriptNode.async = true;
|
|
45
|
+
if (options?.nonce && options.nonce.trim() !== '') {
|
|
46
|
+
scriptNode.nonce = options.nonce;
|
|
47
|
+
}
|
|
48
|
+
scriptNode.setAttribute('data-status', 'loading');
|
|
49
|
+
document.body.appendChild(scriptNode);
|
|
50
|
+
// Store status in attribute on script
|
|
51
|
+
// This can be read by other instances of this hook
|
|
52
|
+
const setAttributeFromEvent = (event)=>{
|
|
53
|
+
const scriptStatus = event.type === 'load' ? 'ready' : 'error';
|
|
54
|
+
scriptNode?.setAttribute('data-status', scriptStatus);
|
|
55
|
+
};
|
|
56
|
+
scriptNode.addEventListener('load', setAttributeFromEvent);
|
|
57
|
+
scriptNode.addEventListener('error', setAttributeFromEvent);
|
|
58
|
+
} else {
|
|
59
|
+
// Grab existing script status from attribute and set to state.
|
|
60
|
+
setStatus(script.status ?? cachedScriptStatus ?? 'loading');
|
|
61
|
+
}
|
|
62
|
+
// Script event handler to update status in state
|
|
63
|
+
// Note: Even if the script already exists we still need to add
|
|
64
|
+
// event handlers to update the state for *this* hook instance.
|
|
65
|
+
const setStateFromEvent = (event)=>{
|
|
66
|
+
const newStatus = event.type === 'load' ? 'ready' : 'error';
|
|
67
|
+
setStatus(newStatus);
|
|
68
|
+
cachedScriptStatuses[src] = newStatus;
|
|
69
|
+
};
|
|
70
|
+
// Add event listeners
|
|
71
|
+
scriptNode.addEventListener('load', setStateFromEvent);
|
|
72
|
+
scriptNode.addEventListener('error', setStateFromEvent);
|
|
73
|
+
// Remove event listeners on cleanup
|
|
74
|
+
return ()=>{
|
|
75
|
+
if (scriptNode) {
|
|
76
|
+
scriptNode.removeEventListener('load', setStateFromEvent);
|
|
77
|
+
scriptNode.removeEventListener('error', setStateFromEvent);
|
|
78
|
+
}
|
|
79
|
+
if (scriptNode && options?.removeOnUnmount) {
|
|
80
|
+
scriptNode.remove();
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}, [
|
|
84
|
+
src,
|
|
85
|
+
options?.shouldPreventLoad,
|
|
86
|
+
options?.nonce,
|
|
87
|
+
options?.removeOnUnmount
|
|
88
|
+
]);
|
|
89
|
+
return status;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
exports.useScript = useScript;
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useState, useEffect } from 'react';
|
|
3
|
+
|
|
4
|
+
// Cached script statuses
|
|
5
|
+
const cachedScriptStatuses = {};
|
|
6
|
+
function getScriptNode(src) {
|
|
7
|
+
const node = document.querySelector(`script[src="${src}"]`);
|
|
8
|
+
const status = node?.getAttribute('data-status');
|
|
9
|
+
return {
|
|
10
|
+
node,
|
|
11
|
+
status
|
|
12
|
+
};
|
|
13
|
+
}
|
|
14
|
+
// @see https://usehooks-ts.com/react-hook/use-script
|
|
15
|
+
function useScript(src, options) {
|
|
16
|
+
const [status, setStatus] = useState(()=>{
|
|
17
|
+
if (!src || options?.shouldPreventLoad) {
|
|
18
|
+
return 'idle';
|
|
19
|
+
}
|
|
20
|
+
if (typeof window === 'undefined') {
|
|
21
|
+
// SSR Handling - always return 'loading'
|
|
22
|
+
return 'loading';
|
|
23
|
+
}
|
|
24
|
+
return cachedScriptStatuses[src] ?? 'loading';
|
|
25
|
+
});
|
|
26
|
+
useEffect(()=>{
|
|
27
|
+
if (!src || options?.shouldPreventLoad) {
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const cachedScriptStatus = cachedScriptStatuses[src];
|
|
31
|
+
if (cachedScriptStatus === 'ready' || cachedScriptStatus === 'error') {
|
|
32
|
+
// If the script is already cached, set its status immediately
|
|
33
|
+
setStatus(cachedScriptStatus);
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
// Fetch existing script element by src
|
|
37
|
+
// It may have been added by another instance of this hook
|
|
38
|
+
const script = getScriptNode(src);
|
|
39
|
+
let scriptNode = script.node;
|
|
40
|
+
if (!scriptNode) {
|
|
41
|
+
// Create script element and add it to document body
|
|
42
|
+
scriptNode = document.createElement('script');
|
|
43
|
+
scriptNode.src = src;
|
|
44
|
+
scriptNode.async = true;
|
|
45
|
+
if (options?.nonce && options.nonce.trim() !== '') {
|
|
46
|
+
scriptNode.nonce = options.nonce;
|
|
47
|
+
}
|
|
48
|
+
scriptNode.setAttribute('data-status', 'loading');
|
|
49
|
+
document.body.appendChild(scriptNode);
|
|
50
|
+
// Store status in attribute on script
|
|
51
|
+
// This can be read by other instances of this hook
|
|
52
|
+
const setAttributeFromEvent = (event)=>{
|
|
53
|
+
const scriptStatus = event.type === 'load' ? 'ready' : 'error';
|
|
54
|
+
scriptNode?.setAttribute('data-status', scriptStatus);
|
|
55
|
+
};
|
|
56
|
+
scriptNode.addEventListener('load', setAttributeFromEvent);
|
|
57
|
+
scriptNode.addEventListener('error', setAttributeFromEvent);
|
|
58
|
+
} else {
|
|
59
|
+
// Grab existing script status from attribute and set to state.
|
|
60
|
+
setStatus(script.status ?? cachedScriptStatus ?? 'loading');
|
|
61
|
+
}
|
|
62
|
+
// Script event handler to update status in state
|
|
63
|
+
// Note: Even if the script already exists we still need to add
|
|
64
|
+
// event handlers to update the state for *this* hook instance.
|
|
65
|
+
const setStateFromEvent = (event)=>{
|
|
66
|
+
const newStatus = event.type === 'load' ? 'ready' : 'error';
|
|
67
|
+
setStatus(newStatus);
|
|
68
|
+
cachedScriptStatuses[src] = newStatus;
|
|
69
|
+
};
|
|
70
|
+
// Add event listeners
|
|
71
|
+
scriptNode.addEventListener('load', setStateFromEvent);
|
|
72
|
+
scriptNode.addEventListener('error', setStateFromEvent);
|
|
73
|
+
// Remove event listeners on cleanup
|
|
74
|
+
return ()=>{
|
|
75
|
+
if (scriptNode) {
|
|
76
|
+
scriptNode.removeEventListener('load', setStateFromEvent);
|
|
77
|
+
scriptNode.removeEventListener('error', setStateFromEvent);
|
|
78
|
+
}
|
|
79
|
+
if (scriptNode && options?.removeOnUnmount) {
|
|
80
|
+
scriptNode.remove();
|
|
81
|
+
}
|
|
82
|
+
};
|
|
83
|
+
}, [
|
|
84
|
+
src,
|
|
85
|
+
options?.shouldPreventLoad,
|
|
86
|
+
options?.nonce,
|
|
87
|
+
options?.removeOnUnmount
|
|
88
|
+
]);
|
|
89
|
+
return status;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
export { useScript as u };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useMemo, useEffect, useCallback } from 'react';
|
|
3
|
+
import { Timeoutable, JsonWebTokenParse } from '@quiltt/core';
|
|
4
|
+
import { u as useStorage } from './useStorage-12s-CpG6X57D.js';
|
|
5
|
+
|
|
6
|
+
// Initialize JWT parser with our specific claims type
|
|
7
|
+
const parse = JsonWebTokenParse;
|
|
8
|
+
// Global timer to manage token expiration across all hook instances
|
|
9
|
+
const sessionTimer = new Timeoutable();
|
|
10
|
+
/**
|
|
11
|
+
* Custom hook to manage JWT session state with automatic expiration handling.
|
|
12
|
+
* Provides global singleton access to the session across components and windows.
|
|
13
|
+
*
|
|
14
|
+
* TODO: Support Rotation before Expiry
|
|
15
|
+
*
|
|
16
|
+
* Handles two types of data flow:
|
|
17
|
+
* 1. Bottom-up (Login): Token passed through setSession callback
|
|
18
|
+
* 2. Top-down (Refresh): State reinitialized on page reload
|
|
19
|
+
*
|
|
20
|
+
* @param storageKey - Key used for storing session in useStorage (defaults to 'session')
|
|
21
|
+
* @returns [session, setSession] - Current session state and setter function
|
|
22
|
+
*/ const useSession = (storageKey = 'session')=>{
|
|
23
|
+
const [token, setToken] = useStorage(storageKey);
|
|
24
|
+
// Parse token into session data, updates when token changes
|
|
25
|
+
const session = useMemo(()=>parse(token), [
|
|
26
|
+
token
|
|
27
|
+
]);
|
|
28
|
+
// Handle session expiration
|
|
29
|
+
useEffect(()=>{
|
|
30
|
+
if (!session) return;
|
|
31
|
+
const expirationMS = session.claims.exp * 1000;
|
|
32
|
+
const expire = ()=>setToken(null);
|
|
33
|
+
const checkExpiration = ()=>{
|
|
34
|
+
if (Date.now() >= expirationMS) {
|
|
35
|
+
expire();
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
};
|
|
40
|
+
// Clear immediately if already expired
|
|
41
|
+
if (checkExpiration()) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
// Set timer to clear session at expiration time
|
|
45
|
+
sessionTimer.set(expire, expirationMS - Date.now());
|
|
46
|
+
// Also check expiration when tab becomes visible (handles idle sessions)
|
|
47
|
+
const handleVisibilityChange = ()=>{
|
|
48
|
+
if (!document.hidden) {
|
|
49
|
+
checkExpiration();
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
// Only add listener in browser environment
|
|
53
|
+
if (typeof document !== 'undefined') {
|
|
54
|
+
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
55
|
+
}
|
|
56
|
+
return ()=>{
|
|
57
|
+
sessionTimer.clear(expire);
|
|
58
|
+
if (typeof document !== 'undefined') {
|
|
59
|
+
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}, [
|
|
63
|
+
session,
|
|
64
|
+
setToken
|
|
65
|
+
]);
|
|
66
|
+
/**
|
|
67
|
+
* Validates and updates the session token.
|
|
68
|
+
* - Handles both direct values and updater functions
|
|
69
|
+
* - Validates new tokens before setting them
|
|
70
|
+
* - Prevents unnecessary updates for same token value
|
|
71
|
+
* - Allows clearing the session with null/undefined
|
|
72
|
+
*/ const setSession = useCallback((nextState)=>{
|
|
73
|
+
const newState = nextState instanceof Function ? nextState(token) : nextState;
|
|
74
|
+
// Only update if:
|
|
75
|
+
// 1. The token has actually changed AND
|
|
76
|
+
// 2. Either the new state is falsy (clearing session) OR it's a valid token
|
|
77
|
+
if (token !== newState && (!newState || parse(newState))) {
|
|
78
|
+
setToken(newState);
|
|
79
|
+
}
|
|
80
|
+
}, [
|
|
81
|
+
token,
|
|
82
|
+
setToken
|
|
83
|
+
]);
|
|
84
|
+
return [
|
|
85
|
+
session,
|
|
86
|
+
setSession
|
|
87
|
+
];
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
export { useSession as u };
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
var react = require('react');
|
|
3
|
+
var core = require('@quiltt/core');
|
|
4
|
+
var useStorage12s = require('./useStorage-12s-D7nllsrI.cjs');
|
|
5
|
+
|
|
6
|
+
// Initialize JWT parser with our specific claims type
|
|
7
|
+
const parse = core.JsonWebTokenParse;
|
|
8
|
+
// Global timer to manage token expiration across all hook instances
|
|
9
|
+
const sessionTimer = new core.Timeoutable();
|
|
10
|
+
/**
|
|
11
|
+
* Custom hook to manage JWT session state with automatic expiration handling.
|
|
12
|
+
* Provides global singleton access to the session across components and windows.
|
|
13
|
+
*
|
|
14
|
+
* TODO: Support Rotation before Expiry
|
|
15
|
+
*
|
|
16
|
+
* Handles two types of data flow:
|
|
17
|
+
* 1. Bottom-up (Login): Token passed through setSession callback
|
|
18
|
+
* 2. Top-down (Refresh): State reinitialized on page reload
|
|
19
|
+
*
|
|
20
|
+
* @param storageKey - Key used for storing session in useStorage (defaults to 'session')
|
|
21
|
+
* @returns [session, setSession] - Current session state and setter function
|
|
22
|
+
*/ const useSession = (storageKey = 'session')=>{
|
|
23
|
+
const [token, setToken] = useStorage12s.useStorage(storageKey);
|
|
24
|
+
// Parse token into session data, updates when token changes
|
|
25
|
+
const session = react.useMemo(()=>parse(token), [
|
|
26
|
+
token
|
|
27
|
+
]);
|
|
28
|
+
// Handle session expiration
|
|
29
|
+
react.useEffect(()=>{
|
|
30
|
+
if (!session) return;
|
|
31
|
+
const expirationMS = session.claims.exp * 1000;
|
|
32
|
+
const expire = ()=>setToken(null);
|
|
33
|
+
const checkExpiration = ()=>{
|
|
34
|
+
if (Date.now() >= expirationMS) {
|
|
35
|
+
expire();
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
return false;
|
|
39
|
+
};
|
|
40
|
+
// Clear immediately if already expired
|
|
41
|
+
if (checkExpiration()) {
|
|
42
|
+
return;
|
|
43
|
+
}
|
|
44
|
+
// Set timer to clear session at expiration time
|
|
45
|
+
sessionTimer.set(expire, expirationMS - Date.now());
|
|
46
|
+
// Also check expiration when tab becomes visible (handles idle sessions)
|
|
47
|
+
const handleVisibilityChange = ()=>{
|
|
48
|
+
if (!document.hidden) {
|
|
49
|
+
checkExpiration();
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
// Only add listener in browser environment
|
|
53
|
+
if (typeof document !== 'undefined') {
|
|
54
|
+
document.addEventListener('visibilitychange', handleVisibilityChange);
|
|
55
|
+
}
|
|
56
|
+
return ()=>{
|
|
57
|
+
sessionTimer.clear(expire);
|
|
58
|
+
if (typeof document !== 'undefined') {
|
|
59
|
+
document.removeEventListener('visibilitychange', handleVisibilityChange);
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
}, [
|
|
63
|
+
session,
|
|
64
|
+
setToken
|
|
65
|
+
]);
|
|
66
|
+
/**
|
|
67
|
+
* Validates and updates the session token.
|
|
68
|
+
* - Handles both direct values and updater functions
|
|
69
|
+
* - Validates new tokens before setting them
|
|
70
|
+
* - Prevents unnecessary updates for same token value
|
|
71
|
+
* - Allows clearing the session with null/undefined
|
|
72
|
+
*/ const setSession = react.useCallback((nextState)=>{
|
|
73
|
+
const newState = nextState instanceof Function ? nextState(token) : nextState;
|
|
74
|
+
// Only update if:
|
|
75
|
+
// 1. The token has actually changed AND
|
|
76
|
+
// 2. Either the new state is falsy (clearing session) OR it's a valid token
|
|
77
|
+
if (token !== newState && (!newState || parse(newState))) {
|
|
78
|
+
setToken(newState);
|
|
79
|
+
}
|
|
80
|
+
}, [
|
|
81
|
+
token,
|
|
82
|
+
setToken
|
|
83
|
+
]);
|
|
84
|
+
return [
|
|
85
|
+
session,
|
|
86
|
+
setSession
|
|
87
|
+
];
|
|
88
|
+
};
|
|
89
|
+
|
|
90
|
+
exports.useSession = useSession;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useCallback, useState, useEffect } from 'react';
|
|
3
|
+
import { GlobalStorage } from '@quiltt/core';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Attempt to persist state with local storage, so it remains after refresh and
|
|
7
|
+
* across open documents. Falls back to in memory storage when localStorage is
|
|
8
|
+
* unavailable.
|
|
9
|
+
*
|
|
10
|
+
* This hook is used in the same way as useState except that you must pass the
|
|
11
|
+
* storage key in the 1st parameter. If the window object is not present (as in SSR),
|
|
12
|
+
* useStorage() will return the default nextState.
|
|
13
|
+
*
|
|
14
|
+
* Expect values to remain in sync
|
|
15
|
+
* Across Hooks
|
|
16
|
+
* Across Reloads
|
|
17
|
+
* Across Windows (Documents)
|
|
18
|
+
*
|
|
19
|
+
* @param key
|
|
20
|
+
* @param initialState
|
|
21
|
+
* @returns {Array} [storage, setStorage]
|
|
22
|
+
*/ const useStorage = (key, initialState)=>{
|
|
23
|
+
const getStorage = useCallback(()=>{
|
|
24
|
+
const state = GlobalStorage.get(key);
|
|
25
|
+
if (state !== undefined) {
|
|
26
|
+
return state;
|
|
27
|
+
}
|
|
28
|
+
return initialState;
|
|
29
|
+
}, [
|
|
30
|
+
key,
|
|
31
|
+
initialState
|
|
32
|
+
]);
|
|
33
|
+
const [hookState, setHookState] = useState(getStorage());
|
|
34
|
+
const setStorage = useCallback((nextState)=>{
|
|
35
|
+
const newState = nextState instanceof Function ? nextState(hookState) : nextState;
|
|
36
|
+
if (hookState !== newState) {
|
|
37
|
+
GlobalStorage.set(key, newState);
|
|
38
|
+
// Immediately update hook state as well
|
|
39
|
+
setHookState(newState);
|
|
40
|
+
}
|
|
41
|
+
}, [
|
|
42
|
+
key,
|
|
43
|
+
hookState
|
|
44
|
+
]);
|
|
45
|
+
useEffect(()=>{
|
|
46
|
+
// Subscribe to storage changes from other sources (e.g., other hook instances, browser tabs)
|
|
47
|
+
const handleStorageChange = (newValue)=>{
|
|
48
|
+
setHookState(newValue);
|
|
49
|
+
};
|
|
50
|
+
GlobalStorage.subscribe(key, handleStorageChange);
|
|
51
|
+
return ()=>GlobalStorage.unsubscribe(key, handleStorageChange);
|
|
52
|
+
}, [
|
|
53
|
+
key
|
|
54
|
+
]);
|
|
55
|
+
return [
|
|
56
|
+
hookState,
|
|
57
|
+
setStorage
|
|
58
|
+
];
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
export { useStorage as u };
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
var react = require('react');
|
|
3
|
+
var core = require('@quiltt/core');
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Attempt to persist state with local storage, so it remains after refresh and
|
|
7
|
+
* across open documents. Falls back to in memory storage when localStorage is
|
|
8
|
+
* unavailable.
|
|
9
|
+
*
|
|
10
|
+
* This hook is used in the same way as useState except that you must pass the
|
|
11
|
+
* storage key in the 1st parameter. If the window object is not present (as in SSR),
|
|
12
|
+
* useStorage() will return the default nextState.
|
|
13
|
+
*
|
|
14
|
+
* Expect values to remain in sync
|
|
15
|
+
* Across Hooks
|
|
16
|
+
* Across Reloads
|
|
17
|
+
* Across Windows (Documents)
|
|
18
|
+
*
|
|
19
|
+
* @param key
|
|
20
|
+
* @param initialState
|
|
21
|
+
* @returns {Array} [storage, setStorage]
|
|
22
|
+
*/ const useStorage = (key, initialState)=>{
|
|
23
|
+
const getStorage = react.useCallback(()=>{
|
|
24
|
+
const state = core.GlobalStorage.get(key);
|
|
25
|
+
if (state !== undefined) {
|
|
26
|
+
return state;
|
|
27
|
+
}
|
|
28
|
+
return initialState;
|
|
29
|
+
}, [
|
|
30
|
+
key,
|
|
31
|
+
initialState
|
|
32
|
+
]);
|
|
33
|
+
const [hookState, setHookState] = react.useState(getStorage());
|
|
34
|
+
const setStorage = react.useCallback((nextState)=>{
|
|
35
|
+
const newState = nextState instanceof Function ? nextState(hookState) : nextState;
|
|
36
|
+
if (hookState !== newState) {
|
|
37
|
+
core.GlobalStorage.set(key, newState);
|
|
38
|
+
// Immediately update hook state as well
|
|
39
|
+
setHookState(newState);
|
|
40
|
+
}
|
|
41
|
+
}, [
|
|
42
|
+
key,
|
|
43
|
+
hookState
|
|
44
|
+
]);
|
|
45
|
+
react.useEffect(()=>{
|
|
46
|
+
// Subscribe to storage changes from other sources (e.g., other hook instances, browser tabs)
|
|
47
|
+
const handleStorageChange = (newValue)=>{
|
|
48
|
+
setHookState(newValue);
|
|
49
|
+
};
|
|
50
|
+
core.GlobalStorage.subscribe(key, handleStorageChange);
|
|
51
|
+
return ()=>core.GlobalStorage.unsubscribe(key, handleStorageChange);
|
|
52
|
+
}, [
|
|
53
|
+
key
|
|
54
|
+
]);
|
|
55
|
+
return [
|
|
56
|
+
hookState,
|
|
57
|
+
setStorage
|
|
58
|
+
];
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
exports.useStorage = useStorage;
|