@capitalos/js 0.1.0 → 0.2.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/README.md +25 -19
- package/dist/index.d.mts +13 -2
- package/dist/index.d.mts.map +1 -1
- package/dist/index.d.ts +13 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +49 -10
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +49 -10
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -1,51 +1,57 @@
|
|
|
1
1
|
# CapitalOS for JavaScript
|
|
2
2
|
|
|
3
|
-
Framework-agnostic JavaScript SDK for
|
|
3
|
+
Framework-agnostic JavaScript SDK for integrating with CapitalOS
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
7
|
+
### npm
|
|
8
|
+
|
|
7
9
|
```bash
|
|
8
10
|
npm install @capitalos/js
|
|
9
11
|
```
|
|
10
12
|
|
|
13
|
+
### yarn
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
yarn add @capitalos/js
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
### pnpm
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
pnpm add @capitalos/js
|
|
23
|
+
```
|
|
24
|
+
|
|
11
25
|
## Documentation
|
|
12
26
|
|
|
13
27
|
Please refer to the [official docs](https://docs.capitalos.com/docs/using-vanilla-js-client-library) for more details.
|
|
14
28
|
|
|
15
29
|
## Usage
|
|
16
30
|
|
|
17
|
-
|
|
31
|
+
In order to mount an Experience you will need to obtain a client authentication token. Refer to the [CapitalOS documentation](https://docs.capitalos.com/docs/using-vanilla-js-client-library) for more information.
|
|
18
32
|
|
|
19
33
|
```ts
|
|
20
34
|
import { createCapitalOsClient } from '@capitalos/js'
|
|
21
35
|
|
|
22
36
|
const capitalOs = createCapitalOsClient({
|
|
23
37
|
getToken: async () => {
|
|
24
|
-
|
|
25
|
-
const
|
|
26
|
-
|
|
38
|
+
// Call your backend which initiates login and returns a one-time token
|
|
39
|
+
const res = await fetch('/api/capitalos/token', { method: 'POST' })
|
|
40
|
+
const json = await res.json()
|
|
41
|
+
return json.token
|
|
27
42
|
},
|
|
28
43
|
})
|
|
29
44
|
|
|
30
|
-
const
|
|
31
|
-
onLoaded: () =>
|
|
32
|
-
|
|
33
|
-
},
|
|
34
|
-
onError: (error) => {
|
|
35
|
-
// Handle CapitalOSError.
|
|
36
|
-
console.error(error.code, error.message)
|
|
37
|
-
},
|
|
45
|
+
const mount = capitalOs.mountCardsApp('#capitalos-cards', {
|
|
46
|
+
onLoaded: () => console.log('Loaded!'),
|
|
47
|
+
onError: (error) => console.error(error.code, error.message),
|
|
38
48
|
})
|
|
39
49
|
|
|
40
|
-
//
|
|
41
|
-
|
|
50
|
+
// Tear down when you're done
|
|
51
|
+
mount.destroy()
|
|
42
52
|
capitalOs.destroy()
|
|
43
53
|
```
|
|
44
54
|
|
|
45
|
-
`mountCardsApp` returns a Mount handle synchronously. Programmer errors such as a missing selector throw synchronously.
|
|
46
|
-
|
|
47
|
-
The SDK manages the iframe lifecycle, auto-sizing, Penpal connection, token exchange, token refresh, and cleanup.
|
|
48
|
-
|
|
49
55
|
## TypeScript support
|
|
50
56
|
|
|
51
57
|
TypeScript definitions for `@capitalos/js` are built into the npm package and should be automatically picked up by your editor.
|
package/dist/index.d.mts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CapitalOSError, CapitalOSError as CapitalOSError$1, ErrorCode, Logger, Logger as Logger$1, ThemeColorScheme, ThemeColorScheme as ThemeColorScheme$1 } from "@capitalos/core";
|
|
1
|
+
import { Account, Account as Account$1, AccountStatus, CapitalOSError, CapitalOSError as CapitalOSError$1, ErrorCode, Logger, Logger as Logger$1, OnboardingEntryPoint, OnboardingExitPoint, ThemeColorScheme, ThemeColorScheme as ThemeColorScheme$1 } from "@capitalos/core";
|
|
2
2
|
|
|
3
3
|
//#region src/types.d.ts
|
|
4
4
|
interface CapitalOsClientConfig {
|
|
@@ -14,14 +14,25 @@ interface MountOptions {
|
|
|
14
14
|
enableLogging?: boolean;
|
|
15
15
|
heightOffsetPx?: number;
|
|
16
16
|
}
|
|
17
|
+
interface OnboardingMountOptions extends MountOptions {
|
|
18
|
+
entryPoint?: OnboardingEntryPoint;
|
|
19
|
+
exitPoint?: OnboardingExitPoint;
|
|
20
|
+
product?: 'billPay' | 'card' | 'invoicing';
|
|
21
|
+
allowExitOnNonApproved?: boolean;
|
|
22
|
+
doneButtonText?: string;
|
|
23
|
+
onDone?: (account: Account$1) => void;
|
|
24
|
+
}
|
|
17
25
|
interface CapitalOsMount {
|
|
18
26
|
ready: Promise<void>;
|
|
19
27
|
destroy(): void;
|
|
20
28
|
}
|
|
21
29
|
interface CapitalOsClient {
|
|
22
30
|
mountCardsApp(target: string | HTMLElement, options?: MountOptions): CapitalOsMount;
|
|
31
|
+
mountBillPayApp(target: string | HTMLElement, options?: MountOptions): CapitalOsMount;
|
|
32
|
+
mountOnboarding(target: string | HTMLElement, options?: OnboardingMountOptions): CapitalOsMount;
|
|
23
33
|
destroy(): void;
|
|
24
34
|
}
|
|
35
|
+
|
|
25
36
|
//#endregion
|
|
26
37
|
//#region src/capital-os-client.d.ts
|
|
27
38
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -30,5 +41,5 @@ declare function createCapitalOsClient(config: CapitalOsClientConfig): CapitalOs
|
|
|
30
41
|
//#endregion
|
|
31
42
|
//# sourceMappingURL=capital-os-client.d.ts.map
|
|
32
43
|
|
|
33
|
-
export { CapitalOSError, CapitalOsClient, CapitalOsClientConfig, CapitalOsMount, ErrorCode, Logger, MountOptions, ThemeColorScheme, createCapitalOsClient };
|
|
44
|
+
export { Account, AccountStatus, CapitalOSError, CapitalOsClient, CapitalOsClientConfig, CapitalOsMount, ErrorCode, Logger, MountOptions, OnboardingMountOptions, ThemeColorScheme, createCapitalOsClient };
|
|
34
45
|
//# 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/capital-os-client.ts"],"sourcesContent":null,"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/capital-os-client.ts"],"sourcesContent":null,"mappings":";;;UASiB,qBAAA;kBACC;EADD,KAAA,CAAA,EAEP,kBAF4B;EAAA,aAAA,CAAA,EAAA,OAAA;EAAA,MACpB,CAAA,EAGP,QAHO;;AAGP,UAGM,YAAA,CAHN;EAAM,QAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAGA,OAAA,CAAA,EAAA,CAAA,KAAY,EAET,gBAFS,EAAA,GAAA,IAAA;EAAA,KAAA,CAAA,EAGnB,kBAHmB;EAAA,aAET,CAAA,EAAA,OAAA;EAAc,cACxB,CAAA,EAAA,MAAA;AAAgB;AAST,UAAA,sBAAA,SAA+B,YAAR,CAAA;EAAA,UAAA,CAAA,EAKzB,oBALyB;EAAA,SAKzB,CAAA,EAKD,mBALC;EAAoB,OAKrB,CAAA,EAAA,SAAA,GAAA,MAAA,GAAA,WAAA;EAAmB,sBAoBZ,CAAA,EAAA,OAAA;EAAO,cA9BoB,CAAA,EAAA,MAAA;EAAY,MAAA,CAAA,EAAA,CAAA,OAAA,EA8BvC,SA9BuC,EAAA,GAAA,IAAA;AAiC5D;AAKiB,UALA,cAAA,CAKe;EAAA,KAAA,EAJvB,OAIuB,CAAA,IAAA,CAAA;EAAA,OACC,EAAA,EAAA,IAAA;;AAAsC,UADtD,eAAA,CACsD;EAAc,aAClD,CAAA,MAAA,EAAA,MAAA,GADF,WACE,EAAA,OAAA,CAAA,EADqB,YACrB,CAAA,EADoC,cACpC;EAAW,eAAY,CAAA,MAAA,EAAA,MAAA,GAAvB,WAAuB,EAAA,OAAA,CAAA,EAAA,YAAA,CAAA,EAAe,cAAf;EAAY,eAAG,CAAA,MAAA,EAAA,MAAA,GACtC,WADsC,EAAA,OAAA,CAAA,EACf,sBADe,CAAA,EACU,cADV;EAAc,OACpD,EAAA,EAAA,IAAA;;;;;;iBC+OnB,qBAAA,SAA8B,wBAAwB"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { CapitalOSError, CapitalOSError as CapitalOSError$1, ErrorCode, Logger, Logger as Logger$1, ThemeColorScheme, ThemeColorScheme as ThemeColorScheme$1 } from "@capitalos/core";
|
|
1
|
+
import { Account, Account as Account$1, AccountStatus, CapitalOSError, CapitalOSError as CapitalOSError$1, ErrorCode, Logger, Logger as Logger$1, OnboardingEntryPoint, OnboardingExitPoint, ThemeColorScheme, ThemeColorScheme as ThemeColorScheme$1 } from "@capitalos/core";
|
|
2
2
|
|
|
3
3
|
//#region src/types.d.ts
|
|
4
4
|
interface CapitalOsClientConfig {
|
|
@@ -14,14 +14,25 @@ interface MountOptions {
|
|
|
14
14
|
enableLogging?: boolean;
|
|
15
15
|
heightOffsetPx?: number;
|
|
16
16
|
}
|
|
17
|
+
interface OnboardingMountOptions extends MountOptions {
|
|
18
|
+
entryPoint?: OnboardingEntryPoint;
|
|
19
|
+
exitPoint?: OnboardingExitPoint;
|
|
20
|
+
product?: 'billPay' | 'card' | 'invoicing';
|
|
21
|
+
allowExitOnNonApproved?: boolean;
|
|
22
|
+
doneButtonText?: string;
|
|
23
|
+
onDone?: (account: Account$1) => void;
|
|
24
|
+
}
|
|
17
25
|
interface CapitalOsMount {
|
|
18
26
|
ready: Promise<void>;
|
|
19
27
|
destroy(): void;
|
|
20
28
|
}
|
|
21
29
|
interface CapitalOsClient {
|
|
22
30
|
mountCardsApp(target: string | HTMLElement, options?: MountOptions): CapitalOsMount;
|
|
31
|
+
mountBillPayApp(target: string | HTMLElement, options?: MountOptions): CapitalOsMount;
|
|
32
|
+
mountOnboarding(target: string | HTMLElement, options?: OnboardingMountOptions): CapitalOsMount;
|
|
23
33
|
destroy(): void;
|
|
24
34
|
}
|
|
35
|
+
|
|
25
36
|
//#endregion
|
|
26
37
|
//#region src/capital-os-client.d.ts
|
|
27
38
|
//# sourceMappingURL=types.d.ts.map
|
|
@@ -30,5 +41,5 @@ declare function createCapitalOsClient(config: CapitalOsClientConfig): CapitalOs
|
|
|
30
41
|
//#endregion
|
|
31
42
|
//# sourceMappingURL=capital-os-client.d.ts.map
|
|
32
43
|
|
|
33
|
-
export { CapitalOSError, CapitalOsClient, CapitalOsClientConfig, CapitalOsMount, ErrorCode, Logger, MountOptions, ThemeColorScheme, createCapitalOsClient };
|
|
44
|
+
export { Account, AccountStatus, CapitalOSError, CapitalOsClient, CapitalOsClientConfig, CapitalOsMount, ErrorCode, Logger, MountOptions, OnboardingMountOptions, ThemeColorScheme, createCapitalOsClient };
|
|
34
45
|
//# 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/capital-os-client.ts"],"sourcesContent":null,"mappings":";;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/capital-os-client.ts"],"sourcesContent":null,"mappings":";;;UASiB,qBAAA;kBACC;EADD,KAAA,CAAA,EAEP,kBAF4B;EAAA,aAAA,CAAA,EAAA,OAAA;EAAA,MACpB,CAAA,EAGP,QAHO;;AAGP,UAGM,YAAA,CAHN;EAAM,QAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAGA,OAAA,CAAA,EAAA,CAAA,KAAY,EAET,gBAFS,EAAA,GAAA,IAAA;EAAA,KAAA,CAAA,EAGnB,kBAHmB;EAAA,aAET,CAAA,EAAA,OAAA;EAAc,cACxB,CAAA,EAAA,MAAA;AAAgB;AAST,UAAA,sBAAA,SAA+B,YAAR,CAAA;EAAA,UAAA,CAAA,EAKzB,oBALyB;EAAA,SAKzB,CAAA,EAKD,mBALC;EAAoB,OAKrB,CAAA,EAAA,SAAA,GAAA,MAAA,GAAA,WAAA;EAAmB,sBAoBZ,CAAA,EAAA,OAAA;EAAO,cA9BoB,CAAA,EAAA,MAAA;EAAY,MAAA,CAAA,EAAA,CAAA,OAAA,EA8BvC,SA9BuC,EAAA,GAAA,IAAA;AAiC5D;AAKiB,UALA,cAAA,CAKe;EAAA,KAAA,EAJvB,OAIuB,CAAA,IAAA,CAAA;EAAA,OACC,EAAA,EAAA,IAAA;;AAAsC,UADtD,eAAA,CACsD;EAAc,aAClD,CAAA,MAAA,EAAA,MAAA,GADF,WACE,EAAA,OAAA,CAAA,EADqB,YACrB,CAAA,EADoC,cACpC;EAAW,eAAY,CAAA,MAAA,EAAA,MAAA,GAAvB,WAAuB,EAAA,OAAA,CAAA,EAAA,YAAA,CAAA,EAAe,cAAf;EAAY,eAAG,CAAA,MAAA,EAAA,MAAA,GACtC,WADsC,EAAA,OAAA,CAAA,EACf,sBADe,CAAA,EACU,cADV;EAAc,OACpD,EAAA,EAAA,IAAA;;;;;;iBC+OnB,qBAAA,SAA8B,wBAAwB"}
|
package/dist/index.js
CHANGED
|
@@ -25,7 +25,7 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
25
25
|
const __capitalos_core = __toESM(require("@capitalos/core"));
|
|
26
26
|
|
|
27
27
|
//#region package.json
|
|
28
|
-
var version = "0.
|
|
28
|
+
var version = "0.2.0";
|
|
29
29
|
|
|
30
30
|
//#endregion
|
|
31
31
|
//#region src/capital-os-client.ts
|
|
@@ -56,7 +56,13 @@ function createReadyDeferred() {
|
|
|
56
56
|
resolve: resolveReady
|
|
57
57
|
};
|
|
58
58
|
}
|
|
59
|
-
|
|
59
|
+
/**
|
|
60
|
+
* Renders a single CapitalOS Experience iframe inside a container, reading the shared
|
|
61
|
+
* Embed controller's session token. An Experience is fully described by its `renderingContext`
|
|
62
|
+
* (entry point + per-Experience fields) and its `componentCallbacks` (parent-exposed methods);
|
|
63
|
+
* this class is otherwise Experience-agnostic.
|
|
64
|
+
*/
|
|
65
|
+
var EmbedMount = class {
|
|
60
66
|
ready;
|
|
61
67
|
iframeManager = null;
|
|
62
68
|
isDestroyed = false;
|
|
@@ -99,7 +105,7 @@ var CardsAppMount = class {
|
|
|
99
105
|
this.iframeManager = new __capitalos_core.IframeManager({
|
|
100
106
|
container: this.params.container,
|
|
101
107
|
tokenData,
|
|
102
|
-
renderingContext: this.
|
|
108
|
+
renderingContext: this.params.renderingContext,
|
|
103
109
|
theme: this.params.options?.theme ?? this.params.sessionConfig.theme,
|
|
104
110
|
enableLogging: this.params.options?.enableLogging ?? this.params.sessionConfig.enableLogging ?? false,
|
|
105
111
|
logger: this.params.sessionConfig.logger,
|
|
@@ -118,13 +124,11 @@ var CardsAppMount = class {
|
|
|
118
124
|
},
|
|
119
125
|
onConnectionError: (error) => {
|
|
120
126
|
this.handleError(error);
|
|
121
|
-
}
|
|
127
|
+
},
|
|
128
|
+
...this.params.componentCallbacks
|
|
122
129
|
}
|
|
123
130
|
});
|
|
124
131
|
}
|
|
125
|
-
getRenderingContext() {
|
|
126
|
-
return { entryPoint: "cardsApp" };
|
|
127
|
-
}
|
|
128
132
|
resolveReady() {
|
|
129
133
|
if (this.isReadySettled) return;
|
|
130
134
|
this.isReadySettled = true;
|
|
@@ -154,14 +158,49 @@ var CapitalOsClientImpl = class {
|
|
|
154
158
|
});
|
|
155
159
|
}
|
|
156
160
|
mountCardsApp(target, options) {
|
|
161
|
+
return this.createMount({
|
|
162
|
+
target,
|
|
163
|
+
options,
|
|
164
|
+
renderingContext: { entryPoint: "cardsApp" }
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
mountBillPayApp(target, options) {
|
|
168
|
+
return this.createMount({
|
|
169
|
+
target,
|
|
170
|
+
options,
|
|
171
|
+
renderingContext: { entryPoint: "billPayApp" }
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
mountOnboarding(target, options) {
|
|
175
|
+
return this.createMount({
|
|
176
|
+
target,
|
|
177
|
+
options,
|
|
178
|
+
renderingContext: {
|
|
179
|
+
entryPoint: "onboarding",
|
|
180
|
+
onboardingEntryPoint: options?.entryPoint,
|
|
181
|
+
onboardingExitPoint: options?.exitPoint,
|
|
182
|
+
allowExitOnNonApproved: options?.allowExitOnNonApproved,
|
|
183
|
+
product: options?.product,
|
|
184
|
+
copy: options?.doneButtonText ? {
|
|
185
|
+
"onboarding.doneButton": options.doneButtonText,
|
|
186
|
+
"onboarding.continueButton": options.doneButtonText,
|
|
187
|
+
"activation.successButton": options.doneButtonText
|
|
188
|
+
} : void 0
|
|
189
|
+
},
|
|
190
|
+
componentCallbacks: { onboarding: { onDone: options?.onDone ?? (() => {}) } }
|
|
191
|
+
});
|
|
192
|
+
}
|
|
193
|
+
createMount(params) {
|
|
157
194
|
if (this.isDestroyed) throw new Error("CapitalOS client has been destroyed");
|
|
158
|
-
const mount = new
|
|
159
|
-
container: resolveTarget(target),
|
|
195
|
+
const mount = new EmbedMount({
|
|
196
|
+
container: resolveTarget(params.target),
|
|
160
197
|
controller: this.controller,
|
|
161
198
|
onDestroy: (destroyedMount) => {
|
|
162
199
|
this.mounts.delete(destroyedMount);
|
|
163
200
|
},
|
|
164
|
-
options,
|
|
201
|
+
options: params.options,
|
|
202
|
+
renderingContext: params.renderingContext,
|
|
203
|
+
componentCallbacks: params.componentCallbacks,
|
|
165
204
|
sessionConfig: this.config
|
|
166
205
|
});
|
|
167
206
|
this.mounts.add(mount);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":["value: unknown","target: string | HTMLElement","resolveReady: () => void","rejectReady: (error: CapitalOSError) => void","params: CardsAppMountParams","CapitalOSError","ErrorCode","tokenData: TokenData","IframeManager","SDK_VERSION","rawError: RawErrorDetails","error: Error","error: unknown","config: CapitalOsClientConfig","options?: MountOptions"],"sources":["../package.json","../src/capital-os-client.ts"],"sourcesContent":["{\n \"name\": \"@capitalos/js\",\n \"version\": \"0.1.0\",\n \"description\": \"Framework-agnostic JavaScript 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 \"package.json\",\n \"README.md\"\n ],\n \"scripts\": {\n \"build\": \"tsdown\",\n \"dev\": \"tsdown --watch\",\n \"lint\": \"eslint src test --ext .ts --fix\",\n \"lint-ci\": \"eslint src test --ext .ts\",\n \"test\": \"vitest run\",\n \"test:pack\": \"pnpm build && pnpm pack\",\n \"prepublishOnly\": \"pnpm build\"\n },\n \"keywords\": [\n \"capitalos\",\n \"javascript\",\n \"sdk\",\n \"iframe\"\n ],\n \"homepage\": \"https://docs.capitalos.com/docs/using-vanilla-js-client-library\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/CapitalOS/theboss\",\n \"directory\": \"sdk/js\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/CapitalOS/theboss/issues\"\n },\n \"author\": \"CapitalOS\",\n \"license\": \"MIT\",\n \"dependencies\": {\n \"@capitalos/core\": \"workspace:*\"\n },\n \"devDependencies\": {\n \"@types/iframe-resizer\": \"^3.5.13\",\n \"@typescript-eslint/eslint-plugin\": \"^7.2.0\",\n \"@typescript-eslint/parser\": \"^7.2.0\",\n \"eslint\": \"^8.57.0\",\n \"eslint-config-prettier\": \"^9.1.0\",\n \"eslint-plugin-prettier\": \"^5.1.3\",\n \"prettier\": \"^3.2.5\",\n \"tsdown\": \"^0.9.2\",\n \"typescript\": \"^5.6.2\",\n \"vitest\": \"^4.1.0\"\n }\n}\n","import {\n CapitalOSError,\n ErrorCode,\n IframeManager,\n createEmbedController,\n toCapitalOSError,\n type EmbedController,\n type IframeManagerConfig,\n type RawErrorDetails,\n type RenderingContext,\n type TokenData,\n} from '@capitalos/core'\n\nimport { version as SDK_VERSION } from '../package.json'\nimport type { CapitalOsClient, CapitalOsClientConfig, CapitalOsMount, MountOptions } from './types'\n\nfunction isHTMLElement(value: unknown): value is HTMLElement {\n return typeof HTMLElement !== 'undefined' && value instanceof HTMLElement\n}\n\nfunction resolveTarget(target: string | HTMLElement): HTMLElement {\n if (typeof target === 'string') {\n if (typeof document === 'undefined') {\n throw new Error('Cannot resolve selector because document is unavailable')\n }\n\n const element = document.querySelector(target)\n if (!isHTMLElement(element)) {\n throw new Error(`No HTMLElement found for selector \"${target}\"`)\n }\n\n return element\n }\n\n if (!isHTMLElement(target)) {\n throw new Error('target must be a CSS selector string or HTMLElement')\n }\n\n return target\n}\n\ntype ReadyDeferred = {\n promise: Promise<void>\n reject: (error: CapitalOSError) => void\n resolve: () => void\n}\n\nfunction createReadyDeferred(): ReadyDeferred {\n let resolveReady: () => void = () => {}\n let rejectReady: (error: CapitalOSError) => void = () => {}\n\n const promise = new Promise<void>((resolve, reject) => {\n resolveReady = resolve\n rejectReady = reject\n })\n\n // `ready` is optional for callers; attach a no-op catch so a rejection never\n // surfaces as an unhandledrejection when nobody awaits it.\n promise.catch(() => {})\n\n return {\n promise,\n reject: rejectReady,\n resolve: resolveReady,\n }\n}\n\ntype CardsAppMountParams = {\n container: HTMLElement\n controller: EmbedController\n onDestroy: (mount: CardsAppMount) => void\n options: MountOptions | undefined\n sessionConfig: CapitalOsClientConfig\n}\n\nclass CardsAppMount implements CapitalOsMount {\n readonly ready: Promise<void>\n\n private iframeManager: IframeManager | null = null\n private isDestroyed = false\n private isReadySettled = false\n private readonly readyDeferred = createReadyDeferred()\n private readonly unsubscribeTokenData: () => void\n private readonly unsubscribeError: () => void\n\n constructor(private readonly params: CardsAppMountParams) {\n this.ready = this.readyDeferred.promise\n\n this.unsubscribeTokenData = params.controller.onTokenData((tokenData) => {\n if (tokenData) {\n this.initializeIframe(tokenData)\n }\n })\n this.unsubscribeError = params.controller.onError((error) => {\n this.handleError(error)\n })\n\n const existingTokenData = params.controller.getTokenData()\n if (existingTokenData) {\n this.initializeIframe(existingTokenData)\n }\n\n // Lazy-start auth on first mount. Failures are intentionally routed through\n // the `onError` subscription above (and `ready`), not this promise.\n void params.controller.start().catch(() => {})\n }\n\n destroy(): void {\n if (this.isDestroyed) {\n return\n }\n\n this.isDestroyed = true\n this.unsubscribeTokenData()\n this.unsubscribeError()\n this.iframeManager?.destroy()\n this.iframeManager = null\n this.params.onDestroy(this)\n\n if (!this.isReadySettled) {\n this.isReadySettled = true\n this.readyDeferred.reject(\n new CapitalOSError({\n code: ErrorCode.internal_error,\n message: 'Mount was destroyed before it finished loading',\n })\n )\n }\n }\n\n private initializeIframe(tokenData: TokenData): void {\n if (this.isDestroyed) {\n return\n }\n\n this.iframeManager?.destroy()\n this.iframeManager = new IframeManager({\n container: this.params.container,\n tokenData,\n renderingContext: this.getRenderingContext(),\n theme: this.params.options?.theme ?? this.params.sessionConfig.theme,\n enableLogging: this.params.options?.enableLogging ?? this.params.sessionConfig.enableLogging ?? false,\n logger: this.params.sessionConfig.logger,\n sdkVersion: SDK_VERSION,\n heightOffsetPx: this.params.options?.heightOffsetPx ?? 12,\n callbacks: {\n onLoad: () => {\n this.resolveReady()\n this.params.options?.onLoaded?.()\n },\n onError: (rawError: RawErrorDetails) => {\n this.handleError(new CapitalOSError(rawError))\n },\n onTokenExpired: () => {\n void this.params.controller.invalidate().catch(() => {})\n },\n onConnectionError: (error: Error) => {\n this.handleError(error)\n },\n } satisfies IframeManagerConfig['callbacks'],\n })\n }\n\n private getRenderingContext(): RenderingContext {\n return { entryPoint: 'cardsApp' }\n }\n\n private resolveReady(): void {\n if (this.isReadySettled) {\n return\n }\n\n this.isReadySettled = true\n this.readyDeferred.resolve()\n }\n\n private handleError(error: unknown): void {\n if (this.isDestroyed) {\n return\n }\n\n const capitalOsError = toCapitalOSError(error)\n\n if (!this.isReadySettled) {\n this.isReadySettled = true\n this.readyDeferred.reject(capitalOsError)\n }\n\n this.params.options?.onError?.(capitalOsError)\n }\n}\n\nclass CapitalOsClientImpl implements CapitalOsClient {\n private readonly controller: EmbedController\n private readonly mounts = new Set<CardsAppMount>()\n private isDestroyed = false\n\n constructor(private readonly config: CapitalOsClientConfig) {\n this.controller = createEmbedController({\n getToken: config.getToken,\n enableLogging: config.enableLogging,\n logger: config.logger,\n sdkVersion: SDK_VERSION,\n })\n }\n\n mountCardsApp(target: string | HTMLElement, options?: MountOptions): CapitalOsMount {\n if (this.isDestroyed) {\n throw new Error('CapitalOS client has been destroyed')\n }\n\n const mount = new CardsAppMount({\n container: resolveTarget(target),\n controller: this.controller,\n onDestroy: (destroyedMount) => {\n this.mounts.delete(destroyedMount)\n },\n options,\n sessionConfig: this.config,\n })\n\n this.mounts.add(mount)\n return mount\n }\n\n destroy(): void {\n if (this.isDestroyed) {\n return\n }\n\n this.isDestroyed = true\n for (const mount of Array.from(this.mounts)) {\n mount.destroy()\n }\n this.mounts.clear()\n this.controller.destroy()\n }\n}\n\nexport function createCapitalOsClient(config: CapitalOsClientConfig): CapitalOsClient {\n return new CapitalOsClientImpl(config)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":["value: unknown","target: string | HTMLElement","resolveReady: () => void","rejectReady: (error: CapitalOSError) => void","params: EmbedMountParams","CapitalOSError","ErrorCode","tokenData: TokenData","IframeManager","SDK_VERSION","rawError: RawErrorDetails","error: Error","error: unknown","config: CapitalOsClientConfig","options?: MountOptions","options?: OnboardingMountOptions","params: {\n target: string | HTMLElement\n options: MountOptions | undefined\n renderingContext: RenderingContext\n componentCallbacks?: ComponentCallbacks\n }"],"sources":["../package.json","../src/capital-os-client.ts"],"sourcesContent":["{\n \"name\": \"@capitalos/js\",\n \"version\": \"0.2.0\",\n \"description\": \"Framework-agnostic JavaScript 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 \"package.json\",\n \"README.md\"\n ],\n \"scripts\": {\n \"build\": \"tsdown\",\n \"dev\": \"tsdown --watch\",\n \"lint\": \"eslint src test --ext .ts --fix\",\n \"lint-ci\": \"eslint src test --ext .ts\",\n \"test\": \"vitest run\",\n \"test:pack\": \"pnpm build && pnpm pack\",\n \"prepublishOnly\": \"pnpm build\"\n },\n \"keywords\": [\n \"capitalos\",\n \"javascript\",\n \"sdk\",\n \"iframe\"\n ],\n \"homepage\": \"https://docs.capitalos.com/docs/using-vanilla-js-client-library\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/CapitalOS/theboss\",\n \"directory\": \"sdk/js\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/CapitalOS/theboss/issues\"\n },\n \"author\": \"CapitalOS\",\n \"license\": \"MIT\",\n \"dependencies\": {\n \"@capitalos/core\": \"workspace:*\"\n },\n \"devDependencies\": {\n \"@types/iframe-resizer\": \"^3.5.13\",\n \"@typescript-eslint/eslint-plugin\": \"^7.2.0\",\n \"@typescript-eslint/parser\": \"^7.2.0\",\n \"eslint\": \"^8.57.0\",\n \"eslint-config-prettier\": \"^9.1.0\",\n \"eslint-plugin-prettier\": \"^5.1.3\",\n \"prettier\": \"^3.2.5\",\n \"tsdown\": \"^0.9.2\",\n \"typescript\": \"^5.6.2\",\n \"vitest\": \"^4.1.0\"\n }\n}\n","import {\n CapitalOSError,\n ErrorCode,\n IframeManager,\n createEmbedController,\n toCapitalOSError,\n type CopyOverrides,\n type EmbedController,\n type IframeManagerConfig,\n type RawErrorDetails,\n type RenderingContext,\n type TokenData,\n} from '@capitalos/core'\n\nimport { version as SDK_VERSION } from '../package.json'\nimport type {\n CapitalOsClient,\n CapitalOsClientConfig,\n CapitalOsMount,\n MountOptions,\n OnboardingMountOptions,\n} from './types'\n\nfunction isHTMLElement(value: unknown): value is HTMLElement {\n return typeof HTMLElement !== 'undefined' && value instanceof HTMLElement\n}\n\nfunction resolveTarget(target: string | HTMLElement): HTMLElement {\n if (typeof target === 'string') {\n if (typeof document === 'undefined') {\n throw new Error('Cannot resolve selector because document is unavailable')\n }\n\n const element = document.querySelector(target)\n if (!isHTMLElement(element)) {\n throw new Error(`No HTMLElement found for selector \"${target}\"`)\n }\n\n return element\n }\n\n if (!isHTMLElement(target)) {\n throw new Error('target must be a CSS selector string or HTMLElement')\n }\n\n return target\n}\n\ntype ReadyDeferred = {\n promise: Promise<void>\n reject: (error: CapitalOSError) => void\n resolve: () => void\n}\n\nfunction createReadyDeferred(): ReadyDeferred {\n let resolveReady: () => void = () => {}\n let rejectReady: (error: CapitalOSError) => void = () => {}\n\n const promise = new Promise<void>((resolve, reject) => {\n resolveReady = resolve\n rejectReady = reject\n })\n\n // `ready` is optional for callers; attach a no-op catch so a rejection never\n // surfaces as an unhandledrejection when nobody awaits it.\n promise.catch(() => {})\n\n return {\n promise,\n reject: rejectReady,\n resolve: resolveReady,\n }\n}\n\n/**\n * Experience-specific Penpal methods the parent exposes to the iframe (e.g. `onboarding.onDone`).\n * Derived from the core `IframeManager` callback surface minus the lifecycle callbacks the mount\n * owns itself, so new Experiences are picked up automatically as core grows its callback bag.\n */\ntype ComponentCallbacks = Omit<\n IframeManagerConfig['callbacks'],\n 'onLoad' | 'onError' | 'onTokenExpired' | 'onConnectionError'\n>\n\ntype EmbedMountParams = {\n container: HTMLElement\n controller: EmbedController\n onDestroy: (mount: EmbedMount) => void\n options: MountOptions | undefined\n renderingContext: RenderingContext\n componentCallbacks?: ComponentCallbacks\n sessionConfig: CapitalOsClientConfig\n}\n\n/**\n * Renders a single CapitalOS Experience iframe inside a container, reading the shared\n * Embed controller's session token. An Experience is fully described by its `renderingContext`\n * (entry point + per-Experience fields) and its `componentCallbacks` (parent-exposed methods);\n * this class is otherwise Experience-agnostic.\n */\nclass EmbedMount implements CapitalOsMount {\n readonly ready: Promise<void>\n\n private iframeManager: IframeManager | null = null\n private isDestroyed = false\n private isReadySettled = false\n private readonly readyDeferred = createReadyDeferred()\n private readonly unsubscribeTokenData: () => void\n private readonly unsubscribeError: () => void\n\n constructor(private readonly params: EmbedMountParams) {\n this.ready = this.readyDeferred.promise\n\n this.unsubscribeTokenData = params.controller.onTokenData((tokenData) => {\n if (tokenData) {\n this.initializeIframe(tokenData)\n }\n })\n this.unsubscribeError = params.controller.onError((error) => {\n this.handleError(error)\n })\n\n const existingTokenData = params.controller.getTokenData()\n if (existingTokenData) {\n this.initializeIframe(existingTokenData)\n }\n\n // Lazy-start auth on first mount. Failures are intentionally routed through\n // the `onError` subscription above (and `ready`), not this promise.\n void params.controller.start().catch(() => {})\n }\n\n destroy(): void {\n if (this.isDestroyed) {\n return\n }\n\n this.isDestroyed = true\n this.unsubscribeTokenData()\n this.unsubscribeError()\n this.iframeManager?.destroy()\n this.iframeManager = null\n this.params.onDestroy(this)\n\n if (!this.isReadySettled) {\n this.isReadySettled = true\n this.readyDeferred.reject(\n new CapitalOSError({\n code: ErrorCode.internal_error,\n message: 'Mount was destroyed before it finished loading',\n })\n )\n }\n }\n\n private initializeIframe(tokenData: TokenData): void {\n if (this.isDestroyed) {\n return\n }\n\n this.iframeManager?.destroy()\n this.iframeManager = new IframeManager({\n container: this.params.container,\n tokenData,\n renderingContext: this.params.renderingContext,\n theme: this.params.options?.theme ?? this.params.sessionConfig.theme,\n enableLogging: this.params.options?.enableLogging ?? this.params.sessionConfig.enableLogging ?? false,\n logger: this.params.sessionConfig.logger,\n sdkVersion: SDK_VERSION,\n heightOffsetPx: this.params.options?.heightOffsetPx ?? 12,\n callbacks: {\n onLoad: () => {\n this.resolveReady()\n this.params.options?.onLoaded?.()\n },\n onError: (rawError: RawErrorDetails) => {\n this.handleError(new CapitalOSError(rawError))\n },\n onTokenExpired: () => {\n void this.params.controller.invalidate().catch(() => {})\n },\n onConnectionError: (error: Error) => {\n this.handleError(error)\n },\n ...this.params.componentCallbacks,\n } satisfies IframeManagerConfig['callbacks'],\n })\n }\n\n private resolveReady(): void {\n if (this.isReadySettled) {\n return\n }\n\n this.isReadySettled = true\n this.readyDeferred.resolve()\n }\n\n private handleError(error: unknown): void {\n if (this.isDestroyed) {\n return\n }\n\n const capitalOsError = toCapitalOSError(error)\n\n if (!this.isReadySettled) {\n this.isReadySettled = true\n this.readyDeferred.reject(capitalOsError)\n }\n\n this.params.options?.onError?.(capitalOsError)\n }\n}\n\nclass CapitalOsClientImpl implements CapitalOsClient {\n private readonly controller: EmbedController\n private readonly mounts = new Set<EmbedMount>()\n private isDestroyed = false\n\n constructor(private readonly config: CapitalOsClientConfig) {\n this.controller = createEmbedController({\n getToken: config.getToken,\n enableLogging: config.enableLogging,\n logger: config.logger,\n sdkVersion: SDK_VERSION,\n })\n }\n\n mountCardsApp(target: string | HTMLElement, options?: MountOptions): CapitalOsMount {\n return this.createMount({\n target,\n options,\n renderingContext: { entryPoint: 'cardsApp' },\n })\n }\n\n mountBillPayApp(target: string | HTMLElement, options?: MountOptions): CapitalOsMount {\n return this.createMount({\n target,\n options,\n renderingContext: { entryPoint: 'billPayApp' },\n })\n }\n\n mountOnboarding(target: string | HTMLElement, options?: OnboardingMountOptions): CapitalOsMount {\n return this.createMount({\n target,\n options,\n renderingContext: {\n entryPoint: 'onboarding',\n onboardingEntryPoint: options?.entryPoint,\n onboardingExitPoint: options?.exitPoint,\n allowExitOnNonApproved: options?.allowExitOnNonApproved,\n product: options?.product,\n copy: options?.doneButtonText\n ? ({\n 'onboarding.doneButton': options.doneButtonText,\n 'onboarding.continueButton': options.doneButtonText,\n 'activation.successButton': options.doneButtonText,\n } satisfies CopyOverrides)\n : undefined,\n },\n // Always expose a function-valued `onDone`: the Onboarding Experience always invokes\n // this Penpal method on completion, so a missing handler would reject with METHOD_NOT_FOUND.\n componentCallbacks: { onboarding: { onDone: options?.onDone ?? (() => {}) } },\n })\n }\n\n private createMount(params: {\n target: string | HTMLElement\n options: MountOptions | undefined\n renderingContext: RenderingContext\n componentCallbacks?: ComponentCallbacks\n }): CapitalOsMount {\n if (this.isDestroyed) {\n throw new Error('CapitalOS client has been destroyed')\n }\n\n const mount = new EmbedMount({\n container: resolveTarget(params.target),\n controller: this.controller,\n onDestroy: (destroyedMount) => {\n this.mounts.delete(destroyedMount)\n },\n options: params.options,\n renderingContext: params.renderingContext,\n componentCallbacks: params.componentCallbacks,\n sessionConfig: this.config,\n })\n\n this.mounts.add(mount)\n return mount\n }\n\n destroy(): void {\n if (this.isDestroyed) {\n return\n }\n\n this.isDestroyed = true\n for (const mount of Array.from(this.mounts)) {\n mount.destroy()\n }\n this.mounts.clear()\n this.controller.destroy()\n }\n}\n\nexport function createCapitalOsClient(config: CapitalOsClientConfig): CapitalOsClient {\n return new CapitalOsClientImpl(config)\n}\n"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { CapitalOSError, CapitalOSError as CapitalOSError$1, ErrorCode, ErrorCode as ErrorCode$1, IframeManager, createEmbedController, toCapitalOSError } from "@capitalos/core";
|
|
2
2
|
|
|
3
3
|
//#region package.json
|
|
4
|
-
var version = "0.
|
|
4
|
+
var version = "0.2.0";
|
|
5
5
|
|
|
6
6
|
//#endregion
|
|
7
7
|
//#region src/capital-os-client.ts
|
|
@@ -32,7 +32,13 @@ function createReadyDeferred() {
|
|
|
32
32
|
resolve: resolveReady
|
|
33
33
|
};
|
|
34
34
|
}
|
|
35
|
-
|
|
35
|
+
/**
|
|
36
|
+
* Renders a single CapitalOS Experience iframe inside a container, reading the shared
|
|
37
|
+
* Embed controller's session token. An Experience is fully described by its `renderingContext`
|
|
38
|
+
* (entry point + per-Experience fields) and its `componentCallbacks` (parent-exposed methods);
|
|
39
|
+
* this class is otherwise Experience-agnostic.
|
|
40
|
+
*/
|
|
41
|
+
var EmbedMount = class {
|
|
36
42
|
ready;
|
|
37
43
|
iframeManager = null;
|
|
38
44
|
isDestroyed = false;
|
|
@@ -75,7 +81,7 @@ var CardsAppMount = class {
|
|
|
75
81
|
this.iframeManager = new IframeManager({
|
|
76
82
|
container: this.params.container,
|
|
77
83
|
tokenData,
|
|
78
|
-
renderingContext: this.
|
|
84
|
+
renderingContext: this.params.renderingContext,
|
|
79
85
|
theme: this.params.options?.theme ?? this.params.sessionConfig.theme,
|
|
80
86
|
enableLogging: this.params.options?.enableLogging ?? this.params.sessionConfig.enableLogging ?? false,
|
|
81
87
|
logger: this.params.sessionConfig.logger,
|
|
@@ -94,13 +100,11 @@ var CardsAppMount = class {
|
|
|
94
100
|
},
|
|
95
101
|
onConnectionError: (error) => {
|
|
96
102
|
this.handleError(error);
|
|
97
|
-
}
|
|
103
|
+
},
|
|
104
|
+
...this.params.componentCallbacks
|
|
98
105
|
}
|
|
99
106
|
});
|
|
100
107
|
}
|
|
101
|
-
getRenderingContext() {
|
|
102
|
-
return { entryPoint: "cardsApp" };
|
|
103
|
-
}
|
|
104
108
|
resolveReady() {
|
|
105
109
|
if (this.isReadySettled) return;
|
|
106
110
|
this.isReadySettled = true;
|
|
@@ -130,14 +134,49 @@ var CapitalOsClientImpl = class {
|
|
|
130
134
|
});
|
|
131
135
|
}
|
|
132
136
|
mountCardsApp(target, options) {
|
|
137
|
+
return this.createMount({
|
|
138
|
+
target,
|
|
139
|
+
options,
|
|
140
|
+
renderingContext: { entryPoint: "cardsApp" }
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
mountBillPayApp(target, options) {
|
|
144
|
+
return this.createMount({
|
|
145
|
+
target,
|
|
146
|
+
options,
|
|
147
|
+
renderingContext: { entryPoint: "billPayApp" }
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
mountOnboarding(target, options) {
|
|
151
|
+
return this.createMount({
|
|
152
|
+
target,
|
|
153
|
+
options,
|
|
154
|
+
renderingContext: {
|
|
155
|
+
entryPoint: "onboarding",
|
|
156
|
+
onboardingEntryPoint: options?.entryPoint,
|
|
157
|
+
onboardingExitPoint: options?.exitPoint,
|
|
158
|
+
allowExitOnNonApproved: options?.allowExitOnNonApproved,
|
|
159
|
+
product: options?.product,
|
|
160
|
+
copy: options?.doneButtonText ? {
|
|
161
|
+
"onboarding.doneButton": options.doneButtonText,
|
|
162
|
+
"onboarding.continueButton": options.doneButtonText,
|
|
163
|
+
"activation.successButton": options.doneButtonText
|
|
164
|
+
} : void 0
|
|
165
|
+
},
|
|
166
|
+
componentCallbacks: { onboarding: { onDone: options?.onDone ?? (() => {}) } }
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
createMount(params) {
|
|
133
170
|
if (this.isDestroyed) throw new Error("CapitalOS client has been destroyed");
|
|
134
|
-
const mount = new
|
|
135
|
-
container: resolveTarget(target),
|
|
171
|
+
const mount = new EmbedMount({
|
|
172
|
+
container: resolveTarget(params.target),
|
|
136
173
|
controller: this.controller,
|
|
137
174
|
onDestroy: (destroyedMount) => {
|
|
138
175
|
this.mounts.delete(destroyedMount);
|
|
139
176
|
},
|
|
140
|
-
options,
|
|
177
|
+
options: params.options,
|
|
178
|
+
renderingContext: params.renderingContext,
|
|
179
|
+
componentCallbacks: params.componentCallbacks,
|
|
141
180
|
sessionConfig: this.config
|
|
142
181
|
});
|
|
143
182
|
this.mounts.add(mount);
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["value: unknown","target: string | HTMLElement","resolveReady: () => void","rejectReady: (error: CapitalOSError) => void","params: CardsAppMountParams","CapitalOSError","ErrorCode","tokenData: TokenData","SDK_VERSION","rawError: RawErrorDetails","error: Error","error: unknown","config: CapitalOsClientConfig","options?: MountOptions"],"sources":["../package.json","../src/capital-os-client.ts"],"sourcesContent":["{\n \"name\": \"@capitalos/js\",\n \"version\": \"0.1.0\",\n \"description\": \"Framework-agnostic JavaScript 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 \"package.json\",\n \"README.md\"\n ],\n \"scripts\": {\n \"build\": \"tsdown\",\n \"dev\": \"tsdown --watch\",\n \"lint\": \"eslint src test --ext .ts --fix\",\n \"lint-ci\": \"eslint src test --ext .ts\",\n \"test\": \"vitest run\",\n \"test:pack\": \"pnpm build && pnpm pack\",\n \"prepublishOnly\": \"pnpm build\"\n },\n \"keywords\": [\n \"capitalos\",\n \"javascript\",\n \"sdk\",\n \"iframe\"\n ],\n \"homepage\": \"https://docs.capitalos.com/docs/using-vanilla-js-client-library\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/CapitalOS/theboss\",\n \"directory\": \"sdk/js\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/CapitalOS/theboss/issues\"\n },\n \"author\": \"CapitalOS\",\n \"license\": \"MIT\",\n \"dependencies\": {\n \"@capitalos/core\": \"workspace:*\"\n },\n \"devDependencies\": {\n \"@types/iframe-resizer\": \"^3.5.13\",\n \"@typescript-eslint/eslint-plugin\": \"^7.2.0\",\n \"@typescript-eslint/parser\": \"^7.2.0\",\n \"eslint\": \"^8.57.0\",\n \"eslint-config-prettier\": \"^9.1.0\",\n \"eslint-plugin-prettier\": \"^5.1.3\",\n \"prettier\": \"^3.2.5\",\n \"tsdown\": \"^0.9.2\",\n \"typescript\": \"^5.6.2\",\n \"vitest\": \"^4.1.0\"\n }\n}\n","import {\n CapitalOSError,\n ErrorCode,\n IframeManager,\n createEmbedController,\n toCapitalOSError,\n type EmbedController,\n type IframeManagerConfig,\n type RawErrorDetails,\n type RenderingContext,\n type TokenData,\n} from '@capitalos/core'\n\nimport { version as SDK_VERSION } from '../package.json'\nimport type { CapitalOsClient, CapitalOsClientConfig, CapitalOsMount, MountOptions } from './types'\n\nfunction isHTMLElement(value: unknown): value is HTMLElement {\n return typeof HTMLElement !== 'undefined' && value instanceof HTMLElement\n}\n\nfunction resolveTarget(target: string | HTMLElement): HTMLElement {\n if (typeof target === 'string') {\n if (typeof document === 'undefined') {\n throw new Error('Cannot resolve selector because document is unavailable')\n }\n\n const element = document.querySelector(target)\n if (!isHTMLElement(element)) {\n throw new Error(`No HTMLElement found for selector \"${target}\"`)\n }\n\n return element\n }\n\n if (!isHTMLElement(target)) {\n throw new Error('target must be a CSS selector string or HTMLElement')\n }\n\n return target\n}\n\ntype ReadyDeferred = {\n promise: Promise<void>\n reject: (error: CapitalOSError) => void\n resolve: () => void\n}\n\nfunction createReadyDeferred(): ReadyDeferred {\n let resolveReady: () => void = () => {}\n let rejectReady: (error: CapitalOSError) => void = () => {}\n\n const promise = new Promise<void>((resolve, reject) => {\n resolveReady = resolve\n rejectReady = reject\n })\n\n // `ready` is optional for callers; attach a no-op catch so a rejection never\n // surfaces as an unhandledrejection when nobody awaits it.\n promise.catch(() => {})\n\n return {\n promise,\n reject: rejectReady,\n resolve: resolveReady,\n }\n}\n\ntype CardsAppMountParams = {\n container: HTMLElement\n controller: EmbedController\n onDestroy: (mount: CardsAppMount) => void\n options: MountOptions | undefined\n sessionConfig: CapitalOsClientConfig\n}\n\nclass CardsAppMount implements CapitalOsMount {\n readonly ready: Promise<void>\n\n private iframeManager: IframeManager | null = null\n private isDestroyed = false\n private isReadySettled = false\n private readonly readyDeferred = createReadyDeferred()\n private readonly unsubscribeTokenData: () => void\n private readonly unsubscribeError: () => void\n\n constructor(private readonly params: CardsAppMountParams) {\n this.ready = this.readyDeferred.promise\n\n this.unsubscribeTokenData = params.controller.onTokenData((tokenData) => {\n if (tokenData) {\n this.initializeIframe(tokenData)\n }\n })\n this.unsubscribeError = params.controller.onError((error) => {\n this.handleError(error)\n })\n\n const existingTokenData = params.controller.getTokenData()\n if (existingTokenData) {\n this.initializeIframe(existingTokenData)\n }\n\n // Lazy-start auth on first mount. Failures are intentionally routed through\n // the `onError` subscription above (and `ready`), not this promise.\n void params.controller.start().catch(() => {})\n }\n\n destroy(): void {\n if (this.isDestroyed) {\n return\n }\n\n this.isDestroyed = true\n this.unsubscribeTokenData()\n this.unsubscribeError()\n this.iframeManager?.destroy()\n this.iframeManager = null\n this.params.onDestroy(this)\n\n if (!this.isReadySettled) {\n this.isReadySettled = true\n this.readyDeferred.reject(\n new CapitalOSError({\n code: ErrorCode.internal_error,\n message: 'Mount was destroyed before it finished loading',\n })\n )\n }\n }\n\n private initializeIframe(tokenData: TokenData): void {\n if (this.isDestroyed) {\n return\n }\n\n this.iframeManager?.destroy()\n this.iframeManager = new IframeManager({\n container: this.params.container,\n tokenData,\n renderingContext: this.getRenderingContext(),\n theme: this.params.options?.theme ?? this.params.sessionConfig.theme,\n enableLogging: this.params.options?.enableLogging ?? this.params.sessionConfig.enableLogging ?? false,\n logger: this.params.sessionConfig.logger,\n sdkVersion: SDK_VERSION,\n heightOffsetPx: this.params.options?.heightOffsetPx ?? 12,\n callbacks: {\n onLoad: () => {\n this.resolveReady()\n this.params.options?.onLoaded?.()\n },\n onError: (rawError: RawErrorDetails) => {\n this.handleError(new CapitalOSError(rawError))\n },\n onTokenExpired: () => {\n void this.params.controller.invalidate().catch(() => {})\n },\n onConnectionError: (error: Error) => {\n this.handleError(error)\n },\n } satisfies IframeManagerConfig['callbacks'],\n })\n }\n\n private getRenderingContext(): RenderingContext {\n return { entryPoint: 'cardsApp' }\n }\n\n private resolveReady(): void {\n if (this.isReadySettled) {\n return\n }\n\n this.isReadySettled = true\n this.readyDeferred.resolve()\n }\n\n private handleError(error: unknown): void {\n if (this.isDestroyed) {\n return\n }\n\n const capitalOsError = toCapitalOSError(error)\n\n if (!this.isReadySettled) {\n this.isReadySettled = true\n this.readyDeferred.reject(capitalOsError)\n }\n\n this.params.options?.onError?.(capitalOsError)\n }\n}\n\nclass CapitalOsClientImpl implements CapitalOsClient {\n private readonly controller: EmbedController\n private readonly mounts = new Set<CardsAppMount>()\n private isDestroyed = false\n\n constructor(private readonly config: CapitalOsClientConfig) {\n this.controller = createEmbedController({\n getToken: config.getToken,\n enableLogging: config.enableLogging,\n logger: config.logger,\n sdkVersion: SDK_VERSION,\n })\n }\n\n mountCardsApp(target: string | HTMLElement, options?: MountOptions): CapitalOsMount {\n if (this.isDestroyed) {\n throw new Error('CapitalOS client has been destroyed')\n }\n\n const mount = new CardsAppMount({\n container: resolveTarget(target),\n controller: this.controller,\n onDestroy: (destroyedMount) => {\n this.mounts.delete(destroyedMount)\n },\n options,\n sessionConfig: this.config,\n })\n\n this.mounts.add(mount)\n return mount\n }\n\n destroy(): void {\n if (this.isDestroyed) {\n return\n }\n\n this.isDestroyed = true\n for (const mount of Array.from(this.mounts)) {\n mount.destroy()\n }\n this.mounts.clear()\n this.controller.destroy()\n }\n}\n\nexport function createCapitalOsClient(config: CapitalOsClientConfig): CapitalOsClient {\n return new CapitalOsClientImpl(config)\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["value: unknown","target: string | HTMLElement","resolveReady: () => void","rejectReady: (error: CapitalOSError) => void","params: EmbedMountParams","CapitalOSError","ErrorCode","tokenData: TokenData","SDK_VERSION","rawError: RawErrorDetails","error: Error","error: unknown","config: CapitalOsClientConfig","options?: MountOptions","options?: OnboardingMountOptions","params: {\n target: string | HTMLElement\n options: MountOptions | undefined\n renderingContext: RenderingContext\n componentCallbacks?: ComponentCallbacks\n }"],"sources":["../package.json","../src/capital-os-client.ts"],"sourcesContent":["{\n \"name\": \"@capitalos/js\",\n \"version\": \"0.2.0\",\n \"description\": \"Framework-agnostic JavaScript 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 \"package.json\",\n \"README.md\"\n ],\n \"scripts\": {\n \"build\": \"tsdown\",\n \"dev\": \"tsdown --watch\",\n \"lint\": \"eslint src test --ext .ts --fix\",\n \"lint-ci\": \"eslint src test --ext .ts\",\n \"test\": \"vitest run\",\n \"test:pack\": \"pnpm build && pnpm pack\",\n \"prepublishOnly\": \"pnpm build\"\n },\n \"keywords\": [\n \"capitalos\",\n \"javascript\",\n \"sdk\",\n \"iframe\"\n ],\n \"homepage\": \"https://docs.capitalos.com/docs/using-vanilla-js-client-library\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/CapitalOS/theboss\",\n \"directory\": \"sdk/js\"\n },\n \"bugs\": {\n \"url\": \"https://github.com/CapitalOS/theboss/issues\"\n },\n \"author\": \"CapitalOS\",\n \"license\": \"MIT\",\n \"dependencies\": {\n \"@capitalos/core\": \"workspace:*\"\n },\n \"devDependencies\": {\n \"@types/iframe-resizer\": \"^3.5.13\",\n \"@typescript-eslint/eslint-plugin\": \"^7.2.0\",\n \"@typescript-eslint/parser\": \"^7.2.0\",\n \"eslint\": \"^8.57.0\",\n \"eslint-config-prettier\": \"^9.1.0\",\n \"eslint-plugin-prettier\": \"^5.1.3\",\n \"prettier\": \"^3.2.5\",\n \"tsdown\": \"^0.9.2\",\n \"typescript\": \"^5.6.2\",\n \"vitest\": \"^4.1.0\"\n }\n}\n","import {\n CapitalOSError,\n ErrorCode,\n IframeManager,\n createEmbedController,\n toCapitalOSError,\n type CopyOverrides,\n type EmbedController,\n type IframeManagerConfig,\n type RawErrorDetails,\n type RenderingContext,\n type TokenData,\n} from '@capitalos/core'\n\nimport { version as SDK_VERSION } from '../package.json'\nimport type {\n CapitalOsClient,\n CapitalOsClientConfig,\n CapitalOsMount,\n MountOptions,\n OnboardingMountOptions,\n} from './types'\n\nfunction isHTMLElement(value: unknown): value is HTMLElement {\n return typeof HTMLElement !== 'undefined' && value instanceof HTMLElement\n}\n\nfunction resolveTarget(target: string | HTMLElement): HTMLElement {\n if (typeof target === 'string') {\n if (typeof document === 'undefined') {\n throw new Error('Cannot resolve selector because document is unavailable')\n }\n\n const element = document.querySelector(target)\n if (!isHTMLElement(element)) {\n throw new Error(`No HTMLElement found for selector \"${target}\"`)\n }\n\n return element\n }\n\n if (!isHTMLElement(target)) {\n throw new Error('target must be a CSS selector string or HTMLElement')\n }\n\n return target\n}\n\ntype ReadyDeferred = {\n promise: Promise<void>\n reject: (error: CapitalOSError) => void\n resolve: () => void\n}\n\nfunction createReadyDeferred(): ReadyDeferred {\n let resolveReady: () => void = () => {}\n let rejectReady: (error: CapitalOSError) => void = () => {}\n\n const promise = new Promise<void>((resolve, reject) => {\n resolveReady = resolve\n rejectReady = reject\n })\n\n // `ready` is optional for callers; attach a no-op catch so a rejection never\n // surfaces as an unhandledrejection when nobody awaits it.\n promise.catch(() => {})\n\n return {\n promise,\n reject: rejectReady,\n resolve: resolveReady,\n }\n}\n\n/**\n * Experience-specific Penpal methods the parent exposes to the iframe (e.g. `onboarding.onDone`).\n * Derived from the core `IframeManager` callback surface minus the lifecycle callbacks the mount\n * owns itself, so new Experiences are picked up automatically as core grows its callback bag.\n */\ntype ComponentCallbacks = Omit<\n IframeManagerConfig['callbacks'],\n 'onLoad' | 'onError' | 'onTokenExpired' | 'onConnectionError'\n>\n\ntype EmbedMountParams = {\n container: HTMLElement\n controller: EmbedController\n onDestroy: (mount: EmbedMount) => void\n options: MountOptions | undefined\n renderingContext: RenderingContext\n componentCallbacks?: ComponentCallbacks\n sessionConfig: CapitalOsClientConfig\n}\n\n/**\n * Renders a single CapitalOS Experience iframe inside a container, reading the shared\n * Embed controller's session token. An Experience is fully described by its `renderingContext`\n * (entry point + per-Experience fields) and its `componentCallbacks` (parent-exposed methods);\n * this class is otherwise Experience-agnostic.\n */\nclass EmbedMount implements CapitalOsMount {\n readonly ready: Promise<void>\n\n private iframeManager: IframeManager | null = null\n private isDestroyed = false\n private isReadySettled = false\n private readonly readyDeferred = createReadyDeferred()\n private readonly unsubscribeTokenData: () => void\n private readonly unsubscribeError: () => void\n\n constructor(private readonly params: EmbedMountParams) {\n this.ready = this.readyDeferred.promise\n\n this.unsubscribeTokenData = params.controller.onTokenData((tokenData) => {\n if (tokenData) {\n this.initializeIframe(tokenData)\n }\n })\n this.unsubscribeError = params.controller.onError((error) => {\n this.handleError(error)\n })\n\n const existingTokenData = params.controller.getTokenData()\n if (existingTokenData) {\n this.initializeIframe(existingTokenData)\n }\n\n // Lazy-start auth on first mount. Failures are intentionally routed through\n // the `onError` subscription above (and `ready`), not this promise.\n void params.controller.start().catch(() => {})\n }\n\n destroy(): void {\n if (this.isDestroyed) {\n return\n }\n\n this.isDestroyed = true\n this.unsubscribeTokenData()\n this.unsubscribeError()\n this.iframeManager?.destroy()\n this.iframeManager = null\n this.params.onDestroy(this)\n\n if (!this.isReadySettled) {\n this.isReadySettled = true\n this.readyDeferred.reject(\n new CapitalOSError({\n code: ErrorCode.internal_error,\n message: 'Mount was destroyed before it finished loading',\n })\n )\n }\n }\n\n private initializeIframe(tokenData: TokenData): void {\n if (this.isDestroyed) {\n return\n }\n\n this.iframeManager?.destroy()\n this.iframeManager = new IframeManager({\n container: this.params.container,\n tokenData,\n renderingContext: this.params.renderingContext,\n theme: this.params.options?.theme ?? this.params.sessionConfig.theme,\n enableLogging: this.params.options?.enableLogging ?? this.params.sessionConfig.enableLogging ?? false,\n logger: this.params.sessionConfig.logger,\n sdkVersion: SDK_VERSION,\n heightOffsetPx: this.params.options?.heightOffsetPx ?? 12,\n callbacks: {\n onLoad: () => {\n this.resolveReady()\n this.params.options?.onLoaded?.()\n },\n onError: (rawError: RawErrorDetails) => {\n this.handleError(new CapitalOSError(rawError))\n },\n onTokenExpired: () => {\n void this.params.controller.invalidate().catch(() => {})\n },\n onConnectionError: (error: Error) => {\n this.handleError(error)\n },\n ...this.params.componentCallbacks,\n } satisfies IframeManagerConfig['callbacks'],\n })\n }\n\n private resolveReady(): void {\n if (this.isReadySettled) {\n return\n }\n\n this.isReadySettled = true\n this.readyDeferred.resolve()\n }\n\n private handleError(error: unknown): void {\n if (this.isDestroyed) {\n return\n }\n\n const capitalOsError = toCapitalOSError(error)\n\n if (!this.isReadySettled) {\n this.isReadySettled = true\n this.readyDeferred.reject(capitalOsError)\n }\n\n this.params.options?.onError?.(capitalOsError)\n }\n}\n\nclass CapitalOsClientImpl implements CapitalOsClient {\n private readonly controller: EmbedController\n private readonly mounts = new Set<EmbedMount>()\n private isDestroyed = false\n\n constructor(private readonly config: CapitalOsClientConfig) {\n this.controller = createEmbedController({\n getToken: config.getToken,\n enableLogging: config.enableLogging,\n logger: config.logger,\n sdkVersion: SDK_VERSION,\n })\n }\n\n mountCardsApp(target: string | HTMLElement, options?: MountOptions): CapitalOsMount {\n return this.createMount({\n target,\n options,\n renderingContext: { entryPoint: 'cardsApp' },\n })\n }\n\n mountBillPayApp(target: string | HTMLElement, options?: MountOptions): CapitalOsMount {\n return this.createMount({\n target,\n options,\n renderingContext: { entryPoint: 'billPayApp' },\n })\n }\n\n mountOnboarding(target: string | HTMLElement, options?: OnboardingMountOptions): CapitalOsMount {\n return this.createMount({\n target,\n options,\n renderingContext: {\n entryPoint: 'onboarding',\n onboardingEntryPoint: options?.entryPoint,\n onboardingExitPoint: options?.exitPoint,\n allowExitOnNonApproved: options?.allowExitOnNonApproved,\n product: options?.product,\n copy: options?.doneButtonText\n ? ({\n 'onboarding.doneButton': options.doneButtonText,\n 'onboarding.continueButton': options.doneButtonText,\n 'activation.successButton': options.doneButtonText,\n } satisfies CopyOverrides)\n : undefined,\n },\n // Always expose a function-valued `onDone`: the Onboarding Experience always invokes\n // this Penpal method on completion, so a missing handler would reject with METHOD_NOT_FOUND.\n componentCallbacks: { onboarding: { onDone: options?.onDone ?? (() => {}) } },\n })\n }\n\n private createMount(params: {\n target: string | HTMLElement\n options: MountOptions | undefined\n renderingContext: RenderingContext\n componentCallbacks?: ComponentCallbacks\n }): CapitalOsMount {\n if (this.isDestroyed) {\n throw new Error('CapitalOS client has been destroyed')\n }\n\n const mount = new EmbedMount({\n container: resolveTarget(params.target),\n controller: this.controller,\n onDestroy: (destroyedMount) => {\n this.mounts.delete(destroyedMount)\n },\n options: params.options,\n renderingContext: params.renderingContext,\n componentCallbacks: params.componentCallbacks,\n sessionConfig: this.config,\n })\n\n this.mounts.add(mount)\n return mount\n }\n\n destroy(): void {\n if (this.isDestroyed) {\n return\n }\n\n this.isDestroyed = true\n for (const mount of Array.from(this.mounts)) {\n mount.destroy()\n }\n this.mounts.clear()\n this.controller.destroy()\n }\n}\n\nexport function createCapitalOsClient(config: CapitalOsClientConfig): CapitalOsClient {\n return new CapitalOsClientImpl(config)\n}\n"],"mappings":"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"}
|