@quiltt/vue 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/README.md +212 -0
- package/dist/components/index.cjs +707 -0
- package/dist/components/index.d.ts +278 -0
- package/dist/components/index.js +703 -0
- package/dist/composables/index.cjs +617 -0
- package/dist/composables/index.d.ts +191 -0
- package/dist/composables/index.js +609 -0
- package/dist/index.cjs +75 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.js +10 -0
- package/dist/plugin/index.cjs +176 -0
- package/dist/plugin/index.d.ts +48 -0
- package/dist/plugin/index.js +171 -0
- package/package.json +81 -0
- package/src/components/QuilttButton.ts +121 -0
- package/src/components/QuilttConnector.ts +215 -0
- package/src/components/QuilttContainer.ts +130 -0
- package/src/components/index.ts +3 -0
- package/src/composables/index.ts +7 -0
- package/src/composables/useQuilttConnector.ts +312 -0
- package/src/composables/useQuilttInstitutions.ts +114 -0
- package/src/composables/useQuilttResolvable.ts +94 -0
- package/src/composables/useQuilttSession.ts +239 -0
- package/src/composables/useQuilttSettings.ts +15 -0
- package/src/composables/useSession.ts +74 -0
- package/src/composables/useStorage.ts +47 -0
- package/src/constants/deprecation-warnings.ts +2 -0
- package/src/index.ts +34 -0
- package/src/plugin/QuilttPlugin.ts +204 -0
- package/src/plugin/index.ts +23 -0
- package/src/plugin/keys.ts +26 -0
- package/src/utils/index.ts +7 -0
- package/src/utils/telemetry.ts +73 -0
- package/src/version.ts +1 -0
|
@@ -0,0 +1,609 @@
|
|
|
1
|
+
import { version as version$1, inject, computed, ref, watch, onMounted, onUnmounted, toValue, shallowRef, getCurrentScope, onScopeDispose } from 'vue';
|
|
2
|
+
import { AuthAPI, JsonWebTokenParse, cdnBase, ConnectorsAPI, GlobalStorage, Timeoutable } from '@quiltt/core';
|
|
3
|
+
import { getSDKAgent as getSDKAgent$1, getBrowserInfo, extractVersionNumber } from '@quiltt/core/utils';
|
|
4
|
+
|
|
5
|
+
const oauthRedirectUrlDeprecationWarning = '[Quiltt] `oauthRedirectUrl` is deprecated. Use `appLauncherUrl` instead. `oauthRedirectUrl` will be removed in the next major release.';
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Gets the Vue version from the runtime
|
|
9
|
+
*/ const getVueVersion = ()=>{
|
|
10
|
+
return version$1 || 'unknown';
|
|
11
|
+
};
|
|
12
|
+
/**
|
|
13
|
+
* Detects if running in a Capacitor native environment
|
|
14
|
+
*/ const getCapacitorInfo = ()=>{
|
|
15
|
+
if (typeof window === 'undefined') return null;
|
|
16
|
+
try {
|
|
17
|
+
if (window.Capacitor?.isNativePlatform?.()) {
|
|
18
|
+
const platform = window.Capacitor.getPlatform?.() || 'native';
|
|
19
|
+
// Map platform names to correct capitalization
|
|
20
|
+
const platformNames = {
|
|
21
|
+
ios: 'iOS',
|
|
22
|
+
android: 'Android',
|
|
23
|
+
web: 'Web'
|
|
24
|
+
};
|
|
25
|
+
const platformName = platformNames[platform.toLowerCase()] || platform;
|
|
26
|
+
return `Capacitor/${platformName}`;
|
|
27
|
+
}
|
|
28
|
+
} catch {
|
|
29
|
+
// Ignore errors
|
|
30
|
+
}
|
|
31
|
+
return null;
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Generates platform information string for Vue web
|
|
35
|
+
* Format: Vue/<version>; <browser>/<version>
|
|
36
|
+
* Or with Capacitor: Vue/<version>; Capacitor/<platform>; <browser>/<version>
|
|
37
|
+
*/ const getPlatformInfo = ()=>{
|
|
38
|
+
const versionStr = getVueVersion();
|
|
39
|
+
const capacitorInfo = getCapacitorInfo();
|
|
40
|
+
const browserInfo = getBrowserInfo();
|
|
41
|
+
if (capacitorInfo) {
|
|
42
|
+
return `Vue/${versionStr}; ${capacitorInfo}; ${browserInfo}`;
|
|
43
|
+
}
|
|
44
|
+
return `Vue/${versionStr}; ${browserInfo}`;
|
|
45
|
+
};
|
|
46
|
+
/**
|
|
47
|
+
* Generates User-Agent string for Vue SDK
|
|
48
|
+
* Format: Quiltt/<sdk-version> (Vue/<vue-version>; <browser>/<version>)
|
|
49
|
+
*/ const getSDKAgent = (sdkVersion)=>{
|
|
50
|
+
const platformInfo = getPlatformInfo();
|
|
51
|
+
return getSDKAgent$1(sdkVersion, platformInfo);
|
|
52
|
+
};
|
|
53
|
+
|
|
54
|
+
var version = "5.1.2";
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Injection keys and types for Quiltt Vue plugin
|
|
58
|
+
*/ // Injection keys for Quiltt state
|
|
59
|
+
const QuilttSessionKey = Symbol.for('quiltt-session');
|
|
60
|
+
const QuilttSetSessionKey = Symbol.for('quiltt-set-session');
|
|
61
|
+
const QuilttClientIdKey = Symbol.for('quiltt-client-id');
|
|
62
|
+
|
|
63
|
+
// Initialize JWT parser
|
|
64
|
+
const parse$1 = JsonWebTokenParse;
|
|
65
|
+
/**
|
|
66
|
+
* Composable for managing Quiltt session state
|
|
67
|
+
*
|
|
68
|
+
* Provides methods for importing, creating, and revoking sessions.
|
|
69
|
+
* Session state is automatically synchronized across components.
|
|
70
|
+
* Requires QuilttPlugin provider context and throws when used without it.
|
|
71
|
+
*/ const useQuilttSession = ()=>{
|
|
72
|
+
const sessionRef = inject(QuilttSessionKey);
|
|
73
|
+
const setSession = inject(QuilttSetSessionKey);
|
|
74
|
+
const clientIdRef = inject(QuilttClientIdKey);
|
|
75
|
+
if (!sessionRef || !setSession) {
|
|
76
|
+
throw new Error('[Quiltt] useQuilttSession must be used within a component where QuilttPlugin is installed. ' + 'Make sure to call app.use(QuilttPlugin) before mounting your app.');
|
|
77
|
+
}
|
|
78
|
+
// Create a computed ref for the session
|
|
79
|
+
const session = computed(()=>sessionRef.value);
|
|
80
|
+
// Create AuthAPI instance (memoized based on clientId)
|
|
81
|
+
const getAuth = ()=>new AuthAPI(clientIdRef?.value);
|
|
82
|
+
/**
|
|
83
|
+
* Import an existing session token
|
|
84
|
+
* Validates the token and sets it as the current session
|
|
85
|
+
*/ const importSession = async (token, environmentId)=>{
|
|
86
|
+
const auth = getAuth();
|
|
87
|
+
// Is there a token?
|
|
88
|
+
if (!token) return !!sessionRef.value;
|
|
89
|
+
// Is this token already imported?
|
|
90
|
+
if (sessionRef.value && sessionRef.value.token === token) return true;
|
|
91
|
+
const jwt = parse$1(token);
|
|
92
|
+
// Is this token a valid JWT?
|
|
93
|
+
if (!jwt) return false;
|
|
94
|
+
// Is this token within the expected environment?
|
|
95
|
+
if (environmentId && jwt.claims.eid !== environmentId) return false;
|
|
96
|
+
// Is this token active?
|
|
97
|
+
const response = await auth.ping(token);
|
|
98
|
+
switch(response.status){
|
|
99
|
+
case 200:
|
|
100
|
+
setSession(token);
|
|
101
|
+
return true;
|
|
102
|
+
case 401:
|
|
103
|
+
return false;
|
|
104
|
+
default:
|
|
105
|
+
throw new Error(`AuthAPI.ping: Unexpected response status ${response.status}`);
|
|
106
|
+
}
|
|
107
|
+
};
|
|
108
|
+
/**
|
|
109
|
+
* Start authentication flow with username/email/phone
|
|
110
|
+
* Returns a session token or challenges for passcode
|
|
111
|
+
*/ const identifySession = async (payload, callbacks)=>{
|
|
112
|
+
const auth = getAuth();
|
|
113
|
+
const response = await auth.identify(payload);
|
|
114
|
+
switch(response.status){
|
|
115
|
+
case 201:
|
|
116
|
+
setSession(response.data.token);
|
|
117
|
+
if (callbacks.onSuccess) return callbacks.onSuccess();
|
|
118
|
+
break;
|
|
119
|
+
case 202:
|
|
120
|
+
if (callbacks.onChallenged) return callbacks.onChallenged();
|
|
121
|
+
break;
|
|
122
|
+
case 403:
|
|
123
|
+
if (callbacks.onForbidden) return callbacks.onForbidden();
|
|
124
|
+
break;
|
|
125
|
+
case 422:
|
|
126
|
+
if (callbacks.onError) return callbacks.onError(response.data);
|
|
127
|
+
break;
|
|
128
|
+
default:
|
|
129
|
+
throw new Error(`AuthAPI.identify: Unexpected response status ${response.status}`);
|
|
130
|
+
}
|
|
131
|
+
};
|
|
132
|
+
/**
|
|
133
|
+
* Complete authentication with passcode
|
|
134
|
+
*/ const authenticateSession = async (payload, callbacks)=>{
|
|
135
|
+
const auth = getAuth();
|
|
136
|
+
const response = await auth.authenticate(payload);
|
|
137
|
+
switch(response.status){
|
|
138
|
+
case 201:
|
|
139
|
+
setSession(response.data.token);
|
|
140
|
+
if (callbacks.onSuccess) return callbacks.onSuccess();
|
|
141
|
+
break;
|
|
142
|
+
case 401:
|
|
143
|
+
if (callbacks.onFailure) return callbacks.onFailure();
|
|
144
|
+
break;
|
|
145
|
+
case 422:
|
|
146
|
+
if (callbacks.onError) return callbacks.onError(response.data);
|
|
147
|
+
break;
|
|
148
|
+
default:
|
|
149
|
+
throw new Error(`AuthAPI.authenticate: Unexpected response status ${response.status}`);
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
/**
|
|
153
|
+
* Revoke the current session
|
|
154
|
+
* Invalidates the token on the server and clears local state
|
|
155
|
+
*/ const revokeSession = async ()=>{
|
|
156
|
+
if (!sessionRef.value) return;
|
|
157
|
+
const auth = getAuth();
|
|
158
|
+
await auth.revoke(sessionRef.value.token);
|
|
159
|
+
setSession(null);
|
|
160
|
+
};
|
|
161
|
+
/**
|
|
162
|
+
* Forget the current session locally without server call
|
|
163
|
+
* Optionally pass a specific token to guard against async processes clearing wrong session
|
|
164
|
+
*/ const forgetSession = (token)=>{
|
|
165
|
+
if (!token || sessionRef.value && token === sessionRef.value.token) {
|
|
166
|
+
setSession(null);
|
|
167
|
+
}
|
|
168
|
+
};
|
|
169
|
+
return {
|
|
170
|
+
session,
|
|
171
|
+
importSession,
|
|
172
|
+
identifySession,
|
|
173
|
+
authenticateSession,
|
|
174
|
+
revokeSession,
|
|
175
|
+
forgetSession
|
|
176
|
+
};
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Load the Quiltt SDK script
|
|
181
|
+
*/ const loadScript = (src, nonce)=>{
|
|
182
|
+
return new Promise((resolve, reject)=>{
|
|
183
|
+
// Check if already loaded
|
|
184
|
+
if (typeof Quiltt !== 'undefined') {
|
|
185
|
+
resolve();
|
|
186
|
+
return;
|
|
187
|
+
}
|
|
188
|
+
// Check if script is already in DOM
|
|
189
|
+
const existing = document.querySelector(`script[src^="${src.split('?')[0]}"]`);
|
|
190
|
+
if (existing) {
|
|
191
|
+
existing.addEventListener('load', ()=>resolve());
|
|
192
|
+
existing.addEventListener('error', ()=>reject(new Error('Failed to load Quiltt SDK')));
|
|
193
|
+
return;
|
|
194
|
+
}
|
|
195
|
+
const script = document.createElement('script');
|
|
196
|
+
script.src = src;
|
|
197
|
+
script.async = true;
|
|
198
|
+
if (nonce) script.nonce = nonce;
|
|
199
|
+
script.onload = ()=>resolve();
|
|
200
|
+
script.onerror = ()=>reject(new Error('Failed to load Quiltt SDK'));
|
|
201
|
+
document.head.appendChild(script);
|
|
202
|
+
});
|
|
203
|
+
};
|
|
204
|
+
/**
|
|
205
|
+
* Composable for managing Quiltt Connector
|
|
206
|
+
*
|
|
207
|
+
* Loads the Quiltt SDK script and provides methods to open/manage connectors.
|
|
208
|
+
* This composable can run without QuilttPlugin session context; when unavailable,
|
|
209
|
+
* it logs a warning and continues without authenticated session state.
|
|
210
|
+
*/ const useQuilttConnector = (connectorId, options)=>{
|
|
211
|
+
const getConnectorId = ()=>toValue(connectorId);
|
|
212
|
+
const getConnectionId = ()=>toValue(options?.connectionId);
|
|
213
|
+
const getInstitution = ()=>toValue(options?.institution);
|
|
214
|
+
const getOauthRedirectUrl = ()=>toValue(options?.oauthRedirectUrl);
|
|
215
|
+
const getAppLauncherUri = ()=>toValue(options?.appLauncherUrl) ?? getOauthRedirectUrl();
|
|
216
|
+
const session = ref();
|
|
217
|
+
try {
|
|
218
|
+
const quilttSession = useQuilttSession();
|
|
219
|
+
session.value = quilttSession.session.value;
|
|
220
|
+
watch(()=>quilttSession.session.value, (nextSession)=>{
|
|
221
|
+
session.value = nextSession;
|
|
222
|
+
}, {
|
|
223
|
+
immediate: true
|
|
224
|
+
});
|
|
225
|
+
} catch (error) {
|
|
226
|
+
console.warn('[Quiltt] useQuilttConnector: QuilttPlugin not found in the current app context. ' + 'Continuing without session authentication.', error);
|
|
227
|
+
}
|
|
228
|
+
const connector = ref();
|
|
229
|
+
const isLoaded = ref(false);
|
|
230
|
+
const isOpening = ref(false);
|
|
231
|
+
const isConnectorOpen = ref(false);
|
|
232
|
+
// Track previous values
|
|
233
|
+
let prevConnectionId = getConnectionId();
|
|
234
|
+
let prevConnectorId = getConnectorId();
|
|
235
|
+
let prevInstitution = getInstitution();
|
|
236
|
+
let prevAppLauncherUri = getAppLauncherUri();
|
|
237
|
+
let connectorCreated = false;
|
|
238
|
+
// Load SDK script on mount
|
|
239
|
+
onMounted(async ()=>{
|
|
240
|
+
const sdkVersion = extractVersionNumber(version);
|
|
241
|
+
const userAgent = getSDKAgent(sdkVersion);
|
|
242
|
+
const scriptUrl = `${cdnBase}/v1/connector.js?agent=${encodeURIComponent(userAgent)}`;
|
|
243
|
+
try {
|
|
244
|
+
await loadScript(scriptUrl, options?.nonce);
|
|
245
|
+
isLoaded.value = true;
|
|
246
|
+
} catch (error) {
|
|
247
|
+
console.error('[Quiltt] Failed to load SDK:', error);
|
|
248
|
+
}
|
|
249
|
+
});
|
|
250
|
+
// Update authentication when session changes
|
|
251
|
+
watch(()=>session.value?.token, (token)=>{
|
|
252
|
+
if (typeof Quiltt !== 'undefined') {
|
|
253
|
+
Quiltt.authenticate(token);
|
|
254
|
+
}
|
|
255
|
+
}, {
|
|
256
|
+
immediate: true
|
|
257
|
+
});
|
|
258
|
+
// Handle script loaded
|
|
259
|
+
watch(isLoaded, (loaded)=>{
|
|
260
|
+
if (!loaded || typeof Quiltt === 'undefined') return;
|
|
261
|
+
// Authenticate with current session
|
|
262
|
+
Quiltt.authenticate(session.value?.token);
|
|
263
|
+
}, {
|
|
264
|
+
immediate: true
|
|
265
|
+
});
|
|
266
|
+
watch(getOauthRedirectUrl, (oauthRedirectUrl)=>{
|
|
267
|
+
if (oauthRedirectUrl !== undefined) {
|
|
268
|
+
console.warn(oauthRedirectUrlDeprecationWarning);
|
|
269
|
+
}
|
|
270
|
+
}, {
|
|
271
|
+
immediate: true
|
|
272
|
+
});
|
|
273
|
+
// Create/update connector when needed
|
|
274
|
+
const updateConnector = ()=>{
|
|
275
|
+
const currentConnectorId = getConnectorId();
|
|
276
|
+
if (!isLoaded.value || typeof Quiltt === 'undefined' || !currentConnectorId) return;
|
|
277
|
+
const currentConnectionId = getConnectionId();
|
|
278
|
+
const currentInstitution = getInstitution();
|
|
279
|
+
const currentAppLauncherUri = getAppLauncherUri();
|
|
280
|
+
// Check for changes
|
|
281
|
+
const connectionIdChanged = prevConnectionId !== currentConnectionId;
|
|
282
|
+
const connectorIdChanged = prevConnectorId !== currentConnectorId;
|
|
283
|
+
const institutionChanged = prevInstitution !== currentInstitution;
|
|
284
|
+
const appLauncherUrlChanged = prevAppLauncherUri !== currentAppLauncherUri;
|
|
285
|
+
const hasChanges = connectionIdChanged || connectorIdChanged || institutionChanged || appLauncherUrlChanged || !connectorCreated;
|
|
286
|
+
if (hasChanges) {
|
|
287
|
+
if (currentConnectionId) {
|
|
288
|
+
// Reconnect mode
|
|
289
|
+
connector.value = Quiltt.reconnect(currentConnectorId, {
|
|
290
|
+
connectionId: currentConnectionId,
|
|
291
|
+
appLauncherUrl: currentAppLauncherUri
|
|
292
|
+
});
|
|
293
|
+
} else {
|
|
294
|
+
// Connect mode
|
|
295
|
+
connector.value = Quiltt.connect(currentConnectorId, {
|
|
296
|
+
institution: currentInstitution,
|
|
297
|
+
appLauncherUrl: currentAppLauncherUri
|
|
298
|
+
});
|
|
299
|
+
}
|
|
300
|
+
connectorCreated = true;
|
|
301
|
+
prevConnectionId = currentConnectionId;
|
|
302
|
+
prevConnectorId = currentConnectorId;
|
|
303
|
+
prevInstitution = currentInstitution;
|
|
304
|
+
prevAppLauncherUri = currentAppLauncherUri;
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
// Watch for changes that require connector update
|
|
308
|
+
watch([
|
|
309
|
+
isLoaded,
|
|
310
|
+
getConnectorId,
|
|
311
|
+
getConnectionId,
|
|
312
|
+
getInstitution,
|
|
313
|
+
getAppLauncherUri
|
|
314
|
+
], updateConnector, {
|
|
315
|
+
immediate: true
|
|
316
|
+
});
|
|
317
|
+
// Register event handlers when connector changes
|
|
318
|
+
watch(connector, (newConnector, oldConnector)=>{
|
|
319
|
+
if (!newConnector) return;
|
|
320
|
+
// Register handlers
|
|
321
|
+
if (options?.onEvent) {
|
|
322
|
+
newConnector.onEvent(options.onEvent);
|
|
323
|
+
}
|
|
324
|
+
newConnector.onOpen((metadata)=>{
|
|
325
|
+
isConnectorOpen.value = true;
|
|
326
|
+
options?.onOpen?.(metadata);
|
|
327
|
+
});
|
|
328
|
+
if (options?.onLoad) {
|
|
329
|
+
newConnector.onLoad(options.onLoad);
|
|
330
|
+
}
|
|
331
|
+
newConnector.onExit((type, metadata)=>{
|
|
332
|
+
isConnectorOpen.value = false;
|
|
333
|
+
options?.onExit?.(type, metadata);
|
|
334
|
+
});
|
|
335
|
+
if (options?.onExitSuccess) {
|
|
336
|
+
newConnector.onExitSuccess(options.onExitSuccess);
|
|
337
|
+
}
|
|
338
|
+
if (options?.onExitAbort) {
|
|
339
|
+
newConnector.onExitAbort(options.onExitAbort);
|
|
340
|
+
}
|
|
341
|
+
if (options?.onExitError) {
|
|
342
|
+
newConnector.onExitError(options.onExitError);
|
|
343
|
+
}
|
|
344
|
+
}, {
|
|
345
|
+
immediate: true
|
|
346
|
+
});
|
|
347
|
+
// Handle deferred opening
|
|
348
|
+
watch([
|
|
349
|
+
connector,
|
|
350
|
+
isOpening
|
|
351
|
+
], ([conn, opening])=>{
|
|
352
|
+
if (conn && opening) {
|
|
353
|
+
isOpening.value = false;
|
|
354
|
+
conn.open();
|
|
355
|
+
}
|
|
356
|
+
});
|
|
357
|
+
// Warn on unmount if connector is still open
|
|
358
|
+
onUnmounted(()=>{
|
|
359
|
+
if (isConnectorOpen.value) {
|
|
360
|
+
console.error('[Quiltt] useQuilttConnector: Component unmounted while Connector is still open. ' + 'This may lead to memory leaks or unexpected behavior.');
|
|
361
|
+
}
|
|
362
|
+
});
|
|
363
|
+
/**
|
|
364
|
+
* Open the connector modal
|
|
365
|
+
*/ const open = ()=>{
|
|
366
|
+
if (getConnectorId()) {
|
|
367
|
+
isOpening.value = true;
|
|
368
|
+
updateConnector();
|
|
369
|
+
} else {
|
|
370
|
+
throw new Error('Must provide connectorId to open Quiltt Connector');
|
|
371
|
+
}
|
|
372
|
+
};
|
|
373
|
+
return {
|
|
374
|
+
open
|
|
375
|
+
};
|
|
376
|
+
};
|
|
377
|
+
|
|
378
|
+
/**
|
|
379
|
+
* Search institutions for a connector.
|
|
380
|
+
* Requires QuilttPlugin session context and throws when used without it.
|
|
381
|
+
*/ const useQuilttInstitutions = (connectorId, onErrorCallback)=>{
|
|
382
|
+
const { session } = useQuilttSession();
|
|
383
|
+
const searchTermInput = ref('');
|
|
384
|
+
const searchTerm = ref('');
|
|
385
|
+
const searchResults = ref([]);
|
|
386
|
+
const isSearching = ref(false);
|
|
387
|
+
const debounceTimer = ref();
|
|
388
|
+
const abortController = shallowRef();
|
|
389
|
+
const sdkVersion = extractVersionNumber(version);
|
|
390
|
+
const connectorsAPI = new ConnectorsAPI(connectorId, getSDKAgent(sdkVersion));
|
|
391
|
+
const handleError = (message)=>{
|
|
392
|
+
const errorMessage = message || 'Unknown error occurred while searching institutions';
|
|
393
|
+
console.error('Quiltt Institutions Search Error:', errorMessage);
|
|
394
|
+
onErrorCallback?.(errorMessage);
|
|
395
|
+
};
|
|
396
|
+
const setSearchTerm = (term)=>{
|
|
397
|
+
searchTermInput.value = term;
|
|
398
|
+
if (debounceTimer.value) {
|
|
399
|
+
clearTimeout(debounceTimer.value);
|
|
400
|
+
}
|
|
401
|
+
if (term.trim().length < 2) {
|
|
402
|
+
abortController.value?.abort();
|
|
403
|
+
abortController.value = undefined;
|
|
404
|
+
searchTerm.value = '';
|
|
405
|
+
searchResults.value = [];
|
|
406
|
+
isSearching.value = false;
|
|
407
|
+
return;
|
|
408
|
+
}
|
|
409
|
+
isSearching.value = true;
|
|
410
|
+
debounceTimer.value = setTimeout(()=>{
|
|
411
|
+
searchTerm.value = term;
|
|
412
|
+
}, 350);
|
|
413
|
+
};
|
|
414
|
+
watch([
|
|
415
|
+
()=>session.value?.token,
|
|
416
|
+
searchTerm
|
|
417
|
+
], async ([token, term])=>{
|
|
418
|
+
if (!token || !connectorId || !term || term.trim().length < 2) {
|
|
419
|
+
return;
|
|
420
|
+
}
|
|
421
|
+
abortController.value?.abort();
|
|
422
|
+
const controller = new AbortController();
|
|
423
|
+
abortController.value = controller;
|
|
424
|
+
try {
|
|
425
|
+
const response = await connectorsAPI.searchInstitutions(token, connectorId, term, controller.signal);
|
|
426
|
+
if (abortController.value !== controller || controller.signal.aborted) {
|
|
427
|
+
return;
|
|
428
|
+
}
|
|
429
|
+
if (response.status === 200) {
|
|
430
|
+
searchResults.value = response.data;
|
|
431
|
+
} else {
|
|
432
|
+
handleError(response.data.message || 'Failed to fetch institutions');
|
|
433
|
+
}
|
|
434
|
+
} catch (error) {
|
|
435
|
+
if (abortController.value === controller && !controller.signal.aborted) {
|
|
436
|
+
handleError(error?.message);
|
|
437
|
+
}
|
|
438
|
+
} finally{
|
|
439
|
+
if (abortController.value === controller && !controller.signal.aborted) {
|
|
440
|
+
isSearching.value = false;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}, {
|
|
444
|
+
immediate: false
|
|
445
|
+
});
|
|
446
|
+
onUnmounted(()=>{
|
|
447
|
+
if (debounceTimer.value) {
|
|
448
|
+
clearTimeout(debounceTimer.value);
|
|
449
|
+
}
|
|
450
|
+
abortController.value?.abort();
|
|
451
|
+
});
|
|
452
|
+
return {
|
|
453
|
+
searchTerm: computed(()=>searchTerm.value),
|
|
454
|
+
searchResults: computed(()=>searchResults.value),
|
|
455
|
+
isSearching: computed(()=>isSearching.value),
|
|
456
|
+
setSearchTerm
|
|
457
|
+
};
|
|
458
|
+
};
|
|
459
|
+
|
|
460
|
+
/**
|
|
461
|
+
* Check whether a provider link is resolvable for a connector.
|
|
462
|
+
* Requires QuilttPlugin session context and throws when used without it.
|
|
463
|
+
*/ const useQuilttResolvable = (connectorId, onErrorCallback)=>{
|
|
464
|
+
const { session } = useQuilttSession();
|
|
465
|
+
const sdkVersion = extractVersionNumber(version);
|
|
466
|
+
const connectorsAPI = new ConnectorsAPI(connectorId, getSDKAgent(sdkVersion));
|
|
467
|
+
const isLoading = ref(false);
|
|
468
|
+
const isResolvable = ref(null);
|
|
469
|
+
const error = ref(null);
|
|
470
|
+
const handleError = (message)=>{
|
|
471
|
+
const errorMessage = message || 'Unknown error occurred while checking resolvability';
|
|
472
|
+
error.value = errorMessage;
|
|
473
|
+
console.error('Quiltt Connector Resolvable Error:', errorMessage);
|
|
474
|
+
onErrorCallback?.(errorMessage);
|
|
475
|
+
};
|
|
476
|
+
const checkResolvable = async (providerId)=>{
|
|
477
|
+
if (!session.value?.token) {
|
|
478
|
+
handleError('Missing session token');
|
|
479
|
+
return null;
|
|
480
|
+
}
|
|
481
|
+
if (!connectorId) {
|
|
482
|
+
handleError('Missing connector ID');
|
|
483
|
+
return null;
|
|
484
|
+
}
|
|
485
|
+
const hasProviderId = Object.values(providerId).some((id)=>!!id);
|
|
486
|
+
if (!hasProviderId) {
|
|
487
|
+
handleError('No provider ID specified');
|
|
488
|
+
return null;
|
|
489
|
+
}
|
|
490
|
+
isLoading.value = true;
|
|
491
|
+
error.value = null;
|
|
492
|
+
try {
|
|
493
|
+
const response = await connectorsAPI.checkResolvable(session.value.token, connectorId, providerId);
|
|
494
|
+
if (response.status === 200) {
|
|
495
|
+
const result = response.data.resolvable;
|
|
496
|
+
isResolvable.value = result;
|
|
497
|
+
return result;
|
|
498
|
+
}
|
|
499
|
+
handleError(response.data.message || 'Failed to check resolvability');
|
|
500
|
+
isResolvable.value = null;
|
|
501
|
+
return null;
|
|
502
|
+
} catch (caught) {
|
|
503
|
+
handleError(caught?.message);
|
|
504
|
+
isResolvable.value = null;
|
|
505
|
+
return null;
|
|
506
|
+
} finally{
|
|
507
|
+
isLoading.value = false;
|
|
508
|
+
}
|
|
509
|
+
};
|
|
510
|
+
return {
|
|
511
|
+
checkResolvable,
|
|
512
|
+
isLoading: computed(()=>isLoading.value),
|
|
513
|
+
isResolvable: computed(()=>isResolvable.value),
|
|
514
|
+
error: computed(()=>error.value)
|
|
515
|
+
};
|
|
516
|
+
};
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* Read plugin-provided Quiltt settings.
|
|
520
|
+
* When used without QuilttPlugin context, values are undefined.
|
|
521
|
+
*/ const useQuilttSettings = ()=>{
|
|
522
|
+
const clientIdRef = inject(QuilttClientIdKey);
|
|
523
|
+
return {
|
|
524
|
+
clientId: computed(()=>clientIdRef?.value)
|
|
525
|
+
};
|
|
526
|
+
};
|
|
527
|
+
|
|
528
|
+
const useStorage = (key, initialState)=>{
|
|
529
|
+
const readStorage = ()=>{
|
|
530
|
+
const current = GlobalStorage.get(key);
|
|
531
|
+
if (current !== undefined) {
|
|
532
|
+
return current;
|
|
533
|
+
}
|
|
534
|
+
return initialState;
|
|
535
|
+
};
|
|
536
|
+
const state = ref(readStorage());
|
|
537
|
+
const setStorage = (nextState)=>{
|
|
538
|
+
const resolved = nextState instanceof Function ? nextState(state.value) : nextState;
|
|
539
|
+
if (state.value !== resolved) {
|
|
540
|
+
GlobalStorage.set(key, resolved);
|
|
541
|
+
state.value = resolved;
|
|
542
|
+
}
|
|
543
|
+
};
|
|
544
|
+
const handleStorageChange = (newValue)=>{
|
|
545
|
+
state.value = newValue;
|
|
546
|
+
};
|
|
547
|
+
GlobalStorage.subscribe(key, handleStorageChange);
|
|
548
|
+
// Use onScopeDispose for cleanup in any effect scope (components, effectScope(), etc.)
|
|
549
|
+
// This safely handles cases where composable is called outside a scope
|
|
550
|
+
if (getCurrentScope()) {
|
|
551
|
+
onScopeDispose(()=>{
|
|
552
|
+
GlobalStorage.unsubscribe(key, handleStorageChange);
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
return {
|
|
556
|
+
storage: computed(()=>state.value),
|
|
557
|
+
setStorage
|
|
558
|
+
};
|
|
559
|
+
};
|
|
560
|
+
|
|
561
|
+
const parse = JsonWebTokenParse;
|
|
562
|
+
const sessionTimer = new Timeoutable();
|
|
563
|
+
const useSession = (storageKey = 'session')=>{
|
|
564
|
+
const { storage: token, setStorage } = useStorage(storageKey);
|
|
565
|
+
const session = ref(parse(token.value));
|
|
566
|
+
let currentExpire = null;
|
|
567
|
+
watch(token, (newToken)=>{
|
|
568
|
+
session.value = parse(newToken);
|
|
569
|
+
}, {
|
|
570
|
+
immediate: true
|
|
571
|
+
});
|
|
572
|
+
watch(()=>session.value, (nextSession)=>{
|
|
573
|
+
if (currentExpire) {
|
|
574
|
+
sessionTimer.clear(currentExpire);
|
|
575
|
+
currentExpire = null;
|
|
576
|
+
}
|
|
577
|
+
if (!nextSession) {
|
|
578
|
+
return;
|
|
579
|
+
}
|
|
580
|
+
const expirationMS = nextSession.claims.exp * 1000;
|
|
581
|
+
const expire = ()=>setStorage(null);
|
|
582
|
+
currentExpire = expire;
|
|
583
|
+
if (Date.now() >= expirationMS) {
|
|
584
|
+
expire();
|
|
585
|
+
return;
|
|
586
|
+
}
|
|
587
|
+
sessionTimer.set(expire, expirationMS - Date.now());
|
|
588
|
+
}, {
|
|
589
|
+
immediate: true
|
|
590
|
+
});
|
|
591
|
+
const setSession = (nextState)=>{
|
|
592
|
+
const resolved = nextState instanceof Function ? nextState(token.value) : nextState;
|
|
593
|
+
if (token.value !== resolved && (!resolved || parse(resolved))) {
|
|
594
|
+
setStorage(resolved);
|
|
595
|
+
}
|
|
596
|
+
};
|
|
597
|
+
onUnmounted(()=>{
|
|
598
|
+
if (currentExpire) {
|
|
599
|
+
sessionTimer.clear(currentExpire);
|
|
600
|
+
currentExpire = null;
|
|
601
|
+
}
|
|
602
|
+
});
|
|
603
|
+
return {
|
|
604
|
+
session: computed(()=>session.value),
|
|
605
|
+
setSession
|
|
606
|
+
};
|
|
607
|
+
};
|
|
608
|
+
|
|
609
|
+
export { useQuilttConnector, useQuilttInstitutions, useQuilttResolvable, useQuilttSession, useQuilttSettings, useSession, useStorage };
|
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
2
|
+
|
|
3
|
+
var api = require('@quiltt/core/api');
|
|
4
|
+
var auth = require('@quiltt/core/auth');
|
|
5
|
+
var config = require('@quiltt/core/config');
|
|
6
|
+
var observables = require('@quiltt/core/observables');
|
|
7
|
+
var storage = require('@quiltt/core/storage');
|
|
8
|
+
var timing = require('@quiltt/core/timing');
|
|
9
|
+
var types = require('@quiltt/core/types');
|
|
10
|
+
var index_cjs = require('./components/index.cjs');
|
|
11
|
+
var index_cjs$1 = require('./composables/index.cjs');
|
|
12
|
+
var index_cjs$2 = require('./plugin/index.cjs');
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
Object.keys(api).forEach(function (k) {
|
|
17
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
18
|
+
enumerable: true,
|
|
19
|
+
get: function () { return api[k]; }
|
|
20
|
+
});
|
|
21
|
+
});
|
|
22
|
+
Object.keys(auth).forEach(function (k) {
|
|
23
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
24
|
+
enumerable: true,
|
|
25
|
+
get: function () { return auth[k]; }
|
|
26
|
+
});
|
|
27
|
+
});
|
|
28
|
+
Object.keys(config).forEach(function (k) {
|
|
29
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
30
|
+
enumerable: true,
|
|
31
|
+
get: function () { return config[k]; }
|
|
32
|
+
});
|
|
33
|
+
});
|
|
34
|
+
Object.keys(observables).forEach(function (k) {
|
|
35
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
36
|
+
enumerable: true,
|
|
37
|
+
get: function () { return observables[k]; }
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
Object.keys(storage).forEach(function (k) {
|
|
41
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
42
|
+
enumerable: true,
|
|
43
|
+
get: function () { return storage[k]; }
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
Object.keys(timing).forEach(function (k) {
|
|
47
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
48
|
+
enumerable: true,
|
|
49
|
+
get: function () { return timing[k]; }
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
Object.keys(types).forEach(function (k) {
|
|
53
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
54
|
+
enumerable: true,
|
|
55
|
+
get: function () { return types[k]; }
|
|
56
|
+
});
|
|
57
|
+
});
|
|
58
|
+
Object.keys(index_cjs).forEach(function (k) {
|
|
59
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
60
|
+
enumerable: true,
|
|
61
|
+
get: function () { return index_cjs[k]; }
|
|
62
|
+
});
|
|
63
|
+
});
|
|
64
|
+
Object.keys(index_cjs$1).forEach(function (k) {
|
|
65
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
66
|
+
enumerable: true,
|
|
67
|
+
get: function () { return index_cjs$1[k]; }
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
Object.keys(index_cjs$2).forEach(function (k) {
|
|
71
|
+
if (k !== 'default' && !Object.prototype.hasOwnProperty.call(exports, k)) Object.defineProperty(exports, k, {
|
|
72
|
+
enumerable: true,
|
|
73
|
+
get: function () { return index_cjs$2[k]; }
|
|
74
|
+
});
|
|
75
|
+
});
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
export * from '@quiltt/core/api';
|
|
2
|
+
export * from '@quiltt/core/auth';
|
|
3
|
+
export * from '@quiltt/core/config';
|
|
4
|
+
export * from '@quiltt/core/observables';
|
|
5
|
+
export * from '@quiltt/core/storage';
|
|
6
|
+
export * from '@quiltt/core/timing';
|
|
7
|
+
export * from '@quiltt/core/types';
|
|
8
|
+
export * from './components/index.js';
|
|
9
|
+
export * from './composables/index.js';
|
|
10
|
+
export * from './plugin/index.js';
|