@capitalos/vue 0.1.0-rc.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/README.md ADDED
@@ -0,0 +1,75 @@
1
+ # CapitalOS for Vue
2
+
3
+ Vue components for integrating with CapitalOS
4
+
5
+ ## Requirements
6
+
7
+ Vue 2.7.0 or later
8
+
9
+ ## Installation
10
+
11
+ ### npm
12
+
13
+ ```bash
14
+ npm install @capitalos/vue
15
+ ```
16
+
17
+ ### yarn
18
+
19
+ ```bash
20
+ yarn add @capitalos/vue
21
+ ```
22
+
23
+ ### pnpm
24
+
25
+ ```bash
26
+ pnpm add @capitalos/vue
27
+ ```
28
+
29
+ ## Documentation
30
+
31
+ Please refer to the [official docs](https://docs.capitalos.com/docs/using-vue-client-library) for more details.
32
+
33
+ ## Usage
34
+
35
+ In order to use the components you will need to obtain a client authentication token. Refer to the [CapitalOS documentation](https://docs.capitalos.com/docs/using-vue-client-library) for more information.
36
+
37
+ ```typescript
38
+ // main.ts
39
+ import { createApp } from 'vue'
40
+ import { createCapitalOs } from '@capitalos/vue'
41
+ import App from './App.vue'
42
+
43
+ const app = createApp(App)
44
+
45
+ app.use(
46
+ createCapitalOs({
47
+ getToken: async () => {
48
+ // Call your backend which initiates login and returns a one-time token
49
+ const res = await fetch('/api/capitalos/token', { method: 'POST' })
50
+ const json = await res.json()
51
+ return json.token
52
+ },
53
+ enableLogging: true,
54
+ })
55
+ )
56
+
57
+ app.mount('#app')
58
+ ```
59
+
60
+ ```vue
61
+ <!-- MyComponent.vue -->
62
+ <template>
63
+ <CardsApp @loaded="onLoaded" @error="onError" />
64
+ </template>
65
+
66
+ <script setup>
67
+ import { CardsApp } from '@capitalos/vue'
68
+ const onLoaded = () => console.log('CardsApp loaded!')
69
+ const onError = (error) => console.error('CardsApp error:', error)
70
+ </script>
71
+ ```
72
+
73
+ ## TypeScript support
74
+
75
+ TypeScript definitions for `@capitalos/vue` are built into the npm package and should be automatically picked up by your editor.
@@ -0,0 +1,52 @@
1
+ import { ComputedRef, DeepReadonly, Ref } from "vue";
2
+ import { CapitalOSError, ErrorCode, Logger, Logger as Logger$1, RawErrorDetails, ThemeColorScheme, ThemeColorScheme as ThemeColorScheme$1, TokenData, TokenData as TokenData$1 } from "@capitalos/core";
3
+
4
+ //#region src/types.d.ts
5
+ interface CapitalOsConfig {
6
+ getToken: () => Promise<string>;
7
+ enableLogging?: boolean;
8
+ logger?: Logger$1;
9
+ theme?: ThemeColorScheme$1;
10
+ }
11
+ interface CapitalOsState {
12
+ tokenData: DeepReadonly<Ref<TokenData$1 | undefined>>;
13
+ isLoading: DeepReadonly<Ref<boolean>>;
14
+ error: DeepReadonly<Ref<Error | undefined>>;
15
+ invalidateToken: () => void;
16
+ config: CapitalOsConfig;
17
+ }
18
+ type AuthState = 'idle' | 'loading' | 'authenticated' | 'error';
19
+
20
+ //#endregion
21
+ //#region src/plugin.d.ts
22
+ //# sourceMappingURL=types.d.ts.map
23
+ declare const CAPITALOS_INJECTION_KEY: unique symbol;
24
+ interface Vue2Constructor {
25
+ mixin: (mixin: Record<string, unknown>) => void;
26
+ version: string;
27
+ }
28
+ interface Vue3App {
29
+ provide: (key: symbol, value: unknown) => void;
30
+ version: string;
31
+ }
32
+ declare function createCapitalOs(config: CapitalOsConfig): {
33
+ install(appOrVue: Vue2Constructor | Vue3App): void;
34
+ };
35
+
36
+ //#endregion
37
+ //#region src/components/CardsApp.d.ts
38
+ declare const CardsApp: any;
39
+
40
+ //#endregion
41
+ //#region src/composables/use-capitalos-auth.d.ts
42
+ //# sourceMappingURL=CardsApp.d.ts.map
43
+ interface UseCapitalOsAuthReturn extends CapitalOsState {
44
+ authState: ComputedRef<AuthState>;
45
+ }
46
+ declare function useCapitalOsAuth(): UseCapitalOsAuthReturn;
47
+
48
+ //#endregion
49
+ //# sourceMappingURL=use-capitalos-auth.d.ts.map
50
+
51
+ export { AuthState, CAPITALOS_INJECTION_KEY, CapitalOSError, CapitalOsConfig, CardsApp, ErrorCode, Logger, RawErrorDetails, ThemeColorScheme, TokenData, UseCapitalOsAuthReturn, createCapitalOs, useCapitalOsAuth };
52
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/plugin.ts","../src/components/CardsApp.ts","../src/composables/use-capitalos-auth.ts"],"sourcesContent":null,"mappings":";;;;UAMiB,eAAA;kBAKC;EALD,aAAA,CAAA,EAAA,OAAe;EAAA,MAAA,CAAA,EAerB,QAfqB;EAAA,KAKd,CAAA,EAeR,kBAfQ;;AAeR,UAMO,cAAA,CANP;EAAgB,SAAA,EAOb,YAPa,CAOA,GAPA,CAOI,WAPJ,GAAA,SAAA,CAAA,CAAA;EAMT,SAAA,EAEJ,YAFkB,CAEL,GAFK,CAAA,OAAA,CAAA,CAAA;EAAA,KAAA,EAGtB,YAHsB,CAGT,GAHS,CAGL,KAHK,GAAA,SAAA,CAAA,CAAA;EAAA,eACD,EAAA,GAAA,GAAA,IAAA;EAAS,MAAb,EAIhB,eAJgB;;AACA,KASd,SAAA,GATc,MAAA,GAAA,SAAA,GAAA,eAAA,GAAA,OAAA;;;;;cCSb;UAsCH,eAAA;ED3EO,KAAA,EAAA,CAAA,KAAA,EC4EA,MD5Ee,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,GAAA,IAAA;EAAA,OAAA,EAAA,MAAA;;UCkFtB,OAAA,CDnEC;EAAM,OAKP,EAAA,CAAA,GAAA,EAAA,MAAA,EAAA,KAAA,EAAA,OAAA,EAAA,GAAA,IAAA;EAAgB,OAAA,EAAA,MAAA;AAM1B;AAA+B,iBCyFf,eAAA,CDzFe,MAAA,ECyFS,eDzFT,CAAA,EAAA;EAAA,OACD,CAAA,QAAA,EC0FR,eD1FQ,GC0FU,OD1FV,CAAA,EAAA,IAAA;CAAS;;;;cEqB1B;;;;;UC/CI,sBAAA,SAA+B;aAInC,YAAY;AHLzB;AAAgC,iBGahB,gBAAA,CAAA,CHbgB,EGaI,sBHbJ"}
@@ -0,0 +1,52 @@
1
+ import { ComputedRef, DeepReadonly, Ref } from "vue";
2
+ import { CapitalOSError, ErrorCode, Logger, Logger as Logger$1, RawErrorDetails, ThemeColorScheme, ThemeColorScheme as ThemeColorScheme$1, TokenData, TokenData as TokenData$1 } from "@capitalos/core";
3
+
4
+ //#region src/types.d.ts
5
+ interface CapitalOsConfig {
6
+ getToken: () => Promise<string>;
7
+ enableLogging?: boolean;
8
+ logger?: Logger$1;
9
+ theme?: ThemeColorScheme$1;
10
+ }
11
+ interface CapitalOsState {
12
+ tokenData: DeepReadonly<Ref<TokenData$1 | undefined>>;
13
+ isLoading: DeepReadonly<Ref<boolean>>;
14
+ error: DeepReadonly<Ref<Error | undefined>>;
15
+ invalidateToken: () => void;
16
+ config: CapitalOsConfig;
17
+ }
18
+ type AuthState = 'idle' | 'loading' | 'authenticated' | 'error';
19
+
20
+ //#endregion
21
+ //#region src/plugin.d.ts
22
+ //# sourceMappingURL=types.d.ts.map
23
+ declare const CAPITALOS_INJECTION_KEY: unique symbol;
24
+ interface Vue2Constructor {
25
+ mixin: (mixin: Record<string, unknown>) => void;
26
+ version: string;
27
+ }
28
+ interface Vue3App {
29
+ provide: (key: symbol, value: unknown) => void;
30
+ version: string;
31
+ }
32
+ declare function createCapitalOs(config: CapitalOsConfig): {
33
+ install(appOrVue: Vue2Constructor | Vue3App): void;
34
+ };
35
+
36
+ //#endregion
37
+ //#region src/components/CardsApp.d.ts
38
+ declare const CardsApp: any;
39
+
40
+ //#endregion
41
+ //#region src/composables/use-capitalos-auth.d.ts
42
+ //# sourceMappingURL=CardsApp.d.ts.map
43
+ interface UseCapitalOsAuthReturn extends CapitalOsState {
44
+ authState: ComputedRef<AuthState>;
45
+ }
46
+ declare function useCapitalOsAuth(): UseCapitalOsAuthReturn;
47
+
48
+ //#endregion
49
+ //# sourceMappingURL=use-capitalos-auth.d.ts.map
50
+
51
+ export { AuthState, CAPITALOS_INJECTION_KEY, CapitalOSError, CapitalOsConfig, CardsApp, ErrorCode, Logger, RawErrorDetails, ThemeColorScheme, TokenData, UseCapitalOsAuthReturn, createCapitalOs, useCapitalOsAuth };
52
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/plugin.ts","../src/components/CardsApp.ts","../src/composables/use-capitalos-auth.ts"],"sourcesContent":null,"mappings":";;;;UAMiB,eAAA;kBAKC;EALD,aAAA,CAAA,EAAA,OAAe;EAAA,MAAA,CAAA,EAerB,QAfqB;EAAA,KAKd,CAAA,EAeR,kBAfQ;;AAeR,UAMO,cAAA,CANP;EAAgB,SAAA,EAOb,YAPa,CAOA,GAPA,CAOI,WAPJ,GAAA,SAAA,CAAA,CAAA;EAMT,SAAA,EAEJ,YAFkB,CAEL,GAFK,CAAA,OAAA,CAAA,CAAA;EAAA,KAAA,EAGtB,YAHsB,CAGT,GAHS,CAGL,KAHK,GAAA,SAAA,CAAA,CAAA;EAAA,eACD,EAAA,GAAA,GAAA,IAAA;EAAS,MAAb,EAIhB,eAJgB;;AACA,KASd,SAAA,GATc,MAAA,GAAA,SAAA,GAAA,eAAA,GAAA,OAAA;;;;;cCSb;UAsCH,eAAA;ED3EO,KAAA,EAAA,CAAA,KAAA,EC4EA,MD5Ee,CAAA,MAAA,EAAA,OAAA,CAAA,EAAA,GAAA,IAAA;EAAA,OAAA,EAAA,MAAA;;UCkFtB,OAAA,CDnEC;EAAM,OAKP,EAAA,CAAA,GAAA,EAAA,MAAA,EAAA,KAAA,EAAA,OAAA,EAAA,GAAA,IAAA;EAAgB,OAAA,EAAA,MAAA;AAM1B;AAA+B,iBCyFf,eAAA,CDzFe,MAAA,ECyFS,eDzFT,CAAA,EAAA;EAAA,OACD,CAAA,QAAA,EC0FR,eD1FQ,GC0FU,OD1FV,CAAA,EAAA,IAAA;CAAS;;;;cEqB1B;;;;;UC/CI,sBAAA,SAA+B;aAInC,YAAY;AHLzB;AAAgC,iBGahB,gBAAA,CAAA,CHbgB,EGaI,sBHbJ"}
package/dist/index.js ADDED
@@ -0,0 +1,280 @@
1
+ "use strict";
2
+ //#region rolldown:runtime
3
+ var __create = Object.create;
4
+ var __defProp = Object.defineProperty;
5
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
6
+ var __getOwnPropNames = Object.getOwnPropertyNames;
7
+ var __getProtoOf = Object.getPrototypeOf;
8
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
9
+ var __copyProps = (to, from, except, desc) => {
10
+ if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
11
+ key = keys[i];
12
+ if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
13
+ get: ((k) => from[k]).bind(null, key),
14
+ enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
15
+ });
16
+ }
17
+ return to;
18
+ };
19
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
20
+ value: mod,
21
+ enumerable: true
22
+ }) : target, mod));
23
+
24
+ //#endregion
25
+ const vue = __toESM(require("vue"));
26
+ const __capitalos_core = __toESM(require("@capitalos/core"));
27
+
28
+ //#region src/plugin.ts
29
+ /**
30
+ * Injection key for CapitalOS state.
31
+ * Used with Vue's provide/inject system.
32
+ */
33
+ const CAPITALOS_INJECTION_KEY = Symbol("capitalos");
34
+ let globalState = null;
35
+ function getGlobalState() {
36
+ return globalState;
37
+ }
38
+ const TOKEN_EXCHANGE_TIMEOUT_MS = 1e4;
39
+ function toError(error) {
40
+ if (error instanceof Error) return error;
41
+ return new Error(String(error));
42
+ }
43
+ let installed = false;
44
+ function isVue3App(appOrVue) {
45
+ return typeof appOrVue.provide === "function";
46
+ }
47
+ /**
48
+ * Creates the CapitalOS Vue plugin.
49
+ *
50
+ * Usage (Vue 3):
51
+ * ```typescript
52
+ * import { createApp } from 'vue'
53
+ * import { createCapitalOs } from '@capitalos/vue'
54
+ *
55
+ * const app = createApp(App)
56
+ * app.use(createCapitalOs({ getToken: async () => { ... } }))
57
+ * app.mount('#app')
58
+ * ```
59
+ *
60
+ * Usage (Vue 2.7):
61
+ * ```typescript
62
+ * import Vue from 'vue'
63
+ * import { createCapitalOs } from '@capitalos/vue'
64
+ *
65
+ * Vue.use(createCapitalOs({ getToken: async () => { ... } }))
66
+ * new Vue({ render: h => h(App) }).$mount('#app')
67
+ * ```
68
+ */
69
+ function createCapitalOs(config) {
70
+ return { install(appOrVue) {
71
+ if (installed) {
72
+ const logger$1 = (0, __capitalos_core.createLogger)(config.enableLogging, config.logger);
73
+ logger$1.warn("[CapitalOS] Plugin already installed, skipping duplicate installation");
74
+ return;
75
+ }
76
+ installed = true;
77
+ const logger = (0, __capitalos_core.createLogger)(config.enableLogging, config.logger);
78
+ const tokenData = (0, vue.ref)(void 0);
79
+ const isLoading = (0, vue.ref)(false);
80
+ const error = (0, vue.ref)(void 0);
81
+ let activeAbortController = null;
82
+ async function refreshToken() {
83
+ if (isLoading.value) {
84
+ logger.log("[CapitalOS] Token refresh already in progress, skipping");
85
+ return;
86
+ }
87
+ logger.log("[CapitalOS] Starting token refresh");
88
+ isLoading.value = true;
89
+ error.value = void 0;
90
+ activeAbortController?.abort();
91
+ const abortController = new AbortController();
92
+ activeAbortController = abortController;
93
+ try {
94
+ const oneTimeToken = await config.getToken();
95
+ logger.log("[CapitalOS] Received one-time token");
96
+ if (abortController.signal.aborted) return;
97
+ const result = await (0, __capitalos_core.exchangeOneTimeToken)({
98
+ oneTimeToken,
99
+ enableLogging: config.enableLogging,
100
+ logger: config.logger,
101
+ timeoutMs: TOKEN_EXCHANGE_TIMEOUT_MS,
102
+ signal: abortController.signal
103
+ });
104
+ if (abortController.signal.aborted) return;
105
+ tokenData.value = {
106
+ token: result.longLivedToken,
107
+ tokenType: __capitalos_core.TokenType.longLived,
108
+ baseUrl: result.baseUrl,
109
+ paramKey: __capitalos_core.TokenParamKey.accessToken,
110
+ paramLocation: __capitalos_core.TokenParamLocation.hash
111
+ };
112
+ logger.log("[CapitalOS] Token exchange complete");
113
+ } catch (e) {
114
+ if (e instanceof Error && e.name === "AbortError") return;
115
+ const err = toError(e);
116
+ logger.error(`[CapitalOS] Token refresh failed: ${err.message}`);
117
+ error.value = err;
118
+ } finally {
119
+ if (activeAbortController === abortController) activeAbortController = null;
120
+ isLoading.value = false;
121
+ }
122
+ }
123
+ function invalidateToken() {
124
+ logger.log("[CapitalOS] Invalidating token");
125
+ tokenData.value = void 0;
126
+ error.value = void 0;
127
+ refreshToken();
128
+ }
129
+ const state = {
130
+ tokenData: (0, vue.readonly)(tokenData),
131
+ isLoading: (0, vue.readonly)(isLoading),
132
+ error: (0, vue.readonly)(error),
133
+ invalidateToken,
134
+ config
135
+ };
136
+ globalState = state;
137
+ if (isVue3App(appOrVue)) appOrVue.provide(CAPITALOS_INJECTION_KEY, state);
138
+ else appOrVue.mixin({ provide() {
139
+ return { [CAPITALOS_INJECTION_KEY]: state };
140
+ } });
141
+ refreshToken();
142
+ } };
143
+ }
144
+
145
+ //#endregion
146
+ //#region src/composables/use-capitalos-auth.ts
147
+ /**
148
+ * @internal
149
+ * Internal composable for accessing CapitalOS authentication state.
150
+ * Used by SDK components - not intended for direct consumer use.
151
+ */
152
+ function useCapitalOsAuth() {
153
+ const state = (0, vue.inject)(CAPITALOS_INJECTION_KEY) ?? getGlobalState();
154
+ if (!state) throw new Error("useCapitalOsAuth must be used in a component where createCapitalOs plugin is installed. Make sure to call app.use(createCapitalOs({ ... })) or Vue.use(createCapitalOs({ ... })) in your main.ts");
155
+ const authState = (0, vue.computed)(() => {
156
+ if (state.isLoading.value) return "loading";
157
+ if (state.error.value) return "error";
158
+ if (state.tokenData.value) return "authenticated";
159
+ return "idle";
160
+ });
161
+ return {
162
+ ...state,
163
+ authState
164
+ };
165
+ }
166
+
167
+ //#endregion
168
+ //#region package.json
169
+ var version = "0.1.0-rc.1";
170
+
171
+ //#endregion
172
+ //#region src/components/CardsApp.ts
173
+ /**
174
+ * CardsApp component that renders the CapitalOS cards interface in an iframe.
175
+ *
176
+ * The component emits events for loading state - consumers should handle their own loading UI:
177
+ *
178
+ * @example
179
+ * ```vue
180
+ * <template>
181
+ * <div v-if="!cardsLoaded">Loading...</div>
182
+ * <CardsApp
183
+ * @loaded="cardsLoaded = true"
184
+ * @error="onCardsError"
185
+ * />
186
+ * </template>
187
+ *
188
+ * <script setup>
189
+ * import { ref } from 'vue'
190
+ * import { CardsApp } from '@capitalos/vue'
191
+ *
192
+ * const cardsLoaded = ref(false)
193
+ * const onCardsError = (error) => console.error(error)
194
+ * </script>
195
+ * ```
196
+ *
197
+ * Note: CardsApp is not designed to be conditionally mounted/unmounted frequently.
198
+ * For show/hide behavior, consumers should toggle visibility (e.g., v-show) rather
199
+ * than mount/unmount repeatedly.
200
+ */
201
+ const CardsApp = (0, vue.defineComponent)({
202
+ name: "CardsApp",
203
+ props: {
204
+ theme: String,
205
+ heightOffsetPx: {
206
+ type: Number,
207
+ default: 12
208
+ },
209
+ enableLogging: Boolean
210
+ },
211
+ emits: {
212
+ loaded: () => true,
213
+ error: (error) => error instanceof __capitalos_core.CapitalOSError
214
+ },
215
+ setup(props, { emit }) {
216
+ const containerRef = (0, vue.ref)(null);
217
+ const { tokenData, config, invalidateToken } = useCapitalOsAuth();
218
+ let iframeManager = null;
219
+ function initializeIframe(token, container) {
220
+ iframeManager?.destroy();
221
+ const resolvedEnableLogging = props.enableLogging ?? config.enableLogging ?? false;
222
+ const resolvedTheme = props.theme ?? config.theme;
223
+ iframeManager = new __capitalos_core.IframeManager({
224
+ container,
225
+ tokenData: token,
226
+ renderingContext: { entryPoint: "cardsApp" },
227
+ theme: resolvedTheme,
228
+ enableLogging: resolvedEnableLogging,
229
+ logger: config.logger,
230
+ sdkVersion: version,
231
+ heightOffsetPx: props.heightOffsetPx,
232
+ callbacks: {
233
+ onLoad: () => {
234
+ emit("loaded");
235
+ },
236
+ onError: (rawError) => {
237
+ const capitalOsError = new __capitalos_core.CapitalOSError(rawError);
238
+ emit("error", capitalOsError);
239
+ },
240
+ onTokenExpired: () => {
241
+ invalidateToken();
242
+ }
243
+ }
244
+ });
245
+ }
246
+ (0, vue.watch)([tokenData, containerRef], ([newTokenData, container]) => {
247
+ if (newTokenData && container) initializeIframe(newTokenData, container);
248
+ }, { immediate: true });
249
+ (0, vue.onUnmounted)(() => {
250
+ iframeManager?.destroy();
251
+ iframeManager = null;
252
+ });
253
+ return () => (0, vue.h)("div", {
254
+ ref: (el) => {
255
+ containerRef.value = el;
256
+ },
257
+ class: "capitalos-iframe-container",
258
+ style: { width: "100%" }
259
+ });
260
+ }
261
+ });
262
+
263
+ //#endregion
264
+ exports.CAPITALOS_INJECTION_KEY = CAPITALOS_INJECTION_KEY
265
+ Object.defineProperty(exports, 'CapitalOSError', {
266
+ enumerable: true,
267
+ get: function () {
268
+ return __capitalos_core.CapitalOSError;
269
+ }
270
+ });
271
+ exports.CardsApp = CardsApp
272
+ Object.defineProperty(exports, 'ErrorCode', {
273
+ enumerable: true,
274
+ get: function () {
275
+ return __capitalos_core.ErrorCode;
276
+ }
277
+ });
278
+ exports.createCapitalOs = createCapitalOs
279
+ exports.useCapitalOsAuth = useCapitalOsAuth
280
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","names":["globalState: CapitalOsState | null","error: unknown","appOrVue: Vue2Constructor | Vue3App","config: CapitalOsConfig","logger","activeAbortController: AbortController | null","TokenType","TokenParamKey","TokenParamLocation","state: CapitalOsState","error: CapitalOSError","CapitalOSError","iframeManager: IframeManager | null","token: TokenData","container: HTMLElement","IframeManager","SDK_VERSION","rawError: RawErrorDetails","el: Element | null"],"sources":["../src/plugin.ts","../src/composables/use-capitalos-auth.ts","../package.json","../src/components/CardsApp.ts"],"sourcesContent":["// =============================================================================\n// MAINTAINER NOTE: Vue 2.7 vs Vue 3 Plugin Compatibility\n// =============================================================================\n//\n// Vue 2.7 and Vue 3 call plugin.install() with DIFFERENT arguments:\n//\n// Vue 3 consumer code:\n// const app = createApp(App)\n// app.use(myPlugin) // → calls myPlugin.install(app) with app INSTANCE\n//\n// Vue 2.7 consumer code:\n// Vue.use(myPlugin) // → calls myPlugin.install(Vue) with Vue CONSTRUCTOR\n//\n// This matters because:\n// - Vue 3's app instance has app.provide() for dependency injection\n// - Vue 2.7's Vue constructor does NOT have provide() - must use Vue.mixin()\n//\n// This plugin detects which API is available and uses the appropriate method.\n// As an additional fallback for edge cases, we also store state in a module-level\n// variable that `useCapitalOsAuth` can access if `inject()` returns undefined.\n//\n// FUTURE: If we ever need to support Vue < 2.7 or have more complex version-\n// specific logic throughout the codebase, consider using `vue-demi` package\n// (https://github.com/vueuse/vue-demi) which abstracts all Vue 2/3 differences.\n// For now, our simple detection is sufficient since Vue 2.7 has Composition API\n// backported and we only need to handle the plugin install signature difference.\n// =============================================================================\n\nimport { ref, readonly } from 'vue'\nimport {\n exchangeOneTimeToken,\n createLogger,\n TokenType,\n TokenParamKey,\n TokenParamLocation,\n type TokenData,\n} from '@capitalos/core'\nimport type { CapitalOsConfig, CapitalOsState } from './types'\n\n/**\n * Injection key for CapitalOS state.\n * Used with Vue's provide/inject system.\n */\nexport const CAPITALOS_INJECTION_KEY = Symbol('capitalos')\n\n// MAINTAINER NOTE: Module-level state storage for Vue 2.7 compatibility fallback.\n//\n// In Vue 2.7, even with the mixin approach, there can be edge cases where\n// `inject()` doesn't find the provided value (e.g., in the root component\n// or components created before the mixin is applied). This global fallback\n// ensures `useCapitalOsAuth()` always has access to the state.\nlet globalState: CapitalOsState | null = null\n\n// @internal - used by useCapitalOsAuth for Vue 2.7 fallback\nexport function getGlobalState(): CapitalOsState | null {\n return globalState\n}\n\nconst TOKEN_EXCHANGE_TIMEOUT_MS = 10_000\n\n// Converts an unknown error to an Error object\nfunction toError(error: unknown): Error {\n if (error instanceof Error) {\n return error\n }\n return new Error(String(error))\n}\n\n// MAINTAINER NOTE: Module-level guard against double installation (singleton pattern).\n//\n// This MUST be module-level (not inside createCapitalOs) because:\n// - The plugin creates reactive state that should only exist once\n// - Multiple installations would create multiple token exchange iframes\n// - Multiple auth states would cause inconsistent behavior\n//\n// If a consumer accidentally calls Vue.use(createCapitalOs(...)) twice,\n// the second call will be silently ignored with a warning.\nlet installed = false\n\n// Type for Vue 2 constructor passed to plugin install.\n// In Vue 2.7, `Vue.use(plugin)` passes the Vue constructor.\ninterface Vue2Constructor {\n mixin: (mixin: Record<string, unknown>) => void\n version: string\n}\n\n// Type for Vue 3 app instance passed to plugin install.\n// In Vue 3, `app.use(plugin)` passes the app instance.\ninterface Vue3App {\n provide: (key: symbol, value: unknown) => void\n version: string\n}\n\n// Detect Vue version by checking for Vue 3's `provide` method.\n// Vue 3 app instances have `app.provide()`, Vue 2 constructors don't.\nfunction isVue3App(appOrVue: Vue2Constructor | Vue3App): appOrVue is Vue3App {\n return typeof (appOrVue as Vue3App).provide === 'function'\n}\n\n/**\n * Creates the CapitalOS Vue plugin.\n *\n * Usage (Vue 3):\n * ```typescript\n * import { createApp } from 'vue'\n * import { createCapitalOs } from '@capitalos/vue'\n *\n * const app = createApp(App)\n * app.use(createCapitalOs({ getToken: async () => { ... } }))\n * app.mount('#app')\n * ```\n *\n * Usage (Vue 2.7):\n * ```typescript\n * import Vue from 'vue'\n * import { createCapitalOs } from '@capitalos/vue'\n *\n * Vue.use(createCapitalOs({ getToken: async () => { ... } }))\n * new Vue({ render: h => h(App) }).$mount('#app')\n * ```\n */\nexport function createCapitalOs(config: CapitalOsConfig) {\n return {\n install(appOrVue: Vue2Constructor | Vue3App) {\n // Prevent double installation\n if (installed) {\n const logger = createLogger(config.enableLogging, config.logger)\n logger.warn('[CapitalOS] Plugin already installed, skipping duplicate installation')\n return\n }\n installed = true\n\n const logger = createLogger(config.enableLogging, config.logger)\n\n // Create reactive state\n const tokenData = ref<TokenData | undefined>(undefined)\n const isLoading = ref(false)\n const error = ref<Error | undefined>(undefined)\n\n // AbortController for cancelling in-flight token exchanges\n let activeAbortController: AbortController | null = null\n\n // Refreshes the token by fetching a new one-time token and exchanging it.\n async function refreshToken(): Promise<void> {\n // Prevent concurrent refresh calls - if already loading, skip\n if (isLoading.value) {\n logger.log('[CapitalOS] Token refresh already in progress, skipping')\n return\n }\n\n logger.log('[CapitalOS] Starting token refresh')\n isLoading.value = true\n error.value = undefined\n\n // Abort any in-flight exchange when a new one begins\n activeAbortController?.abort()\n const abortController = new AbortController()\n activeAbortController = abortController\n\n try {\n const oneTimeToken = await config.getToken()\n logger.log('[CapitalOS] Received one-time token')\n\n // Check if aborted after getToken\n if (abortController.signal.aborted) {\n return\n }\n\n // Exchange for long-lived token using @capitalos/core\n const result = await exchangeOneTimeToken({\n oneTimeToken,\n enableLogging: config.enableLogging,\n logger: config.logger,\n timeoutMs: TOKEN_EXCHANGE_TIMEOUT_MS,\n signal: abortController.signal,\n })\n\n // Check if aborted after exchange\n if (abortController.signal.aborted) {\n return\n }\n\n tokenData.value = {\n token: result.longLivedToken,\n tokenType: TokenType.longLived,\n baseUrl: result.baseUrl,\n paramKey: TokenParamKey.accessToken,\n paramLocation: TokenParamLocation.hash,\n }\n logger.log('[CapitalOS] Token exchange complete')\n } catch (e) {\n // Ignore abort errors\n if (e instanceof Error && e.name === 'AbortError') {\n return\n }\n\n const err = toError(e)\n logger.error(`[CapitalOS] Token refresh failed: ${err.message}`)\n error.value = err\n } finally {\n if (activeAbortController === abortController) {\n activeAbortController = null\n }\n isLoading.value = false\n }\n }\n\n // Invalidates the current token, triggering a refresh.\n // Called when the iframe signals that the token has expired.\n function invalidateToken(): void {\n logger.log('[CapitalOS] Invalidating token')\n tokenData.value = undefined\n error.value = undefined\n refreshToken()\n }\n\n // Create state object to provide\n const state: CapitalOsState = {\n tokenData: readonly(tokenData),\n isLoading: readonly(isLoading),\n error: readonly(error),\n invalidateToken,\n config,\n }\n\n // Store globally as fallback for Vue 2.7 edge cases where inject() fails\n globalState = state\n\n // Provide state using the appropriate API based on Vue version\n if (isVue3App(appOrVue)) {\n // Vue 3: use app.provide() - this makes state available to all components\n // via inject(CAPITALOS_INJECTION_KEY)\n appOrVue.provide(CAPITALOS_INJECTION_KEY, state)\n } else {\n // Vue 2.7: use Vue.mixin() to add a provide function to all components.\n // This is the Vue 2 equivalent of app.provide() - it injects the provide\n // option into every component so inject() can find our state.\n appOrVue.mixin({\n provide() {\n return {\n [CAPITALOS_INJECTION_KEY]: state,\n }\n },\n })\n }\n\n // Start token exchange eagerly (matches Angular behavior)\n refreshToken()\n },\n }\n}\n","import { inject, computed, type ComputedRef } from 'vue'\nimport { CAPITALOS_INJECTION_KEY, getGlobalState } from '../plugin'\nimport type { CapitalOsState, AuthState } from '../types'\n\n/**\n * Return type for useCapitalOsAuth composable\n */\nexport interface UseCapitalOsAuthReturn extends CapitalOsState {\n /**\n * Computed authentication state\n */\n authState: ComputedRef<AuthState>\n}\n\n/**\n * @internal\n * Internal composable for accessing CapitalOS authentication state.\n * Used by SDK components - not intended for direct consumer use.\n */\nexport function useCapitalOsAuth(): UseCapitalOsAuthReturn {\n // MAINTAINER NOTE: Vue 2.7 vs Vue 3 Compatibility\n //\n // Try inject() first - this works in:\n // - Vue 3 (via app.provide)\n // - Vue 2.7 (via mixin provide, in most components)\n //\n // Fall back to getGlobalState() for Vue 2.7 edge cases where inject() fails,\n // such as the root component or components created before the mixin is applied.\n //\n // Do NOT remove the ?? getGlobalState() fallback - it's required for Vue 2.7.\n const state = inject<CapitalOsState>(CAPITALOS_INJECTION_KEY) ?? getGlobalState()\n\n if (!state) {\n throw new Error(\n 'useCapitalOsAuth must be used in a component where createCapitalOs plugin is installed. ' +\n 'Make sure to call app.use(createCapitalOs({ ... })) or Vue.use(createCapitalOs({ ... })) in your main.ts'\n )\n }\n\n const authState = computed<AuthState>(() => {\n if (state.isLoading.value) return 'loading'\n if (state.error.value) return 'error'\n if (state.tokenData.value) return 'authenticated'\n return 'idle'\n })\n\n return {\n ...state,\n authState,\n }\n}\n","{\n \"name\": \"@capitalos/vue\",\n \"version\": \"0.1.0-rc.1\",\n \"description\": \"Vue SDK for CapitalOS\",\n \"main\": \"dist/index.js\",\n \"module\": \"dist/index.mjs\",\n \"types\": \"dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"import\": {\n \"types\": \"./dist/index.d.mts\",\n \"default\": \"./dist/index.mjs\"\n },\n \"require\": {\n \"types\": \"./dist/index.d.ts\",\n \"default\": \"./dist/index.js\"\n }\n }\n },\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsdown\",\n \"dev\": \"tsdown --watch\",\n \"lint\": \"eslint src --ext .ts --fix\",\n \"lint-ci\": \"eslint src --ext .ts\",\n \"start\": \"pnpm --filter vue-test-app dev\",\n \"start:all\": \"concurrently \\\"pnpm --filter sdk-test-server start\\\" \\\"pnpm start\\\"\"\n },\n \"keywords\": [\n \"capitalos\",\n \"vue\",\n \"sdk\",\n \"iframe\"\n ],\n \"author\": \"CapitalOS\",\n \"license\": \"MIT\",\n \"peerDependencies\": {\n \"vue\": \"^2.7.0 || ^3.0.0\"\n },\n \"dependencies\": {\n \"@capitalos/core\": \"workspace:*\"\n },\n \"devDependencies\": {\n \"tsdown\": \"^0.9.2\",\n \"typescript\": \"^5.6.2\",\n \"vue\": \"^3.5.13\",\n \"concurrently\": \"^9.1.2\"\n }\n}\n","// =============================================================================\n// MAINTAINER NOTE: Why defineComponent + render function instead of SFC?\n// =============================================================================\n//\n// This component uses `defineComponent()` with a render function (`h()`) instead\n// of a Single File Component (.vue file) for Vue 2.7/3 compatibility:\n//\n// - SFCs require different compilers for Vue 2 vs Vue 3\n// - Render functions work identically in both versions\n// - Simpler build - no vue-compiler needed, just TypeScript\n//\n// This is a deliberate architectural choice for cross-version compatibility.\n// Do NOT convert this to an SFC without understanding the implications.\n// =============================================================================\n\nimport { defineComponent, h, ref, watch, onUnmounted, type PropType } from 'vue'\nimport {\n IframeManager,\n CapitalOSError,\n type ThemeColorScheme,\n type TokenData,\n type RawErrorDetails,\n} from '@capitalos/core'\nimport { useCapitalOsAuth } from '../composables/use-capitalos-auth'\nimport { version as SDK_VERSION } from '../../package.json'\n\n/**\n * CardsApp component that renders the CapitalOS cards interface in an iframe.\n *\n * The component emits events for loading state - consumers should handle their own loading UI:\n *\n * @example\n * ```vue\n * <template>\n * <div v-if=\"!cardsLoaded\">Loading...</div>\n * <CardsApp\n * @loaded=\"cardsLoaded = true\"\n * @error=\"onCardsError\"\n * />\n * </template>\n *\n * <script setup>\n * import { ref } from 'vue'\n * import { CardsApp } from '@capitalos/vue'\n *\n * const cardsLoaded = ref(false)\n * const onCardsError = (error) => console.error(error)\n * </script>\n * ```\n *\n * Note: CardsApp is not designed to be conditionally mounted/unmounted frequently.\n * For show/hide behavior, consumers should toggle visibility (e.g., v-show) rather\n * than mount/unmount repeatedly.\n */\nexport const CardsApp = defineComponent({\n name: 'CardsApp',\n props: {\n /**\n * Theme color scheme for the cards interface.\n * Overrides the theme set in plugin configuration.\n */\n theme: String as PropType<ThemeColorScheme>,\n /**\n * Offset in pixels to add to the iframe height calculation.\n * Useful for avoiding scrollbars when the content height changes dynamically.\n */\n heightOffsetPx: {\n type: Number,\n default: 12,\n },\n /**\n * Whether to enable logging for debugging purposes.\n * Overrides the enableLogging set in plugin configuration.\n */\n enableLogging: Boolean,\n },\n emits: {\n /**\n * Emitted when the cards interface has finished loading.\n */\n loaded: () => true,\n /**\n * Emitted when an error occurs in the cards interface.\n */\n error: (error: CapitalOSError) => error instanceof CapitalOSError,\n },\n setup(props, { emit }) {\n const containerRef = ref<HTMLElement | null>(null)\n const { tokenData, config, invalidateToken } = useCapitalOsAuth()\n\n let iframeManager: IframeManager | null = null\n\n function initializeIframe(token: TokenData, container: HTMLElement): void {\n // Destroy previous iframe before creating new one (matches Angular/React behavior)\n // This ensures token refresh works correctly - new token = new iframe\n iframeManager?.destroy()\n\n const resolvedEnableLogging = props.enableLogging ?? config.enableLogging ?? false\n const resolvedTheme = props.theme ?? config.theme\n\n iframeManager = new IframeManager({\n container,\n tokenData: token,\n renderingContext: { entryPoint: 'cardsApp' },\n theme: resolvedTheme,\n enableLogging: resolvedEnableLogging,\n logger: config.logger,\n sdkVersion: SDK_VERSION,\n heightOffsetPx: props.heightOffsetPx,\n callbacks: {\n onLoad: () => {\n emit('loaded')\n },\n onError: (rawError: RawErrorDetails) => {\n const capitalOsError = new CapitalOSError(rawError)\n emit('error', capitalOsError)\n },\n onTokenExpired: () => {\n invalidateToken()\n },\n },\n })\n }\n\n // MAINTAINER NOTE: Watch for tokenData and container changes to initialize the iframe.\n //\n // Why watch both tokenData AND containerRef?\n // - tokenData: comes from async token exchange, may not be ready on mount\n // - containerRef: DOM element, available after first render\n //\n // Why { immediate: true }?\n // - If tokenData is already available when component mounts, we want to\n // initialize immediately without waiting for a change\n // - Without this, the iframe wouldn't load until tokenData changes again\n watch(\n [tokenData, containerRef],\n ([newTokenData, container]) => {\n if (newTokenData && container) {\n initializeIframe(newTokenData, container)\n }\n },\n { immediate: true }\n )\n\n onUnmounted(() => {\n iframeManager?.destroy()\n iframeManager = null\n })\n\n // MAINTAINER NOTE: Render function - creates the container div for the iframe.\n //\n // Why use a function ref `ref: (el) => { ... }` instead of `ref: containerRef`?\n //\n // Vue 2.7 and Vue 3 handle template refs differently in render functions:\n // - Vue 3: can use `ref: containerRef` directly\n // - Vue 2.7: requires function ref pattern for reliable element access\n //\n // The function ref pattern works in both versions, so we use it for compatibility.\n // Do NOT change this to `ref: containerRef` - it will break Vue 2.7 support.\n //\n // The `as unknown as string` type assertion is required because:\n // - Vue 3's TypeScript types don't properly express that function refs are valid\n // - The function ref pattern IS valid at runtime in both Vue 2.7 and Vue 3\n // - This is a known limitation of Vue 3's type definitions\n return () =>\n h('div', {\n // Type assertion required: Vue 3's types don't support function refs in h(),\n // but function refs ARE valid at runtime in both Vue 2.7 and Vue 3\n ref: ((el: Element | null) => {\n containerRef.value = el as HTMLElement | null\n }) as unknown as string,\n class: 'capitalos-iframe-container',\n style: { width: '100%' },\n })\n },\n})\n"],"mappings":"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"}
package/dist/index.mjs ADDED
@@ -0,0 +1,241 @@
1
+ import { computed, defineComponent, h, inject, onUnmounted, readonly, ref, watch } from "vue";
2
+ import { CapitalOSError, CapitalOSError as CapitalOSError$1, ErrorCode, IframeManager, TokenParamKey, TokenParamLocation, TokenType, createLogger, exchangeOneTimeToken } from "@capitalos/core";
3
+
4
+ //#region src/plugin.ts
5
+ /**
6
+ * Injection key for CapitalOS state.
7
+ * Used with Vue's provide/inject system.
8
+ */
9
+ const CAPITALOS_INJECTION_KEY = Symbol("capitalos");
10
+ let globalState = null;
11
+ function getGlobalState() {
12
+ return globalState;
13
+ }
14
+ const TOKEN_EXCHANGE_TIMEOUT_MS = 1e4;
15
+ function toError(error) {
16
+ if (error instanceof Error) return error;
17
+ return new Error(String(error));
18
+ }
19
+ let installed = false;
20
+ function isVue3App(appOrVue) {
21
+ return typeof appOrVue.provide === "function";
22
+ }
23
+ /**
24
+ * Creates the CapitalOS Vue plugin.
25
+ *
26
+ * Usage (Vue 3):
27
+ * ```typescript
28
+ * import { createApp } from 'vue'
29
+ * import { createCapitalOs } from '@capitalos/vue'
30
+ *
31
+ * const app = createApp(App)
32
+ * app.use(createCapitalOs({ getToken: async () => { ... } }))
33
+ * app.mount('#app')
34
+ * ```
35
+ *
36
+ * Usage (Vue 2.7):
37
+ * ```typescript
38
+ * import Vue from 'vue'
39
+ * import { createCapitalOs } from '@capitalos/vue'
40
+ *
41
+ * Vue.use(createCapitalOs({ getToken: async () => { ... } }))
42
+ * new Vue({ render: h => h(App) }).$mount('#app')
43
+ * ```
44
+ */
45
+ function createCapitalOs(config) {
46
+ return { install(appOrVue) {
47
+ if (installed) {
48
+ const logger$1 = createLogger(config.enableLogging, config.logger);
49
+ logger$1.warn("[CapitalOS] Plugin already installed, skipping duplicate installation");
50
+ return;
51
+ }
52
+ installed = true;
53
+ const logger = createLogger(config.enableLogging, config.logger);
54
+ const tokenData = ref(void 0);
55
+ const isLoading = ref(false);
56
+ const error = ref(void 0);
57
+ let activeAbortController = null;
58
+ async function refreshToken() {
59
+ if (isLoading.value) {
60
+ logger.log("[CapitalOS] Token refresh already in progress, skipping");
61
+ return;
62
+ }
63
+ logger.log("[CapitalOS] Starting token refresh");
64
+ isLoading.value = true;
65
+ error.value = void 0;
66
+ activeAbortController?.abort();
67
+ const abortController = new AbortController();
68
+ activeAbortController = abortController;
69
+ try {
70
+ const oneTimeToken = await config.getToken();
71
+ logger.log("[CapitalOS] Received one-time token");
72
+ if (abortController.signal.aborted) return;
73
+ const result = await exchangeOneTimeToken({
74
+ oneTimeToken,
75
+ enableLogging: config.enableLogging,
76
+ logger: config.logger,
77
+ timeoutMs: TOKEN_EXCHANGE_TIMEOUT_MS,
78
+ signal: abortController.signal
79
+ });
80
+ if (abortController.signal.aborted) return;
81
+ tokenData.value = {
82
+ token: result.longLivedToken,
83
+ tokenType: TokenType.longLived,
84
+ baseUrl: result.baseUrl,
85
+ paramKey: TokenParamKey.accessToken,
86
+ paramLocation: TokenParamLocation.hash
87
+ };
88
+ logger.log("[CapitalOS] Token exchange complete");
89
+ } catch (e) {
90
+ if (e instanceof Error && e.name === "AbortError") return;
91
+ const err = toError(e);
92
+ logger.error(`[CapitalOS] Token refresh failed: ${err.message}`);
93
+ error.value = err;
94
+ } finally {
95
+ if (activeAbortController === abortController) activeAbortController = null;
96
+ isLoading.value = false;
97
+ }
98
+ }
99
+ function invalidateToken() {
100
+ logger.log("[CapitalOS] Invalidating token");
101
+ tokenData.value = void 0;
102
+ error.value = void 0;
103
+ refreshToken();
104
+ }
105
+ const state = {
106
+ tokenData: readonly(tokenData),
107
+ isLoading: readonly(isLoading),
108
+ error: readonly(error),
109
+ invalidateToken,
110
+ config
111
+ };
112
+ globalState = state;
113
+ if (isVue3App(appOrVue)) appOrVue.provide(CAPITALOS_INJECTION_KEY, state);
114
+ else appOrVue.mixin({ provide() {
115
+ return { [CAPITALOS_INJECTION_KEY]: state };
116
+ } });
117
+ refreshToken();
118
+ } };
119
+ }
120
+
121
+ //#endregion
122
+ //#region src/composables/use-capitalos-auth.ts
123
+ /**
124
+ * @internal
125
+ * Internal composable for accessing CapitalOS authentication state.
126
+ * Used by SDK components - not intended for direct consumer use.
127
+ */
128
+ function useCapitalOsAuth() {
129
+ const state = inject(CAPITALOS_INJECTION_KEY) ?? getGlobalState();
130
+ if (!state) throw new Error("useCapitalOsAuth must be used in a component where createCapitalOs plugin is installed. Make sure to call app.use(createCapitalOs({ ... })) or Vue.use(createCapitalOs({ ... })) in your main.ts");
131
+ const authState = computed(() => {
132
+ if (state.isLoading.value) return "loading";
133
+ if (state.error.value) return "error";
134
+ if (state.tokenData.value) return "authenticated";
135
+ return "idle";
136
+ });
137
+ return {
138
+ ...state,
139
+ authState
140
+ };
141
+ }
142
+
143
+ //#endregion
144
+ //#region package.json
145
+ var version = "0.1.0-rc.1";
146
+
147
+ //#endregion
148
+ //#region src/components/CardsApp.ts
149
+ /**
150
+ * CardsApp component that renders the CapitalOS cards interface in an iframe.
151
+ *
152
+ * The component emits events for loading state - consumers should handle their own loading UI:
153
+ *
154
+ * @example
155
+ * ```vue
156
+ * <template>
157
+ * <div v-if="!cardsLoaded">Loading...</div>
158
+ * <CardsApp
159
+ * @loaded="cardsLoaded = true"
160
+ * @error="onCardsError"
161
+ * />
162
+ * </template>
163
+ *
164
+ * <script setup>
165
+ * import { ref } from 'vue'
166
+ * import { CardsApp } from '@capitalos/vue'
167
+ *
168
+ * const cardsLoaded = ref(false)
169
+ * const onCardsError = (error) => console.error(error)
170
+ * </script>
171
+ * ```
172
+ *
173
+ * Note: CardsApp is not designed to be conditionally mounted/unmounted frequently.
174
+ * For show/hide behavior, consumers should toggle visibility (e.g., v-show) rather
175
+ * than mount/unmount repeatedly.
176
+ */
177
+ const CardsApp = defineComponent({
178
+ name: "CardsApp",
179
+ props: {
180
+ theme: String,
181
+ heightOffsetPx: {
182
+ type: Number,
183
+ default: 12
184
+ },
185
+ enableLogging: Boolean
186
+ },
187
+ emits: {
188
+ loaded: () => true,
189
+ error: (error) => error instanceof CapitalOSError$1
190
+ },
191
+ setup(props, { emit }) {
192
+ const containerRef = ref(null);
193
+ const { tokenData, config, invalidateToken } = useCapitalOsAuth();
194
+ let iframeManager = null;
195
+ function initializeIframe(token, container) {
196
+ iframeManager?.destroy();
197
+ const resolvedEnableLogging = props.enableLogging ?? config.enableLogging ?? false;
198
+ const resolvedTheme = props.theme ?? config.theme;
199
+ iframeManager = new IframeManager({
200
+ container,
201
+ tokenData: token,
202
+ renderingContext: { entryPoint: "cardsApp" },
203
+ theme: resolvedTheme,
204
+ enableLogging: resolvedEnableLogging,
205
+ logger: config.logger,
206
+ sdkVersion: version,
207
+ heightOffsetPx: props.heightOffsetPx,
208
+ callbacks: {
209
+ onLoad: () => {
210
+ emit("loaded");
211
+ },
212
+ onError: (rawError) => {
213
+ const capitalOsError = new CapitalOSError$1(rawError);
214
+ emit("error", capitalOsError);
215
+ },
216
+ onTokenExpired: () => {
217
+ invalidateToken();
218
+ }
219
+ }
220
+ });
221
+ }
222
+ watch([tokenData, containerRef], ([newTokenData, container]) => {
223
+ if (newTokenData && container) initializeIframe(newTokenData, container);
224
+ }, { immediate: true });
225
+ onUnmounted(() => {
226
+ iframeManager?.destroy();
227
+ iframeManager = null;
228
+ });
229
+ return () => h("div", {
230
+ ref: (el) => {
231
+ containerRef.value = el;
232
+ },
233
+ class: "capitalos-iframe-container",
234
+ style: { width: "100%" }
235
+ });
236
+ }
237
+ });
238
+
239
+ //#endregion
240
+ export { CAPITALOS_INJECTION_KEY, CapitalOSError, CardsApp, ErrorCode, createCapitalOs, useCapitalOsAuth };
241
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["globalState: CapitalOsState | null","error: unknown","appOrVue: Vue2Constructor | Vue3App","config: CapitalOsConfig","logger","activeAbortController: AbortController | null","state: CapitalOsState","error: CapitalOSError","CapitalOSError","iframeManager: IframeManager | null","token: TokenData","container: HTMLElement","SDK_VERSION","rawError: RawErrorDetails","el: Element | null"],"sources":["../src/plugin.ts","../src/composables/use-capitalos-auth.ts","../package.json","../src/components/CardsApp.ts"],"sourcesContent":["// =============================================================================\n// MAINTAINER NOTE: Vue 2.7 vs Vue 3 Plugin Compatibility\n// =============================================================================\n//\n// Vue 2.7 and Vue 3 call plugin.install() with DIFFERENT arguments:\n//\n// Vue 3 consumer code:\n// const app = createApp(App)\n// app.use(myPlugin) // → calls myPlugin.install(app) with app INSTANCE\n//\n// Vue 2.7 consumer code:\n// Vue.use(myPlugin) // → calls myPlugin.install(Vue) with Vue CONSTRUCTOR\n//\n// This matters because:\n// - Vue 3's app instance has app.provide() for dependency injection\n// - Vue 2.7's Vue constructor does NOT have provide() - must use Vue.mixin()\n//\n// This plugin detects which API is available and uses the appropriate method.\n// As an additional fallback for edge cases, we also store state in a module-level\n// variable that `useCapitalOsAuth` can access if `inject()` returns undefined.\n//\n// FUTURE: If we ever need to support Vue < 2.7 or have more complex version-\n// specific logic throughout the codebase, consider using `vue-demi` package\n// (https://github.com/vueuse/vue-demi) which abstracts all Vue 2/3 differences.\n// For now, our simple detection is sufficient since Vue 2.7 has Composition API\n// backported and we only need to handle the plugin install signature difference.\n// =============================================================================\n\nimport { ref, readonly } from 'vue'\nimport {\n exchangeOneTimeToken,\n createLogger,\n TokenType,\n TokenParamKey,\n TokenParamLocation,\n type TokenData,\n} from '@capitalos/core'\nimport type { CapitalOsConfig, CapitalOsState } from './types'\n\n/**\n * Injection key for CapitalOS state.\n * Used with Vue's provide/inject system.\n */\nexport const CAPITALOS_INJECTION_KEY = Symbol('capitalos')\n\n// MAINTAINER NOTE: Module-level state storage for Vue 2.7 compatibility fallback.\n//\n// In Vue 2.7, even with the mixin approach, there can be edge cases where\n// `inject()` doesn't find the provided value (e.g., in the root component\n// or components created before the mixin is applied). This global fallback\n// ensures `useCapitalOsAuth()` always has access to the state.\nlet globalState: CapitalOsState | null = null\n\n// @internal - used by useCapitalOsAuth for Vue 2.7 fallback\nexport function getGlobalState(): CapitalOsState | null {\n return globalState\n}\n\nconst TOKEN_EXCHANGE_TIMEOUT_MS = 10_000\n\n// Converts an unknown error to an Error object\nfunction toError(error: unknown): Error {\n if (error instanceof Error) {\n return error\n }\n return new Error(String(error))\n}\n\n// MAINTAINER NOTE: Module-level guard against double installation (singleton pattern).\n//\n// This MUST be module-level (not inside createCapitalOs) because:\n// - The plugin creates reactive state that should only exist once\n// - Multiple installations would create multiple token exchange iframes\n// - Multiple auth states would cause inconsistent behavior\n//\n// If a consumer accidentally calls Vue.use(createCapitalOs(...)) twice,\n// the second call will be silently ignored with a warning.\nlet installed = false\n\n// Type for Vue 2 constructor passed to plugin install.\n// In Vue 2.7, `Vue.use(plugin)` passes the Vue constructor.\ninterface Vue2Constructor {\n mixin: (mixin: Record<string, unknown>) => void\n version: string\n}\n\n// Type for Vue 3 app instance passed to plugin install.\n// In Vue 3, `app.use(plugin)` passes the app instance.\ninterface Vue3App {\n provide: (key: symbol, value: unknown) => void\n version: string\n}\n\n// Detect Vue version by checking for Vue 3's `provide` method.\n// Vue 3 app instances have `app.provide()`, Vue 2 constructors don't.\nfunction isVue3App(appOrVue: Vue2Constructor | Vue3App): appOrVue is Vue3App {\n return typeof (appOrVue as Vue3App).provide === 'function'\n}\n\n/**\n * Creates the CapitalOS Vue plugin.\n *\n * Usage (Vue 3):\n * ```typescript\n * import { createApp } from 'vue'\n * import { createCapitalOs } from '@capitalos/vue'\n *\n * const app = createApp(App)\n * app.use(createCapitalOs({ getToken: async () => { ... } }))\n * app.mount('#app')\n * ```\n *\n * Usage (Vue 2.7):\n * ```typescript\n * import Vue from 'vue'\n * import { createCapitalOs } from '@capitalos/vue'\n *\n * Vue.use(createCapitalOs({ getToken: async () => { ... } }))\n * new Vue({ render: h => h(App) }).$mount('#app')\n * ```\n */\nexport function createCapitalOs(config: CapitalOsConfig) {\n return {\n install(appOrVue: Vue2Constructor | Vue3App) {\n // Prevent double installation\n if (installed) {\n const logger = createLogger(config.enableLogging, config.logger)\n logger.warn('[CapitalOS] Plugin already installed, skipping duplicate installation')\n return\n }\n installed = true\n\n const logger = createLogger(config.enableLogging, config.logger)\n\n // Create reactive state\n const tokenData = ref<TokenData | undefined>(undefined)\n const isLoading = ref(false)\n const error = ref<Error | undefined>(undefined)\n\n // AbortController for cancelling in-flight token exchanges\n let activeAbortController: AbortController | null = null\n\n // Refreshes the token by fetching a new one-time token and exchanging it.\n async function refreshToken(): Promise<void> {\n // Prevent concurrent refresh calls - if already loading, skip\n if (isLoading.value) {\n logger.log('[CapitalOS] Token refresh already in progress, skipping')\n return\n }\n\n logger.log('[CapitalOS] Starting token refresh')\n isLoading.value = true\n error.value = undefined\n\n // Abort any in-flight exchange when a new one begins\n activeAbortController?.abort()\n const abortController = new AbortController()\n activeAbortController = abortController\n\n try {\n const oneTimeToken = await config.getToken()\n logger.log('[CapitalOS] Received one-time token')\n\n // Check if aborted after getToken\n if (abortController.signal.aborted) {\n return\n }\n\n // Exchange for long-lived token using @capitalos/core\n const result = await exchangeOneTimeToken({\n oneTimeToken,\n enableLogging: config.enableLogging,\n logger: config.logger,\n timeoutMs: TOKEN_EXCHANGE_TIMEOUT_MS,\n signal: abortController.signal,\n })\n\n // Check if aborted after exchange\n if (abortController.signal.aborted) {\n return\n }\n\n tokenData.value = {\n token: result.longLivedToken,\n tokenType: TokenType.longLived,\n baseUrl: result.baseUrl,\n paramKey: TokenParamKey.accessToken,\n paramLocation: TokenParamLocation.hash,\n }\n logger.log('[CapitalOS] Token exchange complete')\n } catch (e) {\n // Ignore abort errors\n if (e instanceof Error && e.name === 'AbortError') {\n return\n }\n\n const err = toError(e)\n logger.error(`[CapitalOS] Token refresh failed: ${err.message}`)\n error.value = err\n } finally {\n if (activeAbortController === abortController) {\n activeAbortController = null\n }\n isLoading.value = false\n }\n }\n\n // Invalidates the current token, triggering a refresh.\n // Called when the iframe signals that the token has expired.\n function invalidateToken(): void {\n logger.log('[CapitalOS] Invalidating token')\n tokenData.value = undefined\n error.value = undefined\n refreshToken()\n }\n\n // Create state object to provide\n const state: CapitalOsState = {\n tokenData: readonly(tokenData),\n isLoading: readonly(isLoading),\n error: readonly(error),\n invalidateToken,\n config,\n }\n\n // Store globally as fallback for Vue 2.7 edge cases where inject() fails\n globalState = state\n\n // Provide state using the appropriate API based on Vue version\n if (isVue3App(appOrVue)) {\n // Vue 3: use app.provide() - this makes state available to all components\n // via inject(CAPITALOS_INJECTION_KEY)\n appOrVue.provide(CAPITALOS_INJECTION_KEY, state)\n } else {\n // Vue 2.7: use Vue.mixin() to add a provide function to all components.\n // This is the Vue 2 equivalent of app.provide() - it injects the provide\n // option into every component so inject() can find our state.\n appOrVue.mixin({\n provide() {\n return {\n [CAPITALOS_INJECTION_KEY]: state,\n }\n },\n })\n }\n\n // Start token exchange eagerly (matches Angular behavior)\n refreshToken()\n },\n }\n}\n","import { inject, computed, type ComputedRef } from 'vue'\nimport { CAPITALOS_INJECTION_KEY, getGlobalState } from '../plugin'\nimport type { CapitalOsState, AuthState } from '../types'\n\n/**\n * Return type for useCapitalOsAuth composable\n */\nexport interface UseCapitalOsAuthReturn extends CapitalOsState {\n /**\n * Computed authentication state\n */\n authState: ComputedRef<AuthState>\n}\n\n/**\n * @internal\n * Internal composable for accessing CapitalOS authentication state.\n * Used by SDK components - not intended for direct consumer use.\n */\nexport function useCapitalOsAuth(): UseCapitalOsAuthReturn {\n // MAINTAINER NOTE: Vue 2.7 vs Vue 3 Compatibility\n //\n // Try inject() first - this works in:\n // - Vue 3 (via app.provide)\n // - Vue 2.7 (via mixin provide, in most components)\n //\n // Fall back to getGlobalState() for Vue 2.7 edge cases where inject() fails,\n // such as the root component or components created before the mixin is applied.\n //\n // Do NOT remove the ?? getGlobalState() fallback - it's required for Vue 2.7.\n const state = inject<CapitalOsState>(CAPITALOS_INJECTION_KEY) ?? getGlobalState()\n\n if (!state) {\n throw new Error(\n 'useCapitalOsAuth must be used in a component where createCapitalOs plugin is installed. ' +\n 'Make sure to call app.use(createCapitalOs({ ... })) or Vue.use(createCapitalOs({ ... })) in your main.ts'\n )\n }\n\n const authState = computed<AuthState>(() => {\n if (state.isLoading.value) return 'loading'\n if (state.error.value) return 'error'\n if (state.tokenData.value) return 'authenticated'\n return 'idle'\n })\n\n return {\n ...state,\n authState,\n }\n}\n","{\n \"name\": \"@capitalos/vue\",\n \"version\": \"0.1.0-rc.1\",\n \"description\": \"Vue SDK for CapitalOS\",\n \"main\": \"dist/index.js\",\n \"module\": \"dist/index.mjs\",\n \"types\": \"dist/index.d.ts\",\n \"exports\": {\n \".\": {\n \"import\": {\n \"types\": \"./dist/index.d.mts\",\n \"default\": \"./dist/index.mjs\"\n },\n \"require\": {\n \"types\": \"./dist/index.d.ts\",\n \"default\": \"./dist/index.js\"\n }\n }\n },\n \"files\": [\n \"dist\"\n ],\n \"scripts\": {\n \"build\": \"tsdown\",\n \"dev\": \"tsdown --watch\",\n \"lint\": \"eslint src --ext .ts --fix\",\n \"lint-ci\": \"eslint src --ext .ts\",\n \"start\": \"pnpm --filter vue-test-app dev\",\n \"start:all\": \"concurrently \\\"pnpm --filter sdk-test-server start\\\" \\\"pnpm start\\\"\"\n },\n \"keywords\": [\n \"capitalos\",\n \"vue\",\n \"sdk\",\n \"iframe\"\n ],\n \"author\": \"CapitalOS\",\n \"license\": \"MIT\",\n \"peerDependencies\": {\n \"vue\": \"^2.7.0 || ^3.0.0\"\n },\n \"dependencies\": {\n \"@capitalos/core\": \"workspace:*\"\n },\n \"devDependencies\": {\n \"tsdown\": \"^0.9.2\",\n \"typescript\": \"^5.6.2\",\n \"vue\": \"^3.5.13\",\n \"concurrently\": \"^9.1.2\"\n }\n}\n","// =============================================================================\n// MAINTAINER NOTE: Why defineComponent + render function instead of SFC?\n// =============================================================================\n//\n// This component uses `defineComponent()` with a render function (`h()`) instead\n// of a Single File Component (.vue file) for Vue 2.7/3 compatibility:\n//\n// - SFCs require different compilers for Vue 2 vs Vue 3\n// - Render functions work identically in both versions\n// - Simpler build - no vue-compiler needed, just TypeScript\n//\n// This is a deliberate architectural choice for cross-version compatibility.\n// Do NOT convert this to an SFC without understanding the implications.\n// =============================================================================\n\nimport { defineComponent, h, ref, watch, onUnmounted, type PropType } from 'vue'\nimport {\n IframeManager,\n CapitalOSError,\n type ThemeColorScheme,\n type TokenData,\n type RawErrorDetails,\n} from '@capitalos/core'\nimport { useCapitalOsAuth } from '../composables/use-capitalos-auth'\nimport { version as SDK_VERSION } from '../../package.json'\n\n/**\n * CardsApp component that renders the CapitalOS cards interface in an iframe.\n *\n * The component emits events for loading state - consumers should handle their own loading UI:\n *\n * @example\n * ```vue\n * <template>\n * <div v-if=\"!cardsLoaded\">Loading...</div>\n * <CardsApp\n * @loaded=\"cardsLoaded = true\"\n * @error=\"onCardsError\"\n * />\n * </template>\n *\n * <script setup>\n * import { ref } from 'vue'\n * import { CardsApp } from '@capitalos/vue'\n *\n * const cardsLoaded = ref(false)\n * const onCardsError = (error) => console.error(error)\n * </script>\n * ```\n *\n * Note: CardsApp is not designed to be conditionally mounted/unmounted frequently.\n * For show/hide behavior, consumers should toggle visibility (e.g., v-show) rather\n * than mount/unmount repeatedly.\n */\nexport const CardsApp = defineComponent({\n name: 'CardsApp',\n props: {\n /**\n * Theme color scheme for the cards interface.\n * Overrides the theme set in plugin configuration.\n */\n theme: String as PropType<ThemeColorScheme>,\n /**\n * Offset in pixels to add to the iframe height calculation.\n * Useful for avoiding scrollbars when the content height changes dynamically.\n */\n heightOffsetPx: {\n type: Number,\n default: 12,\n },\n /**\n * Whether to enable logging for debugging purposes.\n * Overrides the enableLogging set in plugin configuration.\n */\n enableLogging: Boolean,\n },\n emits: {\n /**\n * Emitted when the cards interface has finished loading.\n */\n loaded: () => true,\n /**\n * Emitted when an error occurs in the cards interface.\n */\n error: (error: CapitalOSError) => error instanceof CapitalOSError,\n },\n setup(props, { emit }) {\n const containerRef = ref<HTMLElement | null>(null)\n const { tokenData, config, invalidateToken } = useCapitalOsAuth()\n\n let iframeManager: IframeManager | null = null\n\n function initializeIframe(token: TokenData, container: HTMLElement): void {\n // Destroy previous iframe before creating new one (matches Angular/React behavior)\n // This ensures token refresh works correctly - new token = new iframe\n iframeManager?.destroy()\n\n const resolvedEnableLogging = props.enableLogging ?? config.enableLogging ?? false\n const resolvedTheme = props.theme ?? config.theme\n\n iframeManager = new IframeManager({\n container,\n tokenData: token,\n renderingContext: { entryPoint: 'cardsApp' },\n theme: resolvedTheme,\n enableLogging: resolvedEnableLogging,\n logger: config.logger,\n sdkVersion: SDK_VERSION,\n heightOffsetPx: props.heightOffsetPx,\n callbacks: {\n onLoad: () => {\n emit('loaded')\n },\n onError: (rawError: RawErrorDetails) => {\n const capitalOsError = new CapitalOSError(rawError)\n emit('error', capitalOsError)\n },\n onTokenExpired: () => {\n invalidateToken()\n },\n },\n })\n }\n\n // MAINTAINER NOTE: Watch for tokenData and container changes to initialize the iframe.\n //\n // Why watch both tokenData AND containerRef?\n // - tokenData: comes from async token exchange, may not be ready on mount\n // - containerRef: DOM element, available after first render\n //\n // Why { immediate: true }?\n // - If tokenData is already available when component mounts, we want to\n // initialize immediately without waiting for a change\n // - Without this, the iframe wouldn't load until tokenData changes again\n watch(\n [tokenData, containerRef],\n ([newTokenData, container]) => {\n if (newTokenData && container) {\n initializeIframe(newTokenData, container)\n }\n },\n { immediate: true }\n )\n\n onUnmounted(() => {\n iframeManager?.destroy()\n iframeManager = null\n })\n\n // MAINTAINER NOTE: Render function - creates the container div for the iframe.\n //\n // Why use a function ref `ref: (el) => { ... }` instead of `ref: containerRef`?\n //\n // Vue 2.7 and Vue 3 handle template refs differently in render functions:\n // - Vue 3: can use `ref: containerRef` directly\n // - Vue 2.7: requires function ref pattern for reliable element access\n //\n // The function ref pattern works in both versions, so we use it for compatibility.\n // Do NOT change this to `ref: containerRef` - it will break Vue 2.7 support.\n //\n // The `as unknown as string` type assertion is required because:\n // - Vue 3's TypeScript types don't properly express that function refs are valid\n // - The function ref pattern IS valid at runtime in both Vue 2.7 and Vue 3\n // - This is a known limitation of Vue 3's type definitions\n return () =>\n h('div', {\n // Type assertion required: Vue 3's types don't support function refs in h(),\n // but function refs ARE valid at runtime in both Vue 2.7 and Vue 3\n ref: ((el: Element | null) => {\n containerRef.value = el as HTMLElement | null\n }) as unknown as string,\n class: 'capitalos-iframe-container',\n style: { width: '100%' },\n })\n },\n})\n"],"mappings":"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"}
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@capitalos/vue",
3
+ "version": "0.1.0-rc.1",
4
+ "description": "Vue SDK for CapitalOS",
5
+ "main": "dist/index.js",
6
+ "module": "dist/index.mjs",
7
+ "types": "dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "import": {
11
+ "types": "./dist/index.d.mts",
12
+ "default": "./dist/index.mjs"
13
+ },
14
+ "require": {
15
+ "types": "./dist/index.d.ts",
16
+ "default": "./dist/index.js"
17
+ }
18
+ }
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "keywords": [
24
+ "capitalos",
25
+ "vue",
26
+ "sdk",
27
+ "iframe"
28
+ ],
29
+ "author": "CapitalOS",
30
+ "license": "MIT",
31
+ "peerDependencies": {
32
+ "vue": "^2.7.0 || ^3.0.0"
33
+ },
34
+ "dependencies": {
35
+ "@capitalos/core": "0.1.0-rc.1"
36
+ },
37
+ "devDependencies": {
38
+ "tsdown": "^0.9.2",
39
+ "typescript": "^5.6.2",
40
+ "vue": "^3.5.13",
41
+ "concurrently": "^9.1.2"
42
+ },
43
+ "scripts": {
44
+ "build": "tsdown",
45
+ "dev": "tsdown --watch",
46
+ "lint": "eslint src --ext .ts --fix",
47
+ "lint-ci": "eslint src --ext .ts",
48
+ "start": "pnpm --filter vue-test-app dev",
49
+ "start:all": "concurrently \"pnpm --filter sdk-test-server start\" \"pnpm start\""
50
+ }
51
+ }