@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 CHANGED
@@ -1,51 +1,57 @@
1
1
  # CapitalOS for JavaScript
2
2
 
3
- Framework-agnostic JavaScript SDK for embedding CapitalOS Experiences without a dedicated framework package.
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
- Create one CapitalOS client with a backend-backed one-time-token callback, then mount the Cards App into a DOM element.
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
- const response = await fetch('/api/capitalos/token', { method: 'POST' })
25
- const data = await response.json()
26
- return data.token
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 cards = capitalOs.mountCardsApp('#capitalos-cards', {
31
- onLoaded: () => {
32
- // Hide your loading UI.
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
- // Later, when navigating away or closing a modal:
41
- cards.destroy()
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
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/capital-os-client.ts"],"sourcesContent":null,"mappings":";;;UAEiB,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;AAKT,UAAA,cAAA,CACR;EAIQ,KAAA,EAJR,OAIQ,CAAA,IAAe,CAAA;EAAA,OAAA,EAAA,EAAA,IAAA;;AACwB,UADvC,eAAA,CACuC;EAAY,aAAG,CAAA,MAAA,EAAA,MAAA,GAAtC,WAAsC,EAAA,OAAA,CAAA,EAAf,YAAe,CAAA,EAAA,cAAA;EAAc,OAAA,EAAA,EAAA,IAAA;;;;;iBCwNrE,qBAAA,SAA8B,wBAAwB"}
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
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/capital-os-client.ts"],"sourcesContent":null,"mappings":";;;UAEiB,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;AAKT,UAAA,cAAA,CACR;EAIQ,KAAA,EAJR,OAIQ,CAAA,IAAe,CAAA;EAAA,OAAA,EAAA,EAAA,IAAA;;AACwB,UADvC,eAAA,CACuC;EAAY,aAAG,CAAA,MAAA,EAAA,MAAA,GAAtC,WAAsC,EAAA,OAAA,CAAA,EAAf,YAAe,CAAA,EAAA,cAAA;EAAc,OAAA,EAAA,EAAA,IAAA;;;;;iBCwNrE,qBAAA,SAA8B,wBAAwB"}
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.1.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
- var CardsAppMount = class {
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.getRenderingContext(),
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 CardsAppMount({
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.1.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
- var CardsAppMount = class {
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.getRenderingContext(),
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 CardsAppMount({
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);
@@ -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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@capitalos/js",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Framework-agnostic JavaScript SDK for CapitalOS",
5
5
  "main": "dist/index.js",
6
6
  "module": "dist/index.mjs",