@melio-eng/web-sdk 1.0.30 → 1.0.31-pr.59.3147594
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/flows/Flow.d.ts +1 -1
- package/dist/flows/Flow.js +1 -0
- package/dist/flows/InitFlow.js +9 -1
- package/dist/flows/OnboardingFlow.js +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.js +8 -320
- package/package.json +1 -1
package/dist/flows/Flow.d.ts
CHANGED
|
@@ -9,7 +9,7 @@ export declare class Flow implements FlowInstance {
|
|
|
9
9
|
protected environment: Environment;
|
|
10
10
|
protected branchOverride?: string | undefined;
|
|
11
11
|
protected iframe: HTMLIFrameElement | null;
|
|
12
|
-
|
|
12
|
+
protected container: HTMLElement | null;
|
|
13
13
|
private eventListeners;
|
|
14
14
|
protected keepAliveInterval: number | null;
|
|
15
15
|
constructor(containerId: string, config: BaseFlowConfig, partnerName: string, environment: Environment, branchOverride?: string | undefined);
|
package/dist/flows/Flow.js
CHANGED
package/dist/flows/InitFlow.js
CHANGED
|
@@ -15,7 +15,15 @@ export class InitFlow extends Flow {
|
|
|
15
15
|
* Initialize the init flow by creating and injecting a hidden iframe
|
|
16
16
|
*/
|
|
17
17
|
async initialize() {
|
|
18
|
-
this.
|
|
18
|
+
this.container = document.body;
|
|
19
|
+
this.iframe = document.createElement('iframe');
|
|
20
|
+
this.iframe.src = this.createFlowUrl();
|
|
21
|
+
this.iframe.style.width = '1px';
|
|
22
|
+
this.iframe.style.height = '1px';
|
|
23
|
+
this.iframe.style.position = 'absolute';
|
|
24
|
+
this.iframe.style.left = '-9999px';
|
|
25
|
+
this.iframe.style.top = '-9999px';
|
|
26
|
+
this.container.appendChild(this.iframe);
|
|
19
27
|
}
|
|
20
28
|
/**
|
|
21
29
|
* Construct the specific flow URL for initialization
|
|
@@ -18,7 +18,7 @@ export class OnboardingFlow extends Flow {
|
|
|
18
18
|
* Construct the specific flow URL for onboarding - now goes through /auth
|
|
19
19
|
*/
|
|
20
20
|
constructFlowUrl(baseUrl) {
|
|
21
|
-
const params = Object.fromEntries(Object.entries(this.preFilledParams).filter(([
|
|
21
|
+
const params = Object.fromEntries(Object.entries(this.preFilledParams).filter(([, v]) => v !== undefined));
|
|
22
22
|
let preFilledBase64 = '';
|
|
23
23
|
if (Object.keys(params).length > 0) {
|
|
24
24
|
try {
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
|
@@ -1,322 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
* Utility function to get the base URL for a given environment
|
|
3
|
-
*/
|
|
4
|
-
function getBaseUrl(environment) {
|
|
5
|
-
switch (environment) {
|
|
6
|
-
case 'production':
|
|
7
|
-
return 'https://partnerships.production.melioservices.com';
|
|
8
|
-
case 'staging01':
|
|
9
|
-
return 'https://partnerships.staging01.melioservices.com';
|
|
10
|
-
case 'public-qa':
|
|
11
|
-
return 'https://partnerships.public-qa.melioservices.com';
|
|
12
|
-
case 'certification':
|
|
13
|
-
return 'https://partnerships.certification.melioservices.com';
|
|
14
|
-
case 'eilat':
|
|
15
|
-
return 'https://partnerships.eilat.melioservices.com';
|
|
16
|
-
case 'localhost':
|
|
17
|
-
return 'http://localhost:3005';
|
|
18
|
-
default:
|
|
19
|
-
return 'https://partnerships.melioservices.com';
|
|
20
|
-
}
|
|
21
|
-
}
|
|
22
|
-
const isEmptyString = (str) => {
|
|
23
|
-
return !str?.trim();
|
|
24
|
-
};
|
|
25
|
-
/**
|
|
26
|
-
* Flow class implementation for handling iframe flows and events
|
|
27
|
-
*/
|
|
28
|
-
class Flow {
|
|
29
|
-
constructor(containerId, config, partnerName, environment, branchOverride) {
|
|
30
|
-
this.containerId = containerId;
|
|
31
|
-
this.config = config;
|
|
32
|
-
this.partnerName = partnerName;
|
|
33
|
-
this.environment = environment;
|
|
34
|
-
this.branchOverride = branchOverride;
|
|
35
|
-
this.iframe = null;
|
|
36
|
-
this.container = null;
|
|
37
|
-
this.eventListeners = new Map();
|
|
38
|
-
this.keepAliveInterval = null;
|
|
39
|
-
this.setupEventListeners();
|
|
40
|
-
}
|
|
41
|
-
/**
|
|
42
|
-
* Initialize the flow by creating and injecting the iframe
|
|
43
|
-
*/
|
|
44
|
-
async initialize() {
|
|
45
|
-
this.container = document.getElementById(this.containerId);
|
|
46
|
-
if (!this.container) {
|
|
47
|
-
throw new Error(`Container with ID "${this.containerId}" not found`);
|
|
48
|
-
}
|
|
49
|
-
this.iframe = document.createElement('iframe');
|
|
50
|
-
this.iframe.src = this.createFlowUrl();
|
|
51
|
-
this.iframe.style.width = '100%';
|
|
52
|
-
this.iframe.style.height = '1000px';
|
|
53
|
-
this.iframe.style.border = 'none';
|
|
54
|
-
this.iframe.style.display = 'block';
|
|
55
|
-
this.container.appendChild(this.iframe);
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Construct the specific flow URL - can be overridden by subclasses
|
|
59
|
-
*/
|
|
60
|
-
constructFlowUrl(baseUrl) {
|
|
61
|
-
return `${baseUrl}/${this.partnerName}/auth`;
|
|
62
|
-
}
|
|
63
|
-
/**
|
|
64
|
-
* Create flow URL using partner name and environment
|
|
65
|
-
*/
|
|
66
|
-
createFlowUrl() {
|
|
67
|
-
console.log('🔧 Creating flow URL...');
|
|
68
|
-
console.log('📝 Config:', this.config);
|
|
69
|
-
console.log('🏢 Partner:', this.partnerName);
|
|
70
|
-
console.log('🌍 Environment:', this.environment);
|
|
71
|
-
console.log('🔝 Branch Override:', this.branchOverride);
|
|
72
|
-
const baseUrl = getBaseUrl(this.environment);
|
|
73
|
-
let finalUrl = this.constructFlowUrl(baseUrl);
|
|
74
|
-
// Add cdn_branch_override parameter for non-production environments
|
|
75
|
-
if (this.branchOverride && this.environment !== 'production') {
|
|
76
|
-
const separator = finalUrl.includes('?') ? '&' : '?';
|
|
77
|
-
finalUrl += `${separator}cdn_branch_override=${this.branchOverride}`;
|
|
78
|
-
}
|
|
79
|
-
console.log('🌐 Final URL:', finalUrl);
|
|
80
|
-
return finalUrl;
|
|
81
|
-
}
|
|
82
|
-
setupEventListeners() {
|
|
83
|
-
// Add post message handlers for internal events - setHeight, scroll etc
|
|
84
|
-
// Also need to implement callbacks for flow completed exit etc in the platform-app
|
|
85
|
-
window.addEventListener('message', (event) => {
|
|
86
|
-
if (!/melio\.com|melioservices\.com/.test(event.origin)) {
|
|
87
|
-
return;
|
|
88
|
-
}
|
|
89
|
-
const { type, ...data } = event.data;
|
|
90
|
-
console.log('📬 Received message from iframe:', {
|
|
91
|
-
type,
|
|
92
|
-
data,
|
|
93
|
-
origin: event.origin,
|
|
94
|
-
});
|
|
95
|
-
switch (type) {
|
|
96
|
-
case 'FLOW_COMPLETED':
|
|
97
|
-
this.emit('completed', data);
|
|
98
|
-
break;
|
|
99
|
-
case 'FLOW_EXIT':
|
|
100
|
-
this.emit('exit');
|
|
101
|
-
break;
|
|
102
|
-
case 'NAVIGATED_TO_TARGET':
|
|
103
|
-
this.emit('navigated', data);
|
|
104
|
-
break;
|
|
105
|
-
case 'AUTHENTICATION_SUCCESS':
|
|
106
|
-
this.emit('authenticationSucceeded');
|
|
107
|
-
break;
|
|
108
|
-
case 'AUTHENTICATION_ERROR':
|
|
109
|
-
this.emit('authenticationFailed');
|
|
110
|
-
break;
|
|
111
|
-
case 'ONBOARDING_COMPLETED':
|
|
112
|
-
this.emit('completed', { flowName: 'onboarding', ...data });
|
|
113
|
-
break;
|
|
114
|
-
case 'READY_FOR_INTERACTION':
|
|
115
|
-
this.emit('loaded');
|
|
116
|
-
break;
|
|
117
|
-
case 'MELIO_ERROR':
|
|
118
|
-
if (data.code === 'failed_to_sync_bills') {
|
|
119
|
-
this.emit('error', { errorCode: 'billsSyncFailed' });
|
|
120
|
-
}
|
|
121
|
-
break;
|
|
122
|
-
case 'HEIGHT_CHANGE':
|
|
123
|
-
if (this.iframe) {
|
|
124
|
-
this.iframe.style.height = `${data.height}px`;
|
|
125
|
-
}
|
|
126
|
-
break;
|
|
127
|
-
}
|
|
128
|
-
});
|
|
129
|
-
}
|
|
130
|
-
emit(event, data) {
|
|
131
|
-
const listeners = this.eventListeners.get(event);
|
|
132
|
-
if (listeners) {
|
|
133
|
-
listeners.forEach((callback) => {
|
|
134
|
-
try {
|
|
135
|
-
callback(data);
|
|
136
|
-
}
|
|
137
|
-
catch (error) {
|
|
138
|
-
console.error(`Error in ${event} event listener:`, error);
|
|
139
|
-
}
|
|
140
|
-
});
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
on(event, callback) {
|
|
144
|
-
if (!this.eventListeners.has(event)) {
|
|
145
|
-
this.eventListeners.set(event, new Set());
|
|
146
|
-
}
|
|
147
|
-
this.eventListeners.get(event).add(callback);
|
|
148
|
-
}
|
|
149
|
-
/**
|
|
150
|
-
* Remove an event listener
|
|
151
|
-
*/
|
|
152
|
-
off(event, callback) {
|
|
153
|
-
const listeners = this.eventListeners.get(event);
|
|
154
|
-
if (listeners) {
|
|
155
|
-
listeners.delete(callback);
|
|
156
|
-
}
|
|
157
|
-
}
|
|
158
|
-
/**
|
|
159
|
-
* Close the flow and clean up resources
|
|
160
|
-
*/
|
|
161
|
-
close() {
|
|
162
|
-
if (this.keepAliveInterval) {
|
|
163
|
-
clearInterval(this.keepAliveInterval);
|
|
164
|
-
this.keepAliveInterval = null;
|
|
165
|
-
}
|
|
166
|
-
if (this.iframe && this.container) {
|
|
167
|
-
this.container.removeChild(this.iframe);
|
|
168
|
-
this.iframe = null;
|
|
169
|
-
}
|
|
170
|
-
this.eventListeners.clear();
|
|
171
|
-
}
|
|
172
|
-
}
|
|
173
|
-
/**
|
|
174
|
-
* OnboardingFlow subclass with custom URL construction
|
|
175
|
-
*/
|
|
176
|
-
class OnboardingFlow extends Flow {
|
|
177
|
-
constructor(containerId, config, partnerName, environment, authCode, branchOverride) {
|
|
178
|
-
super(containerId, config, partnerName, environment, branchOverride);
|
|
179
|
-
const { userDetails, organizationDetails, enforceOnboarding } = config;
|
|
180
|
-
this.preFilledParams = { userDetails, organizationDetails };
|
|
181
|
-
this.enforceOnboarding = enforceOnboarding;
|
|
182
|
-
this.authCode = authCode;
|
|
183
|
-
if (isEmptyString(this.authCode)) {
|
|
184
|
-
throw new Error('Authorization code is required for onboarding flow. Please call init() before opening onboarding flow.');
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Construct the specific flow URL for onboarding - now goes through /auth
|
|
189
|
-
*/
|
|
190
|
-
constructFlowUrl(baseUrl) {
|
|
191
|
-
const params = Object.fromEntries(Object.entries(this.preFilledParams).filter(([_, v]) => v !== undefined));
|
|
192
|
-
let preFilledBase64 = '';
|
|
193
|
-
if (Object.keys(params).length > 0) {
|
|
194
|
-
try {
|
|
195
|
-
const json = JSON.stringify(params);
|
|
196
|
-
preFilledBase64 = btoa(unescape(encodeURIComponent(json)));
|
|
197
|
-
}
|
|
198
|
-
catch (e) {
|
|
199
|
-
preFilledBase64 = '';
|
|
200
|
-
}
|
|
201
|
-
}
|
|
202
|
-
const targetQueryParams = [];
|
|
203
|
-
if (preFilledBase64) {
|
|
204
|
-
targetQueryParams.push(`preFilledParams=${encodeURIComponent(preFilledBase64)}`);
|
|
205
|
-
}
|
|
206
|
-
if (this.enforceOnboarding !== undefined) {
|
|
207
|
-
targetQueryParams.push(`enforceOnboarding=${this.enforceOnboarding}`);
|
|
208
|
-
}
|
|
209
|
-
targetQueryParams.push(`externalOrigin=${this.partnerName}`);
|
|
210
|
-
const target = `/welcome?${targetQueryParams.join('&')}`;
|
|
211
|
-
const encodedTarget = encodeURIComponent(target);
|
|
212
|
-
return `${baseUrl}/${this.partnerName}/auth?token=${this.authCode}&redirectUrl=${encodedTarget}`;
|
|
213
|
-
}
|
|
214
|
-
}
|
|
215
|
-
/**
|
|
216
|
-
* PayFlow subclass with custom URL construction
|
|
217
|
-
*/
|
|
218
|
-
class PayFlow extends Flow {
|
|
219
|
-
constructor(containerId, config, partnerName, environment, authCode, branchOverride) {
|
|
220
|
-
super(containerId, config, partnerName, environment, branchOverride);
|
|
221
|
-
this.billIds = config.billIds;
|
|
222
|
-
this.authCode = authCode;
|
|
223
|
-
if (isEmptyString(this.authCode)) {
|
|
224
|
-
throw new Error('Authorization code is required for pay flow. Please call init() before opening pay flow.');
|
|
225
|
-
}
|
|
226
|
-
}
|
|
227
|
-
/**
|
|
228
|
-
* Construct the specific flow URL for bill payment - now goes through /auth
|
|
229
|
-
*/
|
|
230
|
-
constructFlowUrl(baseUrl) {
|
|
231
|
-
const billIdsParam = this.billIds.join(',');
|
|
232
|
-
const target = `/external-entries/payment-flow?billIds=${billIdsParam}&externalOrigin=${this.partnerName}`;
|
|
233
|
-
const encodedTarget = encodeURIComponent(target);
|
|
234
|
-
return `${baseUrl}/${this.partnerName}/auth?token=${this.authCode}&redirectUrl=${encodedTarget}`;
|
|
235
|
-
}
|
|
236
|
-
}
|
|
237
|
-
/**
|
|
238
|
-
* SettingsFlow subclass with custom URL construction
|
|
239
|
-
*/
|
|
240
|
-
class SettingsFlow extends Flow {
|
|
241
|
-
constructor(containerId, config, partnerName, environment, authCode, branchOverride) {
|
|
242
|
-
super(containerId, config, partnerName, environment, branchOverride);
|
|
243
|
-
this.authCode = authCode;
|
|
244
|
-
if (isEmptyString(this.authCode)) {
|
|
245
|
-
throw new Error('Authorization code is required for settings flow. Please call init() before opening settings flow.');
|
|
246
|
-
}
|
|
247
|
-
}
|
|
248
|
-
/**
|
|
249
|
-
* Construct the specific flow URL for settings - now goes through /auth
|
|
250
|
-
*/
|
|
251
|
-
constructFlowUrl(baseUrl) {
|
|
252
|
-
const target = '/view-settings';
|
|
253
|
-
const encodedTarget = encodeURIComponent(target);
|
|
254
|
-
return `${baseUrl}/${this.partnerName}/auth?token=${this.authCode}&redirectUrl=${encodedTarget}`;
|
|
255
|
-
}
|
|
256
|
-
}
|
|
257
|
-
class PaymentsDashboardFlow extends Flow {
|
|
258
|
-
constructor(containerId, config, partnerName, environment, authCode, branchOverride) {
|
|
259
|
-
super(containerId, config, partnerName, environment, branchOverride);
|
|
260
|
-
this.paymentId = config.paymentId;
|
|
261
|
-
this.authCode = authCode;
|
|
262
|
-
if (isEmptyString(this.authCode)) {
|
|
263
|
-
throw new Error('Authorization code is required for payments dashboard flow. Please call init() before opening payments dashboard flow.');
|
|
264
|
-
}
|
|
265
|
-
}
|
|
266
|
-
/**
|
|
267
|
-
* Construct the specific flow URL for payments dashboard - now goes through /auth
|
|
268
|
-
*/
|
|
269
|
-
constructFlowUrl(baseUrl) {
|
|
270
|
-
const target = `/pay-dashboard/payments${this.paymentId ? `/${this.paymentId}` : ''}`;
|
|
271
|
-
const encodedTarget = encodeURIComponent(target);
|
|
272
|
-
return `${baseUrl}/${this.partnerName}/auth?token=${this.authCode}&redirectUrl=${encodedTarget}`;
|
|
273
|
-
}
|
|
274
|
-
}
|
|
275
|
-
/**
|
|
276
|
-
* InitFlow subclass for handling initialization with callbacks
|
|
277
|
-
*/
|
|
278
|
-
class InitFlow extends Flow {
|
|
279
|
-
constructor(containerId, config, partnerName, environment, branchOverride) {
|
|
280
|
-
super(containerId, config, partnerName, environment, branchOverride);
|
|
281
|
-
if (isEmptyString(config.authCode)) {
|
|
282
|
-
throw new Error('Authorization code is required for init flow');
|
|
283
|
-
}
|
|
284
|
-
this.authorizationCode = config.authCode;
|
|
285
|
-
}
|
|
286
|
-
/**
|
|
287
|
-
* Initialize the init flow by creating and injecting a hidden iframe
|
|
288
|
-
*/
|
|
289
|
-
async initialize() {
|
|
290
|
-
// For init flow, we create a hidden iframe instead of a visible one
|
|
291
|
-
// Set container to document.body so parent's close() method can remove it
|
|
292
|
-
this.container = document.body;
|
|
293
|
-
this.iframe = document.createElement('iframe');
|
|
294
|
-
this.iframe.src = this.createFlowUrl();
|
|
295
|
-
this.iframe.style.width = '1px';
|
|
296
|
-
this.iframe.style.height = '1px';
|
|
297
|
-
this.iframe.style.position = 'absolute';
|
|
298
|
-
this.iframe.style.left = '-9999px';
|
|
299
|
-
this.iframe.style.top = '-9999px';
|
|
300
|
-
this.container.appendChild(this.iframe);
|
|
301
|
-
}
|
|
302
|
-
/**
|
|
303
|
-
* Construct the specific flow URL for initialization
|
|
304
|
-
*/
|
|
305
|
-
constructFlowUrl(baseUrl) {
|
|
306
|
-
const params = new URLSearchParams({
|
|
307
|
-
token: this.authorizationCode,
|
|
308
|
-
theme: this.partnerName,
|
|
309
|
-
});
|
|
310
|
-
return `${baseUrl}/${this.partnerName}/auth?${params.toString()}`;
|
|
311
|
-
}
|
|
312
|
-
setupKeepAlive() {
|
|
313
|
-
this.keepAliveInterval = window.setInterval(() => {
|
|
314
|
-
if (this.iframe && this.iframe.contentWindow) {
|
|
315
|
-
this.iframe.contentWindow.postMessage({ type: 'USER_ACTIVE_PING' }, '*');
|
|
316
|
-
}
|
|
317
|
-
}, 30000); // Send ping every 30 seconds
|
|
318
|
-
}
|
|
319
|
-
}
|
|
1
|
+
import { InitFlow, OnboardingFlow, PayFlow, SettingsFlow, PaymentsDashboardFlow, isEmptyString, } from './flows/index.js';
|
|
320
2
|
/**
|
|
321
3
|
* Main SDK implementation - now partner agnostic
|
|
322
4
|
*/
|
|
@@ -327,11 +9,16 @@ export class MelioSDK {
|
|
|
327
9
|
this.partnerName = '';
|
|
328
10
|
this.branchOverride = undefined;
|
|
329
11
|
this.authCode = '';
|
|
12
|
+
this.initFlow = null;
|
|
330
13
|
}
|
|
331
14
|
/**
|
|
332
15
|
* Initialize the SDK - triggers auth event without creating iframe
|
|
333
16
|
*/
|
|
334
17
|
init(authenticationCode, options) {
|
|
18
|
+
if (this.initFlow) {
|
|
19
|
+
console.log('SDK already initialized. Returning existing init flow instance.');
|
|
20
|
+
return this.initFlow;
|
|
21
|
+
}
|
|
335
22
|
this.partnerName = options.partnerName;
|
|
336
23
|
this.environment = options.environment || 'production';
|
|
337
24
|
this.branchOverride = options.branchOverride;
|
|
@@ -346,7 +33,8 @@ export class MelioSDK {
|
|
|
346
33
|
console.log('InitFlow initialized successfully');
|
|
347
34
|
if (options.keepAlive)
|
|
348
35
|
initFlow.setupKeepAlive();
|
|
349
|
-
|
|
36
|
+
this.initFlow = initFlow;
|
|
37
|
+
return this.initFlow;
|
|
350
38
|
}
|
|
351
39
|
/**
|
|
352
40
|
* Launch the onboarding flow
|
package/package.json
CHANGED