@quiltt/react-native 4.2.2 → 4.3.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 CHANGED
@@ -1,5 +1,27 @@
1
1
  # @quiltt/react-native
2
2
 
3
+ ## 4.3.0
4
+
5
+ ### Minor Changes
6
+
7
+ - [#363](https://github.com/quiltt/quiltt-js/pull/363) [`641d766`](https://github.com/quiltt/quiltt-js/commit/641d76620ffbb99bc80fdc9998ac936883fe1d06) Thanks [@zubairaziz](https://github.com/zubairaziz)! - Upgrade rails/actioncable to v8
8
+
9
+ ### Patch Changes
10
+
11
+ - Updated dependencies [[`641d766`](https://github.com/quiltt/quiltt-js/commit/641d76620ffbb99bc80fdc9998ac936883fe1d06)]:
12
+ - @quiltt/react@4.3.0
13
+ - @quiltt/core@4.3.0
14
+
15
+ ## 4.2.3
16
+
17
+ ### Patch Changes
18
+
19
+ - [#355](https://github.com/quiltt/quiltt-js/pull/355) [`6d32f3e`](https://github.com/quiltt/quiltt-js/commit/6d32f3e40e7554c512ca63ef532d689d5485e10c) Thanks [@rubendinho](https://github.com/rubendinho)! - Improve error handling in React Native
20
+
21
+ - Updated dependencies [[`6d32f3e`](https://github.com/quiltt/quiltt-js/commit/6d32f3e40e7554c512ca63ef532d689d5485e10c)]:
22
+ - @quiltt/core@4.2.3
23
+ - @quiltt/react@4.2.3
24
+
3
25
  ## 4.2.2
4
26
 
5
27
  ### Patch Changes
package/dist/index.d.ts CHANGED
@@ -14,14 +14,14 @@ declare const checkConnectorUrl: (connectorUrl: string, retryCount?: number) =>
14
14
  */
15
15
  declare const handleOAuthUrl: (oauthUrl: URL | string | null | undefined) => void;
16
16
  type QuilttConnectorProps = {
17
- testId?: string;
18
17
  connectorId: string;
19
18
  connectionId?: string;
20
19
  institution?: string;
21
20
  oauthRedirectUrl: string;
21
+ testId?: string;
22
22
  } & ConnectorSDKCallbacks;
23
23
  declare const QuilttConnector: {
24
- ({ testId, connectorId, connectionId, institution, oauthRedirectUrl, onEvent, onLoad, onExit, onExitSuccess, onExitAbort, onExitError, }: QuilttConnectorProps): react_jsx_runtime.JSX.Element;
24
+ ({ connectorId, connectionId, institution, oauthRedirectUrl, onEvent, onLoad, onExit, onExitSuccess, onExitAbort, onExitError, testId, }: QuilttConnectorProps): react_jsx_runtime.JSX.Element;
25
25
  displayName: string;
26
26
  };
27
27
 
package/dist/index.js CHANGED
@@ -9,7 +9,7 @@ import { URL } from 'react-native-url-polyfill';
9
9
  import { WebView } from 'react-native-webview';
10
10
  import { generateStackTrace, makeBacktrace, getCauses } from '@honeybadger-io/core/build/src/util';
11
11
 
12
- var version = "4.2.2";
12
+ var version = "4.3.0";
13
13
 
14
14
  // Custom Error Reporter to avoid hooking into or colliding with a client's Honeybadger singleton
15
15
  const notifier = {
@@ -91,8 +91,8 @@ class ErrorReporter {
91
91
  }
92
92
 
93
93
  const getErrorMessage = (responseStatus, error)=>{
94
- if (error) return `An error occurred while checking the connector URL: ${error?.name} \n${error?.message}`;
95
- return responseStatus ? `The URL is not routable. Response status: ${responseStatus}` : 'An error occurred while checking the connector URL';
94
+ if (error) return `An error occurred while checking the Connector URL: ${error?.name} \n${error?.message}`;
95
+ return responseStatus ? `An error occurred loading the Connector. Response status: ${responseStatus}` : 'An error occurred while checking the Connector URL';
96
96
  };
97
97
 
98
98
  /**
@@ -256,34 +256,61 @@ const LoadingScreen = ({ testId })=>/*#__PURE__*/ jsx(AndroidSafeAreaView, {
256
256
 
257
257
  const errorReporter = new ErrorReporter(`${Platform.OS} ${Platform.Version}`);
258
258
  const PREFLIGHT_RETRY_COUNT = 3;
259
+ const parseMetadata = (url, connectorId)=>{
260
+ const metadata = {
261
+ connectorId: url.searchParams.get('connectorId') ?? connectorId
262
+ };
263
+ const profileId = url.searchParams.get('profileId');
264
+ if (profileId) metadata.profileId = profileId;
265
+ const connectionId = url.searchParams.get('connectionId');
266
+ if (connectionId) metadata.connectionId = connectionId;
267
+ const connectorSessionId = url.searchParams.get('connectorSession');
268
+ if (connectorSessionId) metadata.connectorSession = {
269
+ id: connectorSessionId
270
+ };
271
+ return metadata;
272
+ };
259
273
  const checkConnectorUrl = async (connectorUrl, retryCount = 0)=>{
260
274
  let responseStatus;
261
- let error;
262
275
  try {
263
276
  const response = await fetch(connectorUrl);
264
- if (!response.ok) {
265
- responseStatus = response.status;
266
- throw new Error('Connector URL is not routable.');
277
+ switch(response.status){
278
+ case 200:
279
+ return {
280
+ checked: true
281
+ };
282
+ case 400:
283
+ console.log('Invalid configuration');
284
+ return {
285
+ checked: true
286
+ };
287
+ case 404:
288
+ console.error('Connector not found');
289
+ return {
290
+ checked: true
291
+ };
292
+ default:
293
+ throw new Error('Connector URL is not routable.');
267
294
  }
268
- return {
269
- checked: true
270
- };
271
- } catch (e) {
272
- error = e;
273
- console.error('Failed to connect to connector URL');
295
+ } catch (error) {
296
+ // Log error for debugging
297
+ console.error(error);
298
+ // Try again
274
299
  if (retryCount < PREFLIGHT_RETRY_COUNT) {
275
300
  const delay = 50 * 2 ** retryCount;
276
301
  await new Promise((resolve)=>setTimeout(resolve, delay));
277
302
  console.log(`Retrying connection... Attempt ${retryCount + 1}`);
278
303
  return checkConnectorUrl(connectorUrl, retryCount + 1);
279
304
  }
305
+ // Report error after retries exhausted
280
306
  const errorMessage = getErrorMessage(responseStatus, error);
281
307
  const errorToSend = error || new Error(errorMessage);
282
308
  const context = {
283
309
  connectorUrl,
284
310
  responseStatus
285
311
  };
286
- if (responseStatus !== 404) await errorReporter.notify(errorToSend, context);
312
+ await errorReporter.notify(errorToSend, context);
313
+ // Return errored preflight check
287
314
  return {
288
315
  checked: true,
289
316
  error: errorMessage
@@ -308,7 +335,7 @@ const checkConnectorUrl = async (connectorUrl, retryCount = 0)=>{
308
335
  const normalizedUrl = normalizeUrlEncoding(urlString);
309
336
  // Open the normalized URL
310
337
  Linking.openURL(normalizedUrl);
311
- } catch (error) {
338
+ } catch (_error) {
312
339
  console.error('OAuth URL handling error');
313
340
  // Only try the fallback if oauthUrl is not null
314
341
  if (oauthUrl != null) {
@@ -316,13 +343,13 @@ const checkConnectorUrl = async (connectorUrl, retryCount = 0)=>{
316
343
  const fallbackUrl = typeof oauthUrl === 'string' ? oauthUrl : oauthUrl.toString();
317
344
  console.log('Attempting fallback OAuth opening');
318
345
  Linking.openURL(fallbackUrl);
319
- } catch (fallbackError) {
346
+ } catch (_fallbackError) {
320
347
  console.error('Fallback OAuth opening failed');
321
348
  }
322
349
  }
323
350
  }
324
351
  };
325
- const QuilttConnector = ({ testId, connectorId, connectionId, institution, oauthRedirectUrl, onEvent, onLoad, onExit, onExitSuccess, onExitAbort, onExitError })=>{
352
+ const QuilttConnector = ({ connectorId, connectionId, institution, oauthRedirectUrl, onEvent, onLoad, onExit, onExitSuccess, onExitAbort, onExitError, testId })=>{
326
353
  const webViewRef = useRef(null);
327
354
  const { session } = useQuilttSession();
328
355
  const [preFlightCheck, setPreFlightCheck] = useState({
@@ -418,7 +445,7 @@ const QuilttConnector = ({ testId, connectorId, connectionId, institution, oauth
418
445
  const handleQuilttEvent = useCallback((url)=>{
419
446
  url.searchParams.delete('source');
420
447
  url.searchParams.append('connectorId', connectorId);
421
- const metadata = Object.fromEntries(url.searchParams);
448
+ const metadata = parseMetadata(url, connectorId);
422
449
  requestAnimationFrame(()=>{
423
450
  const eventType = url.host;
424
451
  switch(eventType){
@@ -461,7 +488,7 @@ const QuilttConnector = ({ testId, connectorId, connectionId, institution, oauth
461
488
  try {
462
489
  const decodedUrl = decodeURIComponent(navigateUrl);
463
490
  handleOAuthUrl(decodedUrl);
464
- } catch (error) {
491
+ } catch (_error) {
465
492
  console.error('Navigate URL decoding failed, using original');
466
493
  handleOAuthUrl(navigateUrl);
467
494
  }
@@ -496,7 +523,9 @@ const QuilttConnector = ({ testId, connectorId, connectionId, institution, oauth
496
523
  handleQuilttEvent(url);
497
524
  return false;
498
525
  }
499
- if (shouldRender(url)) return true;
526
+ if (shouldRender(url)) {
527
+ return true;
528
+ }
500
529
  // Plaid set oauth url by doing window.location.href = url
501
530
  // So we use `handleOAuthUrl` as a catch all and assume all url got to this step is Plaid OAuth url
502
531
  handleOAuthUrl(url);
@@ -506,9 +535,11 @@ const QuilttConnector = ({ testId, connectorId, connectionId, institution, oauth
506
535
  isQuilttEvent,
507
536
  shouldRender
508
537
  ]);
509
- if (!preFlightCheck.checked) return /*#__PURE__*/ jsx(LoadingScreen, {
510
- testId: "loading-screen"
511
- });
538
+ if (!preFlightCheck.checked) {
539
+ return /*#__PURE__*/ jsx(LoadingScreen, {
540
+ testId: "loading-screen"
541
+ });
542
+ }
512
543
  if (preFlightCheck.error) {
513
544
  return /*#__PURE__*/ jsx(ErrorScreen, {
514
545
  testId: "error-screen",
@@ -521,10 +552,8 @@ const QuilttConnector = ({ testId, connectorId, connectionId, institution, oauth
521
552
  return /*#__PURE__*/ jsx(AndroidSafeAreaView, {
522
553
  testId: testId,
523
554
  children: /*#__PURE__*/ jsx(WebView, {
524
- testID: "webview",
525
555
  ref: webViewRef,
526
556
  // Plaid keeps sending window.location = 'about:srcdoc' and causes some noise in RN
527
- // All whitelists are now handled in requestHandler, handleQuilttEvent and handleOAuthUrl
528
557
  style: styles.webview,
529
558
  originWhitelist: [
530
559
  '*'
@@ -540,10 +569,10 @@ const QuilttConnector = ({ testId, connectorId, connectionId, institution, oauth
540
569
  bounces: false,
541
570
  scrollEnabled: true,
542
571
  automaticallyAdjustContentInsets: false,
543
- contentInsetAdjustmentBehavior: "never" // Controls how the WebView adjusts its content layout relative to safe areas and system UI
544
- ,
572
+ contentInsetAdjustmentBehavior: "never",
545
573
  showsVerticalScrollIndicator: false,
546
574
  showsHorizontalScrollIndicator: false,
575
+ testID: "webview",
547
576
  ...Platform.OS === 'ios' ? {
548
577
  decelerationRate: 'normal',
549
578
  keyboardDisplayRequiresUserAction: false,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quiltt/react-native",
3
- "version": "4.2.2",
3
+ "version": "4.3.0",
4
4
  "description": "React Native Components for Quiltt Connector",
5
5
  "homepage": "https://github.com/quiltt/quiltt-js/tree/main/packages/react-native#readme",
6
6
  "repository": {
@@ -30,23 +30,23 @@
30
30
  "dependencies": {
31
31
  "@honeybadger-io/core": "6.6.0",
32
32
  "lodash.debounce": "4.0.8",
33
- "@quiltt/core": "4.2.2",
34
- "@quiltt/react": "4.2.2"
33
+ "@quiltt/core": "4.3.0",
34
+ "@quiltt/react": "4.3.0"
35
35
  },
36
36
  "devDependencies": {
37
- "@biomejs/biome": "1.9.4",
37
+ "@biomejs/biome": "2.2.4",
38
38
  "@types/base-64": "1.0.2",
39
39
  "@types/lodash.debounce": "4.0.9",
40
- "@types/node": "22.15.31",
41
- "@types/react": "18.3.20",
40
+ "@types/node": "22.18.6",
41
+ "@types/react": "18.3.23",
42
42
  "base-64": "1.0.0",
43
- "bunchee": "6.3.4",
43
+ "bunchee": "6.6.0",
44
44
  "react": "18.3.1",
45
45
  "react-native": "0.76.7",
46
46
  "react-native-url-polyfill": "2.0.0",
47
47
  "react-native-webview": "13.12.5",
48
48
  "rimraf": "6.0.1",
49
- "typescript": "5.8.3"
49
+ "typescript": "5.9.2"
50
50
  },
51
51
  "peerDependencies": {
52
52
  "base-64": "^1.0.0",
@@ -1,13 +1,12 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
2
- import { Linking, Platform } from 'react-native'
3
- import { StyleSheet } from 'react-native'
2
+ import { Linking, Platform, StyleSheet } from 'react-native'
3
+
4
+ import type { ConnectorSDKCallbackMetadata, ConnectorSDKCallbacks } from '@quiltt/react'
5
+ import { ConnectorSDKEventType, useQuilttSession } from '@quiltt/react'
4
6
  import { URL } from 'react-native-url-polyfill' // https://github.com/facebook/react-native/issues/16434
5
7
  import { WebView } from 'react-native-webview'
6
8
  import type { ShouldStartLoadRequest } from 'react-native-webview/lib/WebViewTypes'
7
9
 
8
- import { ConnectorSDKEventType, useQuilttSession } from '@quiltt/react'
9
- import type { ConnectorSDKCallbackMetadata, ConnectorSDKCallbacks } from '@quiltt/react'
10
-
11
10
  import {
12
11
  ErrorReporter,
13
12
  getErrorMessage,
@@ -16,6 +15,7 @@ import {
16
15
  smartEncodeURIComponent,
17
16
  } from '@/utils'
18
17
  import { version } from '@/version'
18
+
19
19
  import { AndroidSafeAreaView } from './AndroidSafeAreaView'
20
20
  import { ErrorScreen } from './ErrorScreen'
21
21
  import { LoadingScreen } from './LoadingScreen'
@@ -28,33 +28,65 @@ export type PreFlightCheck = {
28
28
  error?: string
29
29
  }
30
30
 
31
+ const parseMetadata = (url: URL, connectorId: string): ConnectorSDKCallbackMetadata => {
32
+ const metadata: ConnectorSDKCallbackMetadata = {
33
+ connectorId: url.searchParams.get('connectorId') ?? connectorId,
34
+ }
35
+
36
+ const profileId = url.searchParams.get('profileId')
37
+ if (profileId) metadata.profileId = profileId
38
+
39
+ const connectionId = url.searchParams.get('connectionId')
40
+ if (connectionId) metadata.connectionId = connectionId
41
+
42
+ const connectorSessionId = url.searchParams.get('connectorSession')
43
+ if (connectorSessionId) metadata.connectorSession = { id: connectorSessionId }
44
+
45
+ return metadata
46
+ }
47
+
31
48
  export const checkConnectorUrl = async (
32
49
  connectorUrl: string,
33
50
  retryCount = 0
34
51
  ): Promise<PreFlightCheck> => {
35
52
  let responseStatus: number | undefined
36
- let error: Error | undefined
37
53
  try {
38
54
  const response = await fetch(connectorUrl)
39
- if (!response.ok) {
40
- responseStatus = response.status
41
- throw new Error('Connector URL is not routable.')
55
+
56
+ switch (response.status) {
57
+ case 200:
58
+ return { checked: true }
59
+
60
+ case 400:
61
+ console.log('Invalid configuration')
62
+ return { checked: true }
63
+
64
+ case 404:
65
+ console.error('Connector not found')
66
+ return { checked: true }
67
+
68
+ default:
69
+ throw new Error('Connector URL is not routable.')
42
70
  }
43
- return { checked: true }
44
- } catch (e) {
45
- error = e as Error
46
- console.error('Failed to connect to connector URL')
71
+ } catch (error) {
72
+ // Log error for debugging
73
+ console.error(error)
47
74
 
75
+ // Try again
48
76
  if (retryCount < PREFLIGHT_RETRY_COUNT) {
49
77
  const delay = 50 * 2 ** retryCount
50
78
  await new Promise((resolve) => setTimeout(resolve, delay))
51
79
  console.log(`Retrying connection... Attempt ${retryCount + 1}`)
52
80
  return checkConnectorUrl(connectorUrl, retryCount + 1)
53
81
  }
82
+
83
+ // Report error after retries exhausted
54
84
  const errorMessage = getErrorMessage(responseStatus, error as Error)
55
85
  const errorToSend = (error as Error) || new Error(errorMessage)
56
86
  const context = { connectorUrl, responseStatus }
57
- if (responseStatus !== 404) await errorReporter.notify(errorToSend, context)
87
+ await errorReporter.notify(errorToSend, context)
88
+
89
+ // Return errored preflight check
58
90
  return { checked: true, error: errorMessage }
59
91
  }
60
92
  }
@@ -82,7 +114,7 @@ export const handleOAuthUrl = (oauthUrl: URL | string | null | undefined) => {
82
114
 
83
115
  // Open the normalized URL
84
116
  Linking.openURL(normalizedUrl)
85
- } catch (error) {
117
+ } catch (_error) {
86
118
  console.error('OAuth URL handling error')
87
119
 
88
120
  // Only try the fallback if oauthUrl is not null
@@ -91,7 +123,7 @@ export const handleOAuthUrl = (oauthUrl: URL | string | null | undefined) => {
91
123
  const fallbackUrl = typeof oauthUrl === 'string' ? oauthUrl : oauthUrl.toString()
92
124
  console.log('Attempting fallback OAuth opening')
93
125
  Linking.openURL(fallbackUrl)
94
- } catch (fallbackError) {
126
+ } catch (_fallbackError) {
95
127
  console.error('Fallback OAuth opening failed')
96
128
  }
97
129
  }
@@ -99,15 +131,14 @@ export const handleOAuthUrl = (oauthUrl: URL | string | null | undefined) => {
99
131
  }
100
132
 
101
133
  type QuilttConnectorProps = {
102
- testId?: string
103
134
  connectorId: string
104
135
  connectionId?: string
105
136
  institution?: string
106
137
  oauthRedirectUrl: string
138
+ testId?: string
107
139
  } & ConnectorSDKCallbacks
108
140
 
109
141
  const QuilttConnector = ({
110
- testId,
111
142
  connectorId,
112
143
  connectionId,
113
144
  institution,
@@ -118,6 +149,7 @@ const QuilttConnector = ({
118
149
  onExitSuccess,
119
150
  onExitAbort,
120
151
  onExitError,
152
+ testId,
121
153
  }: QuilttConnectorProps) => {
122
154
  const webViewRef = useRef<WebView>(null)
123
155
  const { session } = useQuilttSession()
@@ -211,7 +243,7 @@ const QuilttConnector = ({
211
243
  (url: URL) => {
212
244
  url.searchParams.delete('source')
213
245
  url.searchParams.append('connectorId', connectorId)
214
- const metadata = Object.fromEntries(url.searchParams) as ConnectorSDKCallbackMetadata
246
+ const metadata = parseMetadata(url, connectorId)
215
247
 
216
248
  requestAnimationFrame(() => {
217
249
  const eventType = url.host
@@ -256,7 +288,7 @@ const QuilttConnector = ({
256
288
  try {
257
289
  const decodedUrl = decodeURIComponent(navigateUrl)
258
290
  handleOAuthUrl(decodedUrl)
259
- } catch (error) {
291
+ } catch (_error) {
260
292
  console.error('Navigate URL decoding failed, using original')
261
293
  handleOAuthUrl(navigateUrl)
262
294
  }
@@ -296,7 +328,11 @@ const QuilttConnector = ({
296
328
  handleQuilttEvent(url)
297
329
  return false
298
330
  }
299
- if (shouldRender(url)) return true
331
+
332
+ if (shouldRender(url)) {
333
+ return true
334
+ }
335
+
300
336
  // Plaid set oauth url by doing window.location.href = url
301
337
  // So we use `handleOAuthUrl` as a catch all and assume all url got to this step is Plaid OAuth url
302
338
  handleOAuthUrl(url)
@@ -305,7 +341,10 @@ const QuilttConnector = ({
305
341
  [handleQuilttEvent, isQuilttEvent, shouldRender]
306
342
  )
307
343
 
308
- if (!preFlightCheck.checked) return <LoadingScreen testId="loading-screen" />
344
+ if (!preFlightCheck.checked) {
345
+ return <LoadingScreen testId="loading-screen" />
346
+ }
347
+
309
348
  if (preFlightCheck.error) {
310
349
  return (
311
350
  <ErrorScreen
@@ -319,10 +358,8 @@ const QuilttConnector = ({
319
358
  return (
320
359
  <AndroidSafeAreaView testId={testId}>
321
360
  <WebView
322
- testID="webview"
323
361
  ref={webViewRef}
324
362
  // Plaid keeps sending window.location = 'about:srcdoc' and causes some noise in RN
325
- // All whitelists are now handled in requestHandler, handleQuilttEvent and handleOAuthUrl
326
363
  style={styles.webview}
327
364
  originWhitelist={['*']}
328
365
  source={{ uri: connectorUrl }}
@@ -337,6 +374,7 @@ const QuilttConnector = ({
337
374
  contentInsetAdjustmentBehavior="never" // Controls how the WebView adjusts its content layout relative to safe areas and system UI
338
375
  showsVerticalScrollIndicator={false}
339
376
  showsHorizontalScrollIndicator={false}
377
+ testID="webview"
340
378
  {...(Platform.OS === 'ios'
341
379
  ? {
342
380
  decelerationRate: 'normal',
package/src/index.ts CHANGED
@@ -7,7 +7,6 @@ if (!global.atob) {
7
7
  }
8
8
 
9
9
  export * from '@quiltt/core'
10
-
11
10
  export {
12
11
  QuilttAuthProvider,
13
12
  QuilttProvider,
@@ -1,7 +1,7 @@
1
1
  export const getErrorMessage = (responseStatus?: number, error?: Error): string => {
2
2
  if (error)
3
- return `An error occurred while checking the connector URL: ${error?.name} \n${error?.message}`
3
+ return `An error occurred while checking the Connector URL: ${error?.name} \n${error?.message}`
4
4
  return responseStatus
5
- ? `The URL is not routable. Response status: ${responseStatus}`
6
- : 'An error occurred while checking the connector URL'
5
+ ? `An error occurred loading the Connector. Response status: ${responseStatus}`
6
+ : 'An error occurred while checking the Connector URL'
7
7
  }