@worthai/onboarding-sdk 2.0.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.
Files changed (34) hide show
  1. package/README.md +282 -0
  2. package/dist/index.d.ts +2 -0
  3. package/dist/index.js +200 -0
  4. package/dist/index.js.map +1 -0
  5. package/dist/interfaces/AnyMessage.d.ts +4 -0
  6. package/dist/interfaces/ChildSite.d.ts +2 -0
  7. package/dist/interfaces/ChildSiteMessage.d.ts +8 -0
  8. package/dist/interfaces/ChildSiteSubscription.d.ts +2 -0
  9. package/dist/interfaces/IframeMode.d.ts +1 -0
  10. package/dist/interfaces/IframeQueryParams.d.ts +1 -0
  11. package/dist/interfaces/OnboardingApp.d.ts +2 -0
  12. package/dist/interfaces/OnboardingAppAppSubscription.d.ts +2 -0
  13. package/dist/interfaces/OnboardingAppMessage.d.ts +24 -0
  14. package/dist/interfaces/OnboardingAppMode.d.ts +2 -0
  15. package/dist/interfaces/OnboardingAppSubscription.d.ts +2 -0
  16. package/dist/interfaces/OnboardingConsumerApp.d.ts +2 -0
  17. package/dist/interfaces/OnboardingConsumerAppMessage.d.ts +13 -0
  18. package/dist/interfaces/ParentSite.d.ts +2 -0
  19. package/dist/interfaces/ParentSiteMessage.d.ts +21 -0
  20. package/dist/interfaces/ParentSiteSubscription.d.ts +2 -0
  21. package/dist/interfaces/QueryParams.d.ts +1 -0
  22. package/dist/interfaces/StageNavigation.d.ts +19 -0
  23. package/dist/interfaces/index.d.ts +18 -0
  24. package/dist/mockServiceWorker.js +349 -0
  25. package/dist/mocks/browser.d.ts +1 -0
  26. package/dist/mocks/handlers/authHandler.d.ts +1 -0
  27. package/dist/mocks/handlers/index.d.ts +1 -0
  28. package/dist/utils/createChildSite/index.d.ts +17 -0
  29. package/dist/utils/createOnboardingApp/index.d.ts +18 -0
  30. package/dist/utils/createOnboardingConsumerApp/index.d.ts +11 -0
  31. package/dist/utils/createParentSite/index.d.ts +12 -0
  32. package/dist/utils/getURLQueryParams/index.d.ts +2 -0
  33. package/dist/utils/index.d.ts +4 -0
  34. package/package.json +48 -0
package/README.md ADDED
@@ -0,0 +1,282 @@
1
+ # @joinworth/onboarding-sdk
2
+
3
+ A TypeScript SDK for seamless cross-origin communication between parent and child sites using iframe postMessage API. This SDK provides type-safe utilities for building onboarding flows with secure message passing between applications.
4
+
5
+ ## 📦 Installation
6
+
7
+ ### Within the monorepo
8
+
9
+ ```bash
10
+ # Add as workspace dependency
11
+ pnpm add @joinworth/onboarding-sdk@workspace:*
12
+ ```
13
+
14
+ ### External projects
15
+
16
+ ```bash
17
+ # Using npm
18
+ npm install @joinworth/onboarding-sdk
19
+
20
+ # Using pnpm
21
+ pnpm install @joinworth/onboarding-sdk
22
+ ```
23
+
24
+ ## 🛠️ API Reference
25
+
26
+ ### Onboarding-Specific Utilities
27
+
28
+ #### `createOnboardingApp(args)`
29
+
30
+ Creates an onboarding app instance for parent-to-child communication with type-safe onboarding messages.
31
+
32
+ > **Note**: The following example is illustrative code for vanilla TypeScript. Adapt to your framework's patterns when using React or other JavaScript frameworks.
33
+
34
+ ```typescript
35
+ import { createOnboardingApp } from '@joinworth/onboarding-sdk';
36
+
37
+ // Create an onboarding app instance and communicate with it
38
+ const onboardingApp = createOnboardingApp({
39
+ origin: 'https://onboarding-app.com',
40
+ inviteToken: 'YOUR_INVITE_TOKEN',
41
+ mode: 'embedded',
42
+ });
43
+
44
+ // Listen for onboarding messages
45
+ const subscription = onboardingApp.subscribe((event) => {
46
+ if (event.data.type === 'ROUTE_URL') {
47
+ console.log('Current route:', event.data.payload.url);
48
+ }
49
+ });
50
+
51
+ // ⚠️ Important: Remember to unsubscribe when component unmounts or cleanup is needed
52
+ // subscription.unsubscribe();
53
+
54
+ // Append the onboarding app iframe
55
+ document
56
+ .getElementById('onboarding-container')
57
+ .appendChild(onboardingApp.iframe);
58
+
59
+ // Adding event listeners to Prev and Next buttons (for vanilla JS)
60
+
61
+ const prevButton = document.getElementById('onboarding-prev');
62
+ const nextButton = document.getElementById('onboarding-next');
63
+
64
+ // Next stage navigation command
65
+ prevButton && prevButton.addEventListener('click', () => onboardingApp.prev());
66
+
67
+ // Prev stage navigation command
68
+ nextButton && nextButton.addEventListener('click', () => onboardingApp.next());
69
+
70
+ // In your HTML:
71
+ // <div id="onboarding-container"></div>
72
+ // <button id="onboarding-prev">Back</button>
73
+ // <button id="onboarding-next">Next</button>
74
+ ```
75
+
76
+ #### `createOnboardingConsumerApp(args)`
77
+
78
+ Creates an onboarding consumer app instance for child-to-parent communication with type-safe onboarding messages.
79
+
80
+ > **Note**: The following example is illustrative code for vanilla TypeScript. Adapt to your framework's patterns when using React or other JavaScript frameworks.
81
+
82
+ ```typescript
83
+ import { createOnboardingConsumerApp } from '@joinworth/onboarding-sdk';
84
+
85
+ // Create an onboarding consumer app instance for child-to-parent communication
86
+ const consumerApp = createOnboardingConsumerApp({
87
+ allowedOrigins: ["http://localhost:6006", "http://localhost:7003"],
88
+ });
89
+
90
+ // Send onboarding messages to parent
91
+ consumerApp.postMessage({
92
+ type: "ROUTE_URL";
93
+ payload: {
94
+ url: '/getting-started';
95
+ };
96
+ });
97
+
98
+ // Listen for messages from parent
99
+ const subscription = consumerApp.subscribe((event) => {
100
+ if (event.data.type === 'NEXT_STAGE') {
101
+ // Navigate to next stage
102
+ }
103
+ });
104
+
105
+ // ⚠️ Important: Remember to unsubscribe when component unmounts or cleanup is needed
106
+ // subscription.unsubscribe();
107
+ ```
108
+
109
+ ## 📋 Message Types
110
+
111
+ ### OnboardingAppMessage Events (Child → Parent)
112
+
113
+ | Event Type | Description |
114
+ | -------------------- | ---------------------------------------------------------------------- |
115
+ | `ROUTE_URL` | Notifies parent site of current route changes in onboarding app |
116
+ | `IFRAME_RESIZE` | Notifies parent site when the iframe needs to resize (includes height) |
117
+ | `IFRAME_INITIALIZED` | Notifies parent site when the iframe has finished initializing |
118
+
119
+ ### OnboardingConsumerAppMessage Events (Parent → Child)
120
+
121
+ | Event Type | Description |
122
+ | -------------------------------- | ------------------------------------------------------------------------------ |
123
+ | `NEXT_STAGE` | Navigates the onboarding app to the next stage |
124
+ | `PREV_STAGE` | Navigates the onboarding app to the previous stage |
125
+ | `SKIP_STAGE` | Skips the current stage in the onboarding app |
126
+ | `INVITE_TOKEN_SENT` | Notifies child site that an invite token has been sent (includes token) |
127
+ | `IFRAME_RENDERED_ON_PARENT_SITE` | Notifies child site that the iframe has been rendered on the parent site |
128
+ | `REFRESH_SIZE` | Requests the child site to refresh its size |
129
+ | `IFRAME_INITIALIZED` | Notifies child site that the iframe has been initialized (includes mode) |
130
+ | `SET_CUSTOM_CSS` | Sets custom CSS styles for the iframe (includes CSS string) |
131
+ | `SET_IFRAME_MODE` | Sets the iframe mode (includes mode: 'embedded', 'non-embedded', or undefined) |
132
+
133
+ ## 🔧 Usage Examples
134
+
135
+ > **Note**: The following examples are illustrative code for vanilla TypeScript. If you're using React or other JavaScript frameworks, the code should be adapted to the corresponding framework patterns (hooks, lifecycle methods, etc.).
136
+
137
+ ### Parent Site Implementation
138
+
139
+ ```typescript
140
+ import { createOnboardingApp } from '@joinworth/onboarding-sdk';
141
+
142
+ // Initialize the onboarding app
143
+ const onboardingApp = createOnboardingApp({
144
+ origin: 'https://onboarding.example.com',
145
+ });
146
+
147
+ // Mount the iframe
148
+ document
149
+ .getElementById('onboarding-container')
150
+ ?.appendChild(onboardingApp.iframe);
151
+
152
+ // Listen for route changes
153
+ const subscription = onboardingApp.subscribe((event) => {
154
+ if (event.data.type === 'ROUTE_URL') {
155
+ //Some logic here...
156
+ }
157
+ });
158
+
159
+ // ⚠️ Important: Remember to unsubscribe when component unmounts or cleanup is needed
160
+ // subscription.unsubscribe();
161
+
162
+ // Next stage navigation command
163
+ onboardingApp.next();
164
+
165
+ // Prev stage navigation command
166
+ onboardingApp.prev();
167
+ ```
168
+
169
+ ### Child Site Implementation
170
+
171
+ ```typescript
172
+ import { createOnboardingConsumerApp } from '@joinworth/onboarding-sdk';
173
+
174
+ // Initialize the consumer app
175
+ const consumerApp = createOnboardingConsumerApp({
176
+ allowedOrigins: ['https://parent.example.com'],
177
+ });
178
+
179
+ // Listen for parent commands
180
+ const subscription = consumerApp.subscribe((event) => {
181
+ if (event.data.type === 'NEXT_STAGE') {
182
+ // Add some logic here...
183
+ }
184
+ });
185
+
186
+ // ⚠️ Important: Remember to unsubscribe when component unmounts or cleanup is needed
187
+ // subscription.unsubscribe();
188
+
189
+ // Send form data to parent
190
+ onboardingConsumerApp.postMessage({
191
+ type: 'ROUTE_URL',
192
+ payload: { url: '/some-route-here' },
193
+ });
194
+ ```
195
+
196
+ ### Custom Raw CSS
197
+
198
+ The SDK allows parent sites to inject custom CSS styles into the child iframe. This is useful for customizing the appearance of the onboarding flow to match your brand or design requirements.
199
+
200
+ #### Parent Site: Sending Custom CSS
201
+
202
+ ```typescript
203
+ import { createOnboardingApp } from '@joinworth/onboarding-sdk';
204
+
205
+ const onboardingApp = createOnboardingApp({
206
+ origin: 'https://onboarding.example.com',
207
+ });
208
+
209
+ // Send custom CSS to the child iframe
210
+ onboardingApp.postMessage({
211
+ type: 'SET_CUSTOM_CSS',
212
+ payload: {
213
+ css: `
214
+ body {
215
+ background: #f0f0f0;
216
+ font-family: 'Custom Font', sans-serif;
217
+ }
218
+ .onboarding-container {
219
+ padding: 20px;
220
+ border-radius: 8px;
221
+ }
222
+ `,
223
+ },
224
+ });
225
+ ```
226
+
227
+ #### Child Site: Receiving and Applying Custom CSS
228
+
229
+ ```typescript
230
+ import { createOnboardingConsumerApp } from '@joinworth/onboarding-sdk';
231
+
232
+ const consumerApp = createOnboardingConsumerApp({
233
+ allowedOrigins: ['https://parent.example.com'],
234
+ });
235
+
236
+ // Listen for custom CSS messages
237
+ const subscription = consumerApp.subscribe((event) => {
238
+ if (event.data.type === 'SET_CUSTOM_CSS') {
239
+ // Create or update a style element
240
+ let styleElement = document.getElementById('custom-css-injection');
241
+
242
+ if (!styleElement) {
243
+ styleElement = document.createElement('style');
244
+ styleElement.id = 'custom-css-injection';
245
+ document.head.appendChild(styleElement);
246
+ }
247
+
248
+ // Apply the custom CSS
249
+ styleElement.innerHTML = event.data.payload.css;
250
+ }
251
+ });
252
+
253
+ // ⚠️ Important: Remember to unsubscribe when component unmounts or cleanup is needed
254
+ // subscription.unsubscribe();
255
+ ```
256
+
257
+ > **Note**: The child site should handle CSS injection securely and validate that the CSS doesn't contain malicious content. Consider sanitizing the CSS string before applying it in production environments.
258
+
259
+ ## 🔒 Security
260
+
261
+ The SDK includes built-in security measures:
262
+
263
+ - **Origin validation**: All messages are validated against the specified origin
264
+ - **Type safety**: TypeScript ensures message structure integrity
265
+
266
+ ## 🧪 Development
267
+
268
+ ### Building the SDK
269
+
270
+ ```bash
271
+ pnpm build
272
+ ```
273
+
274
+ ### Running Storybook
275
+
276
+ ```bash
277
+ pnpm storybook
278
+ ```
279
+
280
+ ## 📝 License
281
+
282
+ Private package - All rights reserved.
@@ -0,0 +1,2 @@
1
+ export * from './interfaces';
2
+ export * from './utils';
package/dist/index.js ADDED
@@ -0,0 +1,200 @@
1
+ var __defProp = Object.defineProperty;
2
+ var __name = (target, value) => __defProp(target, "name", { value, configurable: true });
3
+ const createChildSite = /* @__PURE__ */ __name((args) => {
4
+ const { origin, initialRoute = "", mode } = args;
5
+ const queryParams = args.queryParams;
6
+ const iframe = document.createElement("iframe");
7
+ const setMode = /* @__PURE__ */ __name((mode2) => {
8
+ postMessage({
9
+ type: "SET_IFRAME_MODE",
10
+ payload: { mode: mode2 }
11
+ });
12
+ }, "setMode");
13
+ const refreshSize = /* @__PURE__ */ __name(() => {
14
+ postMessage({ type: "REFRESH_SIZE" });
15
+ }, "refreshSize");
16
+ const buildIframeSrc = /* @__PURE__ */ __name(() => {
17
+ let srcUrl;
18
+ if (initialRoute) {
19
+ srcUrl = new URL(initialRoute.replace(/^\//, ""), origin);
20
+ } else {
21
+ srcUrl = new URL(origin);
22
+ }
23
+ for (const [key, value] of Object.entries(queryParams || {})) {
24
+ if (typeof value === "string") {
25
+ srcUrl.searchParams.set(key, value);
26
+ }
27
+ }
28
+ return srcUrl;
29
+ }, "buildIframeSrc");
30
+ const subscribe = /* @__PURE__ */ __name((callback) => {
31
+ const handler = /* @__PURE__ */ __name((event) => {
32
+ if (event.origin !== origin) {
33
+ console.warn("Blocked message from unauthorized origin:", event.origin);
34
+ return;
35
+ }
36
+ callback(event);
37
+ }, "handler");
38
+ window.addEventListener("message", handler);
39
+ return {
40
+ unsubscribe: /* @__PURE__ */ __name(() => window.removeEventListener("message", handler), "unsubscribe")
41
+ };
42
+ }, "subscribe");
43
+ const initializeChildSite = /* @__PURE__ */ __name(() => {
44
+ iframe.style.width = "100%";
45
+ iframe.style.height = "100%";
46
+ iframe.style.border = "none";
47
+ iframe.src = buildIframeSrc().toString();
48
+ subscribe((event) => {
49
+ switch (event.data.type) {
50
+ case "IFRAME_RESIZE":
51
+ iframe.style.border = "none";
52
+ iframe.style.height = `${event.data.payload.height}px`;
53
+ break;
54
+ case "IFRAME_INITIALIZED":
55
+ setMode(mode);
56
+ postMessage({
57
+ type: "IFRAME_RENDERED_ON_PARENT_SITE"
58
+ });
59
+ break;
60
+ }
61
+ });
62
+ }, "initializeChildSite");
63
+ const postMessage = /* @__PURE__ */ __name((message, transfer) => {
64
+ iframe.contentWindow?.postMessage(message, origin, transfer);
65
+ }, "postMessage");
66
+ initializeChildSite();
67
+ return { iframe, subscribe, postMessage, setMode, refreshSize };
68
+ }, "createChildSite");
69
+ const createOnboardingApp = /* @__PURE__ */ __name((args) => {
70
+ const queryParams = {};
71
+ const { inviteToken, ...restArgs } = args;
72
+ const childApp = createChildSite({
73
+ ...restArgs,
74
+ queryParams
75
+ });
76
+ const postMessage = /* @__PURE__ */ __name((message) => {
77
+ childApp.postMessage(message);
78
+ }, "postMessage");
79
+ const subscribe = /* @__PURE__ */ __name((callback) => {
80
+ return childApp.subscribe(callback);
81
+ }, "subscribe");
82
+ const skip = /* @__PURE__ */ __name(() => {
83
+ postMessage({ type: "SKIP_STAGE" });
84
+ }, "skip");
85
+ const next = /* @__PURE__ */ __name(() => {
86
+ postMessage({ type: "NEXT_STAGE" });
87
+ }, "next");
88
+ const prev = /* @__PURE__ */ __name(() => {
89
+ postMessage({ type: "PREV_STAGE" });
90
+ }, "prev");
91
+ const setMode = /* @__PURE__ */ __name((mode) => {
92
+ postMessage({ type: "SET_IFRAME_MODE", payload: { mode } });
93
+ }, "setMode");
94
+ const setCustomCss = /* @__PURE__ */ __name((css) => {
95
+ postMessage({ type: "SET_CUSTOM_CSS", payload: { css } });
96
+ }, "setCustomCss");
97
+ const onIframeInitialized = /* @__PURE__ */ __name(() => {
98
+ const subscription = subscribe((event) => {
99
+ if (event.data.type === "IFRAME_INITIALIZED") {
100
+ postMessage({ type: "INVITE_TOKEN_SENT", payload: { inviteToken } });
101
+ subscription.unsubscribe();
102
+ }
103
+ });
104
+ }, "onIframeInitialized");
105
+ onIframeInitialized();
106
+ return {
107
+ subscribe,
108
+ setMode,
109
+ setCustomCss,
110
+ iframe: childApp.iframe,
111
+ next,
112
+ prev,
113
+ skip,
114
+ refreshSize: childApp.refreshSize
115
+ };
116
+ }, "createOnboardingApp");
117
+ const getURLQueryParams = /* @__PURE__ */ __name(() => {
118
+ const urlSearchParams = new URLSearchParams(window.location.search);
119
+ const queryParams = {};
120
+ urlSearchParams.forEach((value, key) => {
121
+ queryParams[key] = value;
122
+ });
123
+ return queryParams;
124
+ }, "getURLQueryParams");
125
+ const createParentSite = /* @__PURE__ */ __name((args) => {
126
+ const { allowedOrigins } = args;
127
+ const queryParams = getURLQueryParams();
128
+ const notifyHeight = /* @__PURE__ */ __name(() => {
129
+ const height = document.body.scrollHeight;
130
+ postMessage({
131
+ type: "IFRAME_RESIZE",
132
+ payload: { height }
133
+ });
134
+ }, "notifyHeight");
135
+ const postMessage = /* @__PURE__ */ __name((message) => {
136
+ for (const origin of allowedOrigins) {
137
+ window.parent.postMessage(message, origin);
138
+ }
139
+ }, "postMessage");
140
+ const subscribe = /* @__PURE__ */ __name((callback) => {
141
+ const handler = /* @__PURE__ */ __name((event) => {
142
+ if (!allowedOrigins.includes(event.origin)) {
143
+ console.warn("Blocked message from unauthorized origin:", event.origin);
144
+ return;
145
+ }
146
+ callback(event);
147
+ }, "handler");
148
+ window.addEventListener("message", handler);
149
+ return {
150
+ unsubscribe: /* @__PURE__ */ __name(() => window.removeEventListener("message", handler), "unsubscribe")
151
+ };
152
+ }, "subscribe");
153
+ const notifySize = /* @__PURE__ */ __name(() => {
154
+ notifyHeight();
155
+ }, "notifySize");
156
+ const initializeParentSite = /* @__PURE__ */ __name(() => {
157
+ const observer = new ResizeObserver(notifySize);
158
+ observer.observe(document.body);
159
+ subscribe((event) => {
160
+ switch (event.data.type) {
161
+ case "SET_IFRAME_MODE":
162
+ const mode = event.data.payload.mode;
163
+ document.body.style.overflow = mode === "embedded" ? "hidden" : "auto";
164
+ break;
165
+ case "REFRESH_SIZE":
166
+ notifySize();
167
+ break;
168
+ }
169
+ });
170
+ postMessage({ type: "IFRAME_INITIALIZED" });
171
+ }, "initializeParentSite");
172
+ initializeParentSite();
173
+ return {
174
+ notifySize,
175
+ subscribe,
176
+ postMessage,
177
+ queryParams
178
+ };
179
+ }, "createParentSite");
180
+ const createOnboardingConsumerApp = /* @__PURE__ */ __name((args) => {
181
+ const parentApp = createParentSite(args);
182
+ const postMessage = /* @__PURE__ */ __name((message) => {
183
+ parentApp.postMessage(message);
184
+ }, "postMessage");
185
+ const subscribe = /* @__PURE__ */ __name((callback) => {
186
+ return parentApp.subscribe(callback);
187
+ }, "subscribe");
188
+ return {
189
+ postMessage,
190
+ subscribe,
191
+ notifySize: parentApp.notifySize
192
+ };
193
+ }, "createOnboardingConsumerApp");
194
+ export {
195
+ createChildSite,
196
+ createOnboardingApp,
197
+ createOnboardingConsumerApp,
198
+ createParentSite
199
+ };
200
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sources":["../src/utils/createChildSite/index.ts","../src/utils/createOnboardingApp/index.ts","../src/utils/getURLQueryParams/index.ts","../src/utils/createParentSite/index.ts","../src/utils/createOnboardingConsumerApp/index.ts"],"sourcesContent":["import type { AnyMessage } from '@/interfaces/AnyMessage';\nimport type { IframeMode } from '@/interfaces/IframeMode';\nimport type { IframeQueryParams } from '@/interfaces/IframeQueryParams';\nimport type { ParentSiteMessage } from '@/interfaces/ParentSiteMessage';\n\nexport const createChildSite = (args: {\n origin: string;\n initialRoute?: string;\n queryParams?: IframeQueryParams;\n mode?: IframeMode;\n}) => {\n const { origin, initialRoute = '', mode } = args;\n const queryParams = args.queryParams;\n const iframe = document.createElement('iframe');\n\n const setMode = (mode: IframeMode) => {\n postMessage<ParentSiteMessage>({\n type: 'SET_IFRAME_MODE',\n payload: { mode },\n });\n };\n\n const refreshSize = () => {\n postMessage<ParentSiteMessage>({ type: 'REFRESH_SIZE' });\n };\n\n const buildIframeSrc = () => {\n let srcUrl: URL;\n if (initialRoute) {\n srcUrl = new URL(initialRoute.replace(/^\\//, ''), origin);\n } else {\n srcUrl = new URL(origin);\n }\n\n for (const [key, value] of Object.entries(queryParams || {})) {\n if (typeof value === 'string') {\n srcUrl.searchParams.set(key, value);\n }\n }\n\n return srcUrl;\n };\n\n const subscribe = <T extends AnyMessage>(\n callback: (event: MessageEvent<T>) => void\n ) => {\n const handler = (event: MessageEvent<T>) => {\n if (event.origin !== origin) {\n console.warn('Blocked message from unauthorized origin:', event.origin);\n return;\n }\n callback(event);\n };\n\n window.addEventListener('message', handler);\n return {\n unsubscribe: () => window.removeEventListener('message', handler),\n };\n };\n\n const initializeChildSite = () => {\n iframe.style.width = '100%';\n iframe.style.height = '100%';\n iframe.style.border = 'none';\n iframe.src = buildIframeSrc().toString();\n\n subscribe((event) => {\n switch (event.data.type) {\n case 'IFRAME_RESIZE':\n iframe.style.border = 'none';\n iframe.style.height = `${event.data.payload.height}px`;\n break;\n case 'IFRAME_INITIALIZED':\n setMode(mode);\n postMessage<ParentSiteMessage>({\n type: 'IFRAME_RENDERED_ON_PARENT_SITE',\n });\n break;\n }\n });\n };\n\n const postMessage = <TMessage extends AnyMessage>(\n message: TMessage,\n transfer?: Transferable[]\n ) => {\n iframe.contentWindow?.postMessage(message, origin, transfer);\n };\n\n initializeChildSite();\n\n return { iframe, subscribe, postMessage, setMode, refreshSize };\n};\n","import type { AnyMessage } from '@/interfaces/AnyMessage';\nimport type { IframeQueryParams } from '@/interfaces/IframeQueryParams';\nimport type { OnboardingAppMessage } from '@/interfaces/OnboardingAppMessage';\nimport type { OnboardingAppMode } from '@/interfaces/OnboardingAppMode';\nimport type { OnboardingConsumerAppMessage } from '@/interfaces/OnboardingConsumerAppMessage';\nimport { createChildSite } from '@/utils/createChildSite';\n\nexport const createOnboardingApp = (args: {\n origin: string;\n inviteToken: string;\n mode?: OnboardingAppMode;\n}) => {\n const queryParams: IframeQueryParams = {};\n const { inviteToken, ...restArgs } = args;\n const childApp = createChildSite({\n ...restArgs,\n queryParams,\n });\n\n const postMessage = (message: OnboardingConsumerAppMessage) => {\n childApp.postMessage(message as unknown as AnyMessage);\n };\n\n const subscribe = (\n callback: (event: MessageEvent<OnboardingAppMessage>) => void\n ) => {\n return childApp.subscribe(callback);\n };\n\n const skip = () => {\n postMessage({ type: 'SKIP_STAGE' });\n };\n\n const next = () => {\n postMessage({ type: 'NEXT_STAGE' });\n };\n\n const prev = () => {\n postMessage({ type: 'PREV_STAGE' });\n };\n\n const setMode = (mode: OnboardingAppMode) => {\n postMessage({ type: 'SET_IFRAME_MODE', payload: { mode } });\n };\n\n const setCustomCss = (css: string) => {\n postMessage({ type: 'SET_CUSTOM_CSS', payload: { css } });\n };\n\n const onIframeInitialized = () => {\n const subscription = subscribe((event) => {\n if (event.data.type === 'IFRAME_INITIALIZED') {\n postMessage({ type: 'INVITE_TOKEN_SENT', payload: { inviteToken } });\n subscription.unsubscribe();\n }\n });\n };\n\n onIframeInitialized();\n\n return {\n subscribe,\n setMode,\n setCustomCss,\n iframe: childApp.iframe,\n next,\n prev,\n skip,\n refreshSize: childApp.refreshSize,\n };\n};\n","import type { QueryParams } from '@/interfaces/QueryParams';\n\nexport const getURLQueryParams = <T extends QueryParams>() => {\n const urlSearchParams = new URLSearchParams(window.location.search);\n const queryParams: QueryParams = {};\n urlSearchParams.forEach((value, key) => {\n queryParams[key] = value;\n });\n\n return queryParams as T;\n};\n","import type { AnyMessage } from '@/interfaces/AnyMessage';\nimport type { ChildSiteMessage } from '@/interfaces/ChildSiteMessage';\nimport type { IframeMode } from '@/interfaces/IframeMode';\nimport type { IframeQueryParams } from '@/interfaces/IframeQueryParams';\nimport { getURLQueryParams } from '@/utils/getURLQueryParams';\n\nexport const createParentSite = (args: { allowedOrigins: string[] }) => {\n const { allowedOrigins } = args;\n const queryParams = getURLQueryParams<IframeQueryParams>();\n\n const notifyHeight = () => {\n const height = document.body.scrollHeight;\n postMessage<ChildSiteMessage>({\n type: 'IFRAME_RESIZE',\n payload: { height },\n });\n };\n\n const postMessage = <TMessage extends AnyMessage>(message: TMessage) => {\n for (const origin of allowedOrigins) {\n window.parent.postMessage(message, origin);\n }\n };\n\n const subscribe = <T extends AnyMessage>(\n callback: (event: MessageEvent<T>) => void\n ) => {\n const handler = (event: MessageEvent<T>) => {\n if (!allowedOrigins.includes(event.origin)) {\n console.warn('Blocked message from unauthorized origin:', event.origin);\n return;\n }\n callback(event);\n };\n\n window.addEventListener('message', handler);\n return {\n unsubscribe: () => window.removeEventListener('message', handler),\n };\n };\n\n const notifySize = () => {\n notifyHeight();\n };\n\n const initializeParentSite = () => {\n const observer = new ResizeObserver(notifySize);\n observer.observe(document.body);\n\n subscribe((event) => {\n switch (event.data.type) {\n case 'SET_IFRAME_MODE':\n const mode = event.data.payload.mode as IframeMode;\n document.body.style.overflow =\n mode === 'embedded' ? 'hidden' : 'auto';\n break;\n case 'REFRESH_SIZE':\n notifySize();\n break;\n default:\n break;\n }\n });\n\n postMessage<ChildSiteMessage>({ type: 'IFRAME_INITIALIZED' });\n };\n\n initializeParentSite();\n\n return {\n notifySize,\n subscribe,\n postMessage,\n queryParams,\n };\n};\n","import type { OnboardingAppMessage } from '@/interfaces/OnboardingAppMessage';\nimport type { OnboardingConsumerAppMessage } from '@/interfaces/OnboardingConsumerAppMessage';\nimport { createParentSite } from '@/utils/createParentSite';\n\nexport const createOnboardingConsumerApp = (args: {\n allowedOrigins: string[];\n}) => {\n const parentApp = createParentSite(args);\n\n const postMessage = (message: OnboardingAppMessage) => {\n parentApp.postMessage(message);\n };\n\n const subscribe = (\n callback: (event: MessageEvent<OnboardingConsumerAppMessage>) => void\n ) => {\n return parentApp.subscribe(callback);\n };\n\n return {\n postMessage,\n subscribe,\n notifySize: parentApp.notifySize,\n };\n};\n"],"names":["mode"],"mappings":"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"}
@@ -0,0 +1,4 @@
1
+ export type AnyMessage = {
2
+ type: string;
3
+ payload?: any;
4
+ };
@@ -0,0 +1,2 @@
1
+ import { createChildSite } from '../utils/createChildSite';
2
+ export type ChildSite = ReturnType<typeof createChildSite>;
@@ -0,0 +1,8 @@
1
+ export type ChildSiteMessage = {
2
+ type: 'IFRAME_RESIZE';
3
+ payload: {
4
+ height: number;
5
+ };
6
+ } | {
7
+ type: 'IFRAME_INITIALIZED';
8
+ };
@@ -0,0 +1,2 @@
1
+ import { ChildSite } from './ChildSite';
2
+ export type ChildSiteSubscription = ReturnType<ChildSite['subscribe']>;
@@ -0,0 +1 @@
1
+ export type IframeMode = 'embedded' | 'non-embedded' | undefined;
@@ -0,0 +1 @@
1
+ export type IframeQueryParams = {};
@@ -0,0 +1,2 @@
1
+ import { createOnboardingApp } from '../utils/createOnboardingApp';
2
+ export type OnboardingApp = ReturnType<typeof createOnboardingApp>;
@@ -0,0 +1,2 @@
1
+ import { OnboardingConsumerApp } from './OnboardingConsumerApp';
2
+ export type OnboardingConsumerAppSubscription = ReturnType<OnboardingConsumerApp['subscribe']>;
@@ -0,0 +1,24 @@
1
+ import { ChildSiteMessage } from './ChildSiteMessage';
2
+ import { StageNavigation } from './StageNavigation';
3
+ export type OnboardingAppMessage = {
4
+ type: 'ROUTE_URL';
5
+ payload: {
6
+ url: string;
7
+ };
8
+ } | {
9
+ type: 'AUTHENTICATING';
10
+ } | {
11
+ type: 'AUTHENTICATION_SUCCESS';
12
+ } | {
13
+ type: 'ONBOARDING_STARTED';
14
+ } | {
15
+ type: 'AUTHENTICATION_FAILED';
16
+ payload: {
17
+ error: unknown;
18
+ };
19
+ } | {
20
+ type: 'STAGE_NAVIGATION';
21
+ payload: {
22
+ stageNavigation: StageNavigation;
23
+ };
24
+ } | ChildSiteMessage;
@@ -0,0 +1,2 @@
1
+ import { IframeMode } from './IframeMode';
2
+ export type OnboardingAppMode = IframeMode;
@@ -0,0 +1,2 @@
1
+ import { OnboardingApp } from './OnboardingApp';
2
+ export type OnboardingAppSubscription = ReturnType<OnboardingApp['subscribe']>;
@@ -0,0 +1,2 @@
1
+ import { createOnboardingConsumerApp } from '../utils/createOnboardingConsumerApp';
2
+ export type OnboardingConsumerApp = ReturnType<typeof createOnboardingConsumerApp>;
@@ -0,0 +1,13 @@
1
+ import { ParentSiteMessage } from './ParentSiteMessage';
2
+ export type OnboardingConsumerAppMessage = {
3
+ type: 'NEXT_STAGE';
4
+ } | {
5
+ type: 'PREV_STAGE';
6
+ } | {
7
+ type: 'SKIP_STAGE';
8
+ } | {
9
+ type: 'INVITE_TOKEN_SENT';
10
+ payload: {
11
+ inviteToken: string;
12
+ };
13
+ } | ParentSiteMessage;
@@ -0,0 +1,2 @@
1
+ import { createParentSite } from '../utils/createParentSite';
2
+ export type ParentSite = ReturnType<typeof createParentSite>;
@@ -0,0 +1,21 @@
1
+ import { IframeMode } from './IframeMode';
2
+ export type ParentSiteMessage = {
3
+ type: 'IFRAME_RENDERED_ON_PARENT_SITE';
4
+ } | {
5
+ type: 'REFRESH_SIZE';
6
+ } | {
7
+ type: 'IFRAME_INITIALIZED';
8
+ payload: {
9
+ mode: IframeMode;
10
+ };
11
+ } | {
12
+ type: 'SET_CUSTOM_CSS';
13
+ payload: {
14
+ css: string;
15
+ };
16
+ } | {
17
+ type: 'SET_IFRAME_MODE';
18
+ payload: {
19
+ mode: IframeMode;
20
+ };
21
+ };
@@ -0,0 +1,2 @@
1
+ import { ParentSite } from './ParentSite';
2
+ export type ParentSiteSubscription = ReturnType<ParentSite['subscribe']>;
@@ -0,0 +1 @@
1
+ export type QueryParams = Record<string, string | undefined>;
@@ -0,0 +1,19 @@
1
+ export type StageNavigation = {
2
+ stage: string | undefined;
3
+ nextStatus: {
4
+ visible: boolean;
5
+ disabled: boolean;
6
+ loading: boolean;
7
+ isSubmit: boolean;
8
+ };
9
+ prevStatus: {
10
+ visible: boolean;
11
+ disabled: boolean;
12
+ loading: boolean;
13
+ };
14
+ skipStatus: {
15
+ visible: boolean;
16
+ disabled: boolean;
17
+ loading: boolean;
18
+ };
19
+ };
@@ -0,0 +1,18 @@
1
+ export * from './AnyMessage';
2
+ export * from './ChildSite';
3
+ export * from './ChildSiteMessage';
4
+ export * from './ChildSiteSubscription';
5
+ export * from './IframeMode';
6
+ export * from './IframeQueryParams';
7
+ export * from './OnboardingApp';
8
+ export * from './OnboardingAppAppSubscription';
9
+ export * from './OnboardingAppMessage';
10
+ export * from './OnboardingAppMode';
11
+ export * from './OnboardingAppSubscription';
12
+ export * from './OnboardingConsumerApp';
13
+ export * from './OnboardingConsumerAppMessage';
14
+ export * from './ParentSite';
15
+ export * from './ParentSiteMessage';
16
+ export * from './ParentSiteSubscription';
17
+ export * from './QueryParams';
18
+ export * from './StageNavigation';
@@ -0,0 +1,349 @@
1
+ /* eslint-disable */
2
+ /* tslint:disable */
3
+
4
+ /**
5
+ * Mock Service Worker.
6
+ * @see https://github.com/mswjs/msw
7
+ * - Please do NOT modify this file.
8
+ */
9
+
10
+ const PACKAGE_VERSION = '2.11.6'
11
+ const INTEGRITY_CHECKSUM = '4db4a41e972cec1b64cc569c66952d82'
12
+ const IS_MOCKED_RESPONSE = Symbol('isMockedResponse')
13
+ const activeClientIds = new Set()
14
+
15
+ addEventListener('install', function () {
16
+ self.skipWaiting()
17
+ })
18
+
19
+ addEventListener('activate', function (event) {
20
+ event.waitUntil(self.clients.claim())
21
+ })
22
+
23
+ addEventListener('message', async function (event) {
24
+ const clientId = Reflect.get(event.source || {}, 'id')
25
+
26
+ if (!clientId || !self.clients) {
27
+ return
28
+ }
29
+
30
+ const client = await self.clients.get(clientId)
31
+
32
+ if (!client) {
33
+ return
34
+ }
35
+
36
+ const allClients = await self.clients.matchAll({
37
+ type: 'window',
38
+ })
39
+
40
+ switch (event.data) {
41
+ case 'KEEPALIVE_REQUEST': {
42
+ sendToClient(client, {
43
+ type: 'KEEPALIVE_RESPONSE',
44
+ })
45
+ break
46
+ }
47
+
48
+ case 'INTEGRITY_CHECK_REQUEST': {
49
+ sendToClient(client, {
50
+ type: 'INTEGRITY_CHECK_RESPONSE',
51
+ payload: {
52
+ packageVersion: PACKAGE_VERSION,
53
+ checksum: INTEGRITY_CHECKSUM,
54
+ },
55
+ })
56
+ break
57
+ }
58
+
59
+ case 'MOCK_ACTIVATE': {
60
+ activeClientIds.add(clientId)
61
+
62
+ sendToClient(client, {
63
+ type: 'MOCKING_ENABLED',
64
+ payload: {
65
+ client: {
66
+ id: client.id,
67
+ frameType: client.frameType,
68
+ },
69
+ },
70
+ })
71
+ break
72
+ }
73
+
74
+ case 'CLIENT_CLOSED': {
75
+ activeClientIds.delete(clientId)
76
+
77
+ const remainingClients = allClients.filter((client) => {
78
+ return client.id !== clientId
79
+ })
80
+
81
+ // Unregister itself when there are no more clients
82
+ if (remainingClients.length === 0) {
83
+ self.registration.unregister()
84
+ }
85
+
86
+ break
87
+ }
88
+ }
89
+ })
90
+
91
+ addEventListener('fetch', function (event) {
92
+ const requestInterceptedAt = Date.now()
93
+
94
+ // Bypass navigation requests.
95
+ if (event.request.mode === 'navigate') {
96
+ return
97
+ }
98
+
99
+ // Opening the DevTools triggers the "only-if-cached" request
100
+ // that cannot be handled by the worker. Bypass such requests.
101
+ if (
102
+ event.request.cache === 'only-if-cached' &&
103
+ event.request.mode !== 'same-origin'
104
+ ) {
105
+ return
106
+ }
107
+
108
+ // Bypass all requests when there are no active clients.
109
+ // Prevents the self-unregistered worked from handling requests
110
+ // after it's been terminated (still remains active until the next reload).
111
+ if (activeClientIds.size === 0) {
112
+ return
113
+ }
114
+
115
+ const requestId = crypto.randomUUID()
116
+ event.respondWith(handleRequest(event, requestId, requestInterceptedAt))
117
+ })
118
+
119
+ /**
120
+ * @param {FetchEvent} event
121
+ * @param {string} requestId
122
+ * @param {number} requestInterceptedAt
123
+ */
124
+ async function handleRequest(event, requestId, requestInterceptedAt) {
125
+ const client = await resolveMainClient(event)
126
+ const requestCloneForEvents = event.request.clone()
127
+ const response = await getResponse(
128
+ event,
129
+ client,
130
+ requestId,
131
+ requestInterceptedAt,
132
+ )
133
+
134
+ // Send back the response clone for the "response:*" life-cycle events.
135
+ // Ensure MSW is active and ready to handle the message, otherwise
136
+ // this message will pend indefinitely.
137
+ if (client && activeClientIds.has(client.id)) {
138
+ const serializedRequest = await serializeRequest(requestCloneForEvents)
139
+
140
+ // Clone the response so both the client and the library could consume it.
141
+ const responseClone = response.clone()
142
+
143
+ sendToClient(
144
+ client,
145
+ {
146
+ type: 'RESPONSE',
147
+ payload: {
148
+ isMockedResponse: IS_MOCKED_RESPONSE in response,
149
+ request: {
150
+ id: requestId,
151
+ ...serializedRequest,
152
+ },
153
+ response: {
154
+ type: responseClone.type,
155
+ status: responseClone.status,
156
+ statusText: responseClone.statusText,
157
+ headers: Object.fromEntries(responseClone.headers.entries()),
158
+ body: responseClone.body,
159
+ },
160
+ },
161
+ },
162
+ responseClone.body ? [serializedRequest.body, responseClone.body] : [],
163
+ )
164
+ }
165
+
166
+ return response
167
+ }
168
+
169
+ /**
170
+ * Resolve the main client for the given event.
171
+ * Client that issues a request doesn't necessarily equal the client
172
+ * that registered the worker. It's with the latter the worker should
173
+ * communicate with during the response resolving phase.
174
+ * @param {FetchEvent} event
175
+ * @returns {Promise<Client | undefined>}
176
+ */
177
+ async function resolveMainClient(event) {
178
+ const client = await self.clients.get(event.clientId)
179
+
180
+ if (activeClientIds.has(event.clientId)) {
181
+ return client
182
+ }
183
+
184
+ if (client?.frameType === 'top-level') {
185
+ return client
186
+ }
187
+
188
+ const allClients = await self.clients.matchAll({
189
+ type: 'window',
190
+ })
191
+
192
+ return allClients
193
+ .filter((client) => {
194
+ // Get only those clients that are currently visible.
195
+ return client.visibilityState === 'visible'
196
+ })
197
+ .find((client) => {
198
+ // Find the client ID that's recorded in the
199
+ // set of clients that have registered the worker.
200
+ return activeClientIds.has(client.id)
201
+ })
202
+ }
203
+
204
+ /**
205
+ * @param {FetchEvent} event
206
+ * @param {Client | undefined} client
207
+ * @param {string} requestId
208
+ * @param {number} requestInterceptedAt
209
+ * @returns {Promise<Response>}
210
+ */
211
+ async function getResponse(event, client, requestId, requestInterceptedAt) {
212
+ // Clone the request because it might've been already used
213
+ // (i.e. its body has been read and sent to the client).
214
+ const requestClone = event.request.clone()
215
+
216
+ function passthrough() {
217
+ // Cast the request headers to a new Headers instance
218
+ // so the headers can be manipulated with.
219
+ const headers = new Headers(requestClone.headers)
220
+
221
+ // Remove the "accept" header value that marked this request as passthrough.
222
+ // This prevents request alteration and also keeps it compliant with the
223
+ // user-defined CORS policies.
224
+ const acceptHeader = headers.get('accept')
225
+ if (acceptHeader) {
226
+ const values = acceptHeader.split(',').map((value) => value.trim())
227
+ const filteredValues = values.filter(
228
+ (value) => value !== 'msw/passthrough',
229
+ )
230
+
231
+ if (filteredValues.length > 0) {
232
+ headers.set('accept', filteredValues.join(', '))
233
+ } else {
234
+ headers.delete('accept')
235
+ }
236
+ }
237
+
238
+ return fetch(requestClone, { headers })
239
+ }
240
+
241
+ // Bypass mocking when the client is not active.
242
+ if (!client) {
243
+ return passthrough()
244
+ }
245
+
246
+ // Bypass initial page load requests (i.e. static assets).
247
+ // The absence of the immediate/parent client in the map of the active clients
248
+ // means that MSW hasn't dispatched the "MOCK_ACTIVATE" event yet
249
+ // and is not ready to handle requests.
250
+ if (!activeClientIds.has(client.id)) {
251
+ return passthrough()
252
+ }
253
+
254
+ // Notify the client that a request has been intercepted.
255
+ const serializedRequest = await serializeRequest(event.request)
256
+ const clientMessage = await sendToClient(
257
+ client,
258
+ {
259
+ type: 'REQUEST',
260
+ payload: {
261
+ id: requestId,
262
+ interceptedAt: requestInterceptedAt,
263
+ ...serializedRequest,
264
+ },
265
+ },
266
+ [serializedRequest.body],
267
+ )
268
+
269
+ switch (clientMessage.type) {
270
+ case 'MOCK_RESPONSE': {
271
+ return respondWithMock(clientMessage.data)
272
+ }
273
+
274
+ case 'PASSTHROUGH': {
275
+ return passthrough()
276
+ }
277
+ }
278
+
279
+ return passthrough()
280
+ }
281
+
282
+ /**
283
+ * @param {Client} client
284
+ * @param {any} message
285
+ * @param {Array<Transferable>} transferrables
286
+ * @returns {Promise<any>}
287
+ */
288
+ function sendToClient(client, message, transferrables = []) {
289
+ return new Promise((resolve, reject) => {
290
+ const channel = new MessageChannel()
291
+
292
+ channel.port1.onmessage = (event) => {
293
+ if (event.data && event.data.error) {
294
+ return reject(event.data.error)
295
+ }
296
+
297
+ resolve(event.data)
298
+ }
299
+
300
+ client.postMessage(message, [
301
+ channel.port2,
302
+ ...transferrables.filter(Boolean),
303
+ ])
304
+ })
305
+ }
306
+
307
+ /**
308
+ * @param {Response} response
309
+ * @returns {Response}
310
+ */
311
+ function respondWithMock(response) {
312
+ // Setting response status code to 0 is a no-op.
313
+ // However, when responding with a "Response.error()", the produced Response
314
+ // instance will have status code set to 0. Since it's not possible to create
315
+ // a Response instance with status code 0, handle that use-case separately.
316
+ if (response.status === 0) {
317
+ return Response.error()
318
+ }
319
+
320
+ const mockedResponse = new Response(response.body, response)
321
+
322
+ Reflect.defineProperty(mockedResponse, IS_MOCKED_RESPONSE, {
323
+ value: true,
324
+ enumerable: true,
325
+ })
326
+
327
+ return mockedResponse
328
+ }
329
+
330
+ /**
331
+ * @param {Request} request
332
+ */
333
+ async function serializeRequest(request) {
334
+ return {
335
+ url: request.url,
336
+ mode: request.mode,
337
+ method: request.method,
338
+ headers: Object.fromEntries(request.headers.entries()),
339
+ cache: request.cache,
340
+ credentials: request.credentials,
341
+ destination: request.destination,
342
+ integrity: request.integrity,
343
+ redirect: request.redirect,
344
+ referrer: request.referrer,
345
+ referrerPolicy: request.referrerPolicy,
346
+ body: await request.arrayBuffer(),
347
+ keepalive: request.keepalive,
348
+ }
349
+ }
@@ -0,0 +1 @@
1
+ export declare const worker: import('msw/browser').SetupWorker;
@@ -0,0 +1 @@
1
+ export declare const agencyHandler: import('msw').HttpHandler[];
@@ -0,0 +1 @@
1
+ export * from './authHandler';
@@ -0,0 +1,17 @@
1
+ import { AnyMessage } from '../../interfaces/AnyMessage';
2
+ import { IframeMode } from '../../interfaces/IframeMode';
3
+ import { IframeQueryParams } from '../../interfaces/IframeQueryParams';
4
+ export declare const createChildSite: (args: {
5
+ origin: string;
6
+ initialRoute?: string;
7
+ queryParams?: IframeQueryParams;
8
+ mode?: IframeMode;
9
+ }) => {
10
+ iframe: HTMLIFrameElement;
11
+ subscribe: <T extends AnyMessage>(callback: (event: MessageEvent<T>) => void) => {
12
+ unsubscribe: () => void;
13
+ };
14
+ postMessage: <TMessage extends AnyMessage>(message: TMessage, transfer?: Transferable[]) => void;
15
+ setMode: (mode: IframeMode) => void;
16
+ refreshSize: () => void;
17
+ };
@@ -0,0 +1,18 @@
1
+ import { OnboardingAppMessage } from '../../interfaces/OnboardingAppMessage';
2
+ import { OnboardingAppMode } from '../../interfaces/OnboardingAppMode';
3
+ export declare const createOnboardingApp: (args: {
4
+ origin: string;
5
+ inviteToken: string;
6
+ mode?: OnboardingAppMode;
7
+ }) => {
8
+ subscribe: (callback: (event: MessageEvent<OnboardingAppMessage>) => void) => {
9
+ unsubscribe: () => void;
10
+ };
11
+ setMode: (mode: OnboardingAppMode) => void;
12
+ setCustomCss: (css: string) => void;
13
+ iframe: HTMLIFrameElement;
14
+ next: () => void;
15
+ prev: () => void;
16
+ skip: () => void;
17
+ refreshSize: () => void;
18
+ };
@@ -0,0 +1,11 @@
1
+ import { OnboardingAppMessage } from '../../interfaces/OnboardingAppMessage';
2
+ import { OnboardingConsumerAppMessage } from '../../interfaces/OnboardingConsumerAppMessage';
3
+ export declare const createOnboardingConsumerApp: (args: {
4
+ allowedOrigins: string[];
5
+ }) => {
6
+ postMessage: (message: OnboardingAppMessage) => void;
7
+ subscribe: (callback: (event: MessageEvent<OnboardingConsumerAppMessage>) => void) => {
8
+ unsubscribe: () => void;
9
+ };
10
+ notifySize: () => void;
11
+ };
@@ -0,0 +1,12 @@
1
+ import { AnyMessage } from '../../interfaces/AnyMessage';
2
+ import { IframeQueryParams } from '../../interfaces/IframeQueryParams';
3
+ export declare const createParentSite: (args: {
4
+ allowedOrigins: string[];
5
+ }) => {
6
+ notifySize: () => void;
7
+ subscribe: <T extends AnyMessage>(callback: (event: MessageEvent<T>) => void) => {
8
+ unsubscribe: () => void;
9
+ };
10
+ postMessage: <TMessage extends AnyMessage>(message: TMessage) => void;
11
+ queryParams: IframeQueryParams;
12
+ };
@@ -0,0 +1,2 @@
1
+ import { QueryParams } from '../../interfaces/QueryParams';
2
+ export declare const getURLQueryParams: <T extends QueryParams>() => T;
@@ -0,0 +1,4 @@
1
+ export * from './createChildSite';
2
+ export * from './createOnboardingApp';
3
+ export * from './createOnboardingConsumerApp';
4
+ export * from './createParentSite';
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@worthai/onboarding-sdk",
3
+ "version": "2.0.0",
4
+ "type": "module",
5
+ "source": "./src",
6
+ "types": "./dist/index.d.ts",
7
+ "main": "./dist/index.js",
8
+ "files": [
9
+ "dist",
10
+ "README.md"
11
+ ],
12
+ "exports": {
13
+ ".": {
14
+ "types": "./dist/index.d.ts",
15
+ "import": "./dist/index.js"
16
+ }
17
+ },
18
+ "scripts": {
19
+ "dev": "vite",
20
+ "build": "vite build",
21
+ "preview": "vite preview",
22
+ "storybook": "storybook dev -p 6006",
23
+ "build-storybook": "storybook build",
24
+ "bump-patch-version": "npm version patch -m 'chore: bump patch version to %s'",
25
+ "publish-to-github": "npm publish --access public"
26
+ },
27
+ "devDependencies": {
28
+ "@storybook/addon-docs": "^9.1.10",
29
+ "@storybook/html-vite": "^9.1.10",
30
+ "autoprefixer": "^10.4.21",
31
+ "axios": "^1.13.1",
32
+ "msw": "^2.11.6",
33
+ "postcss": "^8.5.6",
34
+ "storybook": "^9.1.10",
35
+ "tailwindcss": "^3.4.18",
36
+ "typescript": "~5.9.3",
37
+ "vite": "^7.1.7",
38
+ "vite-plugin-checker": "^0.11.0",
39
+ "vite-plugin-dts": "^4.5.4",
40
+ "vite-tsconfig-paths": "^5.1.4"
41
+ },
42
+ "packageManager": "pnpm@10.14.0+sha512.ad27a79641b49c3e481a16a805baa71817a04bbe06a38d17e60e2eaee83f6a146c6a688125f5792e48dd5ba30e7da52a5cda4c3992b9ccf333f9ce223af84748",
43
+ "msw": {
44
+ "workerDirectory": [
45
+ "public"
46
+ ]
47
+ }
48
+ }