@quiltt/react 4.3.3 → 4.5.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/CHANGELOG.md +54 -0
- package/dist/{QuilttAuthProvider-12s-4hQ7iysR.js → QuilttAuthProvider-12s-BCpwSX-3.js} +2 -1
- package/dist/QuilttProviderRender-12s-DtQtubjL.js +6 -0
- package/dist/index.d.ts +33 -2
- package/dist/index.js +29 -12
- package/dist/{useQuilttConnector-12s-DTl-EiqV.js → useQuilttConnector-12s-7jon0A8C.js} +3 -3
- package/dist/{useQuilttInstitutions-12s-Cg4OA77c.js → useQuilttInstitutions-12s-Ca5smTKo.js} +5 -5
- package/dist/useQuilttRenderGuard-12s-BbUitnF1.js +37 -0
- package/dist/useQuilttResolvable-12s-CNvWZe0V.js +86 -0
- package/dist/{useQuilttSession-12s-BCq3OL9S.js → useQuilttSession-12s-D10t5Wfh.js} +5 -3
- package/package.json +2 -2
- package/src/components/QuilttButton.tsx +4 -0
- package/src/components/QuilttContainer.tsx +4 -0
- package/src/contexts/QuilttProviderRender.ts +15 -0
- package/src/hooks/index.ts +2 -0
- package/src/hooks/useQuilttInstitutions.ts +5 -8
- package/src/hooks/useQuilttRenderGuard.ts +76 -0
- package/src/hooks/useQuilttResolvable.ts +128 -0
- package/src/hooks/useQuilttSession.ts +2 -2
- package/src/providers/QuilttProvider.tsx +14 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,59 @@
|
|
|
1
1
|
# @quiltt/react
|
|
2
2
|
|
|
3
|
+
## 4.5.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#386](https://github.com/quiltt/quiltt-js/pull/386) [`0bf706c`](https://github.com/quiltt/quiltt-js/commit/0bf706ce2ad926304d6eac739ee58971736f913e) Thanks [@zubairaziz](https://github.com/zubairaziz)! - Update platform webview props
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- [#380](https://github.com/quiltt/quiltt-js/pull/380) [`31cd190`](https://github.com/quiltt/quiltt-js/commit/31cd1902618ebc2314d42dd7aca81b3ab94068ea) Thanks [@sirwolfgang](https://github.com/sirwolfgang)! - Improve useQuilttResolvable error messaging
|
|
12
|
+
|
|
13
|
+
- Updated dependencies [[`31cd190`](https://github.com/quiltt/quiltt-js/commit/31cd1902618ebc2314d42dd7aca81b3ab94068ea), [`0bf706c`](https://github.com/quiltt/quiltt-js/commit/0bf706ce2ad926304d6eac739ee58971736f913e)]:
|
|
14
|
+
- @quiltt/core@4.5.0
|
|
15
|
+
|
|
16
|
+
## 4.4.0
|
|
17
|
+
|
|
18
|
+
### Minor Changes
|
|
19
|
+
|
|
20
|
+
- [#378](https://github.com/quiltt/quiltt-js/pull/378) [`0af4e66`](https://github.com/quiltt/quiltt-js/commit/0af4e6622d1542e0c0c02ac7e897e3e4f9219cbd) Thanks [@sirwolfgang](https://github.com/sirwolfgang)! - Add connector institution search and provider migration support.
|
|
21
|
+
|
|
22
|
+
## New APIs
|
|
23
|
+
|
|
24
|
+
### `useQuilttResolvable` Hook
|
|
25
|
+
|
|
26
|
+
Check if external provider institution IDs (e.g., Plaid) can be migrated to your connector.
|
|
27
|
+
|
|
28
|
+
```typescript
|
|
29
|
+
import { useQuilttResolvable } from "@quiltt/react";
|
|
30
|
+
import { useEffect } from "react";
|
|
31
|
+
|
|
32
|
+
function ResolvableConnector({ plaidInstitutionId, children }) {
|
|
33
|
+
const { checkResolvable, isResolvable, isLoading } =
|
|
34
|
+
useQuilttResolvable("my-connector-id");
|
|
35
|
+
|
|
36
|
+
useEffect(() => {
|
|
37
|
+
checkResolvable({ plaid: plaidInstitutionId });
|
|
38
|
+
}, [plaidInstitutionId]);
|
|
39
|
+
|
|
40
|
+
if (isLoading) return <div>Checking...</div>;
|
|
41
|
+
if (!isResolvable) return null;
|
|
42
|
+
|
|
43
|
+
return <>{children}</>;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// Usage
|
|
47
|
+
<ResolvableConnector plaidInstitutionId="ins_3">
|
|
48
|
+
<QuilttButton connectorId="my-connector-id" />
|
|
49
|
+
</ResolvableConnector>;
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
### Patch Changes
|
|
53
|
+
|
|
54
|
+
- Updated dependencies [[`0af4e66`](https://github.com/quiltt/quiltt-js/commit/0af4e6622d1542e0c0c02ac7e897e3e4f9219cbd)]:
|
|
55
|
+
- @quiltt/core@4.4.0
|
|
56
|
+
|
|
3
57
|
## 4.3.3
|
|
4
58
|
|
|
5
59
|
### Patch Changes
|
|
@@ -5,9 +5,10 @@ import '@apollo/client/react/hooks/useApolloClient.js';
|
|
|
5
5
|
import './QuilttSettings-12s-BK-0SQME.js';
|
|
6
6
|
import './useSession-12s-7GOn4sUn.js';
|
|
7
7
|
import 'use-debounce';
|
|
8
|
+
import './QuilttProviderRender-12s-DtQtubjL.js';
|
|
8
9
|
import { jsx } from 'react/jsx-runtime';
|
|
9
10
|
import { ApolloProvider } from '@apollo/client/react/context/ApolloProvider.js';
|
|
10
|
-
import { u as useQuilttSession } from './useQuilttSession-12s-
|
|
11
|
+
import { u as useQuilttSession } from './useQuilttSession-12s-D10t5Wfh.js';
|
|
11
12
|
|
|
12
13
|
const useAuthenticateSession = (auth, setSession)=>{
|
|
13
14
|
const authenticateSession = useCallback(async (payload, callbacks)=>{
|
package/dist/index.d.ts
CHANGED
|
@@ -125,6 +125,37 @@ type UseQuilttInstitutions = (connectorId: string, onErrorCallback?: (msg: strin
|
|
|
125
125
|
};
|
|
126
126
|
declare const useQuilttInstitutions: UseQuilttInstitutions;
|
|
127
127
|
|
|
128
|
+
/**
|
|
129
|
+
* Internal hook that detects when a Quiltt SDK component may be rendered
|
|
130
|
+
* in the same component as QuilttProvider, which is an anti-pattern that
|
|
131
|
+
* can cause memory context issues and unexpected behavior.
|
|
132
|
+
*
|
|
133
|
+
* **Limitation**: Due to React context propagation, this will trigger for ALL
|
|
134
|
+
* descendants of QuilttProvider, not just direct children. This means it may
|
|
135
|
+
* produce false positives for valid nested component patterns. However, the
|
|
136
|
+
* primary use case (same-component rendering) is reliably detected.
|
|
137
|
+
*
|
|
138
|
+
* When the flag is set, this hook will emit a console error. This primarily
|
|
139
|
+
* helps catch the anti-pattern but may also flag valid nested structures.
|
|
140
|
+
*
|
|
141
|
+
* @param componentName - The name of the component calling this hook (for error messages)
|
|
142
|
+
*/
|
|
143
|
+
declare const useQuilttRenderGuard: (componentName: string) => void;
|
|
144
|
+
|
|
145
|
+
type UseQuilttResolvable = (connectorId: string, onErrorCallback?: (msg: string) => void) => {
|
|
146
|
+
checkResolvable: (providerId: {
|
|
147
|
+
plaid?: string;
|
|
148
|
+
mock?: string;
|
|
149
|
+
mx?: string;
|
|
150
|
+
finicity?: string;
|
|
151
|
+
akoya?: string;
|
|
152
|
+
}) => Promise<boolean | null>;
|
|
153
|
+
isLoading: boolean;
|
|
154
|
+
isResolvable: boolean | null;
|
|
155
|
+
error: string | null;
|
|
156
|
+
};
|
|
157
|
+
declare const useQuilttResolvable: UseQuilttResolvable;
|
|
158
|
+
|
|
128
159
|
type UseQuilttSession = (environmentId?: string) => {
|
|
129
160
|
session: Maybe<QuilttJWT> | undefined;
|
|
130
161
|
importSession: ImportSession;
|
|
@@ -182,5 +213,5 @@ declare const QuilttSettingsProvider: FC<QuilttSettingsProviderProps>;
|
|
|
182
213
|
type QuilttProviderProps = QuilttSettingsProviderProps & QuilttAuthProviderProps;
|
|
183
214
|
declare const QuilttProvider: FC<QuilttProviderProps>;
|
|
184
215
|
|
|
185
|
-
export { QuilttAuthProvider, QuilttButton, QuilttContainer, QuilttProvider, QuilttSettingsProvider, useAuthenticateSession, useEventListener, useIdentifySession, useImportSession, useIsomorphicLayoutEffect, useQuilttClient, useQuilttConnector, useQuilttInstitutions, useQuilttSession, useQuilttSettings, useRevokeSession, useSession, useStorage };
|
|
186
|
-
export type { AuthenticateSession, IdentifySession, ImportSession, QuilttAuthProviderProps, QuilttSettingsProviderProps, RevokeSession, SetSession, UseQuilttInstitutions, UseQuilttSession };
|
|
216
|
+
export { QuilttAuthProvider, QuilttButton, QuilttContainer, QuilttProvider, QuilttSettingsProvider, useAuthenticateSession, useEventListener, useIdentifySession, useImportSession, useIsomorphicLayoutEffect, useQuilttClient, useQuilttConnector, useQuilttInstitutions, useQuilttRenderGuard, useQuilttResolvable, useQuilttSession, useQuilttSettings, useRevokeSession, useSession, useStorage };
|
|
217
|
+
export type { AuthenticateSession, IdentifySession, ImportSession, QuilttAuthProviderProps, QuilttSettingsProviderProps, RevokeSession, SetSession, UseQuilttInstitutions, UseQuilttResolvable, UseQuilttSession };
|
package/dist/index.js
CHANGED
|
@@ -1,17 +1,20 @@
|
|
|
1
1
|
export * from '@quiltt/core';
|
|
2
2
|
import { jsx } from 'react/jsx-runtime';
|
|
3
3
|
import { useRef, useEffect } from 'react';
|
|
4
|
-
import { u as useQuilttConnector } from './useQuilttConnector-12s-
|
|
5
|
-
import {
|
|
6
|
-
|
|
4
|
+
import { u as useQuilttConnector } from './useQuilttConnector-12s-7jon0A8C.js';
|
|
5
|
+
import { u as useQuilttRenderGuard } from './useQuilttRenderGuard-12s-BbUitnF1.js';
|
|
6
|
+
import { i as isDeepEqual, Q as QuilttAuthProvider } from './QuilttAuthProvider-12s-BCpwSX-3.js';
|
|
7
|
+
export { b as useAuthenticateSession, a as useIdentifySession, u as useImportSession, c as useRevokeSession } from './QuilttAuthProvider-12s-BCpwSX-3.js';
|
|
7
8
|
export { u as useEventListener } from './useEventListener-12s-D_-6QIXa.js';
|
|
8
9
|
export { u as useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect-12s-DeTHOKz1.js';
|
|
9
10
|
export { u as useQuilttClient } from './useQuilttClient-12s-CAAUait1.js';
|
|
10
|
-
export { u as useQuilttInstitutions } from './useQuilttInstitutions-12s-
|
|
11
|
-
export { u as
|
|
11
|
+
export { u as useQuilttInstitutions } from './useQuilttInstitutions-12s-Ca5smTKo.js';
|
|
12
|
+
export { u as useQuilttResolvable } from './useQuilttResolvable-12s-CNvWZe0V.js';
|
|
13
|
+
export { u as useQuilttSession } from './useQuilttSession-12s-D10t5Wfh.js';
|
|
12
14
|
export { u as useQuilttSettings } from './useQuilttSettings-12s--rCJoNHD.js';
|
|
13
15
|
export { u as useSession } from './useSession-12s-7GOn4sUn.js';
|
|
14
16
|
export { u as useStorage } from './useStorage-12s-DHcq3Kuh.js';
|
|
17
|
+
import { Q as QuilttProviderRender } from './QuilttProviderRender-12s-DtQtubjL.js';
|
|
15
18
|
import { Q as QuilttSettingsProvider } from './QuilttSettingsProvider-12s-ZcmFmOiZ.js';
|
|
16
19
|
|
|
17
20
|
/**
|
|
@@ -21,6 +24,8 @@ import { Q as QuilttSettingsProvider } from './QuilttSettingsProvider-12s-ZcmFmO
|
|
|
21
24
|
* connector instance with the new connection details. If you need to force a
|
|
22
25
|
* complete remount instead, set forceRemountOnConnectionChange to true.
|
|
23
26
|
*/ const QuilttButton = ({ as, connectorId, connectionId, institution, forceRemountOnConnectionChange = false, onEvent, onOpen, onLoad, onExit, onExitSuccess, onExitAbort, onExitError, onClick, onHtmlLoad, children, ...props })=>{
|
|
27
|
+
// Check flag to warn about potential anti-pattern (may produce false positives for valid nested patterns)
|
|
28
|
+
useQuilttRenderGuard('QuilttButton');
|
|
24
29
|
// Keep track of previous connectionId for change detection
|
|
25
30
|
const prevConnectionIdRef = useRef(connectionId);
|
|
26
31
|
const prevCallbacksRef = useRef({
|
|
@@ -103,6 +108,8 @@ import { Q as QuilttSettingsProvider } from './QuilttSettingsProvider-12s-ZcmFmO
|
|
|
103
108
|
* connector instance with the new connection details. If you need to force a
|
|
104
109
|
* complete remount instead, set forceRemountOnConnectionChange to true.
|
|
105
110
|
*/ const QuilttContainer = ({ as, connectorId, connectionId, forceRemountOnConnectionChange = false, onEvent, onLoad, onExit, onExitSuccess, onExitAbort, onExitError, children, ...props })=>{
|
|
111
|
+
// Check flag to warn about potential anti-pattern (may produce false positives for valid nested patterns)
|
|
112
|
+
useQuilttRenderGuard('QuilttContainer');
|
|
106
113
|
// Keep track of previous connectionId for change detection
|
|
107
114
|
const prevConnectionIdRef = useRef(connectionId);
|
|
108
115
|
const prevCallbacksRef = useRef({
|
|
@@ -162,14 +169,24 @@ import { Q as QuilttSettingsProvider } from './QuilttSettingsProvider-12s-ZcmFmO
|
|
|
162
169
|
};
|
|
163
170
|
|
|
164
171
|
const QuilttProvider = ({ clientId, graphqlClient, token, children })=>{
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
172
|
+
// Set a context flag that SDK components can check to warn about potential anti-patterns.
|
|
173
|
+
// LIMITATION: This flags ALL descendants due to React context propagation, not just same-component usage.
|
|
174
|
+
// Will produce false positives for valid patterns like: <QuilttProvider><MyPage /></QuilttProvider>
|
|
175
|
+
// where MyPage renders SDK components (which is correct usage).
|
|
176
|
+
// The flag-based approach is simple but imprecise - a proper solution would require render stack tracking.
|
|
177
|
+
return /*#__PURE__*/ jsx(QuilttProviderRender.Provider, {
|
|
178
|
+
value: {
|
|
179
|
+
isRenderingProvider: true
|
|
180
|
+
},
|
|
181
|
+
children: /*#__PURE__*/ jsx(QuilttSettingsProvider, {
|
|
182
|
+
clientId: clientId,
|
|
183
|
+
children: /*#__PURE__*/ jsx(QuilttAuthProvider, {
|
|
184
|
+
token: token,
|
|
185
|
+
graphqlClient: graphqlClient,
|
|
186
|
+
children: children
|
|
187
|
+
})
|
|
171
188
|
})
|
|
172
189
|
});
|
|
173
190
|
};
|
|
174
191
|
|
|
175
|
-
export { QuilttAuthProvider, QuilttButton, QuilttContainer, QuilttProvider, QuilttSettingsProvider, useQuilttConnector };
|
|
192
|
+
export { QuilttAuthProvider, QuilttButton, QuilttContainer, QuilttProvider, QuilttSettingsProvider, useQuilttConnector, useQuilttRenderGuard };
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { useState, useRef, useEffect, useCallback } from 'react';
|
|
3
3
|
import { cdnBase } from '@quiltt/core';
|
|
4
|
-
import { u as useQuilttSession } from './useQuilttSession-12s-
|
|
4
|
+
import { u as useQuilttSession } from './useQuilttSession-12s-D10t5Wfh.js';
|
|
5
5
|
import { u as useScript } from './useScript-12s-JCgaTW9n.js';
|
|
6
|
-
import { i as isDeepEqual } from './QuilttAuthProvider-12s-
|
|
6
|
+
import { i as isDeepEqual } from './QuilttAuthProvider-12s-BCpwSX-3.js';
|
|
7
7
|
|
|
8
|
-
var version = "4.
|
|
8
|
+
var version = "4.5.0";
|
|
9
9
|
|
|
10
10
|
const useQuilttConnector = (connectorId, options)=>{
|
|
11
11
|
const status = useScript(`${cdnBase}/v1/connector.js?agent=react-${version}`, {
|
package/dist/{useQuilttInstitutions-12s-Cg4OA77c.js → useQuilttInstitutions-12s-Ca5smTKo.js}
RENAMED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { useMemo, useState, useRef, useEffect, useCallback } from 'react';
|
|
3
|
-
import {
|
|
3
|
+
import { ConnectorsAPI } from '@quiltt/core';
|
|
4
4
|
import { useDebounce } from 'use-debounce';
|
|
5
|
-
import { v as version } from './useQuilttConnector-12s-
|
|
5
|
+
import { v as version } from './useQuilttConnector-12s-7jon0A8C.js';
|
|
6
6
|
import { u as useSession } from './useSession-12s-7GOn4sUn.js';
|
|
7
7
|
|
|
8
8
|
const useQuilttInstitutions = (connectorId, onErrorCallback)=>{
|
|
@@ -18,7 +18,7 @@ const useQuilttInstitutions = (connectorId, onErrorCallback)=>{
|
|
|
18
18
|
typeof navigator !== 'undefined');
|
|
19
19
|
return isReactNative ? `react-native-${version}` : `react-${version}`;
|
|
20
20
|
}, []);
|
|
21
|
-
const
|
|
21
|
+
const connectorsAPI = useMemo(()=>new ConnectorsAPI(connectorId, agent), [
|
|
22
22
|
connectorId,
|
|
23
23
|
agent
|
|
24
24
|
]);
|
|
@@ -58,7 +58,7 @@ const useQuilttInstitutions = (connectorId, onErrorCallback)=>{
|
|
|
58
58
|
return;
|
|
59
59
|
}
|
|
60
60
|
const abortController = new AbortController();
|
|
61
|
-
|
|
61
|
+
connectorsAPI.searchInstitutions(session?.token, connectorId, searchTerm, abortController.signal).then((response)=>{
|
|
62
62
|
if (!abortController.signal.aborted) {
|
|
63
63
|
if (response.status === 200) {
|
|
64
64
|
setSearchResults(response.data);
|
|
@@ -78,7 +78,7 @@ const useQuilttInstitutions = (connectorId, onErrorCallback)=>{
|
|
|
78
78
|
session?.token,
|
|
79
79
|
connectorId,
|
|
80
80
|
searchTerm,
|
|
81
|
-
|
|
81
|
+
connectorsAPI,
|
|
82
82
|
handleError
|
|
83
83
|
]);
|
|
84
84
|
return {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useContext, useRef, useEffect } from 'react';
|
|
3
|
+
import { Q as QuilttProviderRender } from './QuilttProviderRender-12s-DtQtubjL.js';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Internal hook that detects when a Quiltt SDK component may be rendered
|
|
7
|
+
* in the same component as QuilttProvider, which is an anti-pattern that
|
|
8
|
+
* can cause memory context issues and unexpected behavior.
|
|
9
|
+
*
|
|
10
|
+
* **Limitation**: Due to React context propagation, this will trigger for ALL
|
|
11
|
+
* descendants of QuilttProvider, not just direct children. This means it may
|
|
12
|
+
* produce false positives for valid nested component patterns. However, the
|
|
13
|
+
* primary use case (same-component rendering) is reliably detected.
|
|
14
|
+
*
|
|
15
|
+
* When the flag is set, this hook will emit a console error. This primarily
|
|
16
|
+
* helps catch the anti-pattern but may also flag valid nested structures.
|
|
17
|
+
*
|
|
18
|
+
* @param componentName - The name of the component calling this hook (for error messages)
|
|
19
|
+
*/ const useQuilttRenderGuard = (componentName)=>{
|
|
20
|
+
const { isRenderingProvider } = useContext(QuilttProviderRender);
|
|
21
|
+
const hasWarnedRef = useRef(false);
|
|
22
|
+
useEffect(()=>{
|
|
23
|
+
// Only run in development mode and warn once per component instance
|
|
24
|
+
// Support both Node.js (process.env) and Vite (import.meta.env) environments
|
|
25
|
+
const isProduction = process.env.NODE_ENV === 'production' || typeof import.meta !== 'undefined' && import.meta.env?.PROD;
|
|
26
|
+
if (isProduction) return;
|
|
27
|
+
if (isRenderingProvider && !hasWarnedRef.current) {
|
|
28
|
+
hasWarnedRef.current = true;
|
|
29
|
+
console.error(`[Quiltt] ⚠️ POTENTIAL ANTI-PATTERN: ${componentName} may be rendered in the same component as QuilttProvider.\n\n` + `NOTE: This check uses a simple flag and may produce false positives for valid nested patterns.\n` + `If ${componentName} is in a SEPARATE component from QuilttProvider, you can safely ignore this.\n\n` + `The ANTI-PATTERN we're trying to catch:\n` + ` • Rendering Provider and SDK components in the SAME function component\n` + ` • This causes memory context issues because they run at the same React execution layer\n` + ` • React cannot properly manage the context hierarchy in this case\n\n` + `RECOMMENDED PATTERN:\n` + ` • Move QuilttProvider to a parent component or layout\n` + ` • Render ${componentName} in a child component\n\n` + `Example:\n\n` + ` // ✅ CORRECT: Provider in parent, SDK component in child (separate components)\n` + ` function Providers({ children }) {\n` + ` return (\n` + ` <QuilttProvider token={token}>\n` + ` {children}\n` + ` </QuilttProvider>\n` + ` )\n` + ` }\n\n` + ` function MyFeature() {\n` + ` return <${componentName} connectorId="..." />\n` + ` }\n\n` + ` // ❌ ANTI-PATTERN: Both in SAME component\n` + ` function MyFeature() {\n` + ` return (\n` + ` <QuilttProvider token={token}>\n` + ` <${componentName} connectorId="..." />\n` + ` </QuilttProvider>\n` + ` )\n` + ` }\n\n` + `Learn more:\n` + ` • https://github.com/quiltt/quiltt-js/tree/main/packages/react#provider-usage\n` + ` • https://react.dev/reference/react/useContext#my-component-doesnt-see-the-value-from-my-provider`);
|
|
30
|
+
}
|
|
31
|
+
}, [
|
|
32
|
+
isRenderingProvider,
|
|
33
|
+
componentName
|
|
34
|
+
]);
|
|
35
|
+
};
|
|
36
|
+
|
|
37
|
+
export { useQuilttRenderGuard as u };
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
import { useMemo, useState, useRef, useEffect, useCallback } from 'react';
|
|
3
|
+
import { ConnectorsAPI } from '@quiltt/core';
|
|
4
|
+
import { v as version } from './useQuilttConnector-12s-7jon0A8C.js';
|
|
5
|
+
import { u as useSession } from './useSession-12s-7GOn4sUn.js';
|
|
6
|
+
|
|
7
|
+
const useQuilttResolvable = (connectorId, onErrorCallback)=>{
|
|
8
|
+
const agent = useMemo(()=>{
|
|
9
|
+
// Try deprecated navigator.product first (still used in some RN versions)
|
|
10
|
+
if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {
|
|
11
|
+
return `react-native-${version}`;
|
|
12
|
+
}
|
|
13
|
+
// Detect React Native by its unique environment characteristics
|
|
14
|
+
const isReactNative = !!// Has window (unlike Node.js)
|
|
15
|
+
(typeof window !== 'undefined' && // No document in window (unlike browsers)
|
|
16
|
+
typeof window.document === 'undefined' && // Has navigator (unlike Node.js)
|
|
17
|
+
typeof navigator !== 'undefined');
|
|
18
|
+
return isReactNative ? `react-native-${version}` : `react-${version}`;
|
|
19
|
+
}, []);
|
|
20
|
+
const connectorsAPI = useMemo(()=>new ConnectorsAPI(connectorId, agent), [
|
|
21
|
+
connectorId,
|
|
22
|
+
agent
|
|
23
|
+
]);
|
|
24
|
+
const [session] = useSession();
|
|
25
|
+
const [isLoading, setIsLoading] = useState(false);
|
|
26
|
+
const [isResolvable, setIsResolvable] = useState(null);
|
|
27
|
+
const [error, setError] = useState(null);
|
|
28
|
+
// Store callback in ref to maintain stable reference
|
|
29
|
+
const onErrorCallbackRef = useRef(onErrorCallback);
|
|
30
|
+
useEffect(()=>{
|
|
31
|
+
onErrorCallbackRef.current = onErrorCallback;
|
|
32
|
+
});
|
|
33
|
+
const handleError = useCallback((message)=>{
|
|
34
|
+
const errorMessage = message || 'Unknown error occurred while checking resolvability';
|
|
35
|
+
setError(errorMessage);
|
|
36
|
+
console.error('Quiltt Connector Resolvable Error:', errorMessage);
|
|
37
|
+
if (onErrorCallbackRef.current) onErrorCallbackRef.current(errorMessage);
|
|
38
|
+
}, []);
|
|
39
|
+
const checkResolvable = useCallback(async (providerId)=>{
|
|
40
|
+
if (!session?.token) {
|
|
41
|
+
handleError('Missing session token');
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
if (!connectorId) {
|
|
45
|
+
handleError('Missing connector ID');
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
const hasProviderId = Object.values(providerId).some((id)=>!!id);
|
|
49
|
+
if (!hasProviderId) {
|
|
50
|
+
handleError('No provider ID specified');
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
setIsLoading(true);
|
|
54
|
+
setError(null);
|
|
55
|
+
try {
|
|
56
|
+
const response = await connectorsAPI.checkResolvable(session.token, connectorId, providerId);
|
|
57
|
+
if (response.status === 200) {
|
|
58
|
+
const result = response.data.resolvable;
|
|
59
|
+
setIsResolvable(result);
|
|
60
|
+
return result;
|
|
61
|
+
}
|
|
62
|
+
handleError(response.data.message || 'Failed to check resolvability');
|
|
63
|
+
setIsResolvable(null);
|
|
64
|
+
return null;
|
|
65
|
+
} catch (error) {
|
|
66
|
+
handleError(error?.message);
|
|
67
|
+
setIsResolvable(null);
|
|
68
|
+
return null;
|
|
69
|
+
} finally{
|
|
70
|
+
setIsLoading(false);
|
|
71
|
+
}
|
|
72
|
+
}, [
|
|
73
|
+
session?.token,
|
|
74
|
+
connectorId,
|
|
75
|
+
connectorsAPI,
|
|
76
|
+
handleError
|
|
77
|
+
]);
|
|
78
|
+
return {
|
|
79
|
+
checkResolvable,
|
|
80
|
+
isLoading,
|
|
81
|
+
isResolvable,
|
|
82
|
+
error
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
export { useQuilttResolvable as u };
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { useCallback } from 'react';
|
|
2
|
+
import { useMemo, useCallback } from 'react';
|
|
3
3
|
import { AuthAPI } from '@quiltt/core';
|
|
4
|
-
import { u as useImportSession, a as useIdentifySession, b as useAuthenticateSession, c as useRevokeSession } from './QuilttAuthProvider-12s-
|
|
4
|
+
import { u as useImportSession, a as useIdentifySession, b as useAuthenticateSession, c as useRevokeSession } from './QuilttAuthProvider-12s-BCpwSX-3.js';
|
|
5
5
|
import { u as useQuilttSettings } from './useQuilttSettings-12s--rCJoNHD.js';
|
|
6
6
|
import { u as useSession } from './useSession-12s-7GOn4sUn.js';
|
|
7
7
|
|
|
8
8
|
const useQuilttSession = (environmentId)=>{
|
|
9
9
|
const { clientId } = useQuilttSettings();
|
|
10
10
|
const [session, setSession] = useSession();
|
|
11
|
-
const auth = new AuthAPI(clientId)
|
|
11
|
+
const auth = useMemo(()=>new AuthAPI(clientId), [
|
|
12
|
+
clientId
|
|
13
|
+
]);
|
|
12
14
|
const importSession = useImportSession(auth, session, setSession, environmentId);
|
|
13
15
|
const identifySession = useIdentifySession(auth, setSession);
|
|
14
16
|
const authenticateSession = useAuthenticateSession(auth, setSession);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quiltt/react",
|
|
3
|
-
"version": "4.
|
|
3
|
+
"version": "4.5.0",
|
|
4
4
|
"description": "React Components and Hooks for Quiltt Connector",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"quiltt",
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"dependencies": {
|
|
37
37
|
"@apollo/client": "^3.14.0",
|
|
38
38
|
"use-debounce": "^10.0.4",
|
|
39
|
-
"@quiltt/core": "4.
|
|
39
|
+
"@quiltt/core": "4.5.0"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"@biomejs/biome": "2.2.4",
|
|
@@ -4,6 +4,7 @@ import { useEffect, useRef } from 'react'
|
|
|
4
4
|
import type { ConnectorSDKCallbacks } from '@quiltt/core'
|
|
5
5
|
|
|
6
6
|
import { useQuilttConnector } from '@/hooks/useQuilttConnector'
|
|
7
|
+
import { useQuilttRenderGuard } from '@/hooks/useQuilttRenderGuard'
|
|
7
8
|
import type { PropsOf } from '@/types'
|
|
8
9
|
import { isDeepEqual } from '@/utils/isDeepEqual'
|
|
9
10
|
|
|
@@ -62,6 +63,9 @@ export const QuilttButton = <T extends ElementType = 'button'>({
|
|
|
62
63
|
children,
|
|
63
64
|
...props
|
|
64
65
|
}: QuilttButtonProps<T> & PropsOf<T>) => {
|
|
66
|
+
// Check flag to warn about potential anti-pattern (may produce false positives for valid nested patterns)
|
|
67
|
+
useQuilttRenderGuard('QuilttButton')
|
|
68
|
+
|
|
65
69
|
// Keep track of previous connectionId for change detection
|
|
66
70
|
const prevConnectionIdRef = useRef<string | undefined>(connectionId)
|
|
67
71
|
const prevCallbacksRef = useRef({
|
|
@@ -4,6 +4,7 @@ import { useEffect, useRef } from 'react'
|
|
|
4
4
|
import type { ConnectorSDKCallbacks } from '@quiltt/core'
|
|
5
5
|
|
|
6
6
|
import { useQuilttConnector } from '@/hooks/useQuilttConnector'
|
|
7
|
+
import { useQuilttRenderGuard } from '@/hooks/useQuilttRenderGuard'
|
|
7
8
|
import type { PropsOf } from '@/types'
|
|
8
9
|
import { isDeepEqual } from '@/utils/isDeepEqual'
|
|
9
10
|
|
|
@@ -44,6 +45,9 @@ export const QuilttContainer = <T extends ElementType = 'div'>({
|
|
|
44
45
|
children,
|
|
45
46
|
...props
|
|
46
47
|
}: QuilttContainerProps<T> & PropsOf<T>) => {
|
|
48
|
+
// Check flag to warn about potential anti-pattern (may produce false positives for valid nested patterns)
|
|
49
|
+
useQuilttRenderGuard('QuilttContainer')
|
|
50
|
+
|
|
47
51
|
// Keep track of previous connectionId for change detection
|
|
48
52
|
const prevConnectionIdRef = useRef<string | undefined>(connectionId)
|
|
49
53
|
const prevCallbacksRef = useRef({
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { createContext } from 'react'
|
|
4
|
+
|
|
5
|
+
type QuilttProviderRenderContext = {
|
|
6
|
+
/**
|
|
7
|
+
* Flag indicating that QuilttProvider is in the ancestor tree.
|
|
8
|
+
* Due to React context propagation, this is true for ALL descendants,
|
|
9
|
+
* not just those in the same component as the provider.
|
|
10
|
+
* Used to detect potential anti-patterns in component composition.
|
|
11
|
+
*/
|
|
12
|
+
isRenderingProvider?: boolean
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export const QuilttProviderRender = createContext<QuilttProviderRenderContext>({})
|
package/src/hooks/index.ts
CHANGED
|
@@ -3,6 +3,8 @@ export * from './session'
|
|
|
3
3
|
export * from './useQuilttClient'
|
|
4
4
|
export * from './useQuilttConnector'
|
|
5
5
|
export * from './useQuilttInstitutions'
|
|
6
|
+
export * from './useQuilttRenderGuard'
|
|
7
|
+
export * from './useQuilttResolvable'
|
|
6
8
|
export * from './useQuilttSession'
|
|
7
9
|
export * from './useQuilttSettings'
|
|
8
10
|
export * from './useSession'
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
4
4
|
|
|
5
5
|
import type { ErrorData, InstitutionsData } from '@quiltt/core'
|
|
6
|
-
import {
|
|
6
|
+
import { ConnectorsAPI } from '@quiltt/core'
|
|
7
7
|
import { useDebounce } from 'use-debounce'
|
|
8
8
|
|
|
9
9
|
import { version } from '../version'
|
|
@@ -41,10 +41,7 @@ export const useQuilttInstitutions: UseQuilttInstitutions = (connectorId, onErro
|
|
|
41
41
|
return isReactNative ? `react-native-${version}` : `react-${version}`
|
|
42
42
|
}, [])
|
|
43
43
|
|
|
44
|
-
const
|
|
45
|
-
() => new InstitutionsAPI(connectorId, agent),
|
|
46
|
-
[connectorId, agent]
|
|
47
|
-
)
|
|
44
|
+
const connectorsAPI = useMemo(() => new ConnectorsAPI(connectorId, agent), [connectorId, agent])
|
|
48
45
|
const [session] = useSession()
|
|
49
46
|
|
|
50
47
|
const [searchTermInput, setSearchTermInput] = useState('')
|
|
@@ -93,8 +90,8 @@ export const useQuilttInstitutions: UseQuilttInstitutions = (connectorId, onErro
|
|
|
93
90
|
|
|
94
91
|
const abortController = new AbortController()
|
|
95
92
|
|
|
96
|
-
|
|
97
|
-
.
|
|
93
|
+
connectorsAPI
|
|
94
|
+
.searchInstitutions(session?.token, connectorId, searchTerm, abortController.signal)
|
|
98
95
|
.then((response) => {
|
|
99
96
|
if (!abortController.signal.aborted) {
|
|
100
97
|
if (response.status === 200) {
|
|
@@ -113,7 +110,7 @@ export const useQuilttInstitutions: UseQuilttInstitutions = (connectorId, onErro
|
|
|
113
110
|
})
|
|
114
111
|
|
|
115
112
|
return () => abortController.abort()
|
|
116
|
-
}, [session?.token, connectorId, searchTerm,
|
|
113
|
+
}, [session?.token, connectorId, searchTerm, connectorsAPI, handleError])
|
|
117
114
|
|
|
118
115
|
return {
|
|
119
116
|
searchTerm,
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useContext, useEffect, useRef } from 'react'
|
|
4
|
+
|
|
5
|
+
import { QuilttProviderRender } from '@/contexts/QuilttProviderRender'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Internal hook that detects when a Quiltt SDK component may be rendered
|
|
9
|
+
* in the same component as QuilttProvider, which is an anti-pattern that
|
|
10
|
+
* can cause memory context issues and unexpected behavior.
|
|
11
|
+
*
|
|
12
|
+
* **Limitation**: Due to React context propagation, this will trigger for ALL
|
|
13
|
+
* descendants of QuilttProvider, not just direct children. This means it may
|
|
14
|
+
* produce false positives for valid nested component patterns. However, the
|
|
15
|
+
* primary use case (same-component rendering) is reliably detected.
|
|
16
|
+
*
|
|
17
|
+
* When the flag is set, this hook will emit a console error. This primarily
|
|
18
|
+
* helps catch the anti-pattern but may also flag valid nested structures.
|
|
19
|
+
*
|
|
20
|
+
* @param componentName - The name of the component calling this hook (for error messages)
|
|
21
|
+
*/
|
|
22
|
+
export const useQuilttRenderGuard = (componentName: string) => {
|
|
23
|
+
const { isRenderingProvider } = useContext(QuilttProviderRender)
|
|
24
|
+
const hasWarnedRef = useRef(false)
|
|
25
|
+
|
|
26
|
+
useEffect(() => {
|
|
27
|
+
// Only run in development mode and warn once per component instance
|
|
28
|
+
// Support both Node.js (process.env) and Vite (import.meta.env) environments
|
|
29
|
+
const isProduction =
|
|
30
|
+
process.env.NODE_ENV === 'production' ||
|
|
31
|
+
(typeof import.meta !== 'undefined' && (import.meta as any).env?.PROD)
|
|
32
|
+
|
|
33
|
+
if (isProduction) return
|
|
34
|
+
|
|
35
|
+
if (isRenderingProvider && !hasWarnedRef.current) {
|
|
36
|
+
hasWarnedRef.current = true
|
|
37
|
+
console.error(
|
|
38
|
+
`[Quiltt] ⚠️ POTENTIAL ANTI-PATTERN: ${componentName} may be rendered in the same component as QuilttProvider.\n\n` +
|
|
39
|
+
`NOTE: This check uses a simple flag and may produce false positives for valid nested patterns.\n` +
|
|
40
|
+
`If ${componentName} is in a SEPARATE component from QuilttProvider, you can safely ignore this.\n\n` +
|
|
41
|
+
`The ANTI-PATTERN we're trying to catch:\n` +
|
|
42
|
+
` • Rendering Provider and SDK components in the SAME function component\n` +
|
|
43
|
+
` • This causes memory context issues because they run at the same React execution layer\n` +
|
|
44
|
+
` • React cannot properly manage the context hierarchy in this case\n\n` +
|
|
45
|
+
`RECOMMENDED PATTERN:\n` +
|
|
46
|
+
` • Move QuilttProvider to a parent component or layout\n` +
|
|
47
|
+
` • Render ${componentName} in a child component\n\n` +
|
|
48
|
+
`Example:\n\n` +
|
|
49
|
+
` // ✅ CORRECT: Provider in parent, SDK component in child (separate components)\n` +
|
|
50
|
+
` function Providers({ children }) {\n` +
|
|
51
|
+
` return (\n` +
|
|
52
|
+
` <QuilttProvider token={token}>\n` +
|
|
53
|
+
` {children}\n` +
|
|
54
|
+
` </QuilttProvider>\n` +
|
|
55
|
+
` )\n` +
|
|
56
|
+
` }\n\n` +
|
|
57
|
+
` function MyFeature() {\n` +
|
|
58
|
+
` return <${componentName} connectorId="..." />\n` +
|
|
59
|
+
` }\n\n` +
|
|
60
|
+
` // ❌ ANTI-PATTERN: Both in SAME component\n` +
|
|
61
|
+
` function MyFeature() {\n` +
|
|
62
|
+
` return (\n` +
|
|
63
|
+
` <QuilttProvider token={token}>\n` +
|
|
64
|
+
` <${componentName} connectorId="..." />\n` +
|
|
65
|
+
` </QuilttProvider>\n` +
|
|
66
|
+
` )\n` +
|
|
67
|
+
` }\n\n` +
|
|
68
|
+
`Learn more:\n` +
|
|
69
|
+
` • https://github.com/quiltt/quiltt-js/tree/main/packages/react#provider-usage\n` +
|
|
70
|
+
` • https://react.dev/reference/react/useContext#my-component-doesnt-see-the-value-from-my-provider`
|
|
71
|
+
)
|
|
72
|
+
}
|
|
73
|
+
}, [isRenderingProvider, componentName])
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
export default useQuilttRenderGuard
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
4
|
+
|
|
5
|
+
import type { ErrorData, ResolvableData } from '@quiltt/core'
|
|
6
|
+
import { ConnectorsAPI } from '@quiltt/core'
|
|
7
|
+
|
|
8
|
+
import { version } from '../version'
|
|
9
|
+
import useSession from './useSession'
|
|
10
|
+
|
|
11
|
+
export type UseQuilttResolvable = (
|
|
12
|
+
connectorId: string,
|
|
13
|
+
onErrorCallback?: (msg: string) => void
|
|
14
|
+
) => {
|
|
15
|
+
checkResolvable: (providerId: {
|
|
16
|
+
plaid?: string
|
|
17
|
+
mock?: string
|
|
18
|
+
mx?: string
|
|
19
|
+
finicity?: string
|
|
20
|
+
akoya?: string
|
|
21
|
+
}) => Promise<boolean | null>
|
|
22
|
+
isLoading: boolean
|
|
23
|
+
isResolvable: boolean | null
|
|
24
|
+
error: string | null
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export const useQuilttResolvable: UseQuilttResolvable = (connectorId, onErrorCallback) => {
|
|
28
|
+
const agent = useMemo(() => {
|
|
29
|
+
// Try deprecated navigator.product first (still used in some RN versions)
|
|
30
|
+
if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {
|
|
31
|
+
return `react-native-${version}`
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
// Detect React Native by its unique environment characteristics
|
|
35
|
+
const isReactNative = !!(
|
|
36
|
+
// Has window (unlike Node.js)
|
|
37
|
+
(
|
|
38
|
+
typeof window !== 'undefined' &&
|
|
39
|
+
// No document in window (unlike browsers)
|
|
40
|
+
typeof window.document === 'undefined' &&
|
|
41
|
+
// Has navigator (unlike Node.js)
|
|
42
|
+
typeof navigator !== 'undefined'
|
|
43
|
+
)
|
|
44
|
+
)
|
|
45
|
+
|
|
46
|
+
return isReactNative ? `react-native-${version}` : `react-${version}`
|
|
47
|
+
}, [])
|
|
48
|
+
|
|
49
|
+
const connectorsAPI = useMemo(() => new ConnectorsAPI(connectorId, agent), [connectorId, agent])
|
|
50
|
+
const [session] = useSession()
|
|
51
|
+
|
|
52
|
+
const [isLoading, setIsLoading] = useState(false)
|
|
53
|
+
const [isResolvable, setIsResolvable] = useState<boolean | null>(null)
|
|
54
|
+
const [error, setError] = useState<string | null>(null)
|
|
55
|
+
|
|
56
|
+
// Store callback in ref to maintain stable reference
|
|
57
|
+
const onErrorCallbackRef = useRef(onErrorCallback)
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
onErrorCallbackRef.current = onErrorCallback
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
const handleError = useCallback((message: string) => {
|
|
63
|
+
const errorMessage = message || 'Unknown error occurred while checking resolvability'
|
|
64
|
+
|
|
65
|
+
setError(errorMessage)
|
|
66
|
+
console.error('Quiltt Connector Resolvable Error:', errorMessage)
|
|
67
|
+
if (onErrorCallbackRef.current) onErrorCallbackRef.current(errorMessage)
|
|
68
|
+
}, [])
|
|
69
|
+
|
|
70
|
+
const checkResolvable = useCallback(
|
|
71
|
+
async (providerId: {
|
|
72
|
+
plaid?: string
|
|
73
|
+
mock?: string
|
|
74
|
+
mx?: string
|
|
75
|
+
finicity?: string
|
|
76
|
+
akoya?: string
|
|
77
|
+
}): Promise<boolean | null> => {
|
|
78
|
+
if (!session?.token) {
|
|
79
|
+
handleError('Missing session token')
|
|
80
|
+
return null
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (!connectorId) {
|
|
84
|
+
handleError('Missing connector ID')
|
|
85
|
+
return null
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const hasProviderId = Object.values(providerId).some((id) => !!id)
|
|
89
|
+
if (!hasProviderId) {
|
|
90
|
+
handleError('No provider ID specified')
|
|
91
|
+
return null
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
setIsLoading(true)
|
|
95
|
+
setError(null)
|
|
96
|
+
|
|
97
|
+
try {
|
|
98
|
+
const response = await connectorsAPI.checkResolvable(session.token, connectorId, providerId)
|
|
99
|
+
|
|
100
|
+
if (response.status === 200) {
|
|
101
|
+
const result = (response.data as ResolvableData).resolvable
|
|
102
|
+
setIsResolvable(result)
|
|
103
|
+
return result
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
handleError((response.data as ErrorData).message || 'Failed to check resolvability')
|
|
107
|
+
setIsResolvable(null)
|
|
108
|
+
return null
|
|
109
|
+
} catch (error: any) {
|
|
110
|
+
handleError(error?.message)
|
|
111
|
+
setIsResolvable(null)
|
|
112
|
+
return null
|
|
113
|
+
} finally {
|
|
114
|
+
setIsLoading(false)
|
|
115
|
+
}
|
|
116
|
+
},
|
|
117
|
+
[session?.token, connectorId, connectorsAPI, handleError]
|
|
118
|
+
)
|
|
119
|
+
|
|
120
|
+
return {
|
|
121
|
+
checkResolvable,
|
|
122
|
+
isLoading,
|
|
123
|
+
isResolvable,
|
|
124
|
+
error,
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export default useQuilttResolvable
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { useCallback } from 'react'
|
|
3
|
+
import { useCallback, useMemo } from 'react'
|
|
4
4
|
|
|
5
5
|
import type { Maybe, QuilttJWT } from '@quiltt/core'
|
|
6
6
|
import { AuthAPI } from '@quiltt/core'
|
|
@@ -28,7 +28,7 @@ export const useQuilttSession: UseQuilttSession = (environmentId) => {
|
|
|
28
28
|
const { clientId } = useQuilttSettings()
|
|
29
29
|
const [session, setSession] = useSession()
|
|
30
30
|
|
|
31
|
-
const auth = new AuthAPI(clientId)
|
|
31
|
+
const auth = useMemo(() => new AuthAPI(clientId), [clientId])
|
|
32
32
|
const importSession = useImportSession(auth, session, setSession, environmentId)
|
|
33
33
|
const identifySession = useIdentifySession(auth, setSession)
|
|
34
34
|
const authenticateSession = useAuthenticateSession(auth, setSession)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import type { FC } from 'react'
|
|
2
2
|
|
|
3
|
+
import { QuilttProviderRender } from '@/contexts/QuilttProviderRender'
|
|
4
|
+
|
|
3
5
|
import type { QuilttAuthProviderProps } from './QuilttAuthProvider'
|
|
4
6
|
import { QuilttAuthProvider } from './QuilttAuthProvider'
|
|
5
7
|
import type { QuilttSettingsProviderProps } from './QuilttSettingsProvider'
|
|
@@ -13,12 +15,19 @@ export const QuilttProvider: FC<QuilttProviderProps> = ({
|
|
|
13
15
|
token,
|
|
14
16
|
children,
|
|
15
17
|
}) => {
|
|
18
|
+
// Set a context flag that SDK components can check to warn about potential anti-patterns.
|
|
19
|
+
// LIMITATION: This flags ALL descendants due to React context propagation, not just same-component usage.
|
|
20
|
+
// Will produce false positives for valid patterns like: <QuilttProvider><MyPage /></QuilttProvider>
|
|
21
|
+
// where MyPage renders SDK components (which is correct usage).
|
|
22
|
+
// The flag-based approach is simple but imprecise - a proper solution would require render stack tracking.
|
|
16
23
|
return (
|
|
17
|
-
<
|
|
18
|
-
<
|
|
19
|
-
{
|
|
20
|
-
|
|
21
|
-
|
|
24
|
+
<QuilttProviderRender.Provider value={{ isRenderingProvider: true }}>
|
|
25
|
+
<QuilttSettingsProvider clientId={clientId}>
|
|
26
|
+
<QuilttAuthProvider token={token} graphqlClient={graphqlClient}>
|
|
27
|
+
{children}
|
|
28
|
+
</QuilttAuthProvider>
|
|
29
|
+
</QuilttSettingsProvider>
|
|
30
|
+
</QuilttProviderRender.Provider>
|
|
22
31
|
)
|
|
23
32
|
}
|
|
24
33
|
|