@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/dist/index.js 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';
@@ -0,0 +1,176 @@
1
+ Object.defineProperty(exports, '__esModule', { value: true });
2
+
3
+ var vue = require('vue');
4
+ var core = require('@quiltt/core');
5
+
6
+ /**
7
+ * Injection keys and types for Quiltt Vue plugin
8
+ */ // Injection keys for Quiltt state
9
+ const QuilttSessionKey = Symbol.for('quiltt-session');
10
+ const QuilttSetSessionKey = Symbol.for('quiltt-set-session');
11
+ const QuilttClientIdKey = Symbol.for('quiltt-client-id');
12
+
13
+ // Initialize JWT parser with our specific claims type
14
+ const parse = core.JsonWebTokenParse;
15
+ // Storage key for session persistence
16
+ const STORAGE_KEY = 'quiltt:session';
17
+ /**
18
+ * Get stored token from localStorage (browser only)
19
+ */ const getStoredToken = ()=>{
20
+ if (typeof window === 'undefined' || typeof localStorage === 'undefined') {
21
+ return null;
22
+ }
23
+ try {
24
+ return localStorage.getItem(STORAGE_KEY);
25
+ } catch {
26
+ return null;
27
+ }
28
+ };
29
+ /**
30
+ * Store token in localStorage (browser only)
31
+ */ const setStoredToken = (token)=>{
32
+ if (typeof window === 'undefined' || typeof localStorage === 'undefined') {
33
+ return;
34
+ }
35
+ try {
36
+ if (token) {
37
+ localStorage.setItem(STORAGE_KEY, token);
38
+ } else {
39
+ localStorage.removeItem(STORAGE_KEY);
40
+ }
41
+ } catch {
42
+ // Storage not available
43
+ }
44
+ };
45
+ /**
46
+ * Quiltt Vue Plugin
47
+ *
48
+ * Provides session management across your Vue application.
49
+ * Use with `app.use(QuilttPlugin, options)`.
50
+ *
51
+ * @example
52
+ * ```typescript
53
+ * import { createApp } from 'vue'
54
+ * import { QuilttPlugin } from '@quiltt/vue'
55
+ *
56
+ * const app = createApp(App)
57
+ * app.use(QuilttPlugin, { token: '<SESSION_TOKEN>' })
58
+ * app.mount('#app')
59
+ * ```
60
+ */ const QuilttPlugin = {
61
+ install (app, options) {
62
+ // Instance-scoped timeout for session expiration
63
+ let sessionTimeout;
64
+ let isCleanedUp = false;
65
+ let stopSessionWatcher;
66
+ /**
67
+ * Clear the session timeout for this app instance
68
+ */ const clearSessionTimeout = ()=>{
69
+ if (sessionTimeout) {
70
+ clearTimeout(sessionTimeout);
71
+ sessionTimeout = undefined;
72
+ }
73
+ };
74
+ // Initialize with provided token or stored token
75
+ const initialToken = options?.token ?? getStoredToken();
76
+ const initialSession = parse(initialToken);
77
+ // Reactive session state
78
+ const session = vue.ref(initialSession);
79
+ const clientId = vue.ref(options?.clientId);
80
+ /**
81
+ * Set session token
82
+ * Parses token, updates storage, and sets expiration timer
83
+ */ const setSession = (token)=>{
84
+ const parsed = parse(token);
85
+ session.value = parsed;
86
+ setStoredToken(token ?? null);
87
+ // Clear any existing expiration timer
88
+ clearSessionTimeout();
89
+ // Set new expiration timer if session is valid
90
+ if (parsed) {
91
+ const expirationMS = parsed.claims.exp * 1000;
92
+ const timeUntilExpiry = expirationMS - Date.now();
93
+ if (timeUntilExpiry > 0) {
94
+ sessionTimeout = setTimeout(()=>{
95
+ session.value = null;
96
+ setStoredToken(null);
97
+ }, timeUntilExpiry);
98
+ } else {
99
+ // Token already expired
100
+ session.value = null;
101
+ setStoredToken(null);
102
+ }
103
+ }
104
+ };
105
+ // Storage event handler for cross-tab synchronization
106
+ let storageHandler;
107
+ // Listen for storage changes from other tabs/windows
108
+ if (typeof window !== 'undefined') {
109
+ storageHandler = (event)=>{
110
+ if (event.key === STORAGE_KEY) {
111
+ const newSession = parse(event.newValue);
112
+ session.value = newSession;
113
+ }
114
+ };
115
+ window.addEventListener('storage', storageHandler);
116
+ }
117
+ // Cleanup function for when the app is unmounted
118
+ const cleanup = ()=>{
119
+ if (isCleanedUp) {
120
+ return;
121
+ }
122
+ isCleanedUp = true;
123
+ clearSessionTimeout();
124
+ if (stopSessionWatcher) {
125
+ stopSessionWatcher();
126
+ stopSessionWatcher = undefined;
127
+ }
128
+ if (typeof window !== 'undefined' && storageHandler) {
129
+ window.removeEventListener('storage', storageHandler);
130
+ storageHandler = undefined;
131
+ }
132
+ };
133
+ // Register cleanup on app unmount (Vue 3.5+)
134
+ if (typeof app.onUnmount === 'function') {
135
+ app.onUnmount(cleanup);
136
+ }
137
+ // Ensure cleanup runs on all supported Vue versions (3.3+)
138
+ if (typeof app.unmount === 'function') {
139
+ const originalUnmount = app.unmount.bind(app);
140
+ app.unmount = (...args)=>{
141
+ cleanup();
142
+ return originalUnmount(...args);
143
+ };
144
+ }
145
+ // Watch for session changes to update expiration timer
146
+ stopSessionWatcher = vue.watch(()=>session.value, (newSession)=>{
147
+ if (!newSession) {
148
+ clearSessionTimeout();
149
+ return;
150
+ }
151
+ const expirationMS = newSession.claims.exp * 1000;
152
+ const timeUntilExpiry = expirationMS - Date.now();
153
+ if (timeUntilExpiry <= 0) {
154
+ session.value = null;
155
+ setStoredToken(null);
156
+ return;
157
+ }
158
+ clearSessionTimeout();
159
+ sessionTimeout = setTimeout(()=>{
160
+ session.value = null;
161
+ setStoredToken(null);
162
+ }, timeUntilExpiry);
163
+ }, {
164
+ immediate: true
165
+ });
166
+ // Provide session state to all components
167
+ app.provide(QuilttSessionKey, session);
168
+ app.provide(QuilttSetSessionKey, setSession);
169
+ app.provide(QuilttClientIdKey, clientId);
170
+ }
171
+ };
172
+
173
+ exports.QuilttClientIdKey = QuilttClientIdKey;
174
+ exports.QuilttPlugin = QuilttPlugin;
175
+ exports.QuilttSessionKey = QuilttSessionKey;
176
+ exports.QuilttSetSessionKey = QuilttSetSessionKey;
@@ -0,0 +1,48 @@
1
+ import { InjectionKey, Ref, Plugin } from 'vue';
2
+ import { Maybe, QuilttJWT } from '@quiltt/core';
3
+
4
+ /**
5
+ * Injection keys and types for Quiltt Vue plugin
6
+ */
7
+
8
+ declare const QuilttSessionKey: InjectionKey<Ref<Maybe<QuilttJWT> | undefined>>;
9
+ declare const QuilttSetSessionKey: InjectionKey<(token: Maybe<string>) => void>;
10
+ declare const QuilttClientIdKey: InjectionKey<Ref<string | undefined>>;
11
+ interface QuilttPluginOptions {
12
+ /**
13
+ * Initial session token
14
+ */
15
+ token?: string;
16
+ /**
17
+ * Quiltt Client ID (Environment ID)
18
+ */
19
+ clientId?: string;
20
+ }
21
+
22
+ /**
23
+ * Quiltt Vue Plugin implementation
24
+ *
25
+ * Provides session state management via Vue's provide/inject system.
26
+ * Handles token parsing, storage synchronization, and automatic expiration.
27
+ */
28
+
29
+ /**
30
+ * Quiltt Vue Plugin
31
+ *
32
+ * Provides session management across your Vue application.
33
+ * Use with `app.use(QuilttPlugin, options)`.
34
+ *
35
+ * @example
36
+ * ```typescript
37
+ * import { createApp } from 'vue'
38
+ * import { QuilttPlugin } from '@quiltt/vue'
39
+ *
40
+ * const app = createApp(App)
41
+ * app.use(QuilttPlugin, { token: '<SESSION_TOKEN>' })
42
+ * app.mount('#app')
43
+ * ```
44
+ */
45
+ declare const QuilttPlugin: Plugin<[QuilttPluginOptions?]>;
46
+
47
+ export { QuilttClientIdKey, QuilttPlugin, QuilttSessionKey, QuilttSetSessionKey };
48
+ export type { QuilttPluginOptions };
@@ -0,0 +1,171 @@
1
+ import { ref, watch } from 'vue';
2
+ import { JsonWebTokenParse } from '@quiltt/core';
3
+
4
+ /**
5
+ * Injection keys and types for Quiltt Vue plugin
6
+ */ // Injection keys for Quiltt state
7
+ const QuilttSessionKey = Symbol.for('quiltt-session');
8
+ const QuilttSetSessionKey = Symbol.for('quiltt-set-session');
9
+ const QuilttClientIdKey = Symbol.for('quiltt-client-id');
10
+
11
+ // Initialize JWT parser with our specific claims type
12
+ const parse = JsonWebTokenParse;
13
+ // Storage key for session persistence
14
+ const STORAGE_KEY = 'quiltt:session';
15
+ /**
16
+ * Get stored token from localStorage (browser only)
17
+ */ const getStoredToken = ()=>{
18
+ if (typeof window === 'undefined' || typeof localStorage === 'undefined') {
19
+ return null;
20
+ }
21
+ try {
22
+ return localStorage.getItem(STORAGE_KEY);
23
+ } catch {
24
+ return null;
25
+ }
26
+ };
27
+ /**
28
+ * Store token in localStorage (browser only)
29
+ */ const setStoredToken = (token)=>{
30
+ if (typeof window === 'undefined' || typeof localStorage === 'undefined') {
31
+ return;
32
+ }
33
+ try {
34
+ if (token) {
35
+ localStorage.setItem(STORAGE_KEY, token);
36
+ } else {
37
+ localStorage.removeItem(STORAGE_KEY);
38
+ }
39
+ } catch {
40
+ // Storage not available
41
+ }
42
+ };
43
+ /**
44
+ * Quiltt Vue Plugin
45
+ *
46
+ * Provides session management across your Vue application.
47
+ * Use with `app.use(QuilttPlugin, options)`.
48
+ *
49
+ * @example
50
+ * ```typescript
51
+ * import { createApp } from 'vue'
52
+ * import { QuilttPlugin } from '@quiltt/vue'
53
+ *
54
+ * const app = createApp(App)
55
+ * app.use(QuilttPlugin, { token: '<SESSION_TOKEN>' })
56
+ * app.mount('#app')
57
+ * ```
58
+ */ const QuilttPlugin = {
59
+ install (app, options) {
60
+ // Instance-scoped timeout for session expiration
61
+ let sessionTimeout;
62
+ let isCleanedUp = false;
63
+ let stopSessionWatcher;
64
+ /**
65
+ * Clear the session timeout for this app instance
66
+ */ const clearSessionTimeout = ()=>{
67
+ if (sessionTimeout) {
68
+ clearTimeout(sessionTimeout);
69
+ sessionTimeout = undefined;
70
+ }
71
+ };
72
+ // Initialize with provided token or stored token
73
+ const initialToken = options?.token ?? getStoredToken();
74
+ const initialSession = parse(initialToken);
75
+ // Reactive session state
76
+ const session = ref(initialSession);
77
+ const clientId = ref(options?.clientId);
78
+ /**
79
+ * Set session token
80
+ * Parses token, updates storage, and sets expiration timer
81
+ */ const setSession = (token)=>{
82
+ const parsed = parse(token);
83
+ session.value = parsed;
84
+ setStoredToken(token ?? null);
85
+ // Clear any existing expiration timer
86
+ clearSessionTimeout();
87
+ // Set new expiration timer if session is valid
88
+ if (parsed) {
89
+ const expirationMS = parsed.claims.exp * 1000;
90
+ const timeUntilExpiry = expirationMS - Date.now();
91
+ if (timeUntilExpiry > 0) {
92
+ sessionTimeout = setTimeout(()=>{
93
+ session.value = null;
94
+ setStoredToken(null);
95
+ }, timeUntilExpiry);
96
+ } else {
97
+ // Token already expired
98
+ session.value = null;
99
+ setStoredToken(null);
100
+ }
101
+ }
102
+ };
103
+ // Storage event handler for cross-tab synchronization
104
+ let storageHandler;
105
+ // Listen for storage changes from other tabs/windows
106
+ if (typeof window !== 'undefined') {
107
+ storageHandler = (event)=>{
108
+ if (event.key === STORAGE_KEY) {
109
+ const newSession = parse(event.newValue);
110
+ session.value = newSession;
111
+ }
112
+ };
113
+ window.addEventListener('storage', storageHandler);
114
+ }
115
+ // Cleanup function for when the app is unmounted
116
+ const cleanup = ()=>{
117
+ if (isCleanedUp) {
118
+ return;
119
+ }
120
+ isCleanedUp = true;
121
+ clearSessionTimeout();
122
+ if (stopSessionWatcher) {
123
+ stopSessionWatcher();
124
+ stopSessionWatcher = undefined;
125
+ }
126
+ if (typeof window !== 'undefined' && storageHandler) {
127
+ window.removeEventListener('storage', storageHandler);
128
+ storageHandler = undefined;
129
+ }
130
+ };
131
+ // Register cleanup on app unmount (Vue 3.5+)
132
+ if (typeof app.onUnmount === 'function') {
133
+ app.onUnmount(cleanup);
134
+ }
135
+ // Ensure cleanup runs on all supported Vue versions (3.3+)
136
+ if (typeof app.unmount === 'function') {
137
+ const originalUnmount = app.unmount.bind(app);
138
+ app.unmount = (...args)=>{
139
+ cleanup();
140
+ return originalUnmount(...args);
141
+ };
142
+ }
143
+ // Watch for session changes to update expiration timer
144
+ stopSessionWatcher = watch(()=>session.value, (newSession)=>{
145
+ if (!newSession) {
146
+ clearSessionTimeout();
147
+ return;
148
+ }
149
+ const expirationMS = newSession.claims.exp * 1000;
150
+ const timeUntilExpiry = expirationMS - Date.now();
151
+ if (timeUntilExpiry <= 0) {
152
+ session.value = null;
153
+ setStoredToken(null);
154
+ return;
155
+ }
156
+ clearSessionTimeout();
157
+ sessionTimeout = setTimeout(()=>{
158
+ session.value = null;
159
+ setStoredToken(null);
160
+ }, timeUntilExpiry);
161
+ }, {
162
+ immediate: true
163
+ });
164
+ // Provide session state to all components
165
+ app.provide(QuilttSessionKey, session);
166
+ app.provide(QuilttSetSessionKey, setSession);
167
+ app.provide(QuilttClientIdKey, clientId);
168
+ }
169
+ };
170
+
171
+ export { QuilttClientIdKey, QuilttPlugin, QuilttSessionKey, QuilttSetSessionKey };
package/package.json ADDED
@@ -0,0 +1,81 @@
1
+ {
2
+ "name": "@quiltt/vue",
3
+ "version": "5.1.2",
4
+ "description": "Vue 3 Composables and Components for Quiltt Connector",
5
+ "keywords": [
6
+ "quiltt",
7
+ "quiltt-connector",
8
+ "vue",
9
+ "vue3",
10
+ "typescript"
11
+ ],
12
+ "homepage": "https://github.com/quiltt/quiltt-js/tree/main/packages/vue#readme",
13
+ "repository": {
14
+ "type": "git",
15
+ "url": "https://github.com/quiltt/quiltt-js.git",
16
+ "directory": "packages/vue"
17
+ },
18
+ "license": "MIT",
19
+ "bugs": {
20
+ "url": "https://github.com/quiltt/quiltt-js/issues"
21
+ },
22
+ "sideEffects": [],
23
+ "type": "module",
24
+ "exports": {
25
+ ".": {
26
+ "types": "./dist/index.d.ts",
27
+ "require": "./dist/index.cjs",
28
+ "import": "./dist/index.js"
29
+ },
30
+ "./composables": {
31
+ "types": "./dist/composables/index.d.ts",
32
+ "require": "./dist/composables/index.cjs",
33
+ "import": "./dist/composables/index.js"
34
+ },
35
+ "./components": {
36
+ "types": "./dist/components/index.d.ts",
37
+ "require": "./dist/components/index.cjs",
38
+ "import": "./dist/components/index.js"
39
+ },
40
+ "./plugin": {
41
+ "types": "./dist/plugin/index.d.ts",
42
+ "require": "./dist/plugin/index.cjs",
43
+ "import": "./dist/plugin/index.js"
44
+ }
45
+ },
46
+ "types": "./dist/index.d.ts",
47
+ "files": [
48
+ "dist/**",
49
+ "src/**",
50
+ "CHANGELOG.md"
51
+ ],
52
+ "main": "dist/index.js",
53
+ "dependencies": {
54
+ "@quiltt/core": "5.1.2"
55
+ },
56
+ "devDependencies": {
57
+ "@biomejs/biome": "2.4.3",
58
+ "@types/node": "24.11.0",
59
+ "bunchee": "6.9.4",
60
+ "rimraf": "6.1.3",
61
+ "typescript": "5.9.3",
62
+ "vue": "3.5.28"
63
+ },
64
+ "peerDependencies": {
65
+ "vue": "^3.3.0"
66
+ },
67
+ "tags": [
68
+ "quiltt",
69
+ "vue"
70
+ ],
71
+ "publishConfig": {
72
+ "access": "public"
73
+ },
74
+ "scripts": {
75
+ "build": "bunchee",
76
+ "clean": "rimraf .turbo dist",
77
+ "dev": "bunchee --watch",
78
+ "lint": "TIMING=1 biome check src/ tests/ --fix",
79
+ "typecheck": "tsc --project tsconfig.json --noEmit"
80
+ }
81
+ }
@@ -0,0 +1,121 @@
1
+ /**
2
+ * QuilttButton - Button component that opens Quiltt Connector modal
3
+ *
4
+ * Wraps a button (or custom element) that opens the Quiltt Connector
5
+ * in a modal overlay when clicked.
6
+ *
7
+ * @example
8
+ * ```vue
9
+ * <QuilttButton
10
+ * :connector-id="connectorId"
11
+ * @exit-success="handleSuccess"
12
+ * >
13
+ * Add Bank Account
14
+ * </QuilttButton>
15
+ * ```
16
+ */
17
+
18
+ import { computed, defineComponent, h, type PropType, watch } from 'vue'
19
+
20
+ import type { ConnectorSDKCallbackMetadata, ConnectorSDKEventType } from '@quiltt/core'
21
+
22
+ import { useQuilttConnector } from '../composables/useQuilttConnector'
23
+ import { oauthRedirectUrlDeprecationWarning } from '../constants/deprecation-warnings'
24
+
25
+ export const QuilttButton = defineComponent({
26
+ name: 'QuilttButton',
27
+
28
+ props: {
29
+ /** Quiltt Connector ID */
30
+ connectorId: {
31
+ type: String,
32
+ required: true,
33
+ },
34
+ /** Existing connection ID for reconnection */
35
+ connectionId: {
36
+ type: String as PropType<string | undefined>,
37
+ default: undefined,
38
+ },
39
+ /** Pre-select a specific institution */
40
+ institution: {
41
+ type: String as PropType<string | undefined>,
42
+ default: undefined,
43
+ },
44
+ /** Deep link URL for OAuth callbacks (mobile apps) */
45
+ appLauncherUrl: {
46
+ type: String as PropType<string | undefined>,
47
+ default: undefined,
48
+ },
49
+ /**
50
+ * @deprecated Use `appLauncherUrl` instead. This property will be removed in a future version.
51
+ * The OAuth redirect URL for mobile or embedded webview flows.
52
+ */
53
+ oauthRedirectUrl: {
54
+ type: String as PropType<string | undefined>,
55
+ default: undefined,
56
+ },
57
+ /** Render as a different element */
58
+ as: {
59
+ type: String,
60
+ default: 'button',
61
+ },
62
+ },
63
+
64
+ emits: {
65
+ /** Connector loaded */
66
+ load: (_metadata: ConnectorSDKCallbackMetadata) => true,
67
+ /** Connector opened */
68
+ open: (_metadata: ConnectorSDKCallbackMetadata) => true,
69
+ /** Connection successful */
70
+ 'exit-success': (_metadata: ConnectorSDKCallbackMetadata) => true,
71
+ /** User cancelled */
72
+ 'exit-abort': (_metadata: ConnectorSDKCallbackMetadata) => true,
73
+ /** Error occurred */
74
+ 'exit-error': (_metadata: ConnectorSDKCallbackMetadata) => true,
75
+ /** Connector exited (any reason) */
76
+ exit: (_type: ConnectorSDKEventType, _metadata: ConnectorSDKCallbackMetadata) => true,
77
+ /** Any connector event */
78
+ event: (_type: ConnectorSDKEventType, _metadata: ConnectorSDKCallbackMetadata) => true,
79
+ },
80
+
81
+ setup(props, { emit, slots }) {
82
+ watch(
83
+ () => props.oauthRedirectUrl,
84
+ (value) => {
85
+ if (value !== undefined) {
86
+ console.warn(oauthRedirectUrlDeprecationWarning)
87
+ }
88
+ },
89
+ { immediate: true }
90
+ )
91
+
92
+ const effectiveAppLauncherUri = computed(() => props.appLauncherUrl ?? props.oauthRedirectUrl)
93
+
94
+ const { open } = useQuilttConnector(() => props.connectorId, {
95
+ connectionId: () => props.connectionId,
96
+ institution: () => props.institution,
97
+ appLauncherUrl: effectiveAppLauncherUri,
98
+ onEvent: (type, metadata) => emit('event', type, metadata),
99
+ onOpen: (metadata) => emit('open', metadata),
100
+ onLoad: (metadata) => emit('load', metadata),
101
+ onExit: (type, metadata) => emit('exit', type, metadata),
102
+ onExitSuccess: (metadata) => emit('exit-success', metadata),
103
+ onExitAbort: (metadata) => emit('exit-abort', metadata),
104
+ onExitError: (metadata) => emit('exit-error', metadata),
105
+ })
106
+
107
+ const handleClick = () => {
108
+ open()
109
+ }
110
+
111
+ return () =>
112
+ h(
113
+ props.as,
114
+ {
115
+ class: 'quiltt-button',
116
+ onClick: handleClick,
117
+ },
118
+ slots.default?.()
119
+ )
120
+ },
121
+ })