@quiltt/react-native 3.6.2 → 3.6.4

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.4
4
+
5
+ ### Patch Changes
6
+
7
+ - [#246](https://github.com/quiltt/quiltt-js/pull/246) [`38f7904`](https://github.com/quiltt/quiltt-js/commit/38f79048e99dc617d700b62fd285623f9f2ae2fa) Thanks [@zubairaziz](https://github.com/zubairaziz)! - Update exports for react-native
8
+
9
+ - Updated dependencies [[`38f7904`](https://github.com/quiltt/quiltt-js/commit/38f79048e99dc617d700b62fd285623f9f2ae2fa)]:
10
+ - @quiltt/react@3.6.4
11
+ - @quiltt/core@3.6.4
12
+
13
+ ## 3.6.3
14
+
15
+ ### Patch Changes
16
+
17
+ - [#240](https://github.com/quiltt/quiltt-js/pull/240) [`96556d4`](https://github.com/quiltt/quiltt-js/commit/96556d4e4d29b1a5623b78f60d97c49a974e44e8) Thanks [@zubairaziz](https://github.com/zubairaziz)! - Pre-transpile '@quiltt/react-native' code before publish
18
+
19
+ - Updated dependencies [[`96556d4`](https://github.com/quiltt/quiltt-js/commit/96556d4e4d29b1a5623b78f60d97c49a974e44e8)]:
20
+ - @quiltt/core@3.6.3
21
+ - @quiltt/react@3.6.3
22
+
3
23
  ## 3.6.2
4
24
 
5
25
  ### Patch Changes
package/dist/index.cjs ADDED
@@ -0,0 +1,490 @@
1
+ Object.defineProperty(exports, '__esModule', { value: true });
2
+
3
+ var base64 = require('base-64');
4
+ var core = require('@quiltt/core');
5
+ var react = require('@quiltt/react');
6
+ var jsxRuntime = require('react/jsx-runtime');
7
+ var react$1 = require('react');
8
+ var reactNativeUrlPolyfill = require('react-native-url-polyfill');
9
+ var reactNativeWebview = require('react-native-webview');
10
+ var reactNative = require('react-native');
11
+ var util = require('@honeybadger-io/core/build/src/util');
12
+
13
+ // Generated by genversion.
14
+ const version = '3.6.4';
15
+
16
+ const AndroidSafeAreaView = ({ testId, children })=>/*#__PURE__*/ jsxRuntime.jsx(reactNative.SafeAreaView, {
17
+ testID: testId,
18
+ style: styles$1.AndroidSafeArea,
19
+ children: children
20
+ });
21
+ const styles$1 = reactNative.StyleSheet.create({
22
+ AndroidSafeArea: {
23
+ flex: 1,
24
+ backgroundColor: 'white',
25
+ paddingTop: reactNative.Platform.OS === 'android' ? reactNative.StatusBar.currentHeight : 0
26
+ }
27
+ });
28
+
29
+ const ErrorScreen = ({ testId, error, cta })=>/*#__PURE__*/ jsxRuntime.jsx(AndroidSafeAreaView, {
30
+ testId: testId,
31
+ children: /*#__PURE__*/ jsxRuntime.jsxs(reactNative.View, {
32
+ style: [
33
+ styles.container,
34
+ styles.padding
35
+ ],
36
+ children: [
37
+ /*#__PURE__*/ jsxRuntime.jsxs(reactNative.View, {
38
+ style: {
39
+ flex: 1,
40
+ justifyContent: 'center'
41
+ },
42
+ children: [
43
+ /*#__PURE__*/ jsxRuntime.jsx(reactNative.View, {
44
+ style: {
45
+ flexDirection: 'row',
46
+ justifyContent: 'space-between',
47
+ alignItems: 'center',
48
+ marginVertical: 10
49
+ },
50
+ children: /*#__PURE__*/ jsxRuntime.jsx(reactNative.Text, {
51
+ style: [
52
+ styles.title
53
+ ],
54
+ children: "Cannot connect to the internet."
55
+ })
56
+ }),
57
+ /*#__PURE__*/ jsxRuntime.jsx(reactNative.Text, {
58
+ style: [
59
+ styles.subtitle
60
+ ],
61
+ children: error
62
+ })
63
+ ]
64
+ }),
65
+ /*#__PURE__*/ jsxRuntime.jsx(reactNative.Pressable, {
66
+ style: [
67
+ styles.pressable
68
+ ],
69
+ onPress: cta,
70
+ children: /*#__PURE__*/ jsxRuntime.jsx(reactNative.Text, {
71
+ style: [
72
+ styles.pressableText
73
+ ],
74
+ children: "Exit"
75
+ })
76
+ })
77
+ ]
78
+ })
79
+ });
80
+ const styles = reactNative.StyleSheet.create({
81
+ container: {
82
+ flex: 1,
83
+ flexDirection: 'column',
84
+ justifyContent: 'flex-start',
85
+ alignItems: 'stretch',
86
+ backgroundColor: '#F3F4F6'
87
+ },
88
+ title: {
89
+ color: '#1F2937',
90
+ fontSize: 30,
91
+ fontWeight: 'bold'
92
+ },
93
+ subtitle: {
94
+ color: 'rgba(107, 114, 128, 1)'
95
+ },
96
+ padding: {
97
+ paddingHorizontal: 16,
98
+ paddingVertical: 24
99
+ },
100
+ pressable: {
101
+ marginTop: 20,
102
+ backgroundColor: '#1F2937',
103
+ padding: 10,
104
+ paddingHorizontal: 25,
105
+ borderRadius: 5
106
+ },
107
+ pressableText: {
108
+ color: '#fff',
109
+ textAlign: 'center'
110
+ }
111
+ });
112
+
113
+ const LoadingScreen = ({ testId })=>/*#__PURE__*/ jsxRuntime.jsx(AndroidSafeAreaView, {
114
+ testId: testId,
115
+ children: /*#__PURE__*/ jsxRuntime.jsx(reactNative.View, {
116
+ style: {
117
+ flex: 1,
118
+ justifyContent: 'center',
119
+ alignItems: 'center'
120
+ },
121
+ children: /*#__PURE__*/ jsxRuntime.jsx(reactNative.ActivityIndicator, {
122
+ testID: "activity-indicator",
123
+ size: "large",
124
+ color: "#5928A3"
125
+ })
126
+ })
127
+ });
128
+
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
+ const errorReporter = new ErrorReporter(`${reactNative.Platform.OS} ${reactNative.Platform.Version}`);
218
+ const PREFLIGHT_RETRY_COUNT = 3;
219
+ const checkConnectorUrl = async (connectorUrl, retryCount = 0)=>{
220
+ let responseStatus;
221
+ let error;
222
+ let errorOccurred = false;
223
+ try {
224
+ const response = await fetch(connectorUrl);
225
+ if (!response.ok) {
226
+ console.error(`The URL ${connectorUrl} is not routable.`);
227
+ responseStatus = response.status;
228
+ errorOccurred = true;
229
+ } else {
230
+ console.log(`The URL ${connectorUrl} is routable.`);
231
+ return {
232
+ checked: true
233
+ };
234
+ }
235
+ } catch (e) {
236
+ error = e;
237
+ 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);
245
+ }
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
+ };
258
+
259
+ 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);
265
+ };
266
+
267
+ const QuilttConnector = ({ testId, connectorId, connectionId, institution, oauthRedirectUrl, onEvent, onLoad, onExit, onExitSuccess, onExitAbort, onExitError })=>{
268
+ const webViewRef = react$1.useRef(null);
269
+ const { session } = react.useQuilttSession();
270
+ const encodedOAuthRedirectUrl = react$1.useMemo(()=>encodeURIComponent(oauthRedirectUrl), [
271
+ oauthRedirectUrl
272
+ ]);
273
+ const connectorUrl = react$1.useMemo(()=>{
274
+ const url = new reactNativeUrlPolyfill.URL(`https://${connectorId}.quiltt.app`);
275
+ url.searchParams.append('mode', 'webview');
276
+ url.searchParams.append('oauth_redirect_url', encodedOAuthRedirectUrl);
277
+ url.searchParams.append('agent', `react-native-${version}`);
278
+ return url.toString();
279
+ }, [
280
+ connectorId,
281
+ encodedOAuthRedirectUrl
282
+ ]);
283
+ const [preFlightCheck, setPreFlightCheck] = react$1.useState({
284
+ checked: false
285
+ });
286
+ react$1.useEffect(()=>{
287
+ if (preFlightCheck.checked) return;
288
+ const fetchDataAndSetState = async ()=>{
289
+ const connectorUrlStatus = await checkConnectorUrl(connectorUrl);
290
+ setPreFlightCheck(connectorUrlStatus);
291
+ };
292
+ fetchDataAndSetState();
293
+ }, [
294
+ connectorUrl,
295
+ preFlightCheck
296
+ ]);
297
+ const initInjectedJavaScript = react$1.useCallback(()=>{
298
+ const script = `\
299
+ const options = {\
300
+ source: 'quiltt',\
301
+ type: 'Options',\
302
+ token: '${session?.token}',\
303
+ connectorId: '${connectorId}',\
304
+ connectionId: '${connectionId}',\
305
+ institution: '${institution}', \
306
+ };\
307
+ const compactedOptions = Object.keys(options).reduce((acc, key) => {\
308
+ if (options[key] !== 'undefined') {\
309
+ acc[key] = options[key];\
310
+ }\
311
+ return acc;\
312
+ }, {});\
313
+ window.postMessage(compactedOptions);\
314
+ `;
315
+ webViewRef.current?.injectJavaScript(script);
316
+ }, [
317
+ connectionId,
318
+ connectorId,
319
+ institution,
320
+ session?.token
321
+ ]);
322
+ // allowedListUrl & shouldRender ensure we are only rendering Quiltt, MX and Plaid content in Webview
323
+ // For other urls, we assume those are bank urls, which needs to be handle in external browser.
324
+ // TODO: Convert it to a list from Quiltt Server to prevent MX/ Plaid changes.
325
+ const allowedListUrl = react$1.useMemo(()=>[
326
+ 'quiltt.app',
327
+ 'quiltt.dev',
328
+ 'moneydesktop.com',
329
+ 'cdn.plaid.com/link/v2/stable/link.html'
330
+ ], []);
331
+ const isQuilttEvent = react$1.useCallback((url)=>url.protocol === 'quilttconnector:', []);
332
+ const shouldRender = react$1.useCallback((url)=>{
333
+ if (isQuilttEvent(url)) return false;
334
+ if (url.protocol !== 'https:') {
335
+ return false;
336
+ }
337
+ return allowedListUrl.some((href)=>url.href.includes(href));
338
+ }, [
339
+ allowedListUrl,
340
+ isQuilttEvent
341
+ ]);
342
+ const clearLocalStorage = ()=>{
343
+ const script = 'localStorage.clear();';
344
+ webViewRef.current?.injectJavaScript(script);
345
+ };
346
+ const handleQuilttEvent = react$1.useCallback((url)=>{
347
+ url.searchParams.delete('source');
348
+ url.searchParams.append('connectorId', connectorId);
349
+ const metadata = Object.fromEntries(url.searchParams);
350
+ const eventType = url.host;
351
+ switch(eventType){
352
+ case 'Load':
353
+ initInjectedJavaScript();
354
+ onEvent?.(react.ConnectorSDKEventType.Load, metadata);
355
+ onLoad?.(metadata);
356
+ break;
357
+ case 'ExitAbort':
358
+ clearLocalStorage();
359
+ onEvent?.(react.ConnectorSDKEventType.ExitAbort, metadata);
360
+ onExit?.(react.ConnectorSDKEventType.ExitAbort, metadata);
361
+ onExitAbort?.(metadata);
362
+ break;
363
+ case 'ExitError':
364
+ clearLocalStorage();
365
+ onEvent?.(react.ConnectorSDKEventType.ExitError, metadata);
366
+ onExit?.(react.ConnectorSDKEventType.ExitError, metadata);
367
+ onExitError?.(metadata);
368
+ break;
369
+ case 'ExitSuccess':
370
+ clearLocalStorage();
371
+ onEvent?.(react.ConnectorSDKEventType.ExitSuccess, metadata);
372
+ onExit?.(react.ConnectorSDKEventType.ExitSuccess, metadata);
373
+ onExitSuccess?.(metadata);
374
+ break;
375
+ case 'Authenticate':
376
+ break;
377
+ case 'OauthRequested':
378
+ handleOAuthUrl(new reactNativeUrlPolyfill.URL(url.searchParams.get('oauthUrl')));
379
+ break;
380
+ default:
381
+ console.log('unhandled event', url);
382
+ break;
383
+ }
384
+ }, [
385
+ connectorId,
386
+ initInjectedJavaScript,
387
+ onEvent,
388
+ onExit,
389
+ onExitAbort,
390
+ onExitError,
391
+ onExitSuccess,
392
+ onLoad
393
+ ]);
394
+ const requestHandler = react$1.useCallback((request)=>{
395
+ const url = new reactNativeUrlPolyfill.URL(request.url);
396
+ if (isQuilttEvent(url)) {
397
+ handleQuilttEvent(url);
398
+ return false;
399
+ }
400
+ if (shouldRender(url)) return true;
401
+ // Plaid set oauth url by doing window.location.href = url
402
+ // So we use `handleOAuthUrl` as a catch all and assume all url got to this step is Plaid OAuth url
403
+ handleOAuthUrl(url);
404
+ return false;
405
+ }, [
406
+ handleQuilttEvent,
407
+ isQuilttEvent,
408
+ shouldRender
409
+ ]);
410
+ if (!preFlightCheck.checked) return /*#__PURE__*/ jsxRuntime.jsx(LoadingScreen, {
411
+ testId: "loading-screen"
412
+ });
413
+ if (preFlightCheck.error) return /*#__PURE__*/ jsxRuntime.jsx(ErrorScreen, {
414
+ testId: "error-screen",
415
+ error: preFlightCheck.error,
416
+ cta: ()=>onExitError?.({
417
+ connectorId
418
+ })
419
+ });
420
+ return /*#__PURE__*/ jsxRuntime.jsx(AndroidSafeAreaView, {
421
+ testId: testId,
422
+ children: /*#__PURE__*/ jsxRuntime.jsx(reactNativeWebview.WebView, {
423
+ testID: "webview",
424
+ ref: webViewRef,
425
+ // Plaid keeps sending window.location = 'about:srcdoc' and causes some noise in RN
426
+ // All whitelists are now handled in requestHandler, handleQuilttEvent and handleOAuthUrl
427
+ originWhitelist: [
428
+ '*'
429
+ ],
430
+ source: {
431
+ uri: connectorUrl
432
+ },
433
+ onShouldStartLoadWithRequest: requestHandler,
434
+ javaScriptEnabled: true,
435
+ domStorageEnabled: true,
436
+ webviewDebuggingEnabled: true
437
+ })
438
+ });
439
+ };
440
+ QuilttConnector.displayName = 'QuilttConnector';
441
+
442
+ // Hermes doesn't have atob
443
+ // https://github.com/facebook/hermes/issues/1178
444
+ if (!global.atob) {
445
+ global.atob = base64.decode;
446
+ }
447
+
448
+ Object.defineProperty(exports, "QuilttAuthProvider", {
449
+ enumerable: true,
450
+ get: function () { return react.QuilttAuthProvider; }
451
+ });
452
+ Object.defineProperty(exports, "QuilttProvider", {
453
+ enumerable: true,
454
+ get: function () { return react.QuilttProvider; }
455
+ });
456
+ Object.defineProperty(exports, "QuilttSettingsProvider", {
457
+ enumerable: true,
458
+ get: function () { return react.QuilttSettingsProvider; }
459
+ });
460
+ Object.defineProperty(exports, "useQuilttClient", {
461
+ enumerable: true,
462
+ get: function () { return react.useQuilttClient; }
463
+ });
464
+ Object.defineProperty(exports, "useQuilttConnector", {
465
+ enumerable: true,
466
+ get: function () { return react.useQuilttConnector; }
467
+ });
468
+ Object.defineProperty(exports, "useQuilttSession", {
469
+ enumerable: true,
470
+ get: function () { return react.useQuilttSession; }
471
+ });
472
+ Object.defineProperty(exports, "useQuilttSettings", {
473
+ enumerable: true,
474
+ get: function () { return react.useQuilttSettings; }
475
+ });
476
+ Object.defineProperty(exports, "useSession", {
477
+ enumerable: true,
478
+ get: function () { return react.useSession; }
479
+ });
480
+ Object.defineProperty(exports, "useStorage", {
481
+ enumerable: true,
482
+ get: function () { return react.useStorage; }
483
+ });
484
+ exports.QuilttConnector = QuilttConnector;
485
+ Object.keys(core).forEach(function (k) {
486
+ if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
487
+ enumerable: true,
488
+ get: function () { return core[k]; }
489
+ });
490
+ });
@@ -0,0 +1,18 @@
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 };
package/dist/index.d.ts CHANGED
@@ -4,13 +4,14 @@ export { QuilttAuthProvider, QuilttProvider, QuilttSettingsProvider, useQuilttCl
4
4
  import * as react_jsx_runtime from 'react/jsx-runtime';
5
5
 
6
6
  type QuilttConnectorProps = {
7
+ testId?: string;
7
8
  connectorId: string;
8
9
  connectionId?: string;
9
10
  institution?: string;
10
11
  oauthRedirectUrl: string;
11
12
  } & ConnectorSDKCallbacks;
12
13
  declare const QuilttConnector: {
13
- ({ connectorId, connectionId, institution, oauthRedirectUrl, onEvent, onLoad, onExit, onExitSuccess, onExitAbort, onExitError, }: QuilttConnectorProps): react_jsx_runtime.JSX.Element;
14
+ ({ testId, connectorId, connectionId, institution, oauthRedirectUrl, onEvent, onLoad, onExit, onExitSuccess, onExitAbort, onExitError, }: QuilttConnectorProps): react_jsx_runtime.JSX.Element;
14
15
  displayName: string;
15
16
  };
16
17