@capitalos/vue 0.1.0-rc.1 → 0.1.0
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.d.mts +13 -3
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +13 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +214 -56
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +213 -57
- package/dist/index.mjs.map +1 -1
- package/package.json +21 -11
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
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";
|
|
2
|
+
import { Account, AccountStatus, CapitalOSError, ErrorCode, Logger, Logger as Logger$1, RawErrorDetails, ThemeColorScheme, ThemeColorScheme as ThemeColorScheme$1, TokenData, TokenData as TokenData$1 } from "@capitalos/core";
|
|
3
3
|
|
|
4
4
|
//#region src/types.d.ts
|
|
5
5
|
interface CapitalOsConfig {
|
|
@@ -38,8 +38,18 @@ declare function createCapitalOs(config: CapitalOsConfig): {
|
|
|
38
38
|
declare const CardsApp: any;
|
|
39
39
|
|
|
40
40
|
//#endregion
|
|
41
|
-
//#region src/
|
|
41
|
+
//#region src/components/BillPayApp.d.ts
|
|
42
42
|
//# sourceMappingURL=CardsApp.d.ts.map
|
|
43
|
+
declare const BillPayApp: any;
|
|
44
|
+
|
|
45
|
+
//#endregion
|
|
46
|
+
//#region src/components/Onboarding.d.ts
|
|
47
|
+
//# sourceMappingURL=BillPayApp.d.ts.map
|
|
48
|
+
declare const Onboarding: any;
|
|
49
|
+
|
|
50
|
+
//#endregion
|
|
51
|
+
//#region src/composables/use-capitalos-auth.d.ts
|
|
52
|
+
//# sourceMappingURL=Onboarding.d.ts.map
|
|
43
53
|
interface UseCapitalOsAuthReturn extends CapitalOsState {
|
|
44
54
|
authState: ComputedRef<AuthState>;
|
|
45
55
|
}
|
|
@@ -48,5 +58,5 @@ declare function useCapitalOsAuth(): UseCapitalOsAuthReturn;
|
|
|
48
58
|
//#endregion
|
|
49
59
|
//# sourceMappingURL=use-capitalos-auth.d.ts.map
|
|
50
60
|
|
|
51
|
-
export { AuthState, CAPITALOS_INJECTION_KEY, CapitalOSError, CapitalOsConfig, CardsApp, ErrorCode, Logger, RawErrorDetails, ThemeColorScheme, TokenData, UseCapitalOsAuthReturn, createCapitalOs, useCapitalOsAuth };
|
|
61
|
+
export { Account, AccountStatus, AuthState, BillPayApp, CAPITALOS_INJECTION_KEY, CapitalOSError, CapitalOsConfig, CardsApp, ErrorCode, Logger, Onboarding, RawErrorDetails, ThemeColorScheme, TokenData, UseCapitalOsAuthReturn, createCapitalOs, useCapitalOsAuth };
|
|
52
62
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +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;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/plugin.ts","../src/components/CardsApp.ts","../src/components/BillPayApp.ts","../src/components/Onboarding.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;;;;cEL1B;;;;;cCAA;;;;;cCOA;;;;;UC5BI,sBAAA,SAA+B;aAInC,YAAY;ALLzB;AAAgC,iBKahB,gBAAA,CAAA,CLbgB,EKaI,sBLbJ"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
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";
|
|
2
|
+
import { Account, AccountStatus, CapitalOSError, ErrorCode, Logger, Logger as Logger$1, RawErrorDetails, ThemeColorScheme, ThemeColorScheme as ThemeColorScheme$1, TokenData, TokenData as TokenData$1 } from "@capitalos/core";
|
|
3
3
|
|
|
4
4
|
//#region src/types.d.ts
|
|
5
5
|
interface CapitalOsConfig {
|
|
@@ -38,8 +38,18 @@ declare function createCapitalOs(config: CapitalOsConfig): {
|
|
|
38
38
|
declare const CardsApp: any;
|
|
39
39
|
|
|
40
40
|
//#endregion
|
|
41
|
-
//#region src/
|
|
41
|
+
//#region src/components/BillPayApp.d.ts
|
|
42
42
|
//# sourceMappingURL=CardsApp.d.ts.map
|
|
43
|
+
declare const BillPayApp: any;
|
|
44
|
+
|
|
45
|
+
//#endregion
|
|
46
|
+
//#region src/components/Onboarding.d.ts
|
|
47
|
+
//# sourceMappingURL=BillPayApp.d.ts.map
|
|
48
|
+
declare const Onboarding: any;
|
|
49
|
+
|
|
50
|
+
//#endregion
|
|
51
|
+
//#region src/composables/use-capitalos-auth.d.ts
|
|
52
|
+
//# sourceMappingURL=Onboarding.d.ts.map
|
|
43
53
|
interface UseCapitalOsAuthReturn extends CapitalOsState {
|
|
44
54
|
authState: ComputedRef<AuthState>;
|
|
45
55
|
}
|
|
@@ -48,5 +58,5 @@ declare function useCapitalOsAuth(): UseCapitalOsAuthReturn;
|
|
|
48
58
|
//#endregion
|
|
49
59
|
//# sourceMappingURL=use-capitalos-auth.d.ts.map
|
|
50
60
|
|
|
51
|
-
export { AuthState, CAPITALOS_INJECTION_KEY, CapitalOSError, CapitalOsConfig, CardsApp, ErrorCode, Logger, RawErrorDetails, ThemeColorScheme, TokenData, UseCapitalOsAuthReturn, createCapitalOs, useCapitalOsAuth };
|
|
61
|
+
export { Account, AccountStatus, AuthState, BillPayApp, CAPITALOS_INJECTION_KEY, CapitalOSError, CapitalOsConfig, CardsApp, ErrorCode, Logger, Onboarding, RawErrorDetails, ThemeColorScheme, TokenData, UseCapitalOsAuthReturn, createCapitalOs, useCapitalOsAuth };
|
|
52
62
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +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;;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/plugin.ts","../src/components/CardsApp.ts","../src/components/BillPayApp.ts","../src/components/Onboarding.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;;;;cEL1B;;;;;cCAA;;;;;cCOA;;;;;UC5BI,sBAAA,SAA+B;aAInC,YAAY;ALLzB;AAAgC,iBKahB,gBAAA,CAAA,CLbgB,EKaI,sBLbJ"}
|
package/dist/index.js
CHANGED
|
@@ -142,6 +142,27 @@ function createCapitalOs(config) {
|
|
|
142
142
|
} };
|
|
143
143
|
}
|
|
144
144
|
|
|
145
|
+
//#endregion
|
|
146
|
+
//#region src/components/common-props.ts
|
|
147
|
+
/**
|
|
148
|
+
* Common props shared by all CapitalOS Vue components.
|
|
149
|
+
*/
|
|
150
|
+
const commonProps = {
|
|
151
|
+
theme: String,
|
|
152
|
+
heightOffsetPx: {
|
|
153
|
+
type: Number,
|
|
154
|
+
default: 12
|
|
155
|
+
},
|
|
156
|
+
enableLogging: Boolean
|
|
157
|
+
};
|
|
158
|
+
/**
|
|
159
|
+
* Common emits shared by all CapitalOS Vue components.
|
|
160
|
+
*/
|
|
161
|
+
const commonEmits = {
|
|
162
|
+
loaded: () => true,
|
|
163
|
+
error: (error) => error instanceof __capitalos_core.CapitalOSError
|
|
164
|
+
};
|
|
165
|
+
|
|
145
166
|
//#endregion
|
|
146
167
|
//#region src/composables/use-capitalos-auth.ts
|
|
147
168
|
/**
|
|
@@ -166,7 +187,89 @@ function useCapitalOsAuth() {
|
|
|
166
187
|
|
|
167
188
|
//#endregion
|
|
168
189
|
//#region package.json
|
|
169
|
-
var version = "0.1.0
|
|
190
|
+
var version = "0.1.0";
|
|
191
|
+
|
|
192
|
+
//#endregion
|
|
193
|
+
//#region src/composables/use-iframe-component.ts
|
|
194
|
+
/**
|
|
195
|
+
* Composable that handles common iframe component setup.
|
|
196
|
+
*
|
|
197
|
+
* Manages:
|
|
198
|
+
* - Container ref and iframe manager lifecycle
|
|
199
|
+
* - Token data subscription and iframe initialization
|
|
200
|
+
* - Common callbacks (onLoad, onError, onTokenExpired)
|
|
201
|
+
* - Cleanup on unmount
|
|
202
|
+
* - Render function for the container div
|
|
203
|
+
*
|
|
204
|
+
* Note: Rendering context props (e.g., entryPoint, exitPoint for Onboarding) are read once
|
|
205
|
+
* at iframe initialization. Changes to these props after the iframe loads will NOT trigger
|
|
206
|
+
* re-initialization. This matches the behavior of the React SDK.
|
|
207
|
+
*
|
|
208
|
+
* @example
|
|
209
|
+
* ```ts
|
|
210
|
+
* setup(props, { emit }) {
|
|
211
|
+
* const { render } = useIframeComponent({
|
|
212
|
+
* props,
|
|
213
|
+
* emit,
|
|
214
|
+
* getRenderingContext: () => ({ entryPoint: 'cardsApp' }),
|
|
215
|
+
* })
|
|
216
|
+
* return render
|
|
217
|
+
* }
|
|
218
|
+
* ```
|
|
219
|
+
*/
|
|
220
|
+
function useIframeComponent(options) {
|
|
221
|
+
const { props, emit, getRenderingContext, componentCallbacks } = options;
|
|
222
|
+
const containerRef = (0, vue.ref)(null);
|
|
223
|
+
const { tokenData, config, invalidateToken } = useCapitalOsAuth();
|
|
224
|
+
let iframeManager = null;
|
|
225
|
+
function initializeIframe(token, container) {
|
|
226
|
+
iframeManager?.destroy();
|
|
227
|
+
const resolvedEnableLogging = props.enableLogging ?? config.enableLogging ?? false;
|
|
228
|
+
const resolvedTheme = props.theme ?? config.theme;
|
|
229
|
+
iframeManager = new __capitalos_core.IframeManager({
|
|
230
|
+
container,
|
|
231
|
+
tokenData: token,
|
|
232
|
+
renderingContext: getRenderingContext(),
|
|
233
|
+
theme: resolvedTheme,
|
|
234
|
+
enableLogging: resolvedEnableLogging,
|
|
235
|
+
logger: config.logger,
|
|
236
|
+
sdkVersion: version,
|
|
237
|
+
heightOffsetPx: props.heightOffsetPx,
|
|
238
|
+
callbacks: {
|
|
239
|
+
onLoad: () => {
|
|
240
|
+
emit("loaded");
|
|
241
|
+
},
|
|
242
|
+
onError: (rawError) => {
|
|
243
|
+
emit("error", new __capitalos_core.CapitalOSError(rawError));
|
|
244
|
+
},
|
|
245
|
+
onTokenExpired: () => {
|
|
246
|
+
invalidateToken();
|
|
247
|
+
},
|
|
248
|
+
...componentCallbacks
|
|
249
|
+
}
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
(0, vue.watch)([tokenData, containerRef], ([newTokenData, container]) => {
|
|
253
|
+
if (newTokenData && container) initializeIframe(newTokenData, container);
|
|
254
|
+
}, { immediate: true });
|
|
255
|
+
(0, vue.onUnmounted)(() => {
|
|
256
|
+
iframeManager?.destroy();
|
|
257
|
+
iframeManager = null;
|
|
258
|
+
});
|
|
259
|
+
function render() {
|
|
260
|
+
return (0, vue.h)("div", {
|
|
261
|
+
ref: (el) => {
|
|
262
|
+
containerRef.value = el;
|
|
263
|
+
},
|
|
264
|
+
class: "capitalos-iframe-container",
|
|
265
|
+
style: { width: "100%" }
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
containerRef,
|
|
270
|
+
render
|
|
271
|
+
};
|
|
272
|
+
}
|
|
170
273
|
|
|
171
274
|
//#endregion
|
|
172
275
|
//#region src/components/CardsApp.ts
|
|
@@ -193,74 +296,128 @@ var version = "0.1.0-rc.1";
|
|
|
193
296
|
* const onCardsError = (error) => console.error(error)
|
|
194
297
|
* </script>
|
|
195
298
|
* ```
|
|
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
299
|
*/
|
|
201
300
|
const CardsApp = (0, vue.defineComponent)({
|
|
202
301
|
name: "CardsApp",
|
|
302
|
+
props: commonProps,
|
|
303
|
+
emits: commonEmits,
|
|
304
|
+
setup(props, { emit }) {
|
|
305
|
+
const { render } = useIframeComponent({
|
|
306
|
+
props,
|
|
307
|
+
emit,
|
|
308
|
+
getRenderingContext: () => ({ entryPoint: "cardsApp" })
|
|
309
|
+
});
|
|
310
|
+
return render;
|
|
311
|
+
}
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
//#endregion
|
|
315
|
+
//#region src/components/BillPayApp.ts
|
|
316
|
+
/**
|
|
317
|
+
* BillPayApp component that renders the CapitalOS bill payment interface in an iframe.
|
|
318
|
+
*
|
|
319
|
+
* The component emits events for loading state - consumers should handle their own loading UI:
|
|
320
|
+
*
|
|
321
|
+
* @example
|
|
322
|
+
* ```vue
|
|
323
|
+
* <template>
|
|
324
|
+
* <div v-if="!billPayLoaded">Loading...</div>
|
|
325
|
+
* <BillPayApp
|
|
326
|
+
* @loaded="billPayLoaded = true"
|
|
327
|
+
* @error="onBillPayError"
|
|
328
|
+
* />
|
|
329
|
+
* </template>
|
|
330
|
+
*
|
|
331
|
+
* <script setup>
|
|
332
|
+
* import { ref } from 'vue'
|
|
333
|
+
* import { BillPayApp } from '@capitalos/vue'
|
|
334
|
+
*
|
|
335
|
+
* const billPayLoaded = ref(false)
|
|
336
|
+
* const onBillPayError = (error) => console.error(error)
|
|
337
|
+
* </script>
|
|
338
|
+
* ```
|
|
339
|
+
*/
|
|
340
|
+
const BillPayApp = (0, vue.defineComponent)({
|
|
341
|
+
name: "BillPayApp",
|
|
342
|
+
props: commonProps,
|
|
343
|
+
emits: commonEmits,
|
|
344
|
+
setup(props, { emit }) {
|
|
345
|
+
const { render } = useIframeComponent({
|
|
346
|
+
props,
|
|
347
|
+
emit,
|
|
348
|
+
getRenderingContext: () => ({ entryPoint: "billPayApp" })
|
|
349
|
+
});
|
|
350
|
+
return render;
|
|
351
|
+
}
|
|
352
|
+
});
|
|
353
|
+
|
|
354
|
+
//#endregion
|
|
355
|
+
//#region src/components/Onboarding.ts
|
|
356
|
+
/**
|
|
357
|
+
* Onboarding component that renders the CapitalOS onboarding flow in an iframe.
|
|
358
|
+
*
|
|
359
|
+
* The component emits events for loading state and completion - consumers should handle their own loading UI:
|
|
360
|
+
*
|
|
361
|
+
* @example
|
|
362
|
+
* ```vue
|
|
363
|
+
* <template>
|
|
364
|
+
* <div v-if="!onboardingLoaded">Loading...</div>
|
|
365
|
+
* <Onboarding
|
|
366
|
+
* entry-point="welcome"
|
|
367
|
+
* exit-point="activation"
|
|
368
|
+
* @loaded="onboardingLoaded = true"
|
|
369
|
+
* @error="onOnboardingError"
|
|
370
|
+
* @done="onOnboardingComplete"
|
|
371
|
+
* />
|
|
372
|
+
* </template>
|
|
373
|
+
*
|
|
374
|
+
* <script setup>
|
|
375
|
+
* import { ref } from 'vue'
|
|
376
|
+
* import { Onboarding } from '@capitalos/vue'
|
|
377
|
+
*
|
|
378
|
+
* const onboardingLoaded = ref(false)
|
|
379
|
+
* const onOnboardingError = (error) => console.error(error)
|
|
380
|
+
* const onOnboardingComplete = (account) => {
|
|
381
|
+
* console.log('Onboarding complete:', account.status)
|
|
382
|
+
* }
|
|
383
|
+
* </script>
|
|
384
|
+
* ```
|
|
385
|
+
*/
|
|
386
|
+
const Onboarding = (0, vue.defineComponent)({
|
|
387
|
+
name: "Onboarding",
|
|
203
388
|
props: {
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
389
|
+
...commonProps,
|
|
390
|
+
entryPoint: String,
|
|
391
|
+
exitPoint: String,
|
|
392
|
+
allowExitOnNonApproved: {
|
|
393
|
+
type: Boolean,
|
|
394
|
+
default: void 0
|
|
395
|
+
}
|
|
210
396
|
},
|
|
211
397
|
emits: {
|
|
212
|
-
|
|
213
|
-
|
|
398
|
+
...commonEmits,
|
|
399
|
+
done: (_account) => true
|
|
214
400
|
},
|
|
215
401
|
setup(props, { emit }) {
|
|
216
|
-
const
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
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%" }
|
|
402
|
+
const { render } = useIframeComponent({
|
|
403
|
+
props,
|
|
404
|
+
emit,
|
|
405
|
+
getRenderingContext: () => ({
|
|
406
|
+
entryPoint: "onboarding",
|
|
407
|
+
onboardingEntryPoint: props.entryPoint,
|
|
408
|
+
onboardingExitPoint: props.exitPoint,
|
|
409
|
+
allowExitOnNonApproved: props.allowExitOnNonApproved
|
|
410
|
+
}),
|
|
411
|
+
componentCallbacks: { onboarding: { onDone: (account) => {
|
|
412
|
+
emit("done", account);
|
|
413
|
+
} } }
|
|
259
414
|
});
|
|
415
|
+
return render;
|
|
260
416
|
}
|
|
261
417
|
});
|
|
262
418
|
|
|
263
419
|
//#endregion
|
|
420
|
+
exports.BillPayApp = BillPayApp
|
|
264
421
|
exports.CAPITALOS_INJECTION_KEY = CAPITALOS_INJECTION_KEY
|
|
265
422
|
Object.defineProperty(exports, 'CapitalOSError', {
|
|
266
423
|
enumerable: true,
|
|
@@ -275,6 +432,7 @@ Object.defineProperty(exports, 'ErrorCode', {
|
|
|
275
432
|
return __capitalos_core.ErrorCode;
|
|
276
433
|
}
|
|
277
434
|
});
|
|
435
|
+
exports.Onboarding = Onboarding
|
|
278
436
|
exports.createCapitalOs = createCapitalOs
|
|
279
437
|
exports.useCapitalOsAuth = useCapitalOsAuth
|
|
280
438
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +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":";;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AA2CA,MAAa,0BAA0B,OAAO,YAAY;AAQ1D,IAAIA,cAAqC;AAGzC,SAAgB,iBAAwC;AACtD,QAAO;AACR;AAED,MAAM,4BAA4B;AAGlC,SAAS,QAAQC,OAAuB;AACtC,KAAI,iBAAiB,MACnB,QAAO;AAET,QAAO,IAAI,MAAM,OAAO,MAAM;AAC/B;AAWD,IAAI,YAAY;AAkBhB,SAAS,UAAUC,UAA0D;AAC3E,eAAe,SAAqB,YAAY;AACjD;;;;;;;;;;;;;;;;;;;;;;;AAwBD,SAAgB,gBAAgBC,QAAyB;AACvD,QAAO,EACL,QAAQD,UAAqC;AAE3C,MAAI,WAAW;GACb,MAAME,WAAS,mCAAa,OAAO,eAAe,OAAO,OAAO;AAChE,YAAO,KAAK,wEAAwE;AACpF;EACD;AACD,cAAY;EAEZ,MAAM,SAAS,mCAAa,OAAO,eAAe,OAAO,OAAO;EAGhE,MAAM,YAAY,oBAAqC;EACvD,MAAM,YAAY,aAAI,MAAM;EAC5B,MAAM,QAAQ,oBAAiC;EAG/C,IAAIC,wBAAgD;EAGpD,eAAe,eAA8B;AAE3C,OAAI,UAAU,OAAO;AACnB,WAAO,IAAI,0DAA0D;AACrE;GACD;AAED,UAAO,IAAI,qCAAqC;AAChD,aAAU,QAAQ;AAClB,SAAM;AAGN,0BAAuB,OAAO;GAC9B,MAAM,kBAAkB,IAAI;AAC5B,2BAAwB;AAExB,OAAI;IACF,MAAM,eAAe,MAAM,OAAO,UAAU;AAC5C,WAAO,IAAI,sCAAsC;AAGjD,QAAI,gBAAgB,OAAO,QACzB;IAIF,MAAM,SAAS,MAAM,2CAAqB;KACxC;KACA,eAAe,OAAO;KACtB,QAAQ,OAAO;KACf,WAAW;KACX,QAAQ,gBAAgB;IACzB,EAAC;AAGF,QAAI,gBAAgB,OAAO,QACzB;AAGF,cAAU,QAAQ;KAChB,OAAO,OAAO;KACd,WAAWC,2BAAU;KACrB,SAAS,OAAO;KAChB,UAAUC,+BAAc;KACxB,eAAeC,oCAAmB;IACnC;AACD,WAAO,IAAI,sCAAsC;GAClD,SAAQ,GAAG;AAEV,QAAI,aAAa,SAAS,EAAE,SAAS,aACnC;IAGF,MAAM,MAAM,QAAQ,EAAE;AACtB,WAAO,OAAO,oCAAoC,IAAI,QAAQ,EAAE;AAChE,UAAM,QAAQ;GACf,UAAS;AACR,QAAI,0BAA0B,gBAC5B,yBAAwB;AAE1B,cAAU,QAAQ;GACnB;EACF;EAID,SAAS,kBAAwB;AAC/B,UAAO,IAAI,iCAAiC;AAC5C,aAAU;AACV,SAAM;AACN,iBAAc;EACf;EAGD,MAAMC,QAAwB;GAC5B,WAAW,kBAAS,UAAU;GAC9B,WAAW,kBAAS,UAAU;GAC9B,OAAO,kBAAS,MAAM;GACtB;GACA;EACD;AAGD,gBAAc;AAGd,MAAI,UAAU,SAAS,CAGrB,UAAS,QAAQ,yBAAyB,MAAM;MAKhD,UAAS,MAAM,EACb,UAAU;AACR,UAAO,GACJ,0BAA0B,MAC5B;EACF,EACF,EAAC;AAIJ,gBAAc;CACf,EACF;AACF;;;;;;;;;ACvOD,SAAgB,mBAA2C;CAWzD,MAAM,QAAQ,gBAAuB,wBAAwB,IAAI,gBAAgB;AAEjF,MAAK,MACH,OAAM,IAAI,MACR;CAKJ,MAAM,YAAY,kBAAoB,MAAM;AAC1C,MAAI,MAAM,UAAU,MAAO,QAAO;AAClC,MAAI,MAAM,MAAM,MAAO,QAAO;AAC9B,MAAI,MAAM,UAAU,MAAO,QAAO;AAClC,SAAO;CACR,EAAC;AAEF,QAAO;EACL,GAAG;EACH;CACD;AACF;;;;cChDY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;ACoDb,MAAa,WAAW,yBAAgB;CACtC,MAAM;CACN,OAAO;EAKL,OAAO;EAKP,gBAAgB;GACd,MAAM;GACN,SAAS;EACV;EAKD,eAAe;CAChB;CACD,OAAO;EAIL,QAAQ,MAAM;EAId,OAAO,CAACC,UAA0B,iBAAiBC;CACpD;CACD,MAAM,OAAO,EAAE,MAAM,EAAE;EACrB,MAAM,eAAe,aAAwB,KAAK;EAClD,MAAM,EAAE,WAAW,QAAQ,iBAAiB,GAAG,kBAAkB;EAEjE,IAAIC,gBAAsC;EAE1C,SAAS,iBAAiBC,OAAkBC,WAA8B;AAGxE,kBAAe,SAAS;GAExB,MAAM,wBAAwB,MAAM,iBAAiB,OAAO,iBAAiB;GAC7E,MAAM,gBAAgB,MAAM,SAAS,OAAO;AAE5C,mBAAgB,IAAIC,+BAAc;IAChC;IACA,WAAW;IACX,kBAAkB,EAAE,YAAY,WAAY;IAC5C,OAAO;IACP,eAAe;IACf,QAAQ,OAAO;IACf,YAAYC;IACZ,gBAAgB,MAAM;IACtB,WAAW;KACT,QAAQ,MAAM;AACZ,WAAK,SAAS;KACf;KACD,SAAS,CAACC,aAA8B;MACtC,MAAM,iBAAiB,IAAIN,gCAAe;AAC1C,WAAK,SAAS,eAAe;KAC9B;KACD,gBAAgB,MAAM;AACpB,uBAAiB;KAClB;IACF;GACF;EACF;AAYD,iBACE,CAAC,WAAW,YAAa,GACzB,CAAC,CAAC,cAAc,UAAU,KAAK;AAC7B,OAAI,gBAAgB,UAClB,kBAAiB,cAAc,UAAU;EAE5C,GACD,EAAE,WAAW,KAAM,EACpB;AAED,uBAAY,MAAM;AAChB,kBAAe,SAAS;AACxB,mBAAgB;EACjB,EAAC;AAiBF,SAAO,MACL,WAAE,OAAO;GAGP,KAAM,CAACO,OAAuB;AAC5B,iBAAa,QAAQ;GACtB;GACD,OAAO;GACP,OAAO,EAAE,OAAO,OAAQ;EACzB,EAAC;CACL;AACF,EAAC"}
|
|
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","options: UseIframeComponentOptions","iframeManager: IframeManager | null","token: TokenData","container: HTMLElement","IframeManager","SDK_VERSION","rawError: RawErrorDetails","CapitalOSError","el: Element | null","_account: Account","account: Account"],"sources":["../src/plugin.ts","../src/components/common-props.ts","../src/composables/use-capitalos-auth.ts","../package.json","../src/composables/use-iframe-component.ts","../src/components/CardsApp.ts","../src/components/BillPayApp.ts","../src/components/Onboarding.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 type { PropType } from 'vue'\nimport type { ThemeColorScheme } from '@capitalos/core'\nimport { CapitalOSError } from '@capitalos/core'\n\n/**\n * Common props shared by all CapitalOS Vue components.\n */\nexport const commonProps = {\n /**\n * Theme color scheme for the 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\n/**\n * Common emits shared by all CapitalOS Vue components.\n */\nexport const commonEmits = {\n /**\n * Emitted when the interface has finished loading.\n */\n loaded: () => true,\n /**\n * Emitted when an error occurs in the interface.\n */\n error: (error: CapitalOSError) => error instanceof CapitalOSError,\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\",\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 \"test:pack\": \"pnpm build && pnpm pack\",\n \"prepublishOnly\": \"pnpm build\"\n },\n \"keywords\": [\n \"capitalos\",\n \"vue\",\n \"sdk\",\n \"iframe\"\n ],\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/CapitalOS/theboss\",\n \"directory\": \"sdk/vue\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/CapitalOS/theboss/issues\"\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","import { ref, watch, onUnmounted, h, type VNode, type Ref } from 'vue'\nimport {\n IframeManager,\n CapitalOSError,\n type TokenData,\n type RawErrorDetails,\n type RenderingContext,\n type IframeManagerConfig,\n} from '@capitalos/core'\nimport { useCapitalOsAuth } from './use-capitalos-auth'\nimport { version as SDK_VERSION } from '../../package.json'\n\n/**\n * Props expected by useIframeComponent - matches commonProps shape\n */\nexport interface IframeComponentProps {\n theme?: 'light' | 'dark' | 'system'\n heightOffsetPx: number\n enableLogging?: boolean\n}\n\n/**\n * Emit function type for common events\n */\nexport interface CommonEmitFn {\n (event: 'loaded'): void\n (event: 'error', error: CapitalOSError): void\n}\n\n/**\n * Component-specific callbacks to pass to IframeManager.\n * Excludes common callbacks (onLoad, onError, onTokenExpired) which the composable handles.\n */\nexport type ComponentCallbacks = Omit<IframeManagerConfig['callbacks'], 'onLoad' | 'onError' | 'onTokenExpired'>\n\n/**\n * Options for configuring the iframe component\n */\nexport interface UseIframeComponentOptions {\n /** Props from the component */\n props: IframeComponentProps\n /** Emit function from the component */\n emit: CommonEmitFn\n /** Function that returns the rendering context for this component */\n getRenderingContext: () => RenderingContext\n /** Component-specific callbacks (onboarding, tokenExchange, etc.) */\n componentCallbacks?: ComponentCallbacks\n}\n\n/**\n * Return type for useIframeComponent\n */\nexport interface UseIframeComponentReturn {\n /** Ref to the container element */\n containerRef: Ref<HTMLElement | null>\n /** Render function that returns the container div */\n render: () => VNode\n}\n\n/**\n * Composable that handles common iframe component setup.\n *\n * Manages:\n * - Container ref and iframe manager lifecycle\n * - Token data subscription and iframe initialization\n * - Common callbacks (onLoad, onError, onTokenExpired)\n * - Cleanup on unmount\n * - Render function for the container div\n *\n * Note: Rendering context props (e.g., entryPoint, exitPoint for Onboarding) are read once\n * at iframe initialization. Changes to these props after the iframe loads will NOT trigger\n * re-initialization. This matches the behavior of the React SDK.\n *\n * @example\n * ```ts\n * setup(props, { emit }) {\n * const { render } = useIframeComponent({\n * props,\n * emit,\n * getRenderingContext: () => ({ entryPoint: 'cardsApp' }),\n * })\n * return render\n * }\n * ```\n */\nexport function useIframeComponent(options: UseIframeComponentOptions): UseIframeComponentReturn {\n const { props, emit, getRenderingContext, componentCallbacks } = options\n\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: getRenderingContext(),\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 emit('error', new CapitalOSError(rawError))\n },\n onTokenExpired: () => {\n invalidateToken()\n },\n ...componentCallbacks,\n },\n })\n }\n\n // 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 // 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 function render(): VNode {\n return 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 return {\n containerRef,\n render,\n }\n}\n","import { defineComponent } from 'vue'\nimport { commonProps, commonEmits } from './common-props'\nimport { useIframeComponent } from '../composables/use-iframe-component'\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 */\nexport const CardsApp = defineComponent({\n name: 'CardsApp',\n props: commonProps,\n emits: commonEmits,\n setup(props, { emit }) {\n const { render } = useIframeComponent({\n props,\n emit,\n getRenderingContext: () => ({ entryPoint: 'cardsApp' }),\n })\n\n return render\n },\n})\n","import { defineComponent } from 'vue'\nimport { commonProps, commonEmits } from './common-props'\nimport { useIframeComponent } from '../composables/use-iframe-component'\n\n/**\n * BillPayApp component that renders the CapitalOS bill payment 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=\"!billPayLoaded\">Loading...</div>\n * <BillPayApp\n * @loaded=\"billPayLoaded = true\"\n * @error=\"onBillPayError\"\n * />\n * </template>\n *\n * <script setup>\n * import { ref } from 'vue'\n * import { BillPayApp } from '@capitalos/vue'\n *\n * const billPayLoaded = ref(false)\n * const onBillPayError = (error) => console.error(error)\n * </script>\n * ```\n */\nexport const BillPayApp = defineComponent({\n name: 'BillPayApp',\n props: commonProps,\n emits: commonEmits,\n setup(props, { emit }) {\n const { render } = useIframeComponent({\n props,\n emit,\n getRenderingContext: () => ({ entryPoint: 'billPayApp' }),\n })\n\n return render\n },\n})\n","import { defineComponent, type PropType } from 'vue'\nimport type { Account, OnboardingEntryPoint, OnboardingExitPoint } from '@capitalos/core'\nimport { commonProps, commonEmits } from './common-props'\nimport { useIframeComponent } from '../composables/use-iframe-component'\n\n/**\n * Onboarding component that renders the CapitalOS onboarding flow in an iframe.\n *\n * The component emits events for loading state and completion - consumers should handle their own loading UI:\n *\n * @example\n * ```vue\n * <template>\n * <div v-if=\"!onboardingLoaded\">Loading...</div>\n * <Onboarding\n * entry-point=\"welcome\"\n * exit-point=\"activation\"\n * @loaded=\"onboardingLoaded = true\"\n * @error=\"onOnboardingError\"\n * @done=\"onOnboardingComplete\"\n * />\n * </template>\n *\n * <script setup>\n * import { ref } from 'vue'\n * import { Onboarding } from '@capitalos/vue'\n *\n * const onboardingLoaded = ref(false)\n * const onOnboardingError = (error) => console.error(error)\n * const onOnboardingComplete = (account) => {\n * console.log('Onboarding complete:', account.status)\n * }\n * </script>\n * ```\n */\nexport const Onboarding = defineComponent({\n name: 'Onboarding',\n props: {\n ...commonProps,\n /**\n * The starting point of the onboarding flow.\n * @default 'welcome'\n */\n entryPoint: String as PropType<OnboardingEntryPoint>,\n /**\n * The ending point of the onboarding flow.\n * @default 'activation'\n */\n exitPoint: String as PropType<OnboardingExitPoint>,\n /**\n * Whether to allow the user to exit the onboarding flow by clicking the done button\n * when they finished the onboarding form but are still not approved (e.g. waiting or pending).\n * This should only be false if you want the user to stay on the onboarding flow\n * (e.g. to add bank account) and not exit the flow.\n * @default true\n */\n allowExitOnNonApproved: {\n type: Boolean,\n default: undefined,\n },\n },\n emits: {\n ...commonEmits,\n /**\n * Emitted when the onboarding flow completes successfully.\n */\n done: (_account: Account) => true,\n },\n setup(props, { emit }) {\n const { render } = useIframeComponent({\n props,\n emit,\n getRenderingContext: () => ({\n entryPoint: 'onboarding',\n onboardingEntryPoint: props.entryPoint,\n onboardingExitPoint: props.exitPoint,\n allowExitOnNonApproved: props.allowExitOnNonApproved,\n }),\n componentCallbacks: {\n onboarding: {\n onDone: (account: Account) => {\n emit('done', account)\n },\n },\n },\n })\n\n return render\n },\n})\n"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -118,6 +118,27 @@ function createCapitalOs(config) {
|
|
|
118
118
|
} };
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
+
//#endregion
|
|
122
|
+
//#region src/components/common-props.ts
|
|
123
|
+
/**
|
|
124
|
+
* Common props shared by all CapitalOS Vue components.
|
|
125
|
+
*/
|
|
126
|
+
const commonProps = {
|
|
127
|
+
theme: String,
|
|
128
|
+
heightOffsetPx: {
|
|
129
|
+
type: Number,
|
|
130
|
+
default: 12
|
|
131
|
+
},
|
|
132
|
+
enableLogging: Boolean
|
|
133
|
+
};
|
|
134
|
+
/**
|
|
135
|
+
* Common emits shared by all CapitalOS Vue components.
|
|
136
|
+
*/
|
|
137
|
+
const commonEmits = {
|
|
138
|
+
loaded: () => true,
|
|
139
|
+
error: (error) => error instanceof CapitalOSError$1
|
|
140
|
+
};
|
|
141
|
+
|
|
121
142
|
//#endregion
|
|
122
143
|
//#region src/composables/use-capitalos-auth.ts
|
|
123
144
|
/**
|
|
@@ -142,7 +163,89 @@ function useCapitalOsAuth() {
|
|
|
142
163
|
|
|
143
164
|
//#endregion
|
|
144
165
|
//#region package.json
|
|
145
|
-
var version = "0.1.0
|
|
166
|
+
var version = "0.1.0";
|
|
167
|
+
|
|
168
|
+
//#endregion
|
|
169
|
+
//#region src/composables/use-iframe-component.ts
|
|
170
|
+
/**
|
|
171
|
+
* Composable that handles common iframe component setup.
|
|
172
|
+
*
|
|
173
|
+
* Manages:
|
|
174
|
+
* - Container ref and iframe manager lifecycle
|
|
175
|
+
* - Token data subscription and iframe initialization
|
|
176
|
+
* - Common callbacks (onLoad, onError, onTokenExpired)
|
|
177
|
+
* - Cleanup on unmount
|
|
178
|
+
* - Render function for the container div
|
|
179
|
+
*
|
|
180
|
+
* Note: Rendering context props (e.g., entryPoint, exitPoint for Onboarding) are read once
|
|
181
|
+
* at iframe initialization. Changes to these props after the iframe loads will NOT trigger
|
|
182
|
+
* re-initialization. This matches the behavior of the React SDK.
|
|
183
|
+
*
|
|
184
|
+
* @example
|
|
185
|
+
* ```ts
|
|
186
|
+
* setup(props, { emit }) {
|
|
187
|
+
* const { render } = useIframeComponent({
|
|
188
|
+
* props,
|
|
189
|
+
* emit,
|
|
190
|
+
* getRenderingContext: () => ({ entryPoint: 'cardsApp' }),
|
|
191
|
+
* })
|
|
192
|
+
* return render
|
|
193
|
+
* }
|
|
194
|
+
* ```
|
|
195
|
+
*/
|
|
196
|
+
function useIframeComponent(options) {
|
|
197
|
+
const { props, emit, getRenderingContext, componentCallbacks } = options;
|
|
198
|
+
const containerRef = ref(null);
|
|
199
|
+
const { tokenData, config, invalidateToken } = useCapitalOsAuth();
|
|
200
|
+
let iframeManager = null;
|
|
201
|
+
function initializeIframe(token, container) {
|
|
202
|
+
iframeManager?.destroy();
|
|
203
|
+
const resolvedEnableLogging = props.enableLogging ?? config.enableLogging ?? false;
|
|
204
|
+
const resolvedTheme = props.theme ?? config.theme;
|
|
205
|
+
iframeManager = new IframeManager({
|
|
206
|
+
container,
|
|
207
|
+
tokenData: token,
|
|
208
|
+
renderingContext: getRenderingContext(),
|
|
209
|
+
theme: resolvedTheme,
|
|
210
|
+
enableLogging: resolvedEnableLogging,
|
|
211
|
+
logger: config.logger,
|
|
212
|
+
sdkVersion: version,
|
|
213
|
+
heightOffsetPx: props.heightOffsetPx,
|
|
214
|
+
callbacks: {
|
|
215
|
+
onLoad: () => {
|
|
216
|
+
emit("loaded");
|
|
217
|
+
},
|
|
218
|
+
onError: (rawError) => {
|
|
219
|
+
emit("error", new CapitalOSError$1(rawError));
|
|
220
|
+
},
|
|
221
|
+
onTokenExpired: () => {
|
|
222
|
+
invalidateToken();
|
|
223
|
+
},
|
|
224
|
+
...componentCallbacks
|
|
225
|
+
}
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
watch([tokenData, containerRef], ([newTokenData, container]) => {
|
|
229
|
+
if (newTokenData && container) initializeIframe(newTokenData, container);
|
|
230
|
+
}, { immediate: true });
|
|
231
|
+
onUnmounted(() => {
|
|
232
|
+
iframeManager?.destroy();
|
|
233
|
+
iframeManager = null;
|
|
234
|
+
});
|
|
235
|
+
function render() {
|
|
236
|
+
return h("div", {
|
|
237
|
+
ref: (el) => {
|
|
238
|
+
containerRef.value = el;
|
|
239
|
+
},
|
|
240
|
+
class: "capitalos-iframe-container",
|
|
241
|
+
style: { width: "100%" }
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
return {
|
|
245
|
+
containerRef,
|
|
246
|
+
render
|
|
247
|
+
};
|
|
248
|
+
}
|
|
146
249
|
|
|
147
250
|
//#endregion
|
|
148
251
|
//#region src/components/CardsApp.ts
|
|
@@ -169,73 +272,126 @@ var version = "0.1.0-rc.1";
|
|
|
169
272
|
* const onCardsError = (error) => console.error(error)
|
|
170
273
|
* </script>
|
|
171
274
|
* ```
|
|
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
275
|
*/
|
|
177
276
|
const CardsApp = defineComponent({
|
|
178
277
|
name: "CardsApp",
|
|
278
|
+
props: commonProps,
|
|
279
|
+
emits: commonEmits,
|
|
280
|
+
setup(props, { emit }) {
|
|
281
|
+
const { render } = useIframeComponent({
|
|
282
|
+
props,
|
|
283
|
+
emit,
|
|
284
|
+
getRenderingContext: () => ({ entryPoint: "cardsApp" })
|
|
285
|
+
});
|
|
286
|
+
return render;
|
|
287
|
+
}
|
|
288
|
+
});
|
|
289
|
+
|
|
290
|
+
//#endregion
|
|
291
|
+
//#region src/components/BillPayApp.ts
|
|
292
|
+
/**
|
|
293
|
+
* BillPayApp component that renders the CapitalOS bill payment interface in an iframe.
|
|
294
|
+
*
|
|
295
|
+
* The component emits events for loading state - consumers should handle their own loading UI:
|
|
296
|
+
*
|
|
297
|
+
* @example
|
|
298
|
+
* ```vue
|
|
299
|
+
* <template>
|
|
300
|
+
* <div v-if="!billPayLoaded">Loading...</div>
|
|
301
|
+
* <BillPayApp
|
|
302
|
+
* @loaded="billPayLoaded = true"
|
|
303
|
+
* @error="onBillPayError"
|
|
304
|
+
* />
|
|
305
|
+
* </template>
|
|
306
|
+
*
|
|
307
|
+
* <script setup>
|
|
308
|
+
* import { ref } from 'vue'
|
|
309
|
+
* import { BillPayApp } from '@capitalos/vue'
|
|
310
|
+
*
|
|
311
|
+
* const billPayLoaded = ref(false)
|
|
312
|
+
* const onBillPayError = (error) => console.error(error)
|
|
313
|
+
* </script>
|
|
314
|
+
* ```
|
|
315
|
+
*/
|
|
316
|
+
const BillPayApp = defineComponent({
|
|
317
|
+
name: "BillPayApp",
|
|
318
|
+
props: commonProps,
|
|
319
|
+
emits: commonEmits,
|
|
320
|
+
setup(props, { emit }) {
|
|
321
|
+
const { render } = useIframeComponent({
|
|
322
|
+
props,
|
|
323
|
+
emit,
|
|
324
|
+
getRenderingContext: () => ({ entryPoint: "billPayApp" })
|
|
325
|
+
});
|
|
326
|
+
return render;
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
|
|
330
|
+
//#endregion
|
|
331
|
+
//#region src/components/Onboarding.ts
|
|
332
|
+
/**
|
|
333
|
+
* Onboarding component that renders the CapitalOS onboarding flow in an iframe.
|
|
334
|
+
*
|
|
335
|
+
* The component emits events for loading state and completion - consumers should handle their own loading UI:
|
|
336
|
+
*
|
|
337
|
+
* @example
|
|
338
|
+
* ```vue
|
|
339
|
+
* <template>
|
|
340
|
+
* <div v-if="!onboardingLoaded">Loading...</div>
|
|
341
|
+
* <Onboarding
|
|
342
|
+
* entry-point="welcome"
|
|
343
|
+
* exit-point="activation"
|
|
344
|
+
* @loaded="onboardingLoaded = true"
|
|
345
|
+
* @error="onOnboardingError"
|
|
346
|
+
* @done="onOnboardingComplete"
|
|
347
|
+
* />
|
|
348
|
+
* </template>
|
|
349
|
+
*
|
|
350
|
+
* <script setup>
|
|
351
|
+
* import { ref } from 'vue'
|
|
352
|
+
* import { Onboarding } from '@capitalos/vue'
|
|
353
|
+
*
|
|
354
|
+
* const onboardingLoaded = ref(false)
|
|
355
|
+
* const onOnboardingError = (error) => console.error(error)
|
|
356
|
+
* const onOnboardingComplete = (account) => {
|
|
357
|
+
* console.log('Onboarding complete:', account.status)
|
|
358
|
+
* }
|
|
359
|
+
* </script>
|
|
360
|
+
* ```
|
|
361
|
+
*/
|
|
362
|
+
const Onboarding = defineComponent({
|
|
363
|
+
name: "Onboarding",
|
|
179
364
|
props: {
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
365
|
+
...commonProps,
|
|
366
|
+
entryPoint: String,
|
|
367
|
+
exitPoint: String,
|
|
368
|
+
allowExitOnNonApproved: {
|
|
369
|
+
type: Boolean,
|
|
370
|
+
default: void 0
|
|
371
|
+
}
|
|
186
372
|
},
|
|
187
373
|
emits: {
|
|
188
|
-
|
|
189
|
-
|
|
374
|
+
...commonEmits,
|
|
375
|
+
done: (_account) => true
|
|
190
376
|
},
|
|
191
377
|
setup(props, { emit }) {
|
|
192
|
-
const
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
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%" }
|
|
378
|
+
const { render } = useIframeComponent({
|
|
379
|
+
props,
|
|
380
|
+
emit,
|
|
381
|
+
getRenderingContext: () => ({
|
|
382
|
+
entryPoint: "onboarding",
|
|
383
|
+
onboardingEntryPoint: props.entryPoint,
|
|
384
|
+
onboardingExitPoint: props.exitPoint,
|
|
385
|
+
allowExitOnNonApproved: props.allowExitOnNonApproved
|
|
386
|
+
}),
|
|
387
|
+
componentCallbacks: { onboarding: { onDone: (account) => {
|
|
388
|
+
emit("done", account);
|
|
389
|
+
} } }
|
|
235
390
|
});
|
|
391
|
+
return render;
|
|
236
392
|
}
|
|
237
393
|
});
|
|
238
394
|
|
|
239
395
|
//#endregion
|
|
240
|
-
export { CAPITALOS_INJECTION_KEY, CapitalOSError, CardsApp, ErrorCode, createCapitalOs, useCapitalOsAuth };
|
|
396
|
+
export { BillPayApp, CAPITALOS_INJECTION_KEY, CapitalOSError, CardsApp, ErrorCode, Onboarding, createCapitalOs, useCapitalOsAuth };
|
|
241
397
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +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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"}
|
|
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","options: UseIframeComponentOptions","iframeManager: IframeManager | null","token: TokenData","container: HTMLElement","SDK_VERSION","rawError: RawErrorDetails","CapitalOSError","el: Element | null","_account: Account","account: Account"],"sources":["../src/plugin.ts","../src/components/common-props.ts","../src/composables/use-capitalos-auth.ts","../package.json","../src/composables/use-iframe-component.ts","../src/components/CardsApp.ts","../src/components/BillPayApp.ts","../src/components/Onboarding.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 type { PropType } from 'vue'\nimport type { ThemeColorScheme } from '@capitalos/core'\nimport { CapitalOSError } from '@capitalos/core'\n\n/**\n * Common props shared by all CapitalOS Vue components.\n */\nexport const commonProps = {\n /**\n * Theme color scheme for the 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\n/**\n * Common emits shared by all CapitalOS Vue components.\n */\nexport const commonEmits = {\n /**\n * Emitted when the interface has finished loading.\n */\n loaded: () => true,\n /**\n * Emitted when an error occurs in the interface.\n */\n error: (error: CapitalOSError) => error instanceof CapitalOSError,\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\",\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 \"test:pack\": \"pnpm build && pnpm pack\",\n \"prepublishOnly\": \"pnpm build\"\n },\n \"keywords\": [\n \"capitalos\",\n \"vue\",\n \"sdk\",\n \"iframe\"\n ],\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/CapitalOS/theboss\",\n \"directory\": \"sdk/vue\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/CapitalOS/theboss/issues\"\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","import { ref, watch, onUnmounted, h, type VNode, type Ref } from 'vue'\nimport {\n IframeManager,\n CapitalOSError,\n type TokenData,\n type RawErrorDetails,\n type RenderingContext,\n type IframeManagerConfig,\n} from '@capitalos/core'\nimport { useCapitalOsAuth } from './use-capitalos-auth'\nimport { version as SDK_VERSION } from '../../package.json'\n\n/**\n * Props expected by useIframeComponent - matches commonProps shape\n */\nexport interface IframeComponentProps {\n theme?: 'light' | 'dark' | 'system'\n heightOffsetPx: number\n enableLogging?: boolean\n}\n\n/**\n * Emit function type for common events\n */\nexport interface CommonEmitFn {\n (event: 'loaded'): void\n (event: 'error', error: CapitalOSError): void\n}\n\n/**\n * Component-specific callbacks to pass to IframeManager.\n * Excludes common callbacks (onLoad, onError, onTokenExpired) which the composable handles.\n */\nexport type ComponentCallbacks = Omit<IframeManagerConfig['callbacks'], 'onLoad' | 'onError' | 'onTokenExpired'>\n\n/**\n * Options for configuring the iframe component\n */\nexport interface UseIframeComponentOptions {\n /** Props from the component */\n props: IframeComponentProps\n /** Emit function from the component */\n emit: CommonEmitFn\n /** Function that returns the rendering context for this component */\n getRenderingContext: () => RenderingContext\n /** Component-specific callbacks (onboarding, tokenExchange, etc.) */\n componentCallbacks?: ComponentCallbacks\n}\n\n/**\n * Return type for useIframeComponent\n */\nexport interface UseIframeComponentReturn {\n /** Ref to the container element */\n containerRef: Ref<HTMLElement | null>\n /** Render function that returns the container div */\n render: () => VNode\n}\n\n/**\n * Composable that handles common iframe component setup.\n *\n * Manages:\n * - Container ref and iframe manager lifecycle\n * - Token data subscription and iframe initialization\n * - Common callbacks (onLoad, onError, onTokenExpired)\n * - Cleanup on unmount\n * - Render function for the container div\n *\n * Note: Rendering context props (e.g., entryPoint, exitPoint for Onboarding) are read once\n * at iframe initialization. Changes to these props after the iframe loads will NOT trigger\n * re-initialization. This matches the behavior of the React SDK.\n *\n * @example\n * ```ts\n * setup(props, { emit }) {\n * const { render } = useIframeComponent({\n * props,\n * emit,\n * getRenderingContext: () => ({ entryPoint: 'cardsApp' }),\n * })\n * return render\n * }\n * ```\n */\nexport function useIframeComponent(options: UseIframeComponentOptions): UseIframeComponentReturn {\n const { props, emit, getRenderingContext, componentCallbacks } = options\n\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: getRenderingContext(),\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 emit('error', new CapitalOSError(rawError))\n },\n onTokenExpired: () => {\n invalidateToken()\n },\n ...componentCallbacks,\n },\n })\n }\n\n // 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 // 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 function render(): VNode {\n return 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 return {\n containerRef,\n render,\n }\n}\n","import { defineComponent } from 'vue'\nimport { commonProps, commonEmits } from './common-props'\nimport { useIframeComponent } from '../composables/use-iframe-component'\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 */\nexport const CardsApp = defineComponent({\n name: 'CardsApp',\n props: commonProps,\n emits: commonEmits,\n setup(props, { emit }) {\n const { render } = useIframeComponent({\n props,\n emit,\n getRenderingContext: () => ({ entryPoint: 'cardsApp' }),\n })\n\n return render\n },\n})\n","import { defineComponent } from 'vue'\nimport { commonProps, commonEmits } from './common-props'\nimport { useIframeComponent } from '../composables/use-iframe-component'\n\n/**\n * BillPayApp component that renders the CapitalOS bill payment 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=\"!billPayLoaded\">Loading...</div>\n * <BillPayApp\n * @loaded=\"billPayLoaded = true\"\n * @error=\"onBillPayError\"\n * />\n * </template>\n *\n * <script setup>\n * import { ref } from 'vue'\n * import { BillPayApp } from '@capitalos/vue'\n *\n * const billPayLoaded = ref(false)\n * const onBillPayError = (error) => console.error(error)\n * </script>\n * ```\n */\nexport const BillPayApp = defineComponent({\n name: 'BillPayApp',\n props: commonProps,\n emits: commonEmits,\n setup(props, { emit }) {\n const { render } = useIframeComponent({\n props,\n emit,\n getRenderingContext: () => ({ entryPoint: 'billPayApp' }),\n })\n\n return render\n },\n})\n","import { defineComponent, type PropType } from 'vue'\nimport type { Account, OnboardingEntryPoint, OnboardingExitPoint } from '@capitalos/core'\nimport { commonProps, commonEmits } from './common-props'\nimport { useIframeComponent } from '../composables/use-iframe-component'\n\n/**\n * Onboarding component that renders the CapitalOS onboarding flow in an iframe.\n *\n * The component emits events for loading state and completion - consumers should handle their own loading UI:\n *\n * @example\n * ```vue\n * <template>\n * <div v-if=\"!onboardingLoaded\">Loading...</div>\n * <Onboarding\n * entry-point=\"welcome\"\n * exit-point=\"activation\"\n * @loaded=\"onboardingLoaded = true\"\n * @error=\"onOnboardingError\"\n * @done=\"onOnboardingComplete\"\n * />\n * </template>\n *\n * <script setup>\n * import { ref } from 'vue'\n * import { Onboarding } from '@capitalos/vue'\n *\n * const onboardingLoaded = ref(false)\n * const onOnboardingError = (error) => console.error(error)\n * const onOnboardingComplete = (account) => {\n * console.log('Onboarding complete:', account.status)\n * }\n * </script>\n * ```\n */\nexport const Onboarding = defineComponent({\n name: 'Onboarding',\n props: {\n ...commonProps,\n /**\n * The starting point of the onboarding flow.\n * @default 'welcome'\n */\n entryPoint: String as PropType<OnboardingEntryPoint>,\n /**\n * The ending point of the onboarding flow.\n * @default 'activation'\n */\n exitPoint: String as PropType<OnboardingExitPoint>,\n /**\n * Whether to allow the user to exit the onboarding flow by clicking the done button\n * when they finished the onboarding form but are still not approved (e.g. waiting or pending).\n * This should only be false if you want the user to stay on the onboarding flow\n * (e.g. to add bank account) and not exit the flow.\n * @default true\n */\n allowExitOnNonApproved: {\n type: Boolean,\n default: undefined,\n },\n },\n emits: {\n ...commonEmits,\n /**\n * Emitted when the onboarding flow completes successfully.\n */\n done: (_account: Account) => true,\n },\n setup(props, { emit }) {\n const { render } = useIframeComponent({\n props,\n emit,\n getRenderingContext: () => ({\n entryPoint: 'onboarding',\n onboardingEntryPoint: props.entryPoint,\n onboardingExitPoint: props.exitPoint,\n allowExitOnNonApproved: props.allowExitOnNonApproved,\n }),\n componentCallbacks: {\n onboarding: {\n onDone: (account: Account) => {\n emit('done', account)\n },\n },\n },\n })\n\n return render\n },\n})\n"],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@capitalos/vue",
|
|
3
|
-
"version": "0.1.0
|
|
3
|
+
"version": "0.1.0",
|
|
4
4
|
"description": "Vue SDK for CapitalOS",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -20,32 +20,42 @@
|
|
|
20
20
|
"files": [
|
|
21
21
|
"dist"
|
|
22
22
|
],
|
|
23
|
+
"scripts": {
|
|
24
|
+
"build": "tsdown",
|
|
25
|
+
"dev": "tsdown --watch",
|
|
26
|
+
"lint": "eslint src --ext .ts --fix",
|
|
27
|
+
"lint-ci": "eslint src --ext .ts",
|
|
28
|
+
"start": "pnpm --filter vue-test-app dev",
|
|
29
|
+
"start:all": "concurrently \"pnpm --filter sdk-test-server start\" \"pnpm start\"",
|
|
30
|
+
"test:pack": "pnpm build && pnpm pack",
|
|
31
|
+
"prepublishOnly": "pnpm build"
|
|
32
|
+
},
|
|
23
33
|
"keywords": [
|
|
24
34
|
"capitalos",
|
|
25
35
|
"vue",
|
|
26
36
|
"sdk",
|
|
27
37
|
"iframe"
|
|
28
38
|
],
|
|
39
|
+
"repository": {
|
|
40
|
+
"type": "git",
|
|
41
|
+
"url": "https://github.com/CapitalOS/theboss",
|
|
42
|
+
"directory": "sdk/vue"
|
|
43
|
+
},
|
|
44
|
+
"bugs": {
|
|
45
|
+
"url": "https://github.com/CapitalOS/theboss/issues"
|
|
46
|
+
},
|
|
29
47
|
"author": "CapitalOS",
|
|
30
48
|
"license": "MIT",
|
|
31
49
|
"peerDependencies": {
|
|
32
50
|
"vue": "^2.7.0 || ^3.0.0"
|
|
33
51
|
},
|
|
34
52
|
"dependencies": {
|
|
35
|
-
"@capitalos/core": "
|
|
53
|
+
"@capitalos/core": "workspace:*"
|
|
36
54
|
},
|
|
37
55
|
"devDependencies": {
|
|
38
56
|
"tsdown": "^0.9.2",
|
|
39
57
|
"typescript": "^5.6.2",
|
|
40
58
|
"vue": "^3.5.13",
|
|
41
59
|
"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
60
|
}
|
|
51
|
-
}
|
|
61
|
+
}
|