@quiltt/react-native 5.0.0 → 5.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,801 +1,11 @@
1
1
  import { decode } from 'base-64';
2
- import { getUserAgent as getUserAgent$1, QuilttClient, InMemoryCache, createVersionLink } from '@quiltt/core';
3
2
  export * from '@quiltt/core';
4
- import { useQuilttSession, ConnectorSDKEventType, QuilttAuthProvider as QuilttAuthProvider$1, QuilttSettingsProvider } from '@quiltt/react';
5
3
  export { QuilttSettingsProvider, useLazyQuery, useMutation, useQuery, useQuilttClient, useQuilttConnector, useQuilttSession, useQuilttSettings, useSession, useStorage, useSubscription } from '@quiltt/react';
6
- import { jsx, jsxs } from 'react/jsx-runtime';
7
- import React, { forwardRef, useRef, useState, useEffect, useCallback, useMemo, useImperativeHandle } from 'react';
8
- import { Platform, StyleSheet, StatusBar, SafeAreaView, View, Text, Pressable, ActivityIndicator, Linking } from 'react-native';
9
- import { URL } from 'react-native-url-polyfill';
10
- import { WebView } from 'react-native-webview';
11
- import { generateStackTrace, makeBacktrace, getCauses } from '@honeybadger-io/core/build/src/util';
12
- import __node_cjsModule from 'node:module';
13
-
14
- var version = "5.0.0";
15
-
16
- // Custom Error Reporter to avoid hooking into or colliding with a client's Honeybadger singleton
17
- const notifier = {
18
- name: 'Quiltt React Native SDK Reporter',
19
- url: 'https://www.quiltt.dev/connector/sdk/react-native',
20
- version: version
21
- };
22
- class ErrorReporter {
23
- constructor(userAgent){
24
- this.noticeUrl = 'https://api.honeybadger.io/v1/notices';
25
- this.apiKey = process.env.HONEYBADGER_API_KEY_REACT_NATIVE || '';
26
- this.logger = console;
27
- this.userAgent = userAgent;
28
- }
29
- async notify(error, context) {
30
- const headers = {
31
- 'X-API-Key': this.apiKey,
32
- 'Content-Type': 'application/json',
33
- Accept: 'application/json',
34
- 'User-Agent': this.userAgent
35
- };
36
- const payload = await this.buildPayload(error, context);
37
- const method = 'POST';
38
- const body = JSON.stringify(payload);
39
- const mode = 'cors';
40
- fetch(this.noticeUrl, {
41
- headers,
42
- method,
43
- body,
44
- mode
45
- }).then((response)=>{
46
- if (response.status !== 201) {
47
- this.logger.warn(`Error report failed: unknown response from server. code=${response.status}`);
48
- return;
49
- }
50
- return response.json();
51
- }).then((data)=>{
52
- if (data) {
53
- this.logger.info(`Error report sent ⚡ https://app.honeybadger.io/notice/${data?.id}`);
54
- }
55
- });
56
- }
57
- async buildPayload(error, localContext = {}) {
58
- const notice = error;
59
- notice.stack = generateStackTrace();
60
- notice.backtrace = makeBacktrace(notice.stack);
61
- return {
62
- notifier,
63
- error: {
64
- class: notice.name,
65
- message: notice.message,
66
- backtrace: notice.backtrace,
67
- // fingerprint: this.calculateFingerprint(notice),
68
- tags: notice.tags || [],
69
- causes: getCauses(notice, this.logger)
70
- },
71
- request: {
72
- url: notice.url,
73
- component: notice.component,
74
- action: notice.action,
75
- context: localContext || {},
76
- cgi_data: {},
77
- params: {},
78
- session: {}
79
- },
80
- server: {
81
- project_root: notice.projectRoot,
82
- environment_name: this.userAgent,
83
- revision: version,
84
- hostname: this.userAgent,
85
- time: new Date().toUTCString()
86
- },
87
- details: notice.details || {}
88
- };
89
- }
90
- }
91
-
92
- const getErrorMessage = (responseStatus, error)=>{
93
- if (error) return `An error occurred while checking the Connector URL: ${error?.name} \n${error?.message}`;
94
- return responseStatus ? `An error occurred loading the Connector. Response status: ${responseStatus}` : 'An error occurred while checking the Connector URL';
95
- };
96
-
97
- const require$1 = __node_cjsModule.createRequire(import.meta.url);
98
-
99
- // Optionally import react-native-device-info if available
100
- let DeviceInfo = null;
101
- try {
102
- DeviceInfo = require$1('react-native-device-info').default;
103
- } catch {
104
- // react-native-device-info is not installed - will use fallback
105
- }
106
- /**
107
- * Gets the React version from the runtime
108
- */ const getReactVersion = ()=>{
109
- return React.version || 'unknown';
110
- };
111
- /**
112
- * Gets the React Native version from Platform constants
113
- */ const getReactNativeVersion = ()=>{
114
- try {
115
- const rnVersion = Platform.constants?.reactNativeVersion;
116
- if (rnVersion) {
117
- return `${rnVersion.major}.${rnVersion.minor}.${rnVersion.patch}`;
118
- }
119
- } catch (error) {
120
- console.warn('Failed to get React Native version:', error);
121
- }
122
- return 'unknown';
123
- };
124
- /**
125
- * Gets OS information (platform and version)
126
- */ const getOSInfo = ()=>{
127
- try {
128
- const os = Platform.OS // 'ios' or 'android'
129
- ;
130
- const osVersion = Platform.Version // string (iOS) or number (Android)
131
- ;
132
- // Map platform names to correct capitalization
133
- const platformNames = {
134
- ios: 'iOS',
135
- android: 'Android'
136
- };
137
- const osName = platformNames[os] || 'Unknown';
138
- return `${osName}/${osVersion}`;
139
- } catch (error) {
140
- console.warn('Failed to get OS info:', error);
141
- return 'Unknown/Unknown';
142
- }
143
- };
144
- /**
145
- * Gets device model information
146
- * Falls back to 'Unknown' if react-native-device-info is not installed
147
- */ const getDeviceModel = async ()=>{
148
- if (!DeviceInfo) {
149
- return 'Unknown';
150
- }
151
- try {
152
- const model = await DeviceInfo.getModel();
153
- return model || 'Unknown';
154
- } catch (error) {
155
- console.warn('Failed to get device model:', error);
156
- return 'Unknown';
157
- }
158
- };
159
- /**
160
- * Generates platform information string for React Native
161
- * Format: React/<version>; ReactNative/<version>; <OS>/<version>; <device-model>
162
- */ const getPlatformInfo = async ()=>{
163
- const reactVersion = getReactVersion();
164
- const rnVersion = getReactNativeVersion();
165
- const osInfo = getOSInfo();
166
- const deviceModel = await getDeviceModel();
167
- return `React/${reactVersion}; ReactNative/${rnVersion}; ${osInfo}; ${deviceModel}`;
168
- };
169
- /**
170
- * Synchronously generates platform information string for React Native
171
- * Format: React/<version>; ReactNative/<version>; <OS>/<version>; Unknown
172
- * Note: Device model is set to 'Unknown' since it requires async DeviceInfo call
173
- */ const getPlatformInfoSync = ()=>{
174
- const reactVersion = getReactVersion();
175
- const rnVersion = getReactNativeVersion();
176
- const osInfo = getOSInfo();
177
- return `React/${reactVersion}; ReactNative/${rnVersion}; ${osInfo}; Unknown`;
178
- };
179
- /**
180
- * Generates User-Agent string for React Native SDK
181
- * Format: Quiltt/<sdk-version> (React/<version>; ReactNative/<version>; <OS>/<version>; <device-model>)
182
- */ const getUserAgent = async (sdkVersion)=>{
183
- const platformInfo = await getPlatformInfo();
184
- return getUserAgent$1(sdkVersion, platformInfo);
185
- };
186
-
187
- /**
188
- * Checks if a string appears to be already URL encoded
189
- * @param str The string to check
190
- * @returns boolean indicating if the string appears to be URL encoded
191
- */ const isEncoded = (str)=>{
192
- // Check for typical URL encoding patterns like %20, %3A, etc.
193
- const hasEncodedChars = /%[0-9A-F]{2}/i.test(str);
194
- // Check if double encoding has occurred (e.g., %253A instead of %3A)
195
- const hasDoubleEncoding = /%25[0-9A-F]{2}/i.test(str);
196
- // If we have encoded chars but no double encoding, it's likely properly encoded
197
- return hasEncodedChars && !hasDoubleEncoding;
198
- };
199
- /**
200
- * Smart URL encoder that ensures a string is encoded exactly once
201
- * @param str The string to encode
202
- * @returns A properly URL encoded string
203
- */ const smartEncodeURIComponent = (str)=>{
204
- if (!str) return str;
205
- // If it's already encoded, return as is
206
- if (isEncoded(str)) {
207
- console.log('URL already encoded, skipping encoding');
208
- return str;
209
- }
210
- // Otherwise, encode it
211
- const encoded = encodeURIComponent(str);
212
- console.log('URL encoded');
213
- return encoded;
214
- };
215
- /**
216
- * Checks if a string appears to be double-encoded
217
- */ const isDoubleEncoded = (str)=>{
218
- if (!str) return false;
219
- return /%25[0-9A-F]{2}/i.test(str);
220
- };
221
- /**
222
- * Normalizes a URL string by decoding it once if it appears to be double-encoded
223
- */ const normalizeUrlEncoding = (urlStr)=>{
224
- if (isDoubleEncoded(urlStr)) {
225
- console.log('Detected double-encoded URL:', urlStr);
226
- const normalized = decodeURIComponent(urlStr);
227
- console.log('Normalized to:', normalized);
228
- return normalized;
229
- }
230
- return urlStr;
231
- };
232
-
233
- const AndroidSafeAreaView = ({ testId, children })=>/*#__PURE__*/ jsx(SafeAreaView, {
234
- testID: testId,
235
- style: styles$2.AndroidSafeArea,
236
- children: children
237
- });
238
- const styles$2 = StyleSheet.create({
239
- AndroidSafeArea: {
240
- flex: 1,
241
- backgroundColor: 'white',
242
- paddingTop: Platform.OS === 'android' ? StatusBar.currentHeight : 0
243
- }
244
- });
245
-
246
- const ErrorScreen = ({ testId, error, cta })=>/*#__PURE__*/ jsx(AndroidSafeAreaView, {
247
- testId: testId,
248
- children: /*#__PURE__*/ jsxs(View, {
249
- style: [
250
- styles$1.container,
251
- styles$1.padding
252
- ],
253
- children: [
254
- /*#__PURE__*/ jsxs(View, {
255
- style: {
256
- flex: 1,
257
- justifyContent: 'center'
258
- },
259
- children: [
260
- /*#__PURE__*/ jsx(View, {
261
- style: {
262
- flexDirection: 'row',
263
- justifyContent: 'space-between',
264
- alignItems: 'center',
265
- marginVertical: 10
266
- },
267
- children: /*#__PURE__*/ jsx(Text, {
268
- style: [
269
- styles$1.title
270
- ],
271
- children: "Cannot connect to the internet."
272
- })
273
- }),
274
- /*#__PURE__*/ jsx(Text, {
275
- style: [
276
- styles$1.subtitle
277
- ],
278
- children: error
279
- })
280
- ]
281
- }),
282
- /*#__PURE__*/ jsx(Pressable, {
283
- style: [
284
- styles$1.pressable
285
- ],
286
- onPress: cta,
287
- children: /*#__PURE__*/ jsx(Text, {
288
- style: [
289
- styles$1.pressableText
290
- ],
291
- children: "Exit"
292
- })
293
- })
294
- ]
295
- })
296
- });
297
- const styles$1 = StyleSheet.create({
298
- container: {
299
- flex: 1,
300
- flexDirection: 'column',
301
- justifyContent: 'flex-start',
302
- alignItems: 'stretch',
303
- backgroundColor: '#F3F4F6'
304
- },
305
- title: {
306
- color: '#1F2937',
307
- fontSize: 30,
308
- fontWeight: 'bold'
309
- },
310
- subtitle: {
311
- color: 'rgba(107, 114, 128, 1)'
312
- },
313
- padding: {
314
- paddingHorizontal: 16,
315
- paddingVertical: 24
316
- },
317
- pressable: {
318
- marginTop: 20,
319
- backgroundColor: '#1F2937',
320
- padding: 10,
321
- paddingHorizontal: 25,
322
- borderRadius: 5
323
- },
324
- pressableText: {
325
- color: '#fff',
326
- textAlign: 'center'
327
- }
328
- });
329
-
330
- const LoadingScreen = ({ testId })=>/*#__PURE__*/ jsx(AndroidSafeAreaView, {
331
- testId: testId,
332
- children: /*#__PURE__*/ jsx(View, {
333
- style: {
334
- flex: 1,
335
- justifyContent: 'center',
336
- alignItems: 'center'
337
- },
338
- children: /*#__PURE__*/ jsx(ActivityIndicator, {
339
- testID: "activity-indicator",
340
- size: "large",
341
- color: "#5928A3"
342
- })
343
- })
344
- });
345
-
346
- const PREFLIGHT_RETRY_COUNT = 3;
347
- const parseMetadata = (url, connectorId)=>{
348
- const metadata = {
349
- connectorId: url.searchParams.get('connectorId') ?? connectorId
350
- };
351
- const profileId = url.searchParams.get('profileId');
352
- if (profileId) metadata.profileId = profileId;
353
- const connectionId = url.searchParams.get('connectionId');
354
- if (connectionId) metadata.connectionId = connectionId;
355
- const connectorSessionId = url.searchParams.get('connectorSession');
356
- if (connectorSessionId) metadata.connectorSession = {
357
- id: connectorSessionId
358
- };
359
- return metadata;
360
- };
361
- const checkConnectorUrl = async (connectorUrl, errorReporter, retryCount = 0)=>{
362
- let responseStatus;
363
- try {
364
- const response = await fetch(connectorUrl);
365
- switch(response.status){
366
- case 200:
367
- return {
368
- checked: true
369
- };
370
- case 400:
371
- console.log('Invalid configuration');
372
- return {
373
- checked: true
374
- };
375
- case 404:
376
- console.error('Connector not found');
377
- return {
378
- checked: true
379
- };
380
- default:
381
- throw new Error('Connector URL is not routable.');
382
- }
383
- } catch (error) {
384
- // Log error for debugging
385
- console.error(error);
386
- // Try again
387
- if (retryCount < PREFLIGHT_RETRY_COUNT) {
388
- const delay = 50 * 2 ** retryCount;
389
- await new Promise((resolve)=>setTimeout(resolve, delay));
390
- console.log(`Retrying connection... Attempt ${retryCount + 1}`);
391
- return checkConnectorUrl(connectorUrl, errorReporter, retryCount + 1);
392
- }
393
- // Report error after retries exhausted
394
- const errorMessage = getErrorMessage(responseStatus, error);
395
- const errorToSend = error || new Error(errorMessage);
396
- const context = {
397
- connectorUrl,
398
- responseStatus
399
- };
400
- await errorReporter.notify(errorToSend, context);
401
- // Return errored preflight check
402
- return {
403
- checked: true,
404
- error: errorMessage
405
- };
406
- }
407
- };
408
- /**
409
- * Handle opening OAuth URLs with proper encoding detection and normalization
410
- */ const handleOAuthUrl = (oauthUrl)=>{
411
- try {
412
- // Throw error if oauthUrl is null or undefined
413
- if (oauthUrl == null) {
414
- throw new Error('OAuth URL missing');
415
- }
416
- // Convert to string if it's a URL object
417
- const urlString = oauthUrl.toString();
418
- // Throw error if the resulting string is empty
419
- if (!urlString || urlString.trim() === '') {
420
- throw new Error('Empty OAuth URL');
421
- }
422
- // Normalize the URL encoding
423
- const normalizedUrl = normalizeUrlEncoding(urlString);
424
- // Open the normalized URL
425
- Linking.openURL(normalizedUrl);
426
- } catch (_error) {
427
- console.error('OAuth URL handling error');
428
- // Only try the fallback if oauthUrl is not null
429
- if (oauthUrl != null) {
430
- try {
431
- const fallbackUrl = typeof oauthUrl === 'string' ? oauthUrl : oauthUrl.toString();
432
- console.log('Attempting fallback OAuth opening');
433
- Linking.openURL(fallbackUrl);
434
- } catch (_fallbackError) {
435
- console.error('Fallback OAuth opening failed');
436
- }
437
- }
438
- }
439
- };
440
- const QuilttConnector = /*#__PURE__*/ forwardRef(({ connectorId, connectionId, institution, oauthRedirectUrl, onEvent, onLoad, onExit, onExitSuccess, onExitAbort, onExitError, testId }, ref)=>{
441
- const webViewRef = useRef(null);
442
- const { session } = useQuilttSession();
443
- const [preFlightCheck, setPreFlightCheck] = useState({
444
- checked: false
445
- });
446
- const [errorReporter, setErrorReporter] = useState(null);
447
- const [userAgent, setUserAgent] = useState('');
448
- // Initialize error reporter and user agent
449
- useEffect(()=>{
450
- let mounted = true;
451
- const init = async ()=>{
452
- const agent = await getUserAgent(version);
453
- if (mounted) {
454
- setUserAgent(agent);
455
- setErrorReporter(new ErrorReporter(agent));
456
- }
457
- };
458
- init();
459
- return ()=>{
460
- mounted = false;
461
- };
462
- }, []);
463
- // Script to disable scrolling on header
464
- const disableHeaderScrollScript = `
465
- (function() {
466
- const header = document.querySelector('header');
467
- if (header) {
468
- header.style.position = 'fixed';
469
- header.style.top = '0';
470
- header.style.left = '0';
471
- header.style.right = '0';
472
- header.style.zIndex = '1000';
473
- }
474
- })();
475
- `;
476
- const onLoadEnd = useCallback(()=>{
477
- if (Platform.OS === 'ios') {
478
- webViewRef.current?.injectJavaScript(disableHeaderScrollScript);
479
- }
480
- }, []);
481
- // Ensure oauthRedirectUrl is encoded properly - only once
482
- const safeOAuthRedirectUrl = useMemo(()=>{
483
- return smartEncodeURIComponent(oauthRedirectUrl);
484
- }, [
485
- oauthRedirectUrl
486
- ]);
487
- const connectorUrl = useMemo(()=>{
488
- if (!userAgent) return null;
489
- const url = new URL(`https://${connectorId}.quiltt.app`);
490
- // For normal parameters, just append them directly
491
- url.searchParams.append('mode', 'webview');
492
- url.searchParams.append('agent', userAgent);
493
- // For the oauth_redirect_url, we need to be careful
494
- // If it's already encoded, we need to decode it once to prevent
495
- // the automatic encoding that happens with searchParams.append
496
- if (isEncoded(safeOAuthRedirectUrl)) {
497
- const decodedOnce = decodeURIComponent(safeOAuthRedirectUrl);
498
- url.searchParams.append('oauth_redirect_url', decodedOnce);
499
- } else {
500
- url.searchParams.append('oauth_redirect_url', safeOAuthRedirectUrl);
501
- }
502
- return url.toString();
503
- }, [
504
- connectorId,
505
- safeOAuthRedirectUrl,
506
- userAgent
507
- ]);
508
- useEffect(()=>{
509
- if (preFlightCheck.checked || !connectorUrl || !errorReporter) return;
510
- const fetchDataAndSetState = async ()=>{
511
- const connectorUrlStatus = await checkConnectorUrl(connectorUrl, errorReporter);
512
- setPreFlightCheck(connectorUrlStatus);
513
- };
514
- fetchDataAndSetState();
515
- }, [
516
- connectorUrl,
517
- preFlightCheck,
518
- errorReporter
519
- ]);
520
- const initInjectedJavaScript = useCallback(()=>{
521
- const script = `\
522
- const options = {\
523
- source: 'quiltt',\
524
- type: 'Options',\
525
- token: '${session?.token}',\
526
- connectorId: '${connectorId}',\
527
- connectionId: '${connectionId}',\
528
- institution: '${institution}', \
529
- };\
530
- const compactedOptions = Object.keys(options).reduce((acc, key) => {\
531
- if (options[key] !== 'undefined') {\
532
- acc[key] = options[key];\
533
- }\
534
- return acc;\
535
- }, {});\
536
- window.postMessage(compactedOptions);\
537
- `;
538
- webViewRef.current?.injectJavaScript(script);
539
- }, [
540
- connectionId,
541
- connectorId,
542
- institution,
543
- session?.token
544
- ]);
545
- const isQuilttEvent = useCallback((url)=>url.protocol === 'quilttconnector:', []);
546
- const shouldRender = useCallback((url)=>!isQuilttEvent(url), [
547
- isQuilttEvent
548
- ]);
549
- const clearLocalStorage = useCallback(()=>{
550
- const script = 'localStorage.clear();';
551
- webViewRef.current?.injectJavaScript(script);
552
- }, []);
553
- const handleQuilttEvent = useCallback((url)=>{
554
- url.searchParams.delete('source');
555
- url.searchParams.append('connectorId', connectorId);
556
- const metadata = parseMetadata(url, connectorId);
557
- requestAnimationFrame(()=>{
558
- const eventType = url.host;
559
- switch(eventType){
560
- case 'Load':
561
- console.log('Event: Load');
562
- initInjectedJavaScript();
563
- onEvent?.(ConnectorSDKEventType.Load, metadata);
564
- onLoad?.(metadata);
565
- break;
566
- case 'ExitAbort':
567
- console.log('Event: ExitAbort');
568
- clearLocalStorage();
569
- onEvent?.(ConnectorSDKEventType.ExitAbort, metadata);
570
- onExit?.(ConnectorSDKEventType.ExitAbort, metadata);
571
- onExitAbort?.(metadata);
572
- break;
573
- case 'ExitError':
574
- console.log('Event: ExitError');
575
- clearLocalStorage();
576
- onEvent?.(ConnectorSDKEventType.ExitError, metadata);
577
- onExit?.(ConnectorSDKEventType.ExitError, metadata);
578
- onExitError?.(metadata);
579
- break;
580
- case 'ExitSuccess':
581
- console.log('Event: ExitSuccess');
582
- clearLocalStorage();
583
- onEvent?.(ConnectorSDKEventType.ExitSuccess, metadata);
584
- onExit?.(ConnectorSDKEventType.ExitSuccess, metadata);
585
- onExitSuccess?.(metadata);
586
- break;
587
- case 'Authenticate':
588
- console.log('Event: Authenticate');
589
- break;
590
- case 'Navigate':
591
- {
592
- console.log('Event: Navigate');
593
- const navigateUrl = url.searchParams.get('url');
594
- if (navigateUrl) {
595
- if (isEncoded(navigateUrl)) {
596
- try {
597
- const decodedUrl = decodeURIComponent(navigateUrl);
598
- handleOAuthUrl(decodedUrl);
599
- } catch (_error) {
600
- console.error('Navigate URL decoding failed, using original');
601
- handleOAuthUrl(navigateUrl);
602
- }
603
- } else {
604
- handleOAuthUrl(navigateUrl);
605
- }
606
- } else {
607
- console.error('Navigate URL missing from request');
608
- }
609
- break;
610
- }
611
- // NOTE: The `OauthRequested` is deprecated and should not be used
612
- default:
613
- console.log(`Unhandled event: ${eventType}`);
614
- break;
615
- }
616
- });
617
- }, [
618
- clearLocalStorage,
619
- connectorId,
620
- initInjectedJavaScript,
621
- onEvent,
622
- onExit,
623
- onExitAbort,
624
- onExitError,
625
- onExitSuccess,
626
- onLoad
627
- ]);
628
- const requestHandler = useCallback((request)=>{
629
- const url = new URL(request.url);
630
- if (isQuilttEvent(url)) {
631
- handleQuilttEvent(url);
632
- return false;
633
- }
634
- if (shouldRender(url)) {
635
- return true;
636
- }
637
- // Plaid set oauth url by doing window.location.href = url
638
- // So we use `handleOAuthUrl` as a catch all and assume all url got to this step is Plaid OAuth url
639
- handleOAuthUrl(url);
640
- return false;
641
- }, [
642
- handleQuilttEvent,
643
- isQuilttEvent,
644
- shouldRender
645
- ]);
646
- // Expose method to handle OAuth callbacks from parent component
647
- useImperativeHandle(ref, ()=>({
648
- handleOAuthCallback: (callbackUrl)=>{
649
- try {
650
- console.log('Handling OAuth callback:', callbackUrl);
651
- const url = new URL(callbackUrl);
652
- // Extract OAuth callback parameters
653
- const oauthParams = {};
654
- url.searchParams.forEach((value, key)=>{
655
- oauthParams[key] = value;
656
- });
657
- // Send OAuth callback data to the connector via postMessage
658
- // This preserves the connector's state and allows events to fire properly
659
- const message = {
660
- source: 'quiltt',
661
- type: 'OAuthCallback',
662
- data: {
663
- url: callbackUrl,
664
- params: oauthParams
665
- }
666
- };
667
- const script = `
668
- (function() {
669
- try {
670
- window.postMessage(${JSON.stringify(message)});
671
- console.log('OAuth callback message sent to connector');
672
- } catch (e) {
673
- console.error('Failed to send OAuth callback message:', e);
674
- }
675
- })();
676
- true;
677
- `;
678
- webViewRef.current?.injectJavaScript(script);
679
- } catch (error) {
680
- console.error('Error handling OAuth callback:', error);
681
- }
682
- }
683
- }), []);
684
- if (!preFlightCheck.checked || !connectorUrl) {
685
- return /*#__PURE__*/ jsx(LoadingScreen, {
686
- testId: "loading-screen"
687
- });
688
- }
689
- if (preFlightCheck.error) {
690
- return /*#__PURE__*/ jsx(ErrorScreen, {
691
- testId: "error-screen",
692
- error: preFlightCheck.error,
693
- cta: ()=>onExitError?.({
694
- connectorId
695
- })
696
- });
697
- }
698
- return /*#__PURE__*/ jsx(AndroidSafeAreaView, {
699
- testId: testId,
700
- children: /*#__PURE__*/ jsx(WebView, {
701
- ref: webViewRef,
702
- // Plaid keeps sending window.location = 'about:srcdoc' and causes some noise in RN
703
- domStorageEnabled: true,
704
- javaScriptEnabled: true,
705
- onLoadEnd: onLoadEnd,
706
- onShouldStartLoadWithRequest: requestHandler,
707
- originWhitelist: [
708
- '*'
709
- ],
710
- scrollEnabled: true,
711
- showsHorizontalScrollIndicator: false,
712
- showsVerticalScrollIndicator: false,
713
- source: {
714
- uri: connectorUrl
715
- },
716
- style: styles.webview,
717
- testID: "webview",
718
- webviewDebuggingEnabled: true,
719
- ...Platform.OS === 'ios' ? {
720
- allowsBackForwardNavigationGestures: false,
721
- allowsInlineMediaPlayback: true,
722
- automaticallyAdjustContentInsets: false,
723
- bounces: false,
724
- contentInsetAdjustmentBehavior: 'never',
725
- dataDetectorTypes: 'none',
726
- decelerationRate: 'normal',
727
- keyboardDisplayRequiresUserAction: false,
728
- scrollEventThrottle: 16,
729
- startInLoadingState: true
730
- } : {
731
- androidLayerType: 'hardware',
732
- cacheEnabled: true,
733
- cacheMode: 'LOAD_CACHE_ELSE_NETWORK',
734
- overScrollMode: 'never'
735
- }
736
- })
737
- });
738
- });
739
- // Add styles for the WebView container
740
- const styles = StyleSheet.create({
741
- webviewContainer: {
742
- flex: 1,
743
- backgroundColor: '#ffffff'
744
- },
745
- webview: {
746
- flex: 1,
747
- overflow: 'hidden'
748
- }
749
- });
750
- QuilttConnector.displayName = 'QuilttConnector';
751
-
752
- /**
753
- * React Native-specific QuilttAuthProvider that injects platform information
754
- * into the GraphQL client's User-Agent header.
755
- *
756
- * If a token is provided, will validate the token against the api and then import
757
- * it into trusted storage. While this process is happening, the component is put
758
- * into a loading state and the children are not rendered to prevent race conditions
759
- * from triggering within the transitionary state.
760
- */ const QuilttAuthProvider = ({ graphqlClient, token, children })=>{
761
- // Create React Native-specific GraphQL client with platform info if no custom client provided
762
- const platformClient = useMemo(()=>{
763
- if (graphqlClient) {
764
- return graphqlClient;
765
- }
766
- const platformInfo = getPlatformInfoSync();
767
- return new QuilttClient({
768
- cache: new InMemoryCache(),
769
- versionLink: createVersionLink(platformInfo)
770
- });
771
- }, [
772
- graphqlClient
773
- ]);
774
- return /*#__PURE__*/ jsx(QuilttAuthProvider$1, {
775
- token: token,
776
- graphqlClient: platformClient,
777
- children: children
778
- });
779
- };
780
-
781
- /**
782
- * React Native-specific QuilttProvider that combines settings and auth providers
783
- * with platform-specific GraphQL client configuration.
784
- */ const QuilttProvider = ({ clientId, graphqlClient, token, children })=>{
785
- return /*#__PURE__*/ jsx(QuilttSettingsProvider, {
786
- clientId: clientId,
787
- children: /*#__PURE__*/ jsx(QuilttAuthProvider, {
788
- token: token,
789
- graphqlClient: graphqlClient,
790
- children: children
791
- })
792
- });
793
- };
4
+ export * from './components/index.js';
5
+ export * from './providers/index.js';
794
6
 
795
7
  // Hermes doesn't have atob
796
8
  // https://github.com/facebook/hermes/issues/1178
797
9
  if (!global.atob) {
798
10
  global.atob = decode;
799
11
  }
800
-
801
- export { QuilttAuthProvider, QuilttConnector, QuilttProvider, checkConnectorUrl, handleOAuthUrl };