@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.
- package/README.md +282 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.js +200 -0
- package/dist/index.js.map +1 -0
- package/dist/interfaces/AnyMessage.d.ts +4 -0
- package/dist/interfaces/ChildSite.d.ts +2 -0
- package/dist/interfaces/ChildSiteMessage.d.ts +8 -0
- package/dist/interfaces/ChildSiteSubscription.d.ts +2 -0
- package/dist/interfaces/IframeMode.d.ts +1 -0
- package/dist/interfaces/IframeQueryParams.d.ts +1 -0
- package/dist/interfaces/OnboardingApp.d.ts +2 -0
- package/dist/interfaces/OnboardingAppAppSubscription.d.ts +2 -0
- package/dist/interfaces/OnboardingAppMessage.d.ts +24 -0
- package/dist/interfaces/OnboardingAppMode.d.ts +2 -0
- package/dist/interfaces/OnboardingAppSubscription.d.ts +2 -0
- package/dist/interfaces/OnboardingConsumerApp.d.ts +2 -0
- package/dist/interfaces/OnboardingConsumerAppMessage.d.ts +13 -0
- package/dist/interfaces/ParentSite.d.ts +2 -0
- package/dist/interfaces/ParentSiteMessage.d.ts +21 -0
- package/dist/interfaces/ParentSiteSubscription.d.ts +2 -0
- package/dist/interfaces/QueryParams.d.ts +1 -0
- package/dist/interfaces/StageNavigation.d.ts +19 -0
- package/dist/interfaces/index.d.ts +18 -0
- package/dist/mockServiceWorker.js +349 -0
- package/dist/mocks/browser.d.ts +1 -0
- package/dist/mocks/handlers/authHandler.d.ts +1 -0
- package/dist/mocks/handlers/index.d.ts +1 -0
- package/dist/utils/createChildSite/index.d.ts +17 -0
- package/dist/utils/createOnboardingApp/index.d.ts +18 -0
- package/dist/utils/createOnboardingConsumerApp/index.d.ts +11 -0
- package/dist/utils/createParentSite/index.d.ts +12 -0
- package/dist/utils/getURLQueryParams/index.d.ts +2 -0
- package/dist/utils/index.d.ts +4 -0
- 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.
|
package/dist/index.d.ts
ADDED
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 @@
|
|
|
1
|
+
export type IframeMode = 'embedded' | 'non-embedded' | undefined;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export type IframeQueryParams = {};
|
|
@@ -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,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,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 @@
|
|
|
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
|
+
};
|
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
|
+
}
|