@quiltt/react 3.9.1 → 3.9.3
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 +18 -6
- package/dist/{QuilttAuthProvider-client-CqVLC1w4.js → QuilttAuthProvider-client-D6Iok6TS.js} +60 -7
- package/dist/index.d.ts +12 -8
- package/dist/index.js +16 -6
- package/dist/{useQuilttConnector-client-L80c55xy.js → useQuilttConnector-client-720xcXap.js} +3 -3
- package/dist/{useQuilttSession-client-DA9F_3KG.js → useQuilttSession-client-KZRjdl5I.js} +1 -1
- package/dist/{useScript-client-Cx5nb9RW.js → useScript-client-CWRBlZBC.js} +0 -3
- package/package.json +16 -13
- package/src/components/QuilttButton.tsx +38 -12
- package/src/components/QuilttContainer.tsx +4 -4
- package/src/providers/QuilttAuthProvider.tsx +10 -8
- package/src/types.ts +1 -3
- package/src/utils/index.ts +1 -0
- package/src/utils/isDeepEqual.ts +60 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
# @quiltt/react
|
|
2
2
|
|
|
3
|
+
## 3.9.3
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- [#319](https://github.com/quiltt/quiltt-js/pull/319) [`b97a814`](https://github.com/quiltt/quiltt-js/commit/b97a814b87b8bfec9f8b4bb155e1140724e441eb) Thanks [@zubairaziz](https://github.com/zubairaziz)! - Improve typings for query client and react components
|
|
8
|
+
|
|
9
|
+
- Updated dependencies [[`b97a814`](https://github.com/quiltt/quiltt-js/commit/b97a814b87b8bfec9f8b4bb155e1140724e441eb)]:
|
|
10
|
+
- @quiltt/core@3.9.3
|
|
11
|
+
|
|
12
|
+
## 3.9.2
|
|
13
|
+
|
|
14
|
+
### Patch Changes
|
|
15
|
+
|
|
16
|
+
- [#316](https://github.com/quiltt/quiltt-js/pull/316) [`de5d43e`](https://github.com/quiltt/quiltt-js/commit/de5d43e664a8bbb04595816718f5c645a9c3df27) Thanks [@rubendinho](https://github.com/rubendinho)! - Updated `main` param to `package.json` to improve analyzing the package.
|
|
17
|
+
|
|
18
|
+
- [#313](https://github.com/quiltt/quiltt-js/pull/313) [`3b789c9`](https://github.com/quiltt/quiltt-js/commit/3b789c9413ab2f9bdda965248ed7a8ccaf270172) Thanks [@zubairaziz](https://github.com/zubairaziz)! - Update typings for QuilttButton onLoad handler
|
|
19
|
+
|
|
20
|
+
- [#312](https://github.com/quiltt/quiltt-js/pull/312) [`11ba6a3`](https://github.com/quiltt/quiltt-js/commit/11ba6a3af1975349a63640bb99ed0e34ffee3f1c) Thanks [@zubairaziz](https://github.com/zubairaziz)! - Update session handling in QuilttAuthProvider
|
|
21
|
+
|
|
22
|
+
- Updated dependencies [[`de5d43e`](https://github.com/quiltt/quiltt-js/commit/de5d43e664a8bbb04595816718f5c645a9c3df27), [`3b789c9`](https://github.com/quiltt/quiltt-js/commit/3b789c9413ab2f9bdda965248ed7a8ccaf270172), [`11ba6a3`](https://github.com/quiltt/quiltt-js/commit/11ba6a3af1975349a63640bb99ed0e34ffee3f1c)]:
|
|
23
|
+
- @quiltt/core@3.9.2
|
|
24
|
+
|
|
3
25
|
## 3.9.1
|
|
4
26
|
|
|
5
27
|
### Patch Changes
|
package/README.md
CHANGED
|
@@ -9,17 +9,27 @@ See the guides [here](https://www.quiltt.dev/connector/sdks/react).
|
|
|
9
9
|
|
|
10
10
|
## Installation
|
|
11
11
|
|
|
12
|
+
With `npm`:
|
|
13
|
+
|
|
12
14
|
```shell
|
|
13
15
|
$ npm install @quiltt/react
|
|
14
|
-
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
With `yarn`:
|
|
19
|
+
|
|
20
|
+
```shell
|
|
15
21
|
$ yarn add @quiltt/react
|
|
16
|
-
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
With `pnpm`:
|
|
25
|
+
|
|
26
|
+
```shell
|
|
17
27
|
$ pnpm add @quiltt/react
|
|
18
28
|
```
|
|
19
29
|
|
|
20
30
|
## Core Modules and Types
|
|
21
31
|
|
|
22
|
-
The `@quiltt/react` library ships with `@quiltt/core`, which provides an
|
|
32
|
+
The `@quiltt/react` library ships with `@quiltt/core`, which provides an API clients and essential functionality for building Javascript-based applications with Quiltt. See the [Core README](../core/README.md) for more information.
|
|
23
33
|
|
|
24
34
|
## React Components
|
|
25
35
|
|
|
@@ -38,10 +48,11 @@ By default, the rendered component will be a `<button>` element, but you can sup
|
|
|
38
48
|
```tsx
|
|
39
49
|
import { useState } from 'react'
|
|
40
50
|
import { QuilttButton } from '@quiltt/react'
|
|
51
|
+
import type { ConnectorSDKCallbackMetadata } from '@quiltt/react'
|
|
41
52
|
|
|
42
53
|
export const App = () => {
|
|
43
54
|
const [connectionId, setConnectionId] = useState<string>()
|
|
44
|
-
const handleExitSuccess = (metadata) => {
|
|
55
|
+
const handleExitSuccess = (metadata: ConnectorSDKCallbackMetadata) => {
|
|
45
56
|
setConnectionId(metadata?.connectionId)
|
|
46
57
|
}
|
|
47
58
|
|
|
@@ -71,10 +82,11 @@ By default, the rendered component will be a `<div>` element, but you can supply
|
|
|
71
82
|
```tsx
|
|
72
83
|
import { useState } from 'react'
|
|
73
84
|
import { QuilttContainer } from '@quiltt/react'
|
|
85
|
+
import type { ConnectorSDKCallbackMetadata } from '@quiltt/react'
|
|
74
86
|
|
|
75
87
|
export const App = () => {
|
|
76
88
|
const [connectionId, setConnectionId] = useState<string>()
|
|
77
|
-
const handleExitSuccess = (metadata) => {
|
|
89
|
+
const handleExitSuccess = (metadata: ConnectorSDKCallbackMetadata) => {
|
|
78
90
|
setConnectionId(metadata?.connectionId)
|
|
79
91
|
}
|
|
80
92
|
|
|
@@ -184,7 +196,7 @@ export default App
|
|
|
184
196
|
|
|
185
197
|
## Typescript support
|
|
186
198
|
|
|
187
|
-
`@quiltt/react` is written in Typescript and ships with its own type definitions
|
|
199
|
+
`@quiltt/react` is written in Typescript and ships with its own type definitions, as well as the type definitions from `@quiltt/core`.
|
|
188
200
|
|
|
189
201
|
## License
|
|
190
202
|
|
package/dist/{QuilttAuthProvider-client-CqVLC1w4.js → QuilttAuthProvider-client-D6Iok6TS.js}
RENAMED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
'use client';
|
|
2
|
-
import { useCallback, useMemo, useEffect } from 'react';
|
|
2
|
+
import { useCallback, useRef, useMemo, useEffect } from 'react';
|
|
3
3
|
import { JsonWebTokenParse, QuilttClient, InMemoryCache } from '@quiltt/core';
|
|
4
4
|
import '@apollo/client/react/hooks/useApolloClient.js';
|
|
5
5
|
import './useQuilttSettings-client-DU_Qfc8X.js';
|
|
6
6
|
import './useSession-client-CG5lGS9F.js';
|
|
7
7
|
import { jsx } from 'react/jsx-runtime';
|
|
8
8
|
import { ApolloProvider } from '@apollo/client/react/context/ApolloProvider.js';
|
|
9
|
-
import { u as useQuilttSession } from './useQuilttSession-client-
|
|
9
|
+
import { u as useQuilttSession } from './useQuilttSession-client-KZRjdl5I.js';
|
|
10
10
|
|
|
11
11
|
const useIdentifySession = (auth, setSession)=>{
|
|
12
12
|
const identifySession = useCallback(async (payload, callbacks)=>{
|
|
@@ -102,14 +102,64 @@ const useRevokeSession = (auth, session, setSession)=>{
|
|
|
102
102
|
return revokeSession;
|
|
103
103
|
};
|
|
104
104
|
|
|
105
|
+
/**
|
|
106
|
+
* Performs a deep equality comparison between two values
|
|
107
|
+
*
|
|
108
|
+
* This function recursively compares all properties to determine if they are equal.
|
|
109
|
+
*
|
|
110
|
+
* @example
|
|
111
|
+
* ```ts
|
|
112
|
+
* isDeepEqual({ a: 1, b: { c: 2 } }, { a: 1, b: { c: 2 } }) // true
|
|
113
|
+
* isDeepEqual({ a: 1, b: { c: 2 } }, { a: 1, b: { c: 3 } }) // false
|
|
114
|
+
* ```
|
|
115
|
+
*/ const isDeepEqual = (obj1, obj2)=>{
|
|
116
|
+
// Handle primitive types and null/undefined
|
|
117
|
+
if (obj1 === obj2) return true;
|
|
118
|
+
if (obj1 === null || obj2 === null || typeof obj1 !== 'object' || typeof obj2 !== 'object') return false;
|
|
119
|
+
// Handle special object types
|
|
120
|
+
if (obj1 instanceof Date && obj2 instanceof Date) {
|
|
121
|
+
return obj1.getTime() === obj2.getTime();
|
|
122
|
+
}
|
|
123
|
+
if (obj1 instanceof RegExp && obj2 instanceof RegExp) {
|
|
124
|
+
return obj1.toString() === obj2.toString();
|
|
125
|
+
}
|
|
126
|
+
if (obj1 instanceof Map && obj2 instanceof Map) {
|
|
127
|
+
if (obj1.size !== obj2.size) return false;
|
|
128
|
+
for (const [key, value] of obj1){
|
|
129
|
+
if (!obj2.has(key) || !isDeepEqual(value, obj2.get(key))) return false;
|
|
130
|
+
}
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
133
|
+
if (obj1 instanceof Set && obj2 instanceof Set) {
|
|
134
|
+
if (obj1.size !== obj2.size) return false;
|
|
135
|
+
for (const item of obj1){
|
|
136
|
+
if (!Array.from(obj2).some((value)=>isDeepEqual(item, value))) return false;
|
|
137
|
+
}
|
|
138
|
+
return true;
|
|
139
|
+
}
|
|
140
|
+
// Handle arrays
|
|
141
|
+
if (Array.isArray(obj1) && Array.isArray(obj2)) {
|
|
142
|
+
if (obj1.length !== obj2.length) return false;
|
|
143
|
+
return obj1.every((value, index)=>isDeepEqual(value, obj2[index]));
|
|
144
|
+
}
|
|
145
|
+
// If one is array and other isn't, they're not equal
|
|
146
|
+
if (Array.isArray(obj1) || Array.isArray(obj2)) return false;
|
|
147
|
+
const keys1 = Object.keys(obj1);
|
|
148
|
+
const keys2 = Object.keys(obj2);
|
|
149
|
+
if (keys1.length !== keys2.length) return false;
|
|
150
|
+
return keys1.every((key)=>{
|
|
151
|
+
return Object.prototype.hasOwnProperty.call(obj2, key) && isDeepEqual(obj1[key], obj2[key]);
|
|
152
|
+
});
|
|
153
|
+
};
|
|
154
|
+
|
|
105
155
|
/**
|
|
106
156
|
* If a token is provided, will validate the token against the api and then import
|
|
107
157
|
* it into trusted storage. While this process is happening, the component is put
|
|
108
158
|
* into a loading state and the children are not rendered to prevent race conditions
|
|
109
159
|
* from triggering within the transitionary state.
|
|
110
|
-
*
|
|
111
160
|
*/ const QuilttAuthProvider = ({ token, children })=>{
|
|
112
161
|
const { session, importSession } = useQuilttSession();
|
|
162
|
+
const previousSessionRef = useRef(session);
|
|
113
163
|
// @todo: extract into a provider so it can accessed by child components
|
|
114
164
|
const graphQLClient = useMemo(()=>new QuilttClient({
|
|
115
165
|
cache: new InMemoryCache()
|
|
@@ -121,12 +171,15 @@ const useRevokeSession = (auth, session, setSession)=>{
|
|
|
121
171
|
token,
|
|
122
172
|
importSession
|
|
123
173
|
]);
|
|
124
|
-
// Reset Client Store when
|
|
125
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies: We should reset the store whenever the session changes
|
|
174
|
+
// Reset Client Store when session changes (using deep comparison)
|
|
126
175
|
useEffect(()=>{
|
|
127
|
-
|
|
176
|
+
if (!isDeepEqual(session, previousSessionRef.current)) {
|
|
177
|
+
graphQLClient.resetStore();
|
|
178
|
+
previousSessionRef.current = session;
|
|
179
|
+
}
|
|
128
180
|
}, [
|
|
129
|
-
session
|
|
181
|
+
session,
|
|
182
|
+
graphQLClient
|
|
130
183
|
]);
|
|
131
184
|
return /*#__PURE__*/ jsx(ApolloProvider, {
|
|
132
185
|
client: graphQLClient,
|
package/dist/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Maybe, QuilttJWT, UsernamePayload, UnprocessableData, AuthAPI, PasscodePayload, ConnectorSDKConnectorOptions, ConnectorSDKCallbacks } from '@quiltt/core';
|
|
2
2
|
export * from '@quiltt/core';
|
|
3
3
|
import * as react from 'react';
|
|
4
|
-
import { RefObject, useLayoutEffect, Dispatch, SetStateAction, FC, PropsWithChildren,
|
|
4
|
+
import { RefObject, useLayoutEffect, Dispatch, SetStateAction, FC, PropsWithChildren, JSX, ComponentType, ElementType, MouseEvent } from 'react';
|
|
5
5
|
import { useApolloClient } from '@apollo/client/react/hooks/useApolloClient.js';
|
|
6
6
|
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
7
7
|
|
|
@@ -115,7 +115,6 @@ type QuilttAuthProviderProps = PropsWithChildren & {
|
|
|
115
115
|
* it into trusted storage. While this process is happening, the component is put
|
|
116
116
|
* into a loading state and the children are not rendered to prevent race conditions
|
|
117
117
|
* from triggering within the transitionary state.
|
|
118
|
-
*
|
|
119
118
|
*/
|
|
120
119
|
declare const QuilttAuthProvider: FC<QuilttAuthProviderProps>;
|
|
121
120
|
|
|
@@ -133,18 +132,23 @@ type QuilttSettingsProviderProps = PropsWithChildren & {
|
|
|
133
132
|
};
|
|
134
133
|
declare const QuilttSettingsProvider: FC<QuilttSettingsProviderProps>;
|
|
135
134
|
|
|
136
|
-
type AnyTag = string | FC<any> | (new (props: any) => Component);
|
|
137
135
|
type PropsOf<Tag> = Tag extends keyof JSX.IntrinsicElements ? JSX.IntrinsicElements[Tag] : Tag extends ComponentType<infer Props> ? Props & JSX.IntrinsicAttributes : never;
|
|
138
136
|
|
|
139
|
-
type
|
|
137
|
+
type BaseQuilttButtonProps<T extends ElementType> = {
|
|
140
138
|
as?: T;
|
|
141
139
|
connectorId: string;
|
|
142
140
|
connectionId?: string;
|
|
143
141
|
institution?: string;
|
|
144
|
-
|
|
145
|
-
|
|
142
|
+
onClick?: (event: MouseEvent<HTMLElement>) => void;
|
|
143
|
+
};
|
|
144
|
+
type QuilttCallbackProps = Omit<ConnectorSDKCallbacks, 'onLoad'> & {
|
|
145
|
+
onLoad?: ConnectorSDKCallbacks['onLoad'];
|
|
146
|
+
onHtmlLoad?: React.ReactEventHandler<HTMLElement>;
|
|
147
|
+
};
|
|
148
|
+
type QuilttButtonProps<T extends ElementType> = PropsWithChildren<BaseQuilttButtonProps<T> & QuilttCallbackProps>;
|
|
149
|
+
declare const QuilttButton: <T extends ElementType = "button">({ as, connectorId, connectionId, institution, onEvent, onOpen, onLoad, onExit, onExitSuccess, onExitAbort, onExitError, onClick, onHtmlLoad, children, ...props }: QuilttButtonProps<T> & PropsOf<T>) => react_jsx_runtime.JSX.Element;
|
|
146
150
|
|
|
147
|
-
type QuilttContainerProps<T extends
|
|
151
|
+
type QuilttContainerProps<T extends ElementType> = PropsWithChildren<{
|
|
148
152
|
as?: T;
|
|
149
153
|
connectorId: string;
|
|
150
154
|
connectionId?: string;
|
|
@@ -153,6 +157,6 @@ type QuilttContainerProps<T extends AnyTag> = PropsWithChildren<{
|
|
|
153
157
|
* QuilttContainer uses globally shared callbacks. It's recommended you only use
|
|
154
158
|
* one Container at a time.
|
|
155
159
|
*/
|
|
156
|
-
declare const QuilttContainer: <T extends
|
|
160
|
+
declare const QuilttContainer: <T extends ElementType = "div">({ as, connectorId, connectionId, onEvent, onLoad, onExit, onExitSuccess, onExitAbort, onExitError, children, ...props }: QuilttContainerProps<T> & PropsOf<T>) => react_jsx_runtime.JSX.Element;
|
|
157
161
|
|
|
158
162
|
export { type AuthenticateSession, type IdentifySession, type ImportSession, QuilttAuthProvider, QuilttButton, QuilttContainer, QuilttProvider, QuilttSettings, QuilttSettingsProvider, type RevokeSession, type SetSession, type UseQuilttSession, useAuthenticateSession, useEventListener, useIdentifySession, useImportSession, useIsomorphicLayoutEffect, useQuilttClient, useQuilttConnector, useQuilttSession, useQuilttSettings, useRevokeSession, useSession, useStorage };
|
package/dist/index.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
export * from '@quiltt/core';
|
|
2
2
|
export { u as useEventListener } from './useEventListener-client-DVM5xwKY.js';
|
|
3
3
|
export { u as useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect-client-DeTHOKz1.js';
|
|
4
|
-
import { Q as QuilttAuthProvider } from './QuilttAuthProvider-client-
|
|
5
|
-
export { b as useAuthenticateSession, a as useIdentifySession, u as useImportSession, c as useRevokeSession } from './QuilttAuthProvider-client-
|
|
4
|
+
import { Q as QuilttAuthProvider } from './QuilttAuthProvider-client-D6Iok6TS.js';
|
|
5
|
+
export { b as useAuthenticateSession, a as useIdentifySession, u as useImportSession, c as useRevokeSession } from './QuilttAuthProvider-client-D6Iok6TS.js';
|
|
6
6
|
export { u as useQuilttClient } from './useQuilttClient-client-CAAUait1.js';
|
|
7
|
-
import { u as useQuilttConnector } from './useQuilttConnector-client-
|
|
8
|
-
export { u as useQuilttSession } from './useQuilttSession-client-
|
|
7
|
+
import { u as useQuilttConnector } from './useQuilttConnector-client-720xcXap.js';
|
|
8
|
+
export { u as useQuilttSession } from './useQuilttSession-client-KZRjdl5I.js';
|
|
9
9
|
export { Q as QuilttSettings, u as useQuilttSettings } from './useQuilttSettings-client-DU_Qfc8X.js';
|
|
10
10
|
export { u as useSession } from './useSession-client-CG5lGS9F.js';
|
|
11
11
|
export { u as useStorage } from './useStorage-client-B3keU-oI.js';
|
|
@@ -22,7 +22,7 @@ const QuilttProvider = ({ clientId, token, children })=>{
|
|
|
22
22
|
});
|
|
23
23
|
};
|
|
24
24
|
|
|
25
|
-
const QuilttButton = ({ as, connectorId, connectionId, institution, onEvent, onOpen, onLoad, onExit, onExitSuccess, onExitAbort, onExitError, children, ...props })=>{
|
|
25
|
+
const QuilttButton = ({ as, connectorId, connectionId, institution, onEvent, onOpen, onLoad, onExit, onExitSuccess, onExitAbort, onExitError, onClick, onHtmlLoad, children, ...props })=>{
|
|
26
26
|
const { open } = useQuilttConnector(connectorId, {
|
|
27
27
|
connectionId,
|
|
28
28
|
institution,
|
|
@@ -35,8 +35,18 @@ const QuilttButton = ({ as, connectorId, connectionId, institution, onEvent, onO
|
|
|
35
35
|
onExitError
|
|
36
36
|
});
|
|
37
37
|
const Button = as || 'button';
|
|
38
|
+
const handleClick = (event)=>{
|
|
39
|
+
// Call the user's onClick handler first to allow for:
|
|
40
|
+
// 1. Pre-open validation
|
|
41
|
+
// 2. Preventing opening via event.preventDefault()
|
|
42
|
+
// 3. Setting up state before connector opens
|
|
43
|
+
if (onClick) onClick(event);
|
|
44
|
+
// Then open the Quiltt connector
|
|
45
|
+
open();
|
|
46
|
+
};
|
|
38
47
|
return /*#__PURE__*/ jsx(Button, {
|
|
39
|
-
onClick:
|
|
48
|
+
onClick: handleClick,
|
|
49
|
+
onLoad: onHtmlLoad,
|
|
40
50
|
"quiltt-connection": connectionId,
|
|
41
51
|
...props,
|
|
42
52
|
children: children
|
package/dist/{useQuilttConnector-client-L80c55xy.js → useQuilttConnector-client-720xcXap.js}
RENAMED
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { useState, useEffect, useCallback } from 'react';
|
|
3
3
|
import { cdnBase } from '@quiltt/core';
|
|
4
|
-
import { u as useQuilttSession } from './useQuilttSession-client-
|
|
5
|
-
import { u as useScript } from './useScript-client-
|
|
4
|
+
import { u as useQuilttSession } from './useQuilttSession-client-KZRjdl5I.js';
|
|
5
|
+
import { u as useScript } from './useScript-client-CWRBlZBC.js';
|
|
6
6
|
|
|
7
|
-
var version = "3.9.
|
|
7
|
+
var version = "3.9.3";
|
|
8
8
|
|
|
9
9
|
const useQuilttConnector = (connectorId, options)=>{
|
|
10
10
|
const status = useScript(`${cdnBase}/v1/connector.js?agent=react-${version}`);
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
import { 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-client-
|
|
4
|
+
import { u as useImportSession, a as useIdentifySession, b as useAuthenticateSession, c as useRevokeSession } from './QuilttAuthProvider-client-D6Iok6TS.js';
|
|
5
5
|
import { u as useQuilttSettings } from './useQuilttSettings-client-DU_Qfc8X.js';
|
|
6
6
|
import { u as useSession } from './useSession-client-CG5lGS9F.js';
|
|
7
7
|
|
|
@@ -73,9 +73,6 @@ function useScript(src, options) {
|
|
|
73
73
|
scriptNode.removeEventListener('load', setStateFromEvent);
|
|
74
74
|
scriptNode.removeEventListener('error', setStateFromEvent);
|
|
75
75
|
}
|
|
76
|
-
if (scriptNode && options?.removeOnUnmount) {
|
|
77
|
-
scriptNode.remove();
|
|
78
|
-
}
|
|
79
76
|
};
|
|
80
77
|
}, [
|
|
81
78
|
src,
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@quiltt/react",
|
|
3
|
-
"version": "3.9.
|
|
4
|
-
"description": "React
|
|
3
|
+
"version": "3.9.3",
|
|
4
|
+
"description": "React Components and Hooks for Quiltt Connector",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"quiltt",
|
|
7
7
|
"quiltt-connector",
|
|
@@ -15,12 +15,10 @@
|
|
|
15
15
|
"directory": "packages/react"
|
|
16
16
|
},
|
|
17
17
|
"license": "MIT",
|
|
18
|
-
"
|
|
19
|
-
"
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
"./src/hooks/useStorage.ts"
|
|
23
|
-
],
|
|
18
|
+
"bugs": {
|
|
19
|
+
"url": "https://github.com/quiltt/quiltt-js/issues"
|
|
20
|
+
},
|
|
21
|
+
"sideEffects": [],
|
|
24
22
|
"type": "module",
|
|
25
23
|
"exports": {
|
|
26
24
|
".": {
|
|
@@ -34,25 +32,30 @@
|
|
|
34
32
|
"src/**",
|
|
35
33
|
"CHANGELOG.md"
|
|
36
34
|
],
|
|
35
|
+
"main": "dist/index.js",
|
|
37
36
|
"dependencies": {
|
|
38
|
-
"@apollo/client": "^3.
|
|
39
|
-
"@quiltt/core": "3.9.
|
|
37
|
+
"@apollo/client": "^3.12.4",
|
|
38
|
+
"@quiltt/core": "3.9.3"
|
|
40
39
|
},
|
|
41
40
|
"devDependencies": {
|
|
42
41
|
"@biomejs/biome": "1.9.4",
|
|
43
|
-
"@types/node": "22.10.
|
|
42
|
+
"@types/node": "22.10.5",
|
|
44
43
|
"@types/react": "18.3.12",
|
|
45
44
|
"@types/react-dom": "18.3.1",
|
|
46
|
-
"bunchee": "
|
|
45
|
+
"bunchee": "6.2.0",
|
|
47
46
|
"react": "18.3.1",
|
|
48
47
|
"react-dom": "18.3.1",
|
|
49
48
|
"rimraf": "6.0.1",
|
|
50
|
-
"typescript": "5.
|
|
49
|
+
"typescript": "5.7.3"
|
|
51
50
|
},
|
|
52
51
|
"peerDependencies": {
|
|
53
52
|
"react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0",
|
|
54
53
|
"react-dom": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0"
|
|
55
54
|
},
|
|
55
|
+
"tags": [
|
|
56
|
+
"quiltt",
|
|
57
|
+
"react"
|
|
58
|
+
],
|
|
56
59
|
"publishConfig": {
|
|
57
60
|
"access": "public"
|
|
58
61
|
},
|
|
@@ -1,20 +1,33 @@
|
|
|
1
|
-
import type { PropsWithChildren } from 'react'
|
|
1
|
+
import type { ElementType, MouseEvent, PropsWithChildren } from 'react'
|
|
2
2
|
|
|
3
3
|
import type { ConnectorSDKCallbacks } from '@quiltt/core'
|
|
4
4
|
|
|
5
5
|
import { useQuilttConnector } from '../hooks/useQuilttConnector'
|
|
6
|
-
import type {
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
import type { PropsOf } from '../types'
|
|
7
|
+
|
|
8
|
+
// Base button props without callback-specific properties
|
|
9
|
+
type BaseQuilttButtonProps<T extends ElementType> = {
|
|
10
|
+
as?: T
|
|
11
|
+
connectorId: string
|
|
12
|
+
connectionId?: string // For Reconnect Mode
|
|
13
|
+
institution?: string // For Connect Mode
|
|
14
|
+
// Override the native onClick handler
|
|
15
|
+
onClick?: (event: MouseEvent<HTMLElement>) => void
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Separate SDK callback types
|
|
19
|
+
type QuilttCallbackProps = Omit<ConnectorSDKCallbacks, 'onLoad'> & {
|
|
20
|
+
// Separate the SDK onLoad from the HTML onLoad to avoid conflicts
|
|
21
|
+
onLoad?: ConnectorSDKCallbacks['onLoad'] // Handles SDK initialization
|
|
22
|
+
onHtmlLoad?: React.ReactEventHandler<HTMLElement> // Handles DOM element load
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Combined type for the full component
|
|
26
|
+
type QuilttButtonProps<T extends ElementType> = PropsWithChildren<
|
|
27
|
+
BaseQuilttButtonProps<T> & QuilttCallbackProps
|
|
15
28
|
>
|
|
16
29
|
|
|
17
|
-
export const QuilttButton = <T extends
|
|
30
|
+
export const QuilttButton = <T extends ElementType = 'button'>({
|
|
18
31
|
as,
|
|
19
32
|
connectorId,
|
|
20
33
|
connectionId,
|
|
@@ -26,6 +39,8 @@ export const QuilttButton = <T extends AnyTag = 'button'>({
|
|
|
26
39
|
onExitSuccess,
|
|
27
40
|
onExitAbort,
|
|
28
41
|
onExitError,
|
|
42
|
+
onClick,
|
|
43
|
+
onHtmlLoad,
|
|
29
44
|
children,
|
|
30
45
|
...props
|
|
31
46
|
}: QuilttButtonProps<T> & PropsOf<T>) => {
|
|
@@ -43,8 +58,19 @@ export const QuilttButton = <T extends AnyTag = 'button'>({
|
|
|
43
58
|
|
|
44
59
|
const Button = as || 'button'
|
|
45
60
|
|
|
61
|
+
const handleClick = (event: MouseEvent<HTMLElement>) => {
|
|
62
|
+
// Call the user's onClick handler first to allow for:
|
|
63
|
+
// 1. Pre-open validation
|
|
64
|
+
// 2. Preventing opening via event.preventDefault()
|
|
65
|
+
// 3. Setting up state before connector opens
|
|
66
|
+
if (onClick) onClick(event)
|
|
67
|
+
|
|
68
|
+
// Then open the Quiltt connector
|
|
69
|
+
open()
|
|
70
|
+
}
|
|
71
|
+
|
|
46
72
|
return (
|
|
47
|
-
<Button onClick={
|
|
73
|
+
<Button onClick={handleClick} onLoad={onHtmlLoad} quiltt-connection={connectionId} {...props}>
|
|
48
74
|
{children}
|
|
49
75
|
</Button>
|
|
50
76
|
)
|
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import type { PropsWithChildren } from 'react'
|
|
1
|
+
import type { ElementType, PropsWithChildren } from 'react'
|
|
2
2
|
|
|
3
3
|
import type { ConnectorSDKCallbacks } from '@quiltt/core'
|
|
4
4
|
|
|
5
5
|
import { useQuilttConnector } from '../hooks/useQuilttConnector'
|
|
6
|
-
import type {
|
|
6
|
+
import type { PropsOf } from '../types'
|
|
7
7
|
|
|
8
|
-
type QuilttContainerProps<T extends
|
|
8
|
+
type QuilttContainerProps<T extends ElementType> = PropsWithChildren<
|
|
9
9
|
{
|
|
10
10
|
as?: T
|
|
11
11
|
connectorId: string
|
|
@@ -17,7 +17,7 @@ type QuilttContainerProps<T extends AnyTag> = PropsWithChildren<
|
|
|
17
17
|
* QuilttContainer uses globally shared callbacks. It's recommended you only use
|
|
18
18
|
* one Container at a time.
|
|
19
19
|
*/
|
|
20
|
-
export const QuilttContainer = <T extends
|
|
20
|
+
export const QuilttContainer = <T extends ElementType = 'div'>({
|
|
21
21
|
as,
|
|
22
22
|
connectorId,
|
|
23
23
|
connectionId,
|
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
'use client'
|
|
2
2
|
|
|
3
|
+
import { useEffect, useMemo, useRef } from 'react'
|
|
3
4
|
import type { FC, PropsWithChildren } from 'react'
|
|
4
|
-
import { useEffect, useMemo } from 'react'
|
|
5
5
|
|
|
6
6
|
import { ApolloProvider } from '@apollo/client/react/context/ApolloProvider.js'
|
|
7
|
-
|
|
8
7
|
import { InMemoryCache, QuilttClient } from '@quiltt/core'
|
|
9
8
|
|
|
10
|
-
import { useQuilttSession } from '
|
|
9
|
+
import { useQuilttSession } from '@/hooks'
|
|
10
|
+
import { isDeepEqual } from '@/utils'
|
|
11
11
|
|
|
12
12
|
type QuilttAuthProviderProps = PropsWithChildren & {
|
|
13
13
|
/** The Session token obtained from the server */
|
|
@@ -19,10 +19,10 @@ type QuilttAuthProviderProps = PropsWithChildren & {
|
|
|
19
19
|
* it into trusted storage. While this process is happening, the component is put
|
|
20
20
|
* into a loading state and the children are not rendered to prevent race conditions
|
|
21
21
|
* from triggering within the transitionary state.
|
|
22
|
-
*
|
|
23
22
|
*/
|
|
24
23
|
export const QuilttAuthProvider: FC<QuilttAuthProviderProps> = ({ token, children }) => {
|
|
25
24
|
const { session, importSession } = useQuilttSession()
|
|
25
|
+
const previousSessionRef = useRef(session)
|
|
26
26
|
|
|
27
27
|
// @todo: extract into a provider so it can accessed by child components
|
|
28
28
|
const graphQLClient = useMemo(
|
|
@@ -38,11 +38,13 @@ export const QuilttAuthProvider: FC<QuilttAuthProviderProps> = ({ token, childre
|
|
|
38
38
|
if (token) importSession(token)
|
|
39
39
|
}, [token, importSession])
|
|
40
40
|
|
|
41
|
-
// Reset Client Store when
|
|
42
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies: We should reset the store whenever the session changes
|
|
41
|
+
// Reset Client Store when session changes (using deep comparison)
|
|
43
42
|
useEffect(() => {
|
|
44
|
-
|
|
45
|
-
|
|
43
|
+
if (!isDeepEqual(session, previousSessionRef.current)) {
|
|
44
|
+
graphQLClient.resetStore()
|
|
45
|
+
previousSessionRef.current = session
|
|
46
|
+
}
|
|
47
|
+
}, [session, graphQLClient])
|
|
46
48
|
|
|
47
49
|
return <ApolloProvider client={graphQLClient}>{children}</ApolloProvider>
|
|
48
50
|
}
|
package/src/types.ts
CHANGED
|
@@ -1,6 +1,4 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
|
|
3
|
-
export type AnyTag = string | FC<any> | (new (props: any) => Component)
|
|
1
|
+
import type { ComponentType, JSX } from 'react'
|
|
4
2
|
|
|
5
3
|
export type PropsOf<Tag> = Tag extends keyof JSX.IntrinsicElements
|
|
6
4
|
? JSX.IntrinsicElements[Tag]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './isDeepEqual'
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Performs a deep equality comparison between two values
|
|
3
|
+
*
|
|
4
|
+
* This function recursively compares all properties to determine if they are equal.
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```ts
|
|
8
|
+
* isDeepEqual({ a: 1, b: { c: 2 } }, { a: 1, b: { c: 2 } }) // true
|
|
9
|
+
* isDeepEqual({ a: 1, b: { c: 2 } }, { a: 1, b: { c: 3 } }) // false
|
|
10
|
+
* ```
|
|
11
|
+
*/
|
|
12
|
+
export const isDeepEqual = (obj1: unknown, obj2: unknown): boolean => {
|
|
13
|
+
// Handle primitive types and null/undefined
|
|
14
|
+
if (obj1 === obj2) return true
|
|
15
|
+
if (obj1 === null || obj2 === null || typeof obj1 !== 'object' || typeof obj2 !== 'object')
|
|
16
|
+
return false
|
|
17
|
+
|
|
18
|
+
// Handle special object types
|
|
19
|
+
if (obj1 instanceof Date && obj2 instanceof Date) {
|
|
20
|
+
return obj1.getTime() === obj2.getTime()
|
|
21
|
+
}
|
|
22
|
+
if (obj1 instanceof RegExp && obj2 instanceof RegExp) {
|
|
23
|
+
return obj1.toString() === obj2.toString()
|
|
24
|
+
}
|
|
25
|
+
if (obj1 instanceof Map && obj2 instanceof Map) {
|
|
26
|
+
if (obj1.size !== obj2.size) return false
|
|
27
|
+
for (const [key, value] of obj1) {
|
|
28
|
+
if (!obj2.has(key) || !isDeepEqual(value, obj2.get(key))) return false
|
|
29
|
+
}
|
|
30
|
+
return true
|
|
31
|
+
}
|
|
32
|
+
if (obj1 instanceof Set && obj2 instanceof Set) {
|
|
33
|
+
if (obj1.size !== obj2.size) return false
|
|
34
|
+
for (const item of obj1) {
|
|
35
|
+
if (!Array.from(obj2).some((value) => isDeepEqual(item, value))) return false
|
|
36
|
+
}
|
|
37
|
+
return true
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
// Handle arrays
|
|
41
|
+
if (Array.isArray(obj1) && Array.isArray(obj2)) {
|
|
42
|
+
if (obj1.length !== obj2.length) return false
|
|
43
|
+
return obj1.every((value, index) => isDeepEqual(value, obj2[index]))
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// If one is array and other isn't, they're not equal
|
|
47
|
+
if (Array.isArray(obj1) || Array.isArray(obj2)) return false
|
|
48
|
+
|
|
49
|
+
const keys1 = Object.keys(obj1)
|
|
50
|
+
const keys2 = Object.keys(obj2)
|
|
51
|
+
|
|
52
|
+
if (keys1.length !== keys2.length) return false
|
|
53
|
+
|
|
54
|
+
return keys1.every((key) => {
|
|
55
|
+
return (
|
|
56
|
+
Object.prototype.hasOwnProperty.call(obj2, key) &&
|
|
57
|
+
isDeepEqual((obj1 as any)[key], (obj2 as any)[key])
|
|
58
|
+
)
|
|
59
|
+
})
|
|
60
|
+
}
|