@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 +20 -0
- package/dist/index.cjs +129 -155
- package/dist/index.d.ts +8 -1
- package/dist/index.js +128 -156
- package/package.json +14 -20
- package/src/components/AndroidSafeAreaView.tsx +2 -3
- package/src/components/ErrorScreen.tsx +1 -1
- package/src/components/QuilttConnector.tsx +60 -39
- package/src/utils/error/ErrorReporter.ts +1 -1
- package/src/utils/index.ts +0 -1
- package/src/version.ts +1 -1
- package/dist/index.d.cts +0 -18
- package/src/utils/connector/checkConnectorUrl.ts +0 -47
- package/src/utils/connector/handleOAuthUrl.ts +0 -9
- package/src/utils/connector/handleQuilttEvent.ts +0 -0
- package/src/utils/connector/index.ts +0 -2
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.
|
|
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
|
-
|
|
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
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
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
|
-
|
|
261
|
-
|
|
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)
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
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.
|
|
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
|
-
|
|
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
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
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
|
-
|
|
260
|
-
|
|
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)
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
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.
|
|
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
|
-
"
|
|
31
|
-
"
|
|
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
|
-
"@
|
|
36
|
-
"@types/base-64": "
|
|
37
|
-
"@types/node": "20.
|
|
38
|
-
"@types/react": "18.
|
|
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
|
-
"
|
|
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": "
|
|
52
|
-
"typescript": "5.
|
|
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
|
|
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,24 +1,66 @@
|
|
|
1
1
|
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
|
|
2
|
-
|
|
3
|
-
|
|
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
|
-
|
|
21
|
-
|
|
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
|
|
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',
|
package/src/utils/index.ts
CHANGED
package/src/version.ts
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// Generated by genversion.
|
|
2
|
-
export const version = '3.6.
|
|
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
|
-
}
|
|
File without changes
|