@quiltt/capacitor 5.1.2
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/LICENSE.md +9 -0
- package/QuilttConnector.podspec +17 -0
- package/README.md +214 -0
- package/android/build.gradle +59 -0
- package/android/proguard-rules.pro +11 -0
- package/android/src/main/AndroidManifest.xml +2 -0
- package/android/src/main/kotlin/app/quiltt/capacitor/QuilttConnectorPlugin.kt +83 -0
- package/dist/index.cjs +12 -0
- package/dist/index.d.ts +86 -0
- package/dist/index.js +10 -0
- package/dist/react.cjs +296 -0
- package/dist/react.d.ts +109 -0
- package/dist/react.js +288 -0
- package/dist/vue.cjs +19 -0
- package/dist/vue.d.ts +87 -0
- package/dist/vue.js +11 -0
- package/dist/web-BgcuNl8a.cjs +42 -0
- package/dist/web-CUWsqcUV.js +42 -0
- package/ios/Sources/QuilttConnectorPlugin/QuilttConnectorPlugin.swift +98 -0
- package/package.json +121 -0
- package/src/components/QuilttConnector.tsx +349 -0
- package/src/components/index.ts +1 -0
- package/src/definitions.ts +81 -0
- package/src/index.ts +31 -0
- package/src/plugin.ts +11 -0
- package/src/react.ts +33 -0
- package/src/vue.ts +42 -0
- package/src/web.ts +44 -0
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import {
|
|
2
|
+
forwardRef,
|
|
3
|
+
useCallback,
|
|
4
|
+
useEffect,
|
|
5
|
+
useImperativeHandle,
|
|
6
|
+
useMemo,
|
|
7
|
+
useRef,
|
|
8
|
+
useState,
|
|
9
|
+
} from 'react'
|
|
10
|
+
|
|
11
|
+
import type { ConnectorSDKCallbackMetadata, ConnectorSDKCallbacks } from '@quiltt/react'
|
|
12
|
+
import { ConnectorSDKEventType, useQuilttSession } from '@quiltt/react'
|
|
13
|
+
|
|
14
|
+
import { QuilttConnector as QuilttConnectorPlugin } from '../plugin'
|
|
15
|
+
|
|
16
|
+
export type QuilttConnectorHandle = {
|
|
17
|
+
handleOAuthCallback: (url: string) => void
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
type QuilttConnectorProps = {
|
|
21
|
+
connectorId: string
|
|
22
|
+
connectionId?: string
|
|
23
|
+
institution?: string
|
|
24
|
+
/**
|
|
25
|
+
* The app launcher URL for mobile OAuth flows.
|
|
26
|
+
* This URL should be a Universal Link (iOS) or App Link (Android) that redirects back to your app.
|
|
27
|
+
*/
|
|
28
|
+
appLauncherUrl?: string
|
|
29
|
+
style?: React.CSSProperties
|
|
30
|
+
className?: string
|
|
31
|
+
} & ConnectorSDKCallbacks
|
|
32
|
+
|
|
33
|
+
const trustedQuilttHostSuffixes = ['quiltt.io', 'quiltt.dev', 'quiltt.app']
|
|
34
|
+
|
|
35
|
+
const isTrustedQuilttOrigin = (origin: string): boolean => {
|
|
36
|
+
try {
|
|
37
|
+
const originUrl = new URL(origin)
|
|
38
|
+
if (originUrl.protocol !== 'https:') {
|
|
39
|
+
return false
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
const hostname = originUrl.hostname.toLowerCase()
|
|
43
|
+
return trustedQuilttHostSuffixes.some(
|
|
44
|
+
(suffix) => hostname === suffix || hostname.endsWith(`.${suffix}`)
|
|
45
|
+
)
|
|
46
|
+
} catch {
|
|
47
|
+
return false
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const decodeIfEncoded = (value: string): string => {
|
|
52
|
+
try {
|
|
53
|
+
const decoded = decodeURIComponent(value)
|
|
54
|
+
return decoded === value ? value : decoded
|
|
55
|
+
} catch {
|
|
56
|
+
return value
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const normalizeUrlValue = (value: string): string => decodeIfEncoded(value.trim())
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* QuilttConnector component for Capacitor apps
|
|
64
|
+
* Embeds the Quiltt Connector in an iframe and handles OAuth flows via native plugins
|
|
65
|
+
*/
|
|
66
|
+
export const QuilttConnector = forwardRef<QuilttConnectorHandle, QuilttConnectorProps>(
|
|
67
|
+
(
|
|
68
|
+
{
|
|
69
|
+
connectorId,
|
|
70
|
+
connectionId,
|
|
71
|
+
institution,
|
|
72
|
+
appLauncherUrl,
|
|
73
|
+
style,
|
|
74
|
+
className,
|
|
75
|
+
onEvent,
|
|
76
|
+
onLoad,
|
|
77
|
+
onExit,
|
|
78
|
+
onExitSuccess,
|
|
79
|
+
onExitAbort,
|
|
80
|
+
onExitError,
|
|
81
|
+
},
|
|
82
|
+
ref
|
|
83
|
+
) => {
|
|
84
|
+
const iframeRef = useRef<HTMLIFrameElement>(null)
|
|
85
|
+
const { session } = useQuilttSession()
|
|
86
|
+
const [isLoaded, setIsLoaded] = useState(false)
|
|
87
|
+
const [loadError, setLoadError] = useState<string | null>(null)
|
|
88
|
+
|
|
89
|
+
// Connector origin for secure postMessage targeting
|
|
90
|
+
const connectorOrigin = useMemo(() => `https://${connectorId}.quiltt.app`, [connectorId])
|
|
91
|
+
|
|
92
|
+
// Build connector URL
|
|
93
|
+
const connectorUrl = useMemo(() => {
|
|
94
|
+
const url = new URL(connectorOrigin)
|
|
95
|
+
|
|
96
|
+
if (session?.token) {
|
|
97
|
+
url.searchParams.set('token', session.token)
|
|
98
|
+
}
|
|
99
|
+
if (connectionId) {
|
|
100
|
+
url.searchParams.set('connectionId', connectionId)
|
|
101
|
+
}
|
|
102
|
+
if (institution) {
|
|
103
|
+
url.searchParams.set('institution', institution)
|
|
104
|
+
}
|
|
105
|
+
if (appLauncherUrl) {
|
|
106
|
+
url.searchParams.set('app_launcher_url', normalizeUrlValue(appLauncherUrl))
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
if (typeof window !== 'undefined') {
|
|
110
|
+
url.searchParams.set('embed_location', window.location.href)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
// Set mode for inline iframe embedding
|
|
114
|
+
url.searchParams.set('mode', 'INLINE')
|
|
115
|
+
|
|
116
|
+
return url.toString()
|
|
117
|
+
}, [connectorOrigin, session?.token, connectionId, institution, appLauncherUrl])
|
|
118
|
+
|
|
119
|
+
useEffect(() => {
|
|
120
|
+
setIsLoaded(false)
|
|
121
|
+
setLoadError(null)
|
|
122
|
+
|
|
123
|
+
const abortController = new AbortController()
|
|
124
|
+
|
|
125
|
+
const runPreflight = async () => {
|
|
126
|
+
try {
|
|
127
|
+
await fetch(connectorUrl, {
|
|
128
|
+
method: 'GET',
|
|
129
|
+
mode: 'no-cors',
|
|
130
|
+
credentials: 'omit',
|
|
131
|
+
signal: abortController.signal,
|
|
132
|
+
})
|
|
133
|
+
} catch {
|
|
134
|
+
setLoadError('Unable to reach Quiltt Connector. Check network and connector settings.')
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
void runPreflight()
|
|
139
|
+
|
|
140
|
+
return () => {
|
|
141
|
+
abortController.abort()
|
|
142
|
+
}
|
|
143
|
+
}, [connectorUrl])
|
|
144
|
+
|
|
145
|
+
useEffect(() => {
|
|
146
|
+
if (isLoaded || loadError) {
|
|
147
|
+
return
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
const timeoutId = window.setTimeout(() => {
|
|
151
|
+
setLoadError('Connector took too long to load. Please retry.')
|
|
152
|
+
}, 15000)
|
|
153
|
+
|
|
154
|
+
return () => {
|
|
155
|
+
window.clearTimeout(timeoutId)
|
|
156
|
+
}
|
|
157
|
+
}, [isLoaded, loadError])
|
|
158
|
+
|
|
159
|
+
const postOAuthCallbackToIframe = useCallback(
|
|
160
|
+
(callbackUrl: string) => {
|
|
161
|
+
if (!iframeRef.current?.contentWindow) {
|
|
162
|
+
return
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
const normalizedCallbackUrl = normalizeUrlValue(callbackUrl)
|
|
166
|
+
|
|
167
|
+
try {
|
|
168
|
+
const callback = new URL(normalizedCallbackUrl)
|
|
169
|
+
const params: Record<string, string> = {}
|
|
170
|
+
callback.searchParams.forEach((value, key) => {
|
|
171
|
+
params[key] = value
|
|
172
|
+
})
|
|
173
|
+
|
|
174
|
+
iframeRef.current.contentWindow.postMessage(
|
|
175
|
+
{
|
|
176
|
+
source: 'quiltt',
|
|
177
|
+
type: 'OAuthCallback',
|
|
178
|
+
data: {
|
|
179
|
+
url: normalizedCallbackUrl,
|
|
180
|
+
params,
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
connectorOrigin
|
|
184
|
+
)
|
|
185
|
+
} catch {
|
|
186
|
+
iframeRef.current.contentWindow.postMessage(
|
|
187
|
+
{
|
|
188
|
+
source: 'quiltt',
|
|
189
|
+
type: 'OAuthCallback',
|
|
190
|
+
data: {
|
|
191
|
+
url: normalizedCallbackUrl,
|
|
192
|
+
params: {},
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
connectorOrigin
|
|
196
|
+
)
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
[connectorOrigin]
|
|
200
|
+
)
|
|
201
|
+
|
|
202
|
+
// Handle messages from the iframe
|
|
203
|
+
// The platform MessageBus sends: { source: 'quiltt', type: 'Load'|'ExitSuccess'|..., ...metadata }
|
|
204
|
+
const handleMessage = useCallback(
|
|
205
|
+
(event: MessageEvent) => {
|
|
206
|
+
// Validate origin
|
|
207
|
+
if (!isTrustedQuilttOrigin(event.origin)) {
|
|
208
|
+
return
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const data = event.data || {}
|
|
212
|
+
// Validate message is from Quiltt MessageBus
|
|
213
|
+
if (data.source !== 'quiltt' || !data.type) return
|
|
214
|
+
|
|
215
|
+
const { type, connectionId: msgConnectionId, profileId, connectorSession, url } = data
|
|
216
|
+
|
|
217
|
+
// Build metadata from message fields
|
|
218
|
+
const metadata: ConnectorSDKCallbackMetadata = {
|
|
219
|
+
connectorId,
|
|
220
|
+
...(profileId && { profileId }),
|
|
221
|
+
...(msgConnectionId && { connectionId: msgConnectionId }),
|
|
222
|
+
...(connectorSession && { connectorSession }),
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
switch (type) {
|
|
226
|
+
case 'Load':
|
|
227
|
+
setIsLoaded(true)
|
|
228
|
+
setLoadError(null)
|
|
229
|
+
onEvent?.(ConnectorSDKEventType.Load, metadata)
|
|
230
|
+
onLoad?.(metadata)
|
|
231
|
+
break
|
|
232
|
+
|
|
233
|
+
case 'ExitSuccess':
|
|
234
|
+
onEvent?.(ConnectorSDKEventType.ExitSuccess, metadata)
|
|
235
|
+
onExit?.(ConnectorSDKEventType.ExitSuccess, metadata)
|
|
236
|
+
onExitSuccess?.(metadata)
|
|
237
|
+
break
|
|
238
|
+
|
|
239
|
+
case 'ExitAbort':
|
|
240
|
+
onEvent?.(ConnectorSDKEventType.ExitAbort, metadata)
|
|
241
|
+
onExit?.(ConnectorSDKEventType.ExitAbort, metadata)
|
|
242
|
+
onExitAbort?.(metadata)
|
|
243
|
+
break
|
|
244
|
+
|
|
245
|
+
case 'ExitError':
|
|
246
|
+
onEvent?.(ConnectorSDKEventType.ExitError, metadata)
|
|
247
|
+
onExit?.(ConnectorSDKEventType.ExitError, metadata)
|
|
248
|
+
onExitError?.(metadata)
|
|
249
|
+
break
|
|
250
|
+
|
|
251
|
+
case 'Navigate':
|
|
252
|
+
// OAuth URL - open in system browser
|
|
253
|
+
if (url) {
|
|
254
|
+
QuilttConnectorPlugin.openUrl({ url })
|
|
255
|
+
}
|
|
256
|
+
break
|
|
257
|
+
|
|
258
|
+
default:
|
|
259
|
+
// console.log(`Unhandled event: ${eventType}`)
|
|
260
|
+
break
|
|
261
|
+
}
|
|
262
|
+
},
|
|
263
|
+
[connectorId, onEvent, onLoad, onExit, onExitSuccess, onExitAbort, onExitError]
|
|
264
|
+
)
|
|
265
|
+
|
|
266
|
+
// Set up message listener
|
|
267
|
+
useEffect(() => {
|
|
268
|
+
window.addEventListener('message', handleMessage)
|
|
269
|
+
return () => window.removeEventListener('message', handleMessage)
|
|
270
|
+
}, [handleMessage])
|
|
271
|
+
|
|
272
|
+
// Listen for OAuth callbacks via deep links
|
|
273
|
+
useEffect(() => {
|
|
274
|
+
const listener = QuilttConnectorPlugin.addListener('deepLink', (event) => {
|
|
275
|
+
if (event.url) {
|
|
276
|
+
postOAuthCallbackToIframe(event.url)
|
|
277
|
+
}
|
|
278
|
+
})
|
|
279
|
+
|
|
280
|
+
// Check if app was launched with a URL
|
|
281
|
+
QuilttConnectorPlugin.getLaunchUrl().then((result) => {
|
|
282
|
+
if (result?.url) {
|
|
283
|
+
postOAuthCallbackToIframe(result.url)
|
|
284
|
+
}
|
|
285
|
+
})
|
|
286
|
+
|
|
287
|
+
return () => {
|
|
288
|
+
listener.then((l) => l.remove())
|
|
289
|
+
}
|
|
290
|
+
}, [postOAuthCallbackToIframe])
|
|
291
|
+
|
|
292
|
+
// Expose method to handle OAuth callbacks from parent component
|
|
293
|
+
useImperativeHandle(
|
|
294
|
+
ref,
|
|
295
|
+
() => ({
|
|
296
|
+
handleOAuthCallback: (callbackUrl: string) => {
|
|
297
|
+
postOAuthCallbackToIframe(callbackUrl)
|
|
298
|
+
},
|
|
299
|
+
}),
|
|
300
|
+
[postOAuthCallbackToIframe]
|
|
301
|
+
)
|
|
302
|
+
|
|
303
|
+
return (
|
|
304
|
+
<div
|
|
305
|
+
className={className}
|
|
306
|
+
style={{
|
|
307
|
+
width: '100%',
|
|
308
|
+
height: '100%',
|
|
309
|
+
position: 'relative',
|
|
310
|
+
...style,
|
|
311
|
+
}}
|
|
312
|
+
>
|
|
313
|
+
<iframe
|
|
314
|
+
ref={iframeRef}
|
|
315
|
+
src={connectorUrl}
|
|
316
|
+
title="Quiltt Connector"
|
|
317
|
+
allow="publickey-credentials-get *"
|
|
318
|
+
style={{
|
|
319
|
+
border: 'none',
|
|
320
|
+
width: '100%',
|
|
321
|
+
height: '100%',
|
|
322
|
+
}}
|
|
323
|
+
onError={() => {
|
|
324
|
+
setLoadError('Unable to load Quiltt Connector iframe.')
|
|
325
|
+
}}
|
|
326
|
+
/>
|
|
327
|
+
|
|
328
|
+
{loadError ? (
|
|
329
|
+
<div
|
|
330
|
+
style={{
|
|
331
|
+
position: 'absolute',
|
|
332
|
+
inset: 0,
|
|
333
|
+
display: 'flex',
|
|
334
|
+
alignItems: 'center',
|
|
335
|
+
justifyContent: 'center',
|
|
336
|
+
padding: '16px',
|
|
337
|
+
textAlign: 'center',
|
|
338
|
+
backgroundColor: '#fff',
|
|
339
|
+
}}
|
|
340
|
+
>
|
|
341
|
+
{loadError}
|
|
342
|
+
</div>
|
|
343
|
+
) : null}
|
|
344
|
+
</div>
|
|
345
|
+
)
|
|
346
|
+
}
|
|
347
|
+
)
|
|
348
|
+
|
|
349
|
+
QuilttConnector.displayName = 'QuilttConnector'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './QuilttConnector'
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import type { PluginListenerHandle } from '@capacitor/core'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Options for opening a URL in the system browser
|
|
5
|
+
*/
|
|
6
|
+
export interface OpenUrlOptions {
|
|
7
|
+
/**
|
|
8
|
+
* The URL to open in the system browser
|
|
9
|
+
*/
|
|
10
|
+
url: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Event data when a deep link is received
|
|
15
|
+
*/
|
|
16
|
+
export interface DeepLinkEvent {
|
|
17
|
+
/**
|
|
18
|
+
* The full URL that was used to open the app, or null if no URL was present
|
|
19
|
+
*/
|
|
20
|
+
url: string | null
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Listener function for deep link events
|
|
25
|
+
*/
|
|
26
|
+
export type DeepLinkListener = (event: DeepLinkEvent) => void
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* The Quiltt Connector Capacitor plugin interface.
|
|
30
|
+
*
|
|
31
|
+
* This plugin handles native functionality required for the Quiltt Connector:
|
|
32
|
+
* - Opening OAuth URLs in the system browser
|
|
33
|
+
* - Handling deep links / App Links / Universal Links for OAuth callbacks
|
|
34
|
+
*/
|
|
35
|
+
export interface QuilttConnectorPlugin {
|
|
36
|
+
/**
|
|
37
|
+
* Open a URL in the system browser.
|
|
38
|
+
*
|
|
39
|
+
* This is used for OAuth flows where the user needs to authenticate
|
|
40
|
+
* with their financial institution in an external browser.
|
|
41
|
+
*
|
|
42
|
+
* @param options - The options containing the URL to open
|
|
43
|
+
* @returns A promise that resolves when the browser is opened
|
|
44
|
+
*
|
|
45
|
+
* @since 5.0.3
|
|
46
|
+
*/
|
|
47
|
+
openUrl(options: OpenUrlOptions): Promise<{ completed: boolean }>
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Get the URL that was used to launch the app, if any.
|
|
51
|
+
*
|
|
52
|
+
* This is useful for handling OAuth callbacks when the app is opened
|
|
53
|
+
* from a Universal Link (iOS) or App Link (Android).
|
|
54
|
+
*
|
|
55
|
+
* @returns A promise that resolves with the launch URL, or undefined if none
|
|
56
|
+
*
|
|
57
|
+
* @since 5.0.3
|
|
58
|
+
*/
|
|
59
|
+
getLaunchUrl(): Promise<DeepLinkEvent>
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Listen for deep link events.
|
|
63
|
+
*
|
|
64
|
+
* This is called when the app is opened via a Universal Link (iOS)
|
|
65
|
+
* or App Link (Android), typically during OAuth callback flows.
|
|
66
|
+
*
|
|
67
|
+
* @param eventName - The event name ('deepLink')
|
|
68
|
+
* @param listenerFunc - The callback function to handle the event
|
|
69
|
+
* @returns A promise that resolves with a handle to remove the listener
|
|
70
|
+
*
|
|
71
|
+
* @since 5.0.3
|
|
72
|
+
*/
|
|
73
|
+
addListener(eventName: 'deepLink', listenerFunc: DeepLinkListener): Promise<PluginListenerHandle>
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Remove all listeners for this plugin.
|
|
77
|
+
*
|
|
78
|
+
* @since 5.0.3
|
|
79
|
+
*/
|
|
80
|
+
removeAllListeners(): Promise<void>
|
|
81
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @quiltt/capacitor - Framework-agnostic Capacitor plugin for Quiltt Connector
|
|
3
|
+
*
|
|
4
|
+
* This entry point provides the native Capacitor plugin without any
|
|
5
|
+
* framework dependencies. Works with Vue, Angular, Svelte, vanilla JS, etc.
|
|
6
|
+
*
|
|
7
|
+
* For React apps, import from '@quiltt/capacitor/react' instead.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```typescript
|
|
11
|
+
* import { QuilttConnector } from '@quiltt/capacitor'
|
|
12
|
+
*
|
|
13
|
+
* // Open OAuth URL in system browser
|
|
14
|
+
* await QuilttConnector.openUrl({ url: 'https://...' })
|
|
15
|
+
*
|
|
16
|
+
* // Listen for deep link callbacks
|
|
17
|
+
* await QuilttConnector.addListener('deepLink', ({ url }) => {
|
|
18
|
+
* console.log('OAuth callback:', url)
|
|
19
|
+
* })
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
// Export type definitions
|
|
24
|
+
export type {
|
|
25
|
+
DeepLinkEvent,
|
|
26
|
+
DeepLinkListener,
|
|
27
|
+
OpenUrlOptions,
|
|
28
|
+
QuilttConnectorPlugin,
|
|
29
|
+
} from './definitions'
|
|
30
|
+
// Export native plugin
|
|
31
|
+
export { QuilttConnector } from './plugin'
|
package/src/plugin.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { registerPlugin } from '@capacitor/core'
|
|
2
|
+
|
|
3
|
+
import type { QuilttConnectorPlugin } from './definitions'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Native Capacitor plugin for deep link handling and URL opening
|
|
7
|
+
* Used internally by QuilttConnector component for OAuth flows
|
|
8
|
+
*/
|
|
9
|
+
export const QuilttConnector = registerPlugin<QuilttConnectorPlugin>('QuilttConnector', {
|
|
10
|
+
web: () => import('./web').then((m) => new m.QuilttConnectorWeb()),
|
|
11
|
+
})
|
package/src/react.ts
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @quiltt/capacitor/react - React components for Quiltt Connector in Capacitor apps
|
|
3
|
+
*
|
|
4
|
+
* This entry point provides React components and hooks for Capacitor apps.
|
|
5
|
+
* Requires React 16.8+ and @quiltt/react as peer dependencies.
|
|
6
|
+
*
|
|
7
|
+
* For non-React apps (Vue, Angular, Svelte), import from '@quiltt/capacitor' instead.
|
|
8
|
+
*
|
|
9
|
+
* @example
|
|
10
|
+
* ```tsx
|
|
11
|
+
* import { QuilttConnector, useQuilttSession } from '@quiltt/capacitor/react'
|
|
12
|
+
*
|
|
13
|
+
* function App() {
|
|
14
|
+
* return (
|
|
15
|
+
* <QuilttConnector
|
|
16
|
+
* connectorId="<CONNECTOR_ID>"
|
|
17
|
+
* onExitSuccess={({ connectionId }) => console.log(connectionId)}
|
|
18
|
+
* />
|
|
19
|
+
* )
|
|
20
|
+
* }
|
|
21
|
+
* ```
|
|
22
|
+
*/
|
|
23
|
+
|
|
24
|
+
// Re-export all @quiltt/react functionality for convenience
|
|
25
|
+
export * from '@quiltt/react'
|
|
26
|
+
|
|
27
|
+
// Export Capacitor-specific QuilttConnector component
|
|
28
|
+
export type { QuilttConnectorHandle } from './components'
|
|
29
|
+
export { QuilttConnector } from './components'
|
|
30
|
+
// Export plugin type definitions
|
|
31
|
+
export * from './definitions'
|
|
32
|
+
// Export native plugin for advanced use cases
|
|
33
|
+
export { QuilttConnector as QuilttConnectorPlugin } from './plugin'
|
package/src/vue.ts
ADDED
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @quiltt/capacitor/vue - Vue 3 components for Quiltt Connector in Capacitor apps
|
|
3
|
+
*
|
|
4
|
+
* This entry point provides Vue 3 components and composables for Capacitor apps.
|
|
5
|
+
* Requires Vue 3.3+ and @quiltt/vue as peer dependencies.
|
|
6
|
+
*
|
|
7
|
+
* For non-framework apps in plain JS (Vue, Angular, Svelte, etc.), you can also use
|
|
8
|
+
* the framework-agnostic import from '@quiltt/capacitor' directly.
|
|
9
|
+
*
|
|
10
|
+
* @example
|
|
11
|
+
* ```typescript
|
|
12
|
+
* import { createApp } from 'vue'
|
|
13
|
+
* import { QuilttPlugin } from '@quiltt/capacitor/vue'
|
|
14
|
+
*
|
|
15
|
+
* const app = createApp(App)
|
|
16
|
+
* app.use(QuilttPlugin, { token: '<SESSION_TOKEN>' })
|
|
17
|
+
* app.mount('#app')
|
|
18
|
+
* ```
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```vue
|
|
22
|
+
* <script setup>
|
|
23
|
+
* import { QuilttConnector, useQuilttSession } from '@quiltt/capacitor/vue'
|
|
24
|
+
* </script>
|
|
25
|
+
*
|
|
26
|
+
* <template>
|
|
27
|
+
* <QuilttConnector
|
|
28
|
+
* connector-id="<CONNECTOR_ID>"
|
|
29
|
+
* @exit-success="handleSuccess"
|
|
30
|
+
* @navigate="handleNavigate"
|
|
31
|
+
* />
|
|
32
|
+
* </template>
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
|
|
36
|
+
// Re-export all @quiltt/vue functionality
|
|
37
|
+
export * from '@quiltt/vue'
|
|
38
|
+
|
|
39
|
+
// Export plugin type definitions
|
|
40
|
+
export * from './definitions'
|
|
41
|
+
// Export native plugin for OAuth handling
|
|
42
|
+
export { QuilttConnector as QuilttConnectorPlugin } from './plugin'
|
package/src/web.ts
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { WebPlugin } from '@capacitor/core'
|
|
2
|
+
|
|
3
|
+
import type { DeepLinkEvent, OpenUrlOptions, QuilttConnectorPlugin } from './definitions'
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Web implementation of the Quiltt Connector plugin.
|
|
7
|
+
*
|
|
8
|
+
* On web, OAuth flows typically work without special handling since
|
|
9
|
+
* the browser handles redirects automatically. This implementation
|
|
10
|
+
* provides basic functionality for web compatibility.
|
|
11
|
+
*/
|
|
12
|
+
export class QuilttConnectorWeb extends WebPlugin implements QuilttConnectorPlugin {
|
|
13
|
+
/**
|
|
14
|
+
* Open a URL in a new browser tab/window.
|
|
15
|
+
*
|
|
16
|
+
* On web, this simply opens the URL in a new tab using window.open.
|
|
17
|
+
*/
|
|
18
|
+
async openUrl(options: OpenUrlOptions): Promise<{ completed: boolean }> {
|
|
19
|
+
window.open(options.url, '_blank', 'noopener,noreferrer')
|
|
20
|
+
return { completed: true }
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Get the launch URL on web.
|
|
25
|
+
*
|
|
26
|
+
* On web, we check the current URL for any OAuth callback parameters.
|
|
27
|
+
* This is useful when the user is redirected back to the app after OAuth.
|
|
28
|
+
*/
|
|
29
|
+
async getLaunchUrl(): Promise<DeepLinkEvent> {
|
|
30
|
+
const currentUrl = window.location.href
|
|
31
|
+
|
|
32
|
+
// Check if the current URL contains OAuth callback parameters
|
|
33
|
+
// Common patterns include: code=, state=, error=
|
|
34
|
+
const url = new URL(currentUrl)
|
|
35
|
+
const hasOAuthParams =
|
|
36
|
+
url.searchParams.has('code') || url.searchParams.has('state') || url.searchParams.has('error')
|
|
37
|
+
|
|
38
|
+
if (hasOAuthParams) {
|
|
39
|
+
return { url: currentUrl }
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
return { url: null }
|
|
43
|
+
}
|
|
44
|
+
}
|