@quiltt/react 4.3.0 → 4.3.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 CHANGED
@@ -1,5 +1,14 @@
1
1
  # @quiltt/react
2
2
 
3
+ ## 4.3.1
4
+
5
+ ### Patch Changes
6
+
7
+ - [#366](https://github.com/quiltt/quiltt-js/pull/366) [`dc376b5`](https://github.com/quiltt/quiltt-js/commit/dc376b52dd824d7867ca74677bbfd5c54eff5cdc) Thanks [@sirwolfgang](https://github.com/sirwolfgang)! - Warn if useQuilttConnector is unmounted while in use
8
+
9
+ - Updated dependencies [[`dc376b5`](https://github.com/quiltt/quiltt-js/commit/dc376b52dd824d7867ca74677bbfd5c54eff5cdc)]:
10
+ - @quiltt/core@4.3.1
11
+
3
12
  ## 4.3.0
4
13
 
5
14
  ### Minor Changes
@@ -7,7 +7,7 @@ import './useSession-12s-7GOn4sUn.js';
7
7
  import 'use-debounce';
8
8
  import { jsx } from 'react/jsx-runtime';
9
9
  import { ApolloProvider } from '@apollo/client/react/context/ApolloProvider.js';
10
- import { u as useQuilttSession } from './useQuilttSession-12s-QlPCR8A-.js';
10
+ import { u as useQuilttSession } from './useQuilttSession-12s-CM6ALGSN.js';
11
11
 
12
12
  const useAuthenticateSession = (auth, setSession)=>{
13
13
  const authenticateSession = useCallback(async (payload, callbacks)=>{
@@ -158,13 +158,15 @@ const useRevokeSession = (auth, session, setSession)=>{
158
158
  * it into trusted storage. While this process is happening, the component is put
159
159
  * into a loading state and the children are not rendered to prevent race conditions
160
160
  * from triggering within the transitionary state.
161
- */ const QuilttAuthProvider = ({ token, children })=>{
161
+ */ const QuilttAuthProvider = ({ graphqlClient, token, children })=>{
162
162
  const { session, importSession } = useQuilttSession();
163
163
  const previousSessionRef = useRef(session);
164
- // @todo: extract into a provider so it can accessed by child components
165
- const graphQLClient = useMemo(()=>new QuilttClient({
164
+ // Memoize the client to avoid unnecessary re-renders
165
+ const apolloClient = useMemo(()=>graphqlClient || new QuilttClient({
166
166
  cache: new InMemoryCache()
167
- }), []);
167
+ }), [
168
+ graphqlClient
169
+ ]);
168
170
  // Import passed in token
169
171
  useEffect(()=>{
170
172
  if (token) importSession(token);
@@ -175,15 +177,15 @@ const useRevokeSession = (auth, session, setSession)=>{
175
177
  // Reset Client Store when session changes (using deep comparison)
176
178
  useEffect(()=>{
177
179
  if (!isDeepEqual(session, previousSessionRef.current)) {
178
- graphQLClient.resetStore();
180
+ apolloClient.resetStore();
179
181
  previousSessionRef.current = session;
180
182
  }
181
183
  }, [
182
184
  session,
183
- graphQLClient
185
+ apolloClient
184
186
  ]);
185
187
  return /*#__PURE__*/ jsx(ApolloProvider, {
186
- client: graphQLClient,
188
+ client: apolloClient,
187
189
  children: children
188
190
  });
189
191
  };
package/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ConnectorSDKCallbacks, Maybe, QuilttJWT, PasscodePayload, UnprocessableData, AuthAPI, UsernamePayload, ConnectorSDKConnectorOptions, InstitutionsData } from '@quiltt/core';
1
+ import { ConnectorSDKCallbacks, Maybe, QuilttJWT, PasscodePayload, UnprocessableData, AuthAPI, UsernamePayload, ConnectorSDKConnectorOptions, InstitutionsData, QuilttClient } from '@quiltt/core';
2
2
  export * from '@quiltt/core';
3
3
  import * as react_jsx_runtime from 'react/jsx-runtime';
4
4
  import { JSX, ComponentType, ElementType, PropsWithChildren, MouseEvent, RefObject, useEffect, Dispatch, SetStateAction, FC } from 'react';
@@ -159,7 +159,9 @@ declare const useQuilttSettings: () => {
159
159
  declare const useStorage: <T>(key: string, initialState?: Maybe<T>) => [Maybe<T> | undefined, Dispatch<SetStateAction<Maybe<T> | undefined>>];
160
160
 
161
161
  type QuilttAuthProviderProps = PropsWithChildren & {
162
- /** The Session token obtained from the server */
162
+ /** A custom QuilttClient instance to use instead of the default */
163
+ graphqlClient?: QuilttClient;
164
+ /** The Quiltt Session token obtained from the server */
163
165
  token?: string;
164
166
  };
165
167
  /**
@@ -170,19 +172,14 @@ type QuilttAuthProviderProps = PropsWithChildren & {
170
172
  */
171
173
  declare const QuilttAuthProvider: FC<QuilttAuthProviderProps>;
172
174
 
173
- type QuilttProviderProps = PropsWithChildren & {
174
- /** The client ID for the client-side Auth API */
175
- clientId?: string;
176
- /** The Session token obtained from the server */
177
- token?: string;
178
- };
179
- declare const QuilttProvider: FC<QuilttProviderProps>;
180
-
181
175
  type QuilttSettingsProviderProps = PropsWithChildren & {
182
- /** The Client ID to use for the client-side Auth API */
176
+ /** The Client ID to use for the passwordless Auth API */
183
177
  clientId?: string;
184
178
  };
185
179
  declare const QuilttSettingsProvider: FC<QuilttSettingsProviderProps>;
186
180
 
181
+ type QuilttProviderProps = QuilttSettingsProviderProps & QuilttAuthProviderProps;
182
+ declare const QuilttProvider: FC<QuilttProviderProps>;
183
+
187
184
  export { QuilttAuthProvider, QuilttButton, QuilttContainer, QuilttProvider, QuilttSettingsProvider, useAuthenticateSession, useEventListener, useIdentifySession, useImportSession, useIsomorphicLayoutEffect, useQuilttClient, useQuilttConnector, useQuilttInstitutions, useQuilttSession, useQuilttSettings, useRevokeSession, useSession, useStorage };
188
- export type { AuthenticateSession, IdentifySession, ImportSession, RevokeSession, SetSession, UseQuilttInstitutions, UseQuilttSession };
185
+ export type { AuthenticateSession, IdentifySession, ImportSession, QuilttAuthProviderProps, QuilttSettingsProviderProps, RevokeSession, SetSession, UseQuilttInstitutions, UseQuilttSession };
package/dist/index.js CHANGED
@@ -1,14 +1,14 @@
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-BaTUXD56.js';
5
- import { i as isDeepEqual, Q as QuilttAuthProvider } from './QuilttAuthProvider-12s-DNnAmo8E.js';
6
- export { b as useAuthenticateSession, a as useIdentifySession, u as useImportSession, c as useRevokeSession } from './QuilttAuthProvider-12s-DNnAmo8E.js';
4
+ import { u as useQuilttConnector } from './useQuilttConnector-12s-CL6uHdqe.js';
5
+ import { i as isDeepEqual, Q as QuilttAuthProvider } from './QuilttAuthProvider-12s-BZuGySu0.js';
6
+ export { b as useAuthenticateSession, a as useIdentifySession, u as useImportSession, c as useRevokeSession } from './QuilttAuthProvider-12s-BZuGySu0.js';
7
7
  export { u as useEventListener } from './useEventListener-12s-D_-6QIXa.js';
8
8
  export { u as useIsomorphicLayoutEffect } from './useIsomorphicLayoutEffect-12s-DeTHOKz1.js';
9
9
  export { u as useQuilttClient } from './useQuilttClient-12s-CAAUait1.js';
10
- export { u as useQuilttInstitutions } from './useQuilttInstitutions-12s-Djd5k9Ae.js';
11
- export { u as useQuilttSession } from './useQuilttSession-12s-QlPCR8A-.js';
10
+ export { u as useQuilttInstitutions } from './useQuilttInstitutions-12s-FLdw-CQZ.js';
11
+ export { u as useQuilttSession } from './useQuilttSession-12s-CM6ALGSN.js';
12
12
  export { u as useQuilttSettings } from './useQuilttSettings-12s--rCJoNHD.js';
13
13
  export { u as useSession } from './useSession-12s-7GOn4sUn.js';
14
14
  export { u as useStorage } from './useStorage-12s-DHcq3Kuh.js';
@@ -159,11 +159,12 @@ import { Q as QuilttSettingsProvider } from './QuilttSettingsProvider-12s-ZcmFmO
159
159
  }, containerKey);
160
160
  };
161
161
 
162
- const QuilttProvider = ({ clientId, token, children })=>{
162
+ const QuilttProvider = ({ clientId, graphqlClient, token, children })=>{
163
163
  return /*#__PURE__*/ jsx(QuilttSettingsProvider, {
164
164
  clientId: clientId,
165
165
  children: /*#__PURE__*/ jsx(QuilttAuthProvider, {
166
166
  token: token,
167
+ graphqlClient: graphqlClient,
167
168
  children: children
168
169
  })
169
170
  });
@@ -1,10 +1,10 @@
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-QlPCR8A-.js';
4
+ import { u as useQuilttSession } from './useQuilttSession-12s-CM6ALGSN.js';
5
5
  import { u as useScript } from './useScript-12s-JCgaTW9n.js';
6
6
 
7
- var version = "4.3.0";
7
+ var version = "4.3.1";
8
8
 
9
9
  const useQuilttConnector = (connectorId, options)=>{
10
10
  const status = useScript(`${cdnBase}/v1/connector.js?agent=react-${version}`, {
@@ -19,6 +19,8 @@ const useQuilttConnector = (connectorId, options)=>{
19
19
  const prevConnectionIdRef = useRef(options?.connectionId);
20
20
  const prevConnectorIdRef = useRef(connectorId);
21
21
  const connectorCreatedRef = useRef(false);
22
+ // Track whether the connector is currently open
23
+ const isConnectorOpenRef = useRef(false);
22
24
  // Set Session
23
25
  // biome-ignore lint/correctness/useExhaustiveDependencies: trigger effects when script status changes too
24
26
  useEffect(()=>{
@@ -62,14 +64,27 @@ const useQuilttConnector = (connectorId, options)=>{
62
64
  options?.institution,
63
65
  status
64
66
  ]);
67
+ // Internal handlers to track connector state
68
+ const handleOpen = useCallback((metadata)=>{
69
+ isConnectorOpenRef.current = true;
70
+ options?.onOpen?.(metadata);
71
+ }, [
72
+ options?.onOpen
73
+ ]);
74
+ const handleExit = useCallback((type, metadata)=>{
75
+ isConnectorOpenRef.current = false;
76
+ options?.onExit?.(type, metadata);
77
+ }, [
78
+ options?.onExit
79
+ ]);
65
80
  // Register event handlers
66
81
  useEffect(()=>{
67
82
  if (!connector) return;
68
83
  const handlers = {
69
84
  onEvent: options?.onEvent,
70
- onOpen: options?.onOpen,
85
+ onOpen: handleOpen,
71
86
  onLoad: options?.onLoad,
72
- onExit: options?.onExit,
87
+ onExit: handleExit,
73
88
  onExitSuccess: options?.onExitSuccess,
74
89
  onExitAbort: options?.onExitAbort,
75
90
  onExitError: options?.onExitError
@@ -93,9 +108,9 @@ const useQuilttConnector = (connectorId, options)=>{
93
108
  }, [
94
109
  connector,
95
110
  options?.onEvent,
96
- options?.onOpen,
111
+ handleOpen,
97
112
  options?.onLoad,
98
- options?.onExit,
113
+ handleExit,
99
114
  options?.onExitSuccess,
100
115
  options?.onExitAbort,
101
116
  options?.onExitError
@@ -111,6 +126,14 @@ const useQuilttConnector = (connectorId, options)=>{
111
126
  connector,
112
127
  isOpening
113
128
  ]);
129
+ // Cleanup effect - runs when the hook is torn down
130
+ useEffect(()=>{
131
+ return ()=>{
132
+ if (isConnectorOpenRef.current) {
133
+ console.error('[Quiltt] useQuilttConnector: Component unmounted while Connector is still open. ' + 'This may lead to memory leaks or unexpected behavior. ' + 'Ensure the Connector is properly closed before component unmount.');
134
+ }
135
+ };
136
+ }, []);
114
137
  const open = useCallback(()=>{
115
138
  if (connectorId) {
116
139
  setIsOpening(true);
@@ -2,7 +2,7 @@
2
2
  import { useMemo, useState, useCallback, useEffect } from 'react';
3
3
  import { InstitutionsAPI } from '@quiltt/core';
4
4
  import { useDebounce } from 'use-debounce';
5
- import { v as version } from './useQuilttConnector-12s-BaTUXD56.js';
5
+ import { v as version } from './useQuilttConnector-12s-CL6uHdqe.js';
6
6
  import { u as useSession } from './useSession-12s-7GOn4sUn.js';
7
7
 
8
8
  const useQuilttInstitutions = (connectorId, onErrorCallback)=>{
@@ -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-12s-DNnAmo8E.js';
4
+ import { u as useImportSession, a as useIdentifySession, b as useAuthenticateSession, c as useRevokeSession } from './QuilttAuthProvider-12s-BZuGySu0.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
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quiltt/react",
3
- "version": "4.3.0",
3
+ "version": "4.3.1",
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.3.0"
39
+ "@quiltt/core": "4.3.1"
40
40
  },
41
41
  "devDependencies": {
42
42
  "@biomejs/biome": "2.2.4",
@@ -35,6 +35,9 @@ export const useQuilttConnector = (
35
35
  const prevConnectorIdRef = useRef<string | undefined>(connectorId)
36
36
  const connectorCreatedRef = useRef<boolean>(false)
37
37
 
38
+ // Track whether the connector is currently open
39
+ const isConnectorOpenRef = useRef<boolean>(false)
40
+
38
41
  // Set Session
39
42
  // biome-ignore lint/correctness/useExhaustiveDependencies: trigger effects when script status changes too
40
43
  useEffect(() => {
@@ -73,15 +76,32 @@ export const useQuilttConnector = (
73
76
  }
74
77
  }, [connectorId, options?.connectionId, options?.institution, status])
75
78
 
79
+ // Internal handlers to track connector state
80
+ const handleOpen = useCallback(
81
+ (metadata: any) => {
82
+ isConnectorOpenRef.current = true
83
+ options?.onOpen?.(metadata)
84
+ },
85
+ [options?.onOpen]
86
+ )
87
+
88
+ const handleExit = useCallback(
89
+ (type: any, metadata: any) => {
90
+ isConnectorOpenRef.current = false
91
+ options?.onExit?.(type, metadata)
92
+ },
93
+ [options?.onExit]
94
+ )
95
+
76
96
  // Register event handlers
77
97
  useEffect(() => {
78
98
  if (!connector) return
79
99
 
80
100
  const handlers = {
81
101
  onEvent: options?.onEvent,
82
- onOpen: options?.onOpen,
102
+ onOpen: handleOpen,
83
103
  onLoad: options?.onLoad,
84
- onExit: options?.onExit,
104
+ onExit: handleExit,
85
105
  onExitSuccess: options?.onExitSuccess,
86
106
  onExitAbort: options?.onExitAbort,
87
107
  onExitError: options?.onExitError,
@@ -107,9 +127,9 @@ export const useQuilttConnector = (
107
127
  }, [
108
128
  connector,
109
129
  options?.onEvent,
110
- options?.onOpen,
130
+ handleOpen,
111
131
  options?.onLoad,
112
- options?.onExit,
132
+ handleExit,
113
133
  options?.onExitSuccess,
114
134
  options?.onExitAbort,
115
135
  options?.onExitError,
@@ -124,6 +144,19 @@ export const useQuilttConnector = (
124
144
  }
125
145
  }, [connector, isOpening])
126
146
 
147
+ // Cleanup effect - runs when the hook is torn down
148
+ useEffect(() => {
149
+ return () => {
150
+ if (isConnectorOpenRef.current) {
151
+ console.error(
152
+ '[Quiltt] useQuilttConnector: Component unmounted while Connector is still open. ' +
153
+ 'This may lead to memory leaks or unexpected behavior. ' +
154
+ 'Ensure the Connector is properly closed before component unmount.'
155
+ )
156
+ }
157
+ }
158
+ }, [])
159
+
127
160
  const open = useCallback(() => {
128
161
  if (connectorId) {
129
162
  setIsOpening(true)
@@ -9,8 +9,10 @@ import { InMemoryCache, QuilttClient } from '@quiltt/core'
9
9
  import { useQuilttSession } from '@/hooks'
10
10
  import { isDeepEqual } from '@/utils'
11
11
 
12
- type QuilttAuthProviderProps = PropsWithChildren & {
13
- /** The Session token obtained from the server */
12
+ export type QuilttAuthProviderProps = PropsWithChildren & {
13
+ /** A custom QuilttClient instance to use instead of the default */
14
+ graphqlClient?: QuilttClient
15
+ /** The Quiltt Session token obtained from the server */
14
16
  token?: string
15
17
  }
16
18
 
@@ -20,17 +22,22 @@ type QuilttAuthProviderProps = PropsWithChildren & {
20
22
  * into a loading state and the children are not rendered to prevent race conditions
21
23
  * from triggering within the transitionary state.
22
24
  */
23
- export const QuilttAuthProvider: FC<QuilttAuthProviderProps> = ({ token, children }) => {
25
+ export const QuilttAuthProvider: FC<QuilttAuthProviderProps> = ({
26
+ graphqlClient,
27
+ token,
28
+ children,
29
+ }) => {
24
30
  const { session, importSession } = useQuilttSession()
25
31
  const previousSessionRef = useRef(session)
26
32
 
27
- // @todo: extract into a provider so it can accessed by child components
28
- const graphQLClient = useMemo(
33
+ // Memoize the client to avoid unnecessary re-renders
34
+ const apolloClient = useMemo(
29
35
  () =>
36
+ graphqlClient ||
30
37
  new QuilttClient({
31
38
  cache: new InMemoryCache(),
32
39
  }),
33
- []
40
+ [graphqlClient]
34
41
  )
35
42
 
36
43
  // Import passed in token
@@ -41,12 +48,12 @@ export const QuilttAuthProvider: FC<QuilttAuthProviderProps> = ({ token, childre
41
48
  // Reset Client Store when session changes (using deep comparison)
42
49
  useEffect(() => {
43
50
  if (!isDeepEqual(session, previousSessionRef.current)) {
44
- graphQLClient.resetStore()
51
+ apolloClient.resetStore()
45
52
  previousSessionRef.current = session
46
53
  }
47
- }, [session, graphQLClient])
54
+ }, [session, apolloClient])
48
55
 
49
- return <ApolloProvider client={graphQLClient}>{children}</ApolloProvider>
56
+ return <ApolloProvider client={apolloClient}>{children}</ApolloProvider>
50
57
  }
51
58
 
52
59
  export default QuilttAuthProvider
@@ -1,19 +1,23 @@
1
- import type { FC, PropsWithChildren } from 'react'
1
+ import type { FC } from 'react'
2
2
 
3
+ import type { QuilttAuthProviderProps } from './QuilttAuthProvider'
3
4
  import { QuilttAuthProvider } from './QuilttAuthProvider'
5
+ import type { QuilttSettingsProviderProps } from './QuilttSettingsProvider'
4
6
  import { QuilttSettingsProvider } from './QuilttSettingsProvider'
5
7
 
6
- type QuilttProviderProps = PropsWithChildren & {
7
- /** The client ID for the client-side Auth API */
8
- clientId?: string
9
- /** The Session token obtained from the server */
10
- token?: string
11
- }
8
+ type QuilttProviderProps = QuilttSettingsProviderProps & QuilttAuthProviderProps
12
9
 
13
- export const QuilttProvider: FC<QuilttProviderProps> = ({ clientId, token, children }) => {
10
+ export const QuilttProvider: FC<QuilttProviderProps> = ({
11
+ clientId,
12
+ graphqlClient,
13
+ token,
14
+ children,
15
+ }) => {
14
16
  return (
15
17
  <QuilttSettingsProvider clientId={clientId}>
16
- <QuilttAuthProvider token={token}>{children}</QuilttAuthProvider>
18
+ <QuilttAuthProvider token={token} graphqlClient={graphqlClient}>
19
+ {children}
20
+ </QuilttAuthProvider>
17
21
  </QuilttSettingsProvider>
18
22
  )
19
23
  }
@@ -5,8 +5,8 @@ import { useState } from 'react'
5
5
 
6
6
  import { QuilttSettings } from '@/contexts/QuilttSettings'
7
7
 
8
- type QuilttSettingsProviderProps = PropsWithChildren & {
9
- /** The Client ID to use for the client-side Auth API */
8
+ export type QuilttSettingsProviderProps = PropsWithChildren & {
9
+ /** The Client ID to use for the passwordless Auth API */
10
10
  clientId?: string
11
11
  }
12
12