@quiltt/react 4.4.0 → 4.5.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 +22 -0
- package/README.md +11 -4
- package/dist/{QuilttAuthProvider-12s-4hQ7iysR.js → QuilttAuthProvider-12s-DE-ePRo_.js} +3 -2
- package/dist/QuilttProviderRender-12s-DtQtubjL.js +6 -0
- package/dist/index.d.ts +26 -9
- package/dist/index.js +29 -13
- package/dist/{useQuilttConnector-12s-BiIbmt7I.js → useQuilttConnector-12s-D8VedbrT.js} +3 -3
- package/dist/{useQuilttInstitutions-12s-0_y37UHg.js → useQuilttInstitutions-12s-DExzPnfb.js} +1 -1
- package/dist/useQuilttRenderGuard-12s-CsS2Ma6Q.js +36 -0
- package/dist/{useQuilttResolvable-12s-C2mjy5zs.js → useQuilttResolvable-12s-C0BV3AvR.js} +15 -8
- package/dist/{useQuilttSession-12s-BCq3OL9S.js → useQuilttSession-12s-sRvULtJv.js} +5 -3
- package/package.json +11 -11
- 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 -1
- package/src/hooks/useQuilttRenderGuard.ts +73 -0
- package/src/hooks/useQuilttResolvable.ts +24 -23
- package/src/hooks/useQuilttSession.ts +2 -2
- package/src/providers/QuilttAuthProvider.tsx +1 -1
- package/src/providers/QuilttProvider.tsx +14 -5
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @quiltt/react
|
|
2
2
|
|
|
3
|
+
## 4.5.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#389](https://github.com/quiltt/quiltt-js/pull/389) [`a6a2a7e`](https://github.com/quiltt/quiltt-js/commit/a6a2a7ea94c7204a69b53f191ee738bcdc520a10) Thanks [@zubairaziz](https://github.com/zubairaziz)! - Upgrade React versions across all projects
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`a6a2a7e`](https://github.com/quiltt/quiltt-js/commit/a6a2a7ea94c7204a69b53f191ee738bcdc520a10)]:
|
|
10
|
+
- @quiltt/core@4.5.1
|
|
11
|
+
|
|
12
|
+
## 4.5.0
|
|
13
|
+
|
|
14
|
+
### Minor Changes
|
|
15
|
+
|
|
16
|
+
- [#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
|
|
17
|
+
|
|
18
|
+
### Patch Changes
|
|
19
|
+
|
|
20
|
+
- [#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
|
|
21
|
+
|
|
22
|
+
- Updated dependencies [[`31cd190`](https://github.com/quiltt/quiltt-js/commit/31cd1902618ebc2314d42dd7aca81b3ab94068ea), [`0bf706c`](https://github.com/quiltt/quiltt-js/commit/0bf706ce2ad926304d6eac739ee58971736f913e)]:
|
|
23
|
+
- @quiltt/core@4.5.0
|
|
24
|
+
|
|
3
25
|
## 4.4.0
|
|
4
26
|
|
|
5
27
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -7,24 +7,26 @@
|
|
|
7
7
|
|
|
8
8
|
See the guides [here](https://www.quiltt.dev/connector/sdks/react).
|
|
9
9
|
|
|
10
|
+
For general project information and contributing guidelines, see the [main repository README](../../README.md).
|
|
11
|
+
|
|
10
12
|
## Installation
|
|
11
13
|
|
|
12
14
|
With `npm`:
|
|
13
15
|
|
|
14
16
|
```shell
|
|
15
|
-
|
|
17
|
+
npm install @quiltt/react
|
|
16
18
|
```
|
|
17
19
|
|
|
18
20
|
With `yarn`:
|
|
19
21
|
|
|
20
22
|
```shell
|
|
21
|
-
|
|
23
|
+
yarn add @quiltt/react
|
|
22
24
|
```
|
|
23
25
|
|
|
24
26
|
With `pnpm`:
|
|
25
27
|
|
|
26
28
|
```shell
|
|
27
|
-
|
|
29
|
+
pnpm add @quiltt/react
|
|
28
30
|
```
|
|
29
31
|
|
|
30
32
|
## Core Modules and Types
|
|
@@ -204,4 +206,9 @@ This project is licensed under the terms of the MIT license. See the [LICENSE](L
|
|
|
204
206
|
|
|
205
207
|
## Contributing
|
|
206
208
|
|
|
207
|
-
For information on how to contribute to this project, please refer to the [
|
|
209
|
+
For information on how to contribute to this project, please refer to the [repository contributing guidelines](../../CONTRIBUTING.md).
|
|
210
|
+
|
|
211
|
+
## Related Packages
|
|
212
|
+
|
|
213
|
+
- [`@quiltt/core`](../core#readme) - Essential functionality and types
|
|
214
|
+
- [`@quiltt/react-native`](../react-native#readme) - React Native and Expo components
|
|
@@ -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-sRvULtJv.js';
|
|
11
12
|
|
|
12
13
|
const useAuthenticateSession = (auth, setSession)=>{
|
|
13
14
|
const authenticateSession = useCallback(async (payload, callbacks)=>{
|
|
@@ -165,7 +166,7 @@ const useRevokeSession = (auth, session, setSession)=>{
|
|
|
165
166
|
*/ const QuilttAuthProvider = ({ graphqlClient, token, children })=>{
|
|
166
167
|
const { session, importSession } = useQuilttSession();
|
|
167
168
|
const previousSessionRef = useRef(session);
|
|
168
|
-
const previousTokenRef = useRef();
|
|
169
|
+
const previousTokenRef = useRef(undefined);
|
|
169
170
|
// Memoize the client to avoid unnecessary re-renders
|
|
170
171
|
const apolloClient = useMemo(()=>graphqlClient || new QuilttClient({
|
|
171
172
|
cache: new InMemoryCache()
|
package/dist/index.d.ts
CHANGED
|
@@ -117,6 +117,31 @@ declare const useQuilttConnector: (connectorId?: string, options?: ConnectorSDKC
|
|
|
117
117
|
open: () => void;
|
|
118
118
|
};
|
|
119
119
|
|
|
120
|
+
type UseQuilttInstitutions = (connectorId: string, onErrorCallback?: (msg: string) => void) => {
|
|
121
|
+
searchTerm: string;
|
|
122
|
+
searchResults: InstitutionsData;
|
|
123
|
+
isSearching: boolean;
|
|
124
|
+
setSearchTerm: (term: string) => void;
|
|
125
|
+
};
|
|
126
|
+
declare const useQuilttInstitutions: UseQuilttInstitutions;
|
|
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
|
+
|
|
120
145
|
type UseQuilttResolvable = (connectorId: string, onErrorCallback?: (msg: string) => void) => {
|
|
121
146
|
checkResolvable: (providerId: {
|
|
122
147
|
plaid?: string;
|
|
@@ -131,14 +156,6 @@ type UseQuilttResolvable = (connectorId: string, onErrorCallback?: (msg: string)
|
|
|
131
156
|
};
|
|
132
157
|
declare const useQuilttResolvable: UseQuilttResolvable;
|
|
133
158
|
|
|
134
|
-
type UseQuilttInstitutions = (connectorId: string, onErrorCallback?: (msg: string) => void) => {
|
|
135
|
-
searchTerm: string;
|
|
136
|
-
searchResults: InstitutionsData;
|
|
137
|
-
isSearching: boolean;
|
|
138
|
-
setSearchTerm: (term: string) => void;
|
|
139
|
-
};
|
|
140
|
-
declare const useQuilttInstitutions: UseQuilttInstitutions;
|
|
141
|
-
|
|
142
159
|
type UseQuilttSession = (environmentId?: string) => {
|
|
143
160
|
session: Maybe<QuilttJWT> | undefined;
|
|
144
161
|
importSession: ImportSession;
|
|
@@ -196,5 +213,5 @@ declare const QuilttSettingsProvider: FC<QuilttSettingsProviderProps>;
|
|
|
196
213
|
type QuilttProviderProps = QuilttSettingsProviderProps & QuilttAuthProviderProps;
|
|
197
214
|
declare const QuilttProvider: FC<QuilttProviderProps>;
|
|
198
215
|
|
|
199
|
-
export { QuilttAuthProvider, QuilttButton, QuilttContainer, QuilttProvider, QuilttSettingsProvider, useAuthenticateSession, useEventListener, useIdentifySession, useImportSession, useIsomorphicLayoutEffect, useQuilttClient, useQuilttConnector, useQuilttInstitutions, useQuilttResolvable, useQuilttSession, useQuilttSettings, useRevokeSession, useSession, useStorage };
|
|
216
|
+
export { QuilttAuthProvider, QuilttButton, QuilttContainer, QuilttProvider, QuilttSettingsProvider, useAuthenticateSession, useEventListener, useIdentifySession, useImportSession, useIsomorphicLayoutEffect, useQuilttClient, useQuilttConnector, useQuilttInstitutions, useQuilttRenderGuard, useQuilttResolvable, useQuilttSession, useQuilttSettings, useRevokeSession, useSession, useStorage };
|
|
200
217
|
export type { AuthenticateSession, IdentifySession, ImportSession, QuilttAuthProviderProps, QuilttSettingsProviderProps, RevokeSession, SetSession, UseQuilttInstitutions, UseQuilttResolvable, UseQuilttSession };
|
package/dist/index.js
CHANGED
|
@@ -1,18 +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-D8VedbrT.js';
|
|
5
|
+
import { u as useQuilttRenderGuard } from './useQuilttRenderGuard-12s-CsS2Ma6Q.js';
|
|
6
|
+
import { i as isDeepEqual, Q as QuilttAuthProvider } from './QuilttAuthProvider-12s-DE-ePRo_.js';
|
|
7
|
+
export { b as useAuthenticateSession, a as useIdentifySession, u as useImportSession, c as useRevokeSession } from './QuilttAuthProvider-12s-DE-ePRo_.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
|
|
11
|
-
export { u as
|
|
12
|
-
export { u as useQuilttSession } from './useQuilttSession-12s-
|
|
11
|
+
export { u as useQuilttInstitutions } from './useQuilttInstitutions-12s-DExzPnfb.js';
|
|
12
|
+
export { u as useQuilttResolvable } from './useQuilttResolvable-12s-C0BV3AvR.js';
|
|
13
|
+
export { u as useQuilttSession } from './useQuilttSession-12s-sRvULtJv.js';
|
|
13
14
|
export { u as useQuilttSettings } from './useQuilttSettings-12s--rCJoNHD.js';
|
|
14
15
|
export { u as useSession } from './useSession-12s-7GOn4sUn.js';
|
|
15
16
|
export { u as useStorage } from './useStorage-12s-DHcq3Kuh.js';
|
|
17
|
+
import { Q as QuilttProviderRender } from './QuilttProviderRender-12s-DtQtubjL.js';
|
|
16
18
|
import { Q as QuilttSettingsProvider } from './QuilttSettingsProvider-12s-ZcmFmOiZ.js';
|
|
17
19
|
|
|
18
20
|
/**
|
|
@@ -22,6 +24,8 @@ import { Q as QuilttSettingsProvider } from './QuilttSettingsProvider-12s-ZcmFmO
|
|
|
22
24
|
* connector instance with the new connection details. If you need to force a
|
|
23
25
|
* complete remount instead, set forceRemountOnConnectionChange to true.
|
|
24
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');
|
|
25
29
|
// Keep track of previous connectionId for change detection
|
|
26
30
|
const prevConnectionIdRef = useRef(connectionId);
|
|
27
31
|
const prevCallbacksRef = useRef({
|
|
@@ -104,6 +108,8 @@ import { Q as QuilttSettingsProvider } from './QuilttSettingsProvider-12s-ZcmFmO
|
|
|
104
108
|
* connector instance with the new connection details. If you need to force a
|
|
105
109
|
* complete remount instead, set forceRemountOnConnectionChange to true.
|
|
106
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');
|
|
107
113
|
// Keep track of previous connectionId for change detection
|
|
108
114
|
const prevConnectionIdRef = useRef(connectionId);
|
|
109
115
|
const prevCallbacksRef = useRef({
|
|
@@ -163,14 +169,24 @@ import { Q as QuilttSettingsProvider } from './QuilttSettingsProvider-12s-ZcmFmO
|
|
|
163
169
|
};
|
|
164
170
|
|
|
165
171
|
const QuilttProvider = ({ clientId, graphqlClient, token, children })=>{
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
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
|
+
})
|
|
172
188
|
})
|
|
173
189
|
});
|
|
174
190
|
};
|
|
175
191
|
|
|
176
|
-
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-sRvULtJv.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-DE-ePRo_.js';
|
|
7
7
|
|
|
8
|
-
var version = "4.
|
|
8
|
+
var version = "4.5.1";
|
|
9
9
|
|
|
10
10
|
const useQuilttConnector = (connectorId, options)=>{
|
|
11
11
|
const status = useScript(`${cdnBase}/v1/connector.js?agent=react-${version}`, {
|
package/dist/{useQuilttInstitutions-12s-0_y37UHg.js → useQuilttInstitutions-12s-DExzPnfb.js}
RENAMED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { useMemo, useState, useRef, useEffect, useCallback } from 'react';
|
|
3
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-D8VedbrT.js';
|
|
6
6
|
import { u as useSession } from './useSession-12s-7GOn4sUn.js';
|
|
7
7
|
|
|
8
8
|
const useQuilttInstitutions = (connectorId, onErrorCallback)=>{
|
|
@@ -0,0 +1,36 @@
|
|
|
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
|
+
const isProduction = process.env.NODE_ENV === 'production';
|
|
25
|
+
if (isProduction) return;
|
|
26
|
+
if (isRenderingProvider && !hasWarnedRef.current) {
|
|
27
|
+
hasWarnedRef.current = true;
|
|
28
|
+
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`);
|
|
29
|
+
}
|
|
30
|
+
}, [
|
|
31
|
+
isRenderingProvider,
|
|
32
|
+
componentName
|
|
33
|
+
]);
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
export { useQuilttRenderGuard as u };
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { useMemo, useState, useCallback } from 'react';
|
|
2
|
+
import { useMemo, useState, useRef, useEffect, useCallback } from 'react';
|
|
3
3
|
import { ConnectorsAPI } from '@quiltt/core';
|
|
4
|
-
import { v as version } from './useQuilttConnector-12s-
|
|
4
|
+
import { v as version } from './useQuilttConnector-12s-D8VedbrT.js';
|
|
5
5
|
import { u as useSession } from './useSession-12s-7GOn4sUn.js';
|
|
6
6
|
|
|
7
7
|
const useQuilttResolvable = (connectorId, onErrorCallback)=>{
|
|
@@ -25,17 +25,24 @@ const useQuilttResolvable = (connectorId, onErrorCallback)=>{
|
|
|
25
25
|
const [isLoading, setIsLoading] = useState(false);
|
|
26
26
|
const [isResolvable, setIsResolvable] = useState(null);
|
|
27
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
|
+
});
|
|
28
33
|
const handleError = useCallback((message)=>{
|
|
29
34
|
const errorMessage = message || 'Unknown error occurred while checking resolvability';
|
|
30
35
|
setError(errorMessage);
|
|
31
36
|
console.error('Quiltt Connector Resolvable Error:', errorMessage);
|
|
32
|
-
if (
|
|
33
|
-
}, [
|
|
34
|
-
onErrorCallback
|
|
35
|
-
]);
|
|
37
|
+
if (onErrorCallbackRef.current) onErrorCallbackRef.current(errorMessage);
|
|
38
|
+
}, []);
|
|
36
39
|
const checkResolvable = useCallback(async (providerId)=>{
|
|
37
|
-
if (!session?.token
|
|
38
|
-
handleError('Missing session token
|
|
40
|
+
if (!session?.token) {
|
|
41
|
+
handleError('Missing session token');
|
|
42
|
+
return null;
|
|
43
|
+
}
|
|
44
|
+
if (!connectorId) {
|
|
45
|
+
handleError('Missing connector ID');
|
|
39
46
|
return null;
|
|
40
47
|
}
|
|
41
48
|
const hasProviderId = Object.values(providerId).some((id)=>!!id);
|
|
@@ -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-DE-ePRo_.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.1",
|
|
4
4
|
"description": "React Components and Hooks for Quiltt Connector",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"quiltt",
|
|
@@ -36,18 +36,18 @@
|
|
|
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.1"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
|
-
"@biomejs/biome": "2.
|
|
43
|
-
"@types/node": "22.
|
|
44
|
-
"@types/react": "
|
|
45
|
-
"@types/react-dom": "
|
|
46
|
-
"bunchee": "6.6.
|
|
47
|
-
"react": "
|
|
48
|
-
"react-dom": "
|
|
49
|
-
"rimraf": "6.
|
|
50
|
-
"typescript": "5.9.
|
|
42
|
+
"@biomejs/biome": "2.3.8",
|
|
43
|
+
"@types/node": "22.19.2",
|
|
44
|
+
"@types/react": "19.2.7",
|
|
45
|
+
"@types/react-dom": "19.2.3",
|
|
46
|
+
"bunchee": "6.6.2",
|
|
47
|
+
"react": "19.2.3",
|
|
48
|
+
"react-dom": "19.2.3",
|
|
49
|
+
"rimraf": "6.1.2",
|
|
50
|
+
"typescript": "5.9.3"
|
|
51
51
|
},
|
|
52
52
|
"peerDependencies": {
|
|
53
53
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
|
@@ -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
|
@@ -2,8 +2,9 @@ export * from './helpers'
|
|
|
2
2
|
export * from './session'
|
|
3
3
|
export * from './useQuilttClient'
|
|
4
4
|
export * from './useQuilttConnector'
|
|
5
|
-
export * from './useQuilttResolvable'
|
|
6
5
|
export * from './useQuilttInstitutions'
|
|
6
|
+
export * from './useQuilttRenderGuard'
|
|
7
|
+
export * from './useQuilttResolvable'
|
|
7
8
|
export * from './useQuilttSession'
|
|
8
9
|
export * from './useQuilttSettings'
|
|
9
10
|
export * from './useSession'
|
|
@@ -0,0 +1,73 @@
|
|
|
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
|
+
const isProduction = process.env.NODE_ENV === 'production'
|
|
29
|
+
|
|
30
|
+
if (isProduction) return
|
|
31
|
+
|
|
32
|
+
if (isRenderingProvider && !hasWarnedRef.current) {
|
|
33
|
+
hasWarnedRef.current = true
|
|
34
|
+
console.error(
|
|
35
|
+
`[Quiltt] ⚠️ POTENTIAL ANTI-PATTERN: ${componentName} may be rendered in the same component as QuilttProvider.\n\n` +
|
|
36
|
+
`NOTE: This check uses a simple flag and may produce false positives for valid nested patterns.\n` +
|
|
37
|
+
`If ${componentName} is in a SEPARATE component from QuilttProvider, you can safely ignore this.\n\n` +
|
|
38
|
+
`The ANTI-PATTERN we're trying to catch:\n` +
|
|
39
|
+
` • Rendering Provider and SDK components in the SAME function component\n` +
|
|
40
|
+
` • This causes memory context issues because they run at the same React execution layer\n` +
|
|
41
|
+
` • React cannot properly manage the context hierarchy in this case\n\n` +
|
|
42
|
+
`RECOMMENDED PATTERN:\n` +
|
|
43
|
+
` • Move QuilttProvider to a parent component or layout\n` +
|
|
44
|
+
` • Render ${componentName} in a child component\n\n` +
|
|
45
|
+
`Example:\n\n` +
|
|
46
|
+
` // ✅ CORRECT: Provider in parent, SDK component in child (separate components)\n` +
|
|
47
|
+
` function Providers({ children }) {\n` +
|
|
48
|
+
` return (\n` +
|
|
49
|
+
` <QuilttProvider token={token}>\n` +
|
|
50
|
+
` {children}\n` +
|
|
51
|
+
` </QuilttProvider>\n` +
|
|
52
|
+
` )\n` +
|
|
53
|
+
` }\n\n` +
|
|
54
|
+
` function MyFeature() {\n` +
|
|
55
|
+
` return <${componentName} connectorId="..." />\n` +
|
|
56
|
+
` }\n\n` +
|
|
57
|
+
` // ❌ ANTI-PATTERN: Both in SAME component\n` +
|
|
58
|
+
` function MyFeature() {\n` +
|
|
59
|
+
` return (\n` +
|
|
60
|
+
` <QuilttProvider token={token}>\n` +
|
|
61
|
+
` <${componentName} connectorId="..." />\n` +
|
|
62
|
+
` </QuilttProvider>\n` +
|
|
63
|
+
` )\n` +
|
|
64
|
+
` }\n\n` +
|
|
65
|
+
`Learn more:\n` +
|
|
66
|
+
` • https://github.com/quiltt/quiltt-js/tree/main/packages/react#provider-usage\n` +
|
|
67
|
+
` • https://react.dev/reference/react/useContext#my-component-doesnt-see-the-value-from-my-provider`
|
|
68
|
+
)
|
|
69
|
+
}
|
|
70
|
+
}, [isRenderingProvider, componentName])
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export default useQuilttRenderGuard
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
-
import { useCallback, useMemo, useState } from 'react'
|
|
3
|
+
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
4
4
|
|
|
5
5
|
import type { ErrorData, ResolvableData } from '@quiltt/core'
|
|
6
6
|
import { ConnectorsAPI } from '@quiltt/core'
|
|
@@ -10,7 +10,7 @@ import useSession from './useSession'
|
|
|
10
10
|
|
|
11
11
|
export type UseQuilttResolvable = (
|
|
12
12
|
connectorId: string,
|
|
13
|
-
onErrorCallback?: (msg: string) => void
|
|
13
|
+
onErrorCallback?: (msg: string) => void
|
|
14
14
|
) => {
|
|
15
15
|
checkResolvable: (providerId: {
|
|
16
16
|
plaid?: string
|
|
@@ -24,10 +24,7 @@ export type UseQuilttResolvable = (
|
|
|
24
24
|
error: string | null
|
|
25
25
|
}
|
|
26
26
|
|
|
27
|
-
export const useQuilttResolvable: UseQuilttResolvable = (
|
|
28
|
-
connectorId,
|
|
29
|
-
onErrorCallback,
|
|
30
|
-
) => {
|
|
27
|
+
export const useQuilttResolvable: UseQuilttResolvable = (connectorId, onErrorCallback) => {
|
|
31
28
|
const agent = useMemo(() => {
|
|
32
29
|
// Try deprecated navigator.product first (still used in some RN versions)
|
|
33
30
|
if (typeof navigator !== 'undefined' && navigator.product === 'ReactNative') {
|
|
@@ -56,16 +53,19 @@ export const useQuilttResolvable: UseQuilttResolvable = (
|
|
|
56
53
|
const [isResolvable, setIsResolvable] = useState<boolean | null>(null)
|
|
57
54
|
const [error, setError] = useState<string | null>(null)
|
|
58
55
|
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
// Store callback in ref to maintain stable reference
|
|
57
|
+
const onErrorCallbackRef = useRef(onErrorCallback)
|
|
58
|
+
useEffect(() => {
|
|
59
|
+
onErrorCallbackRef.current = onErrorCallback
|
|
60
|
+
})
|
|
62
61
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
69
|
|
|
70
70
|
const checkResolvable = useCallback(
|
|
71
71
|
async (providerId: {
|
|
@@ -75,8 +75,13 @@ export const useQuilttResolvable: UseQuilttResolvable = (
|
|
|
75
75
|
finicity?: string
|
|
76
76
|
akoya?: string
|
|
77
77
|
}): Promise<boolean | null> => {
|
|
78
|
-
if (!session?.token
|
|
79
|
-
handleError('Missing session token
|
|
78
|
+
if (!session?.token) {
|
|
79
|
+
handleError('Missing session token')
|
|
80
|
+
return null
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (!connectorId) {
|
|
84
|
+
handleError('Missing connector ID')
|
|
80
85
|
return null
|
|
81
86
|
}
|
|
82
87
|
|
|
@@ -90,11 +95,7 @@ export const useQuilttResolvable: UseQuilttResolvable = (
|
|
|
90
95
|
setError(null)
|
|
91
96
|
|
|
92
97
|
try {
|
|
93
|
-
const response = await connectorsAPI.checkResolvable(
|
|
94
|
-
session.token,
|
|
95
|
-
connectorId,
|
|
96
|
-
providerId,
|
|
97
|
-
)
|
|
98
|
+
const response = await connectorsAPI.checkResolvable(session.token, connectorId, providerId)
|
|
98
99
|
|
|
99
100
|
if (response.status === 200) {
|
|
100
101
|
const result = (response.data as ResolvableData).resolvable
|
|
@@ -113,7 +114,7 @@ export const useQuilttResolvable: UseQuilttResolvable = (
|
|
|
113
114
|
setIsLoading(false)
|
|
114
115
|
}
|
|
115
116
|
},
|
|
116
|
-
[session?.token, connectorId, connectorsAPI, handleError]
|
|
117
|
+
[session?.token, connectorId, connectorsAPI, handleError]
|
|
117
118
|
)
|
|
118
119
|
|
|
119
120
|
return {
|
|
@@ -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)
|
|
@@ -29,7 +29,7 @@ export const QuilttAuthProvider: FC<QuilttAuthProviderProps> = ({
|
|
|
29
29
|
}) => {
|
|
30
30
|
const { session, importSession } = useQuilttSession()
|
|
31
31
|
const previousSessionRef = useRef(session)
|
|
32
|
-
const previousTokenRef = useRef<string | undefined>()
|
|
32
|
+
const previousTokenRef = useRef<string | undefined>(undefined)
|
|
33
33
|
|
|
34
34
|
// Memoize the client to avoid unnecessary re-renders
|
|
35
35
|
const apolloClient = useMemo(
|
|
@@ -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
|
|