@quiltt/react-native 3.6.9 → 3.6.11

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,25 @@
1
1
  # @quiltt/react-native
2
2
 
3
+ ## 3.6.11
4
+
5
+ ### Patch Changes
6
+
7
+ - [#271](https://github.com/quiltt/quiltt-js/pull/271) [`a9ea2a7`](https://github.com/quiltt/quiltt-js/commit/a9ea2a7c6592dd5245183996ce0d26ffb53f2ed9) Thanks [@zubairaziz](https://github.com/zubairaziz)! - Add 'rol' to private claims
8
+
9
+ - Updated dependencies [[`a9ea2a7`](https://github.com/quiltt/quiltt-js/commit/a9ea2a7c6592dd5245183996ce0d26ffb53f2ed9)]:
10
+ - @quiltt/react@3.6.11
11
+ - @quiltt/core@3.6.11
12
+
13
+ ## 3.6.10
14
+
15
+ ### Patch Changes
16
+
17
+ - [#268](https://github.com/quiltt/quiltt-js/pull/268) [`8a82094`](https://github.com/quiltt/quiltt-js/commit/8a82094a709d0d7e1478ec32142be33825323708) Thanks [@zubairaziz](https://github.com/zubairaziz)! - Migrate linter to Biome
18
+
19
+ - Updated dependencies [[`8a82094`](https://github.com/quiltt/quiltt-js/commit/8a82094a709d0d7e1478ec32142be33825323708)]:
20
+ - @quiltt/react@3.6.10
21
+ - @quiltt/core@3.6.10
22
+
3
23
  ## 3.6.9
4
24
 
5
25
  ### Patch Changes
package/dist/index.cjs CHANGED
@@ -5,13 +5,101 @@ var core = require('@quiltt/core');
5
5
  var react = require('@quiltt/react');
6
6
  var jsxRuntime = require('react/jsx-runtime');
7
7
  var react$1 = require('react');
8
+ var reactNative = require('react-native');
8
9
  var reactNativeUrlPolyfill = require('react-native-url-polyfill');
9
10
  var reactNativeWebview = require('react-native-webview');
10
- var reactNative = require('react-native');
11
11
  var util = require('@honeybadger-io/core/build/src/util');
12
12
 
13
13
  // Generated by genversion.
14
- const version = '3.6.9';
14
+ const version = '3.6.11';
15
+
16
+ const ErrorReporterConfig = {
17
+ honeybadger_api_key: 'undefined'
18
+ };
19
+
20
+ // Quick hack to send error to Honeybadger to debug why the connector is not routable
21
+ const notifier = {
22
+ name: 'Quiltt React Native SDK Reporter',
23
+ url: 'https://www.quiltt.dev/guides/connector/react-native',
24
+ version: version
25
+ };
26
+ class ErrorReporter {
27
+ constructor(platform){
28
+ this.noticeUrl = 'https://api.honeybadger.io/v1/notices';
29
+ this.apiKey = ErrorReporterConfig.honeybadger_api_key;
30
+ this.clientName = 'react-native-sdk';
31
+ this.clientVersion = version;
32
+ this.platform = platform;
33
+ this.logger = console;
34
+ this.userAgent = `${this.clientName} ${this.clientVersion}; ${this.platform}`;
35
+ }
36
+ async send(error, context) {
37
+ const headers = {
38
+ 'X-API-Key': this.apiKey,
39
+ 'Content-Type': 'application/json',
40
+ Accept: 'application/json',
41
+ 'User-Agent': `${this.clientName} ${this.clientVersion}; ${this.platform}`
42
+ };
43
+ const payload = await this.buildPayload(error, context);
44
+ const method = 'POST';
45
+ const body = JSON.stringify(payload);
46
+ const mode = 'cors';
47
+ fetch(this.noticeUrl, {
48
+ headers,
49
+ method,
50
+ body,
51
+ mode
52
+ }).then((response)=>{
53
+ if (response.status !== 201) {
54
+ this.logger.warn(`Error report failed: unknown response from server. code=${response.status}`);
55
+ return;
56
+ }
57
+ return response.json();
58
+ }).then((data)=>{
59
+ if (data) {
60
+ this.logger.info(`Error report sent ⚡ https://app.honeybadger.io/notice/${data?.id}`);
61
+ }
62
+ });
63
+ }
64
+ async buildPayload(error, localContext = {}) {
65
+ const notice = error;
66
+ notice.stack = util.generateStackTrace();
67
+ notice.backtrace = util.makeBacktrace(notice.stack);
68
+ return {
69
+ notifier,
70
+ error: {
71
+ class: notice.name,
72
+ message: notice.message,
73
+ backtrace: notice.backtrace,
74
+ // fingerprint: this.calculateFingerprint(notice),
75
+ tags: notice.tags || [],
76
+ causes: util.getCauses(notice, this.logger)
77
+ },
78
+ request: {
79
+ url: notice.url,
80
+ component: notice.component,
81
+ action: notice.action,
82
+ context: localContext || {},
83
+ cgi_data: {},
84
+ params: {},
85
+ session: {}
86
+ },
87
+ server: {
88
+ project_root: notice.projectRoot,
89
+ environment_name: this.userAgent,
90
+ revision: version,
91
+ hostname: this.platform,
92
+ time: new Date().toUTCString()
93
+ },
94
+ details: notice.details || {}
95
+ };
96
+ }
97
+ }
98
+
99
+ const getErrorMessage = (responseStatus, error)=>{
100
+ if (error) return `An error occurred while checking the connector URL: ${error?.name} \n${error?.message}`;
101
+ return responseStatus ? `The URL is not routable. Response status: ${responseStatus}` : 'An error occurred while checking the connector URL';
102
+ };
15
103
 
16
104
  const AndroidSafeAreaView = ({ testId, children })=>/*#__PURE__*/ jsxRuntime.jsx(reactNative.SafeAreaView, {
17
105
  testID: testId,
@@ -126,144 +214,47 @@ const LoadingScreen = ({ testId })=>/*#__PURE__*/ jsxRuntime.jsx(AndroidSafeArea
126
214
  })
127
215
  });
128
216
 
129
- const ErrorReporterConfig = {
130
- honeybadger_api_key: 'undefined'
131
- };
132
-
133
- // Quick hack to send error to Honeybadger to debug why the connector is not routable
134
- const notifier = {
135
- name: 'Quiltt React Native SDK Reporter',
136
- url: 'https://www.quiltt.dev/guides/connector/react-native',
137
- version: version
138
- };
139
- class ErrorReporter {
140
- constructor(platform){
141
- this.noticeUrl = 'https://api.honeybadger.io/v1/notices';
142
- this.apiKey = ErrorReporterConfig.honeybadger_api_key;
143
- this.clientName = 'react-native-sdk';
144
- this.clientVersion = version;
145
- this.platform = platform;
146
- this.logger = console;
147
- this.userAgent = `${this.clientName} ${this.clientVersion}; ${this.platform}`;
148
- }
149
- async send(error, context) {
150
- const headers = {
151
- 'X-API-Key': this.apiKey,
152
- 'Content-Type': 'application/json',
153
- Accept: 'application/json',
154
- 'User-Agent': `${this.clientName} ${this.clientVersion}; ${this.platform}`
155
- };
156
- const payload = await this.buildPayload(error, context);
157
- const method = 'POST';
158
- const body = JSON.stringify(payload);
159
- const mode = 'cors';
160
- fetch(this.noticeUrl, {
161
- headers,
162
- method,
163
- body,
164
- mode
165
- }).then((response)=>{
166
- if (response.status !== 201) {
167
- this.logger.warn(`Error report failed: unknown response from server. code=${response.status}`);
168
- return;
169
- }
170
- return response.json();
171
- }).then((data)=>{
172
- if (data) {
173
- this.logger.info(`Error report sent ⚡ https://app.honeybadger.io/notice/${data?.id}`);
174
- }
175
- });
176
- }
177
- async buildPayload(error, localContext = {}) {
178
- const notice = error;
179
- notice.stack = util.generateStackTrace();
180
- notice.backtrace = util.makeBacktrace(notice.stack);
181
- return {
182
- notifier,
183
- error: {
184
- class: notice.name,
185
- message: notice.message,
186
- backtrace: notice.backtrace,
187
- // fingerprint: this.calculateFingerprint(notice),
188
- tags: notice.tags || [],
189
- causes: util.getCauses(notice, this.logger)
190
- },
191
- request: {
192
- url: notice.url,
193
- component: notice.component,
194
- action: notice.action,
195
- context: localContext || {},
196
- cgi_data: {},
197
- params: {},
198
- session: {}
199
- },
200
- server: {
201
- project_root: notice.projectRoot,
202
- environment_name: this.userAgent,
203
- revision: version,
204
- hostname: this.platform,
205
- time: new Date().toUTCString()
206
- },
207
- details: notice.details || {}
208
- };
209
- }
210
- }
211
-
212
- const getErrorMessage = (responseStatus, error)=>{
213
- if (error) return `An error occurred while checking the connector URL: ${error?.name} \n${error?.message}`;
214
- return responseStatus ? `The URL is not routable. Response status: ${responseStatus}` : 'An error occurred while checking the connector URL';
215
- };
216
-
217
217
  const errorReporter = new ErrorReporter(`${reactNative.Platform.OS} ${reactNative.Platform.Version}`);
218
218
  const PREFLIGHT_RETRY_COUNT = 3;
219
219
  const checkConnectorUrl = async (connectorUrl, retryCount = 0)=>{
220
220
  let responseStatus;
221
221
  let error;
222
- let errorOccurred = false;
223
222
  try {
224
223
  const response = await fetch(connectorUrl);
225
224
  if (!response.ok) {
226
- console.error(`The URL ${connectorUrl} is not routable.`);
227
225
  responseStatus = response.status;
228
- errorOccurred = true;
229
- } else {
230
- console.log(`The URL ${connectorUrl} is routable.`);
231
- return {
232
- checked: true
233
- };
226
+ throw new Error(`The URL ${connectorUrl} is not routable.`);
234
227
  }
228
+ console.log(`The URL ${connectorUrl} is routable.`);
229
+ return {
230
+ checked: true
231
+ };
235
232
  } catch (e) {
236
233
  error = e;
237
234
  console.error(`An error occurred while checking the connector URL: ${error}`);
238
- errorOccurred = true;
239
- }
240
- if (errorOccurred && retryCount < PREFLIGHT_RETRY_COUNT) {
241
- const delay = 50 * Math.pow(2, retryCount);
242
- await new Promise((resolve)=>setTimeout(resolve, delay));
243
- console.log(`Retrying... Attempt number ${retryCount + 1}`);
244
- return checkConnectorUrl(connectorUrl, retryCount + 1);
235
+ if (retryCount < PREFLIGHT_RETRY_COUNT) {
236
+ const delay = 50 * 2 ** retryCount;
237
+ await new Promise((resolve)=>setTimeout(resolve, delay));
238
+ console.log(`Retrying... Attempt number ${retryCount + 1}`);
239
+ return checkConnectorUrl(connectorUrl, retryCount + 1);
240
+ }
241
+ const errorMessage = getErrorMessage(responseStatus, error);
242
+ const errorToSend = error || new Error(errorMessage);
243
+ const context = {
244
+ connectorUrl,
245
+ responseStatus
246
+ };
247
+ if (responseStatus !== 404) await errorReporter.send(errorToSend, context);
248
+ return {
249
+ checked: true,
250
+ error: errorMessage
251
+ };
245
252
  }
246
- const errorMessage = getErrorMessage(responseStatus, error);
247
- const errorToSend = error || new Error(errorMessage);
248
- const context = {
249
- connectorUrl,
250
- responseStatus
251
- };
252
- if (responseStatus !== 404) await errorReporter.send(errorToSend, context);
253
- return {
254
- checked: true,
255
- error: errorMessage
256
- };
257
253
  };
258
-
259
254
  const handleOAuthUrl = (oauthUrl)=>{
260
- if (oauthUrl.protocol !== 'https:') {
261
- console.log(`handleOAuthUrl - Skipping non https url - ${oauthUrl.href}`);
262
- return;
263
- }
264
- reactNative.Linking.openURL(oauthUrl.href);
255
+ console.log(`handleOAuthUrl - Opening URL - ${oauthUrl.toString()}`);
256
+ reactNative.Linking.openURL(oauthUrl.toString());
265
257
  };
266
-
267
258
  const QuilttConnector = ({ testId, connectorId, connectionId, institution, oauthRedirectUrl, onEvent, onLoad, onExit, onExitSuccess, onExitAbort, onExitError })=>{
268
259
  const webViewRef = react$1.useRef(null);
269
260
  const { session } = react.useQuilttSession();
@@ -319,36 +310,14 @@ const QuilttConnector = ({ testId, connectorId, connectionId, institution, oauth
319
310
  institution,
320
311
  session?.token
321
312
  ]);
322
- // urlAllowList & shouldRender ensure we are only rendering Quiltt, MX and Plaid content in Webview
323
- // For other urls, we assume those are bank urls, which need to be handled in external browser.
324
- // TODO: Need to regroup on this and figure out a better way to handle a URL allow list
325
- // const urlAllowList = useMemo(
326
- // () => [
327
- // 'quiltt.io',
328
- // 'quiltt.app',
329
- // 'quiltt.dev',
330
- // 'moneydesktop.com',
331
- // 'plaid.com',
332
- // 'https://cdn.plaid.com/link',
333
- // 'https://www.google.com/recaptcha',
334
- // 'https://challenges.cloudflare.com',
335
- // 'https://api.stripe.com',
336
- // 'https://cdn.jsdelivr.net',
337
- // 'https://auth0.com',
338
- // ],
339
- // []
340
- // )
341
313
  const isQuilttEvent = react$1.useCallback((url)=>url.protocol === 'quilttconnector:', []);
342
- const shouldRender = react$1.useCallback((url)=>{
343
- if (isQuilttEvent(url)) return false;
344
- return url.protocol === 'https:';
345
- }, [
314
+ const shouldRender = react$1.useCallback((url)=>!isQuilttEvent(url), [
346
315
  isQuilttEvent
347
316
  ]);
348
- const clearLocalStorage = ()=>{
317
+ const clearLocalStorage = react$1.useCallback(()=>{
349
318
  const script = 'localStorage.clear();';
350
319
  webViewRef.current?.injectJavaScript(script);
351
- };
320
+ }, []);
352
321
  const handleQuilttEvent = react$1.useCallback((url)=>{
353
322
  url.searchParams.delete('source');
354
323
  url.searchParams.append('connectorId', connectorId);
@@ -388,6 +357,7 @@ const QuilttConnector = ({ testId, connectorId, connectionId, institution, oauth
388
357
  break;
389
358
  }
390
359
  }, [
360
+ clearLocalStorage,
391
361
  connectorId,
392
362
  initInjectedJavaScript,
393
363
  onEvent,
@@ -416,13 +386,15 @@ const QuilttConnector = ({ testId, connectorId, connectionId, institution, oauth
416
386
  if (!preFlightCheck.checked) return /*#__PURE__*/ jsxRuntime.jsx(LoadingScreen, {
417
387
  testId: "loading-screen"
418
388
  });
419
- if (preFlightCheck.error) return /*#__PURE__*/ jsxRuntime.jsx(ErrorScreen, {
420
- testId: "error-screen",
421
- error: preFlightCheck.error,
422
- cta: ()=>onExitError?.({
423
- connectorId
424
- })
425
- });
389
+ if (preFlightCheck.error) {
390
+ return /*#__PURE__*/ jsxRuntime.jsx(ErrorScreen, {
391
+ testId: "error-screen",
392
+ error: preFlightCheck.error,
393
+ cta: ()=>onExitError?.({
394
+ connectorId
395
+ })
396
+ });
397
+ }
426
398
  return /*#__PURE__*/ jsxRuntime.jsx(AndroidSafeAreaView, {
427
399
  testId: testId,
428
400
  children: /*#__PURE__*/ jsxRuntime.jsx(reactNativeWebview.WebView, {
@@ -488,6 +460,8 @@ Object.defineProperty(exports, "useStorage", {
488
460
  get: function () { return react.useStorage; }
489
461
  });
490
462
  exports.QuilttConnector = QuilttConnector;
463
+ exports.checkConnectorUrl = checkConnectorUrl;
464
+ exports.handleOAuthUrl = handleOAuthUrl;
491
465
  Object.keys(core).forEach(function (k) {
492
466
  if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
493
467
  enumerable: true,
package/dist/index.d.ts CHANGED
@@ -2,7 +2,14 @@ export * from '@quiltt/core';
2
2
  import { ConnectorSDKCallbacks } from '@quiltt/react';
3
3
  export { QuilttAuthProvider, QuilttProvider, QuilttSettingsProvider, useQuilttClient, useQuilttConnector, useQuilttSession, useQuilttSettings, useSession, useStorage } from '@quiltt/react';
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
+ import { URL } from 'react-native-url-polyfill';
5
6
 
7
+ type PreFlightCheck = {
8
+ checked: boolean;
9
+ error?: string;
10
+ };
11
+ declare const checkConnectorUrl: (connectorUrl: string, retryCount?: number) => Promise<PreFlightCheck>;
12
+ declare const handleOAuthUrl: (oauthUrl: URL | string) => void;
6
13
  type QuilttConnectorProps = {
7
14
  testId?: string;
8
15
  connectorId: string;
@@ -15,4 +22,4 @@ declare const QuilttConnector: {
15
22
  displayName: string;
16
23
  };
17
24
 
18
- export { QuilttConnector };
25
+ export { type PreFlightCheck, QuilttConnector, checkConnectorUrl, handleOAuthUrl };
package/dist/index.js CHANGED
@@ -4,13 +4,101 @@ import { useQuilttSession, ConnectorSDKEventType } from '@quiltt/react';
4
4
  export { QuilttAuthProvider, QuilttProvider, QuilttSettingsProvider, useQuilttClient, useQuilttConnector, useQuilttSession, useQuilttSettings, useSession, useStorage } from '@quiltt/react';
5
5
  import { jsx, jsxs } from 'react/jsx-runtime';
6
6
  import { useRef, useMemo, useState, useEffect, useCallback } from 'react';
7
+ import { StyleSheet, Platform, StatusBar, SafeAreaView, View, Text, Pressable, ActivityIndicator, Linking } from 'react-native';
7
8
  import { URL } from 'react-native-url-polyfill';
8
9
  import { WebView } from 'react-native-webview';
9
- import { StyleSheet, Platform, StatusBar, SafeAreaView, View, Text, Pressable, ActivityIndicator, Linking } from 'react-native';
10
10
  import { generateStackTrace, makeBacktrace, getCauses } from '@honeybadger-io/core/build/src/util';
11
11
 
12
12
  // Generated by genversion.
13
- const version = '3.6.9';
13
+ const version = '3.6.11';
14
+
15
+ const ErrorReporterConfig = {
16
+ honeybadger_api_key: 'undefined'
17
+ };
18
+
19
+ // Quick hack to send error to Honeybadger to debug why the connector is not routable
20
+ const notifier = {
21
+ name: 'Quiltt React Native SDK Reporter',
22
+ url: 'https://www.quiltt.dev/guides/connector/react-native',
23
+ version: version
24
+ };
25
+ class ErrorReporter {
26
+ constructor(platform){
27
+ this.noticeUrl = 'https://api.honeybadger.io/v1/notices';
28
+ this.apiKey = ErrorReporterConfig.honeybadger_api_key;
29
+ this.clientName = 'react-native-sdk';
30
+ this.clientVersion = version;
31
+ this.platform = platform;
32
+ this.logger = console;
33
+ this.userAgent = `${this.clientName} ${this.clientVersion}; ${this.platform}`;
34
+ }
35
+ async send(error, context) {
36
+ const headers = {
37
+ 'X-API-Key': this.apiKey,
38
+ 'Content-Type': 'application/json',
39
+ Accept: 'application/json',
40
+ 'User-Agent': `${this.clientName} ${this.clientVersion}; ${this.platform}`
41
+ };
42
+ const payload = await this.buildPayload(error, context);
43
+ const method = 'POST';
44
+ const body = JSON.stringify(payload);
45
+ const mode = 'cors';
46
+ fetch(this.noticeUrl, {
47
+ headers,
48
+ method,
49
+ body,
50
+ mode
51
+ }).then((response)=>{
52
+ if (response.status !== 201) {
53
+ this.logger.warn(`Error report failed: unknown response from server. code=${response.status}`);
54
+ return;
55
+ }
56
+ return response.json();
57
+ }).then((data)=>{
58
+ if (data) {
59
+ this.logger.info(`Error report sent ⚡ https://app.honeybadger.io/notice/${data?.id}`);
60
+ }
61
+ });
62
+ }
63
+ async buildPayload(error, localContext = {}) {
64
+ const notice = error;
65
+ notice.stack = generateStackTrace();
66
+ notice.backtrace = makeBacktrace(notice.stack);
67
+ return {
68
+ notifier,
69
+ error: {
70
+ class: notice.name,
71
+ message: notice.message,
72
+ backtrace: notice.backtrace,
73
+ // fingerprint: this.calculateFingerprint(notice),
74
+ tags: notice.tags || [],
75
+ causes: getCauses(notice, this.logger)
76
+ },
77
+ request: {
78
+ url: notice.url,
79
+ component: notice.component,
80
+ action: notice.action,
81
+ context: localContext || {},
82
+ cgi_data: {},
83
+ params: {},
84
+ session: {}
85
+ },
86
+ server: {
87
+ project_root: notice.projectRoot,
88
+ environment_name: this.userAgent,
89
+ revision: version,
90
+ hostname: this.platform,
91
+ time: new Date().toUTCString()
92
+ },
93
+ details: notice.details || {}
94
+ };
95
+ }
96
+ }
97
+
98
+ const getErrorMessage = (responseStatus, error)=>{
99
+ if (error) return `An error occurred while checking the connector URL: ${error?.name} \n${error?.message}`;
100
+ return responseStatus ? `The URL is not routable. Response status: ${responseStatus}` : 'An error occurred while checking the connector URL';
101
+ };
14
102
 
15
103
  const AndroidSafeAreaView = ({ testId, children })=>/*#__PURE__*/ jsx(SafeAreaView, {
16
104
  testID: testId,
@@ -125,144 +213,47 @@ const LoadingScreen = ({ testId })=>/*#__PURE__*/ jsx(AndroidSafeAreaView, {
125
213
  })
126
214
  });
127
215
 
128
- const ErrorReporterConfig = {
129
- honeybadger_api_key: 'undefined'
130
- };
131
-
132
- // Quick hack to send error to Honeybadger to debug why the connector is not routable
133
- const notifier = {
134
- name: 'Quiltt React Native SDK Reporter',
135
- url: 'https://www.quiltt.dev/guides/connector/react-native',
136
- version: version
137
- };
138
- class ErrorReporter {
139
- constructor(platform){
140
- this.noticeUrl = 'https://api.honeybadger.io/v1/notices';
141
- this.apiKey = ErrorReporterConfig.honeybadger_api_key;
142
- this.clientName = 'react-native-sdk';
143
- this.clientVersion = version;
144
- this.platform = platform;
145
- this.logger = console;
146
- this.userAgent = `${this.clientName} ${this.clientVersion}; ${this.platform}`;
147
- }
148
- async send(error, context) {
149
- const headers = {
150
- 'X-API-Key': this.apiKey,
151
- 'Content-Type': 'application/json',
152
- Accept: 'application/json',
153
- 'User-Agent': `${this.clientName} ${this.clientVersion}; ${this.platform}`
154
- };
155
- const payload = await this.buildPayload(error, context);
156
- const method = 'POST';
157
- const body = JSON.stringify(payload);
158
- const mode = 'cors';
159
- fetch(this.noticeUrl, {
160
- headers,
161
- method,
162
- body,
163
- mode
164
- }).then((response)=>{
165
- if (response.status !== 201) {
166
- this.logger.warn(`Error report failed: unknown response from server. code=${response.status}`);
167
- return;
168
- }
169
- return response.json();
170
- }).then((data)=>{
171
- if (data) {
172
- this.logger.info(`Error report sent ⚡ https://app.honeybadger.io/notice/${data?.id}`);
173
- }
174
- });
175
- }
176
- async buildPayload(error, localContext = {}) {
177
- const notice = error;
178
- notice.stack = generateStackTrace();
179
- notice.backtrace = makeBacktrace(notice.stack);
180
- return {
181
- notifier,
182
- error: {
183
- class: notice.name,
184
- message: notice.message,
185
- backtrace: notice.backtrace,
186
- // fingerprint: this.calculateFingerprint(notice),
187
- tags: notice.tags || [],
188
- causes: getCauses(notice, this.logger)
189
- },
190
- request: {
191
- url: notice.url,
192
- component: notice.component,
193
- action: notice.action,
194
- context: localContext || {},
195
- cgi_data: {},
196
- params: {},
197
- session: {}
198
- },
199
- server: {
200
- project_root: notice.projectRoot,
201
- environment_name: this.userAgent,
202
- revision: version,
203
- hostname: this.platform,
204
- time: new Date().toUTCString()
205
- },
206
- details: notice.details || {}
207
- };
208
- }
209
- }
210
-
211
- const getErrorMessage = (responseStatus, error)=>{
212
- if (error) return `An error occurred while checking the connector URL: ${error?.name} \n${error?.message}`;
213
- return responseStatus ? `The URL is not routable. Response status: ${responseStatus}` : 'An error occurred while checking the connector URL';
214
- };
215
-
216
216
  const errorReporter = new ErrorReporter(`${Platform.OS} ${Platform.Version}`);
217
217
  const PREFLIGHT_RETRY_COUNT = 3;
218
218
  const checkConnectorUrl = async (connectorUrl, retryCount = 0)=>{
219
219
  let responseStatus;
220
220
  let error;
221
- let errorOccurred = false;
222
221
  try {
223
222
  const response = await fetch(connectorUrl);
224
223
  if (!response.ok) {
225
- console.error(`The URL ${connectorUrl} is not routable.`);
226
224
  responseStatus = response.status;
227
- errorOccurred = true;
228
- } else {
229
- console.log(`The URL ${connectorUrl} is routable.`);
230
- return {
231
- checked: true
232
- };
225
+ throw new Error(`The URL ${connectorUrl} is not routable.`);
233
226
  }
227
+ console.log(`The URL ${connectorUrl} is routable.`);
228
+ return {
229
+ checked: true
230
+ };
234
231
  } catch (e) {
235
232
  error = e;
236
233
  console.error(`An error occurred while checking the connector URL: ${error}`);
237
- errorOccurred = true;
238
- }
239
- if (errorOccurred && retryCount < PREFLIGHT_RETRY_COUNT) {
240
- const delay = 50 * Math.pow(2, retryCount);
241
- await new Promise((resolve)=>setTimeout(resolve, delay));
242
- console.log(`Retrying... Attempt number ${retryCount + 1}`);
243
- return checkConnectorUrl(connectorUrl, retryCount + 1);
234
+ if (retryCount < PREFLIGHT_RETRY_COUNT) {
235
+ const delay = 50 * 2 ** retryCount;
236
+ await new Promise((resolve)=>setTimeout(resolve, delay));
237
+ console.log(`Retrying... Attempt number ${retryCount + 1}`);
238
+ return checkConnectorUrl(connectorUrl, retryCount + 1);
239
+ }
240
+ const errorMessage = getErrorMessage(responseStatus, error);
241
+ const errorToSend = error || new Error(errorMessage);
242
+ const context = {
243
+ connectorUrl,
244
+ responseStatus
245
+ };
246
+ if (responseStatus !== 404) await errorReporter.send(errorToSend, context);
247
+ return {
248
+ checked: true,
249
+ error: errorMessage
250
+ };
244
251
  }
245
- const errorMessage = getErrorMessage(responseStatus, error);
246
- const errorToSend = error || new Error(errorMessage);
247
- const context = {
248
- connectorUrl,
249
- responseStatus
250
- };
251
- if (responseStatus !== 404) await errorReporter.send(errorToSend, context);
252
- return {
253
- checked: true,
254
- error: errorMessage
255
- };
256
252
  };
257
-
258
253
  const handleOAuthUrl = (oauthUrl)=>{
259
- if (oauthUrl.protocol !== 'https:') {
260
- console.log(`handleOAuthUrl - Skipping non https url - ${oauthUrl.href}`);
261
- return;
262
- }
263
- Linking.openURL(oauthUrl.href);
254
+ console.log(`handleOAuthUrl - Opening URL - ${oauthUrl.toString()}`);
255
+ Linking.openURL(oauthUrl.toString());
264
256
  };
265
-
266
257
  const QuilttConnector = ({ testId, connectorId, connectionId, institution, oauthRedirectUrl, onEvent, onLoad, onExit, onExitSuccess, onExitAbort, onExitError })=>{
267
258
  const webViewRef = useRef(null);
268
259
  const { session } = useQuilttSession();
@@ -318,36 +309,14 @@ const QuilttConnector = ({ testId, connectorId, connectionId, institution, oauth
318
309
  institution,
319
310
  session?.token
320
311
  ]);
321
- // urlAllowList & shouldRender ensure we are only rendering Quiltt, MX and Plaid content in Webview
322
- // For other urls, we assume those are bank urls, which need to be handled in external browser.
323
- // TODO: Need to regroup on this and figure out a better way to handle a URL allow list
324
- // const urlAllowList = useMemo(
325
- // () => [
326
- // 'quiltt.io',
327
- // 'quiltt.app',
328
- // 'quiltt.dev',
329
- // 'moneydesktop.com',
330
- // 'plaid.com',
331
- // 'https://cdn.plaid.com/link',
332
- // 'https://www.google.com/recaptcha',
333
- // 'https://challenges.cloudflare.com',
334
- // 'https://api.stripe.com',
335
- // 'https://cdn.jsdelivr.net',
336
- // 'https://auth0.com',
337
- // ],
338
- // []
339
- // )
340
312
  const isQuilttEvent = useCallback((url)=>url.protocol === 'quilttconnector:', []);
341
- const shouldRender = useCallback((url)=>{
342
- if (isQuilttEvent(url)) return false;
343
- return url.protocol === 'https:';
344
- }, [
313
+ const shouldRender = useCallback((url)=>!isQuilttEvent(url), [
345
314
  isQuilttEvent
346
315
  ]);
347
- const clearLocalStorage = ()=>{
316
+ const clearLocalStorage = useCallback(()=>{
348
317
  const script = 'localStorage.clear();';
349
318
  webViewRef.current?.injectJavaScript(script);
350
- };
319
+ }, []);
351
320
  const handleQuilttEvent = useCallback((url)=>{
352
321
  url.searchParams.delete('source');
353
322
  url.searchParams.append('connectorId', connectorId);
@@ -387,6 +356,7 @@ const QuilttConnector = ({ testId, connectorId, connectionId, institution, oauth
387
356
  break;
388
357
  }
389
358
  }, [
359
+ clearLocalStorage,
390
360
  connectorId,
391
361
  initInjectedJavaScript,
392
362
  onEvent,
@@ -415,13 +385,15 @@ const QuilttConnector = ({ testId, connectorId, connectionId, institution, oauth
415
385
  if (!preFlightCheck.checked) return /*#__PURE__*/ jsx(LoadingScreen, {
416
386
  testId: "loading-screen"
417
387
  });
418
- if (preFlightCheck.error) return /*#__PURE__*/ jsx(ErrorScreen, {
419
- testId: "error-screen",
420
- error: preFlightCheck.error,
421
- cta: ()=>onExitError?.({
422
- connectorId
423
- })
424
- });
388
+ if (preFlightCheck.error) {
389
+ return /*#__PURE__*/ jsx(ErrorScreen, {
390
+ testId: "error-screen",
391
+ error: preFlightCheck.error,
392
+ cta: ()=>onExitError?.({
393
+ connectorId
394
+ })
395
+ });
396
+ }
425
397
  return /*#__PURE__*/ jsx(AndroidSafeAreaView, {
426
398
  testId: testId,
427
399
  children: /*#__PURE__*/ jsx(WebView, {
@@ -450,4 +422,4 @@ if (!global.atob) {
450
422
  global.atob = decode;
451
423
  }
452
424
 
453
- export { QuilttConnector };
425
+ export { QuilttConnector, checkConnectorUrl, handleOAuthUrl };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quiltt/react-native",
3
- "version": "3.6.9",
3
+ "version": "3.6.11",
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": {
@@ -27,29 +27,23 @@
27
27
  ],
28
28
  "dependencies": {
29
29
  "@honeybadger-io/core": "6.6.0",
30
- "@quiltt/core": "3.6.9",
31
- "@quiltt/react": "3.6.9"
30
+ "base-64": "1.0.0",
31
+ "react-native-url-polyfill": "2.0.0",
32
+ "react-native-webview": "13.10.2",
33
+ "@quiltt/core": "3.6.11",
34
+ "@quiltt/react": "3.6.11"
32
35
  },
33
36
  "devDependencies": {
34
37
  "@apollo/client": "3.9.9",
35
- "@trivago/prettier-plugin-sort-imports": "4.1.1",
36
- "@types/base-64": "0.1.0",
37
- "@types/node": "20.12.7",
38
- "@types/react": "18.2.73",
38
+ "@biomejs/biome": "1.8.3",
39
+ "@types/base-64": "1.0.2",
40
+ "@types/node": "20.14.10",
41
+ "@types/react": "18.3.3",
39
42
  "@types/react-native": "0.72.5",
40
- "@typescript-eslint/eslint-plugin": "5.60.1",
41
- "@typescript-eslint/parser": "5.60.1",
42
- "bunchee": "4.4.8",
43
- "eslint": "8.43.0",
44
- "eslint-config-prettier": "8.8.0",
45
- "eslint-plugin-jsx-a11y": "6.7.1",
46
- "eslint-plugin-prettier": "4.2.1",
47
- "eslint-plugin-react": "7.32.2",
48
- "eslint-plugin-react-hooks": "4.6.0",
49
- "prettier": "2.8.8",
43
+ "bunchee": "5.2.2",
50
44
  "react": "18.2.0",
51
- "rimraf": "5.0.5",
52
- "typescript": "5.4.3"
45
+ "rimraf": "6.0.0",
46
+ "typescript": "5.5.3"
53
47
  },
54
48
  "peerDependencies": {
55
49
  "base-64": "*",
@@ -67,7 +61,7 @@
67
61
  "build": "pnpm run addApiKey && pnpm run addVersion && bunchee",
68
62
  "clean": "rimraf .turbo dist",
69
63
  "dev": "bunchee --watch",
70
- "lint": "TIMING=1 eslint --ext .js,.jsx,.ts,.tsx src/ --fix",
64
+ "lint": "TIMING=1 biome check src/ --fix",
71
65
  "typecheck": "tsc --project tsconfig.json --noEmit"
72
66
  }
73
67
  }
@@ -1,6 +1,5 @@
1
- import { PropsWithChildren } from 'react'
2
-
3
- import { SafeAreaView, StyleSheet, Platform, StatusBar } from 'react-native'
1
+ import type { PropsWithChildren } from 'react'
2
+ import { Platform, SafeAreaView, StatusBar, StyleSheet } from 'react-native'
4
3
 
5
4
  type AndroidSafeAreaViewProps = PropsWithChildren & {
6
5
  testId?: string
@@ -1,4 +1,4 @@
1
- import { View, Text, Pressable, StyleSheet } from 'react-native'
1
+ import { Pressable, StyleSheet, Text, View } from 'react-native'
2
2
 
3
3
  import { AndroidSafeAreaView } from './AndroidSafeAreaView'
4
4
 
@@ -1,24 +1,66 @@
1
1
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
2
-
3
- // React Native's URL implementation is incomplete
4
- // https://github.com/facebook/react-native/issues/16434
5
- import { URL } from 'react-native-url-polyfill'
2
+ import { Linking, Platform } from 'react-native'
3
+ import { URL } from 'react-native-url-polyfill' // https://github.com/facebook/react-native/issues/16434
6
4
  import { WebView } from 'react-native-webview'
7
5
  import type { ShouldStartLoadRequest } from 'react-native-webview/lib/WebViewTypes'
8
6
 
9
7
  import {
10
- ConnectorSDKCallbackMetadata,
11
- ConnectorSDKCallbacks,
8
+ type ConnectorSDKCallbackMetadata,
9
+ type ConnectorSDKCallbacks,
12
10
  ConnectorSDKEventType,
13
11
  useQuilttSession,
14
12
  } from '@quiltt/react'
15
13
 
14
+ import { ErrorReporter, getErrorMessage } from '../utils'
16
15
  import { version } from '../version'
17
16
  import { AndroidSafeAreaView } from './AndroidSafeAreaView'
18
17
  import { ErrorScreen } from './ErrorScreen'
19
18
  import { LoadingScreen } from './LoadingScreen'
20
- import { checkConnectorUrl, handleOAuthUrl } from '../utils'
21
- import type { PreFlightCheck } from '../utils'
19
+
20
+ const errorReporter = new ErrorReporter(`${Platform.OS} ${Platform.Version}`)
21
+ const PREFLIGHT_RETRY_COUNT = 3
22
+
23
+ export type PreFlightCheck = {
24
+ checked: boolean
25
+ error?: string
26
+ }
27
+
28
+ export const checkConnectorUrl = async (
29
+ connectorUrl: string,
30
+ retryCount = 0
31
+ ): Promise<PreFlightCheck> => {
32
+ let responseStatus: number | undefined
33
+ let error: Error | undefined
34
+ try {
35
+ const response = await fetch(connectorUrl)
36
+ if (!response.ok) {
37
+ responseStatus = response.status
38
+ throw new Error(`The URL ${connectorUrl} is not routable.`)
39
+ }
40
+ console.log(`The URL ${connectorUrl} is routable.`)
41
+ return { checked: true }
42
+ } catch (e) {
43
+ error = e as Error
44
+ console.error(`An error occurred while checking the connector URL: ${error}`)
45
+
46
+ if (retryCount < PREFLIGHT_RETRY_COUNT) {
47
+ const delay = 50 * 2 ** retryCount
48
+ await new Promise((resolve) => setTimeout(resolve, delay))
49
+ console.log(`Retrying... Attempt number ${retryCount + 1}`)
50
+ return checkConnectorUrl(connectorUrl, retryCount + 1)
51
+ }
52
+ const errorMessage = getErrorMessage(responseStatus, error as Error)
53
+ const errorToSend = (error as Error) || new Error(errorMessage)
54
+ const context = { connectorUrl, responseStatus }
55
+ if (responseStatus !== 404) await errorReporter.send(errorToSend, context)
56
+ return { checked: true, error: errorMessage }
57
+ }
58
+ }
59
+
60
+ export const handleOAuthUrl = (oauthUrl: URL | string) => {
61
+ console.log(`handleOAuthUrl - Opening URL - ${oauthUrl.toString()}`)
62
+ Linking.openURL(oauthUrl.toString())
63
+ }
22
64
 
23
65
  type QuilttConnectorProps = {
24
66
  testId?: string
@@ -43,17 +85,20 @@ const QuilttConnector = ({
43
85
  }: QuilttConnectorProps) => {
44
86
  const webViewRef = useRef<WebView>(null)
45
87
  const { session } = useQuilttSession()
88
+
46
89
  const encodedOAuthRedirectUrl = useMemo(
47
90
  () => encodeURIComponent(oauthRedirectUrl),
48
91
  [oauthRedirectUrl]
49
92
  )
93
+
50
94
  const connectorUrl = useMemo(() => {
51
- const url: URL = new URL(`https://${connectorId}.quiltt.app`)
95
+ const url = new URL(`https://${connectorId}.quiltt.app`)
52
96
  url.searchParams.append('mode', 'webview')
53
97
  url.searchParams.append('oauth_redirect_url', encodedOAuthRedirectUrl)
54
98
  url.searchParams.append('agent', `react-native-${version}`)
55
99
  return url.toString()
56
100
  }, [connectorId, encodedOAuthRedirectUrl])
101
+
57
102
  const [preFlightCheck, setPreFlightCheck] = useState<PreFlightCheck>({ checked: false })
58
103
 
59
104
  useEffect(() => {
@@ -86,40 +131,14 @@ const QuilttConnector = ({
86
131
  webViewRef.current?.injectJavaScript(script)
87
132
  }, [connectionId, connectorId, institution, session?.token])
88
133
 
89
- // urlAllowList & shouldRender ensure we are only rendering Quiltt, MX and Plaid content in Webview
90
- // For other urls, we assume those are bank urls, which need to be handled in external browser.
91
- // TODO: Need to regroup on this and figure out a better way to handle a URL allow list
92
- // const urlAllowList = useMemo(
93
- // () => [
94
- // 'quiltt.io',
95
- // 'quiltt.app',
96
- // 'quiltt.dev',
97
- // 'moneydesktop.com',
98
- // 'plaid.com',
99
- // 'https://cdn.plaid.com/link',
100
- // 'https://www.google.com/recaptcha',
101
- // 'https://challenges.cloudflare.com',
102
- // 'https://api.stripe.com',
103
- // 'https://cdn.jsdelivr.net',
104
- // 'https://auth0.com',
105
- // ],
106
- // []
107
- // )
108
-
109
134
  const isQuilttEvent = useCallback((url: URL) => url.protocol === 'quilttconnector:', [])
110
135
 
111
- const shouldRender = useCallback(
112
- (url: URL) => {
113
- if (isQuilttEvent(url)) return false
114
- return url.protocol === 'https:'
115
- },
116
- [isQuilttEvent]
117
- )
136
+ const shouldRender = useCallback((url: URL) => !isQuilttEvent(url), [isQuilttEvent])
118
137
 
119
- const clearLocalStorage = () => {
138
+ const clearLocalStorage = useCallback(() => {
120
139
  const script = 'localStorage.clear();'
121
140
  webViewRef.current?.injectJavaScript(script)
122
- }
141
+ }, [])
123
142
 
124
143
  const handleQuilttEvent = useCallback(
125
144
  (url: URL) => {
@@ -164,6 +183,7 @@ const QuilttConnector = ({
164
183
  }
165
184
  },
166
185
  [
186
+ clearLocalStorage,
167
187
  connectorId,
168
188
  initInjectedJavaScript,
169
189
  onEvent,
@@ -193,7 +213,7 @@ const QuilttConnector = ({
193
213
  )
194
214
 
195
215
  if (!preFlightCheck.checked) return <LoadingScreen testId="loading-screen" />
196
- if (preFlightCheck.error)
216
+ if (preFlightCheck.error) {
197
217
  return (
198
218
  <ErrorScreen
199
219
  testId="error-screen"
@@ -201,6 +221,7 @@ const QuilttConnector = ({
201
221
  cta={() => onExitError?.({ connectorId })}
202
222
  />
203
223
  )
224
+ }
204
225
 
205
226
  return (
206
227
  <AndroidSafeAreaView testId={testId}>
@@ -2,8 +2,8 @@
2
2
  import type { Notice, NoticeTransportPayload } from '@honeybadger-io/core/build/src/types'
3
3
  import { generateStackTrace, getCauses, makeBacktrace } from '@honeybadger-io/core/build/src/util'
4
4
 
5
- import { ErrorReporterConfig } from './ErrorReporterConfig'
6
5
  import { version } from '../../version'
6
+ import { ErrorReporterConfig } from './ErrorReporterConfig'
7
7
 
8
8
  const notifier = {
9
9
  name: 'Quiltt React Native SDK Reporter',
@@ -1,2 +1 @@
1
- export * from './connector'
2
1
  export * from './error'
package/src/version.ts CHANGED
@@ -1,2 +1,2 @@
1
1
  // Generated by genversion.
2
- export const version = '3.6.9'
2
+ export const version = '3.6.11'
package/dist/index.d.cts DELETED
@@ -1,18 +0,0 @@
1
- export * from '@quiltt/core';
2
- import { ConnectorSDKCallbacks } from '@quiltt/react';
3
- export { QuilttAuthProvider, QuilttProvider, QuilttSettingsProvider, useQuilttClient, useQuilttConnector, useQuilttSession, useQuilttSettings, useSession, useStorage } from '@quiltt/react';
4
- import * as react_jsx_runtime from 'react/jsx-runtime';
5
-
6
- type QuilttConnectorProps = {
7
- testId?: string;
8
- connectorId: string;
9
- connectionId?: string;
10
- institution?: string;
11
- oauthRedirectUrl: string;
12
- } & ConnectorSDKCallbacks;
13
- declare const QuilttConnector: {
14
- ({ testId, connectorId, connectionId, institution, oauthRedirectUrl, onEvent, onLoad, onExit, onExitSuccess, onExitAbort, onExitError, }: QuilttConnectorProps): react_jsx_runtime.JSX.Element;
15
- displayName: string;
16
- };
17
-
18
- export { QuilttConnector };
@@ -1,47 +0,0 @@
1
- import { getErrorMessage, ErrorReporter } from '../error'
2
- import { Platform } from 'react-native'
3
-
4
- const errorReporter = new ErrorReporter(`${Platform.OS} ${Platform.Version}`)
5
- const PREFLIGHT_RETRY_COUNT = 3
6
-
7
- export type PreFlightCheck = {
8
- checked: boolean
9
- error?: string
10
- }
11
-
12
- export const checkConnectorUrl = async (
13
- connectorUrl: string,
14
- retryCount = 0
15
- ): Promise<PreFlightCheck> => {
16
- let responseStatus
17
- let error
18
- let errorOccurred = false
19
- try {
20
- const response = await fetch(connectorUrl)
21
- if (!response.ok) {
22
- console.error(`The URL ${connectorUrl} is not routable.`)
23
- responseStatus = response.status
24
- errorOccurred = true
25
- } else {
26
- console.log(`The URL ${connectorUrl} is routable.`)
27
- return { checked: true }
28
- }
29
- } catch (e) {
30
- error = e
31
- console.error(`An error occurred while checking the connector URL: ${error}`)
32
- errorOccurred = true
33
- }
34
-
35
- if (errorOccurred && retryCount < PREFLIGHT_RETRY_COUNT) {
36
- const delay = 50 * Math.pow(2, retryCount)
37
- await new Promise((resolve) => setTimeout(resolve, delay))
38
- console.log(`Retrying... Attempt number ${retryCount + 1}`)
39
- return checkConnectorUrl(connectorUrl, retryCount + 1)
40
- }
41
-
42
- const errorMessage = getErrorMessage(responseStatus, error as Error)
43
- const errorToSend = (error as Error) || new Error(errorMessage)
44
- const context = { connectorUrl, responseStatus }
45
- if (responseStatus !== 404) await errorReporter.send(errorToSend, context)
46
- return { checked: true, error: errorMessage }
47
- }
@@ -1,9 +0,0 @@
1
- import { Linking } from 'react-native'
2
-
3
- export const handleOAuthUrl = (oauthUrl: URL) => {
4
- if (oauthUrl.protocol !== 'https:') {
5
- console.log(`handleOAuthUrl - Skipping non https url - ${oauthUrl.href}`)
6
- return
7
- }
8
- Linking.openURL(oauthUrl.href)
9
- }
File without changes
@@ -1,2 +0,0 @@
1
- export * from './checkConnectorUrl'
2
- export * from './handleOAuthUrl'