@salla.sa/embedded-sdk 0.2.1 → 0.2.3
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 +1 -0
- package/dist/cjs/index.js +3 -3
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +504 -513
- package/dist/esm/index.js.map +1 -1
- package/dist/system/index.js +3 -3
- package/dist/system/index.js.map +1 -1
- package/dist/types/index.d.ts +151 -110
- package/dist/umd/index.js +3 -3
- package/dist/umd/index.js.map +1 -1
- package/package.json +1 -1
package/dist/types/index.d.ts
CHANGED
|
@@ -4,6 +4,22 @@
|
|
|
4
4
|
*/
|
|
5
5
|
declare type ActionClickCallback = (value: string) => void;
|
|
6
6
|
|
|
7
|
+
/**
|
|
8
|
+
* Addon information returned by getAddons.
|
|
9
|
+
*/
|
|
10
|
+
declare interface AddonInfo {
|
|
11
|
+
/** Unique slug identifier */
|
|
12
|
+
slug: string;
|
|
13
|
+
/** Internal product ID */
|
|
14
|
+
product_id: number;
|
|
15
|
+
/** Internal price ID */
|
|
16
|
+
product_price_id: number;
|
|
17
|
+
/** Display name */
|
|
18
|
+
name: string;
|
|
19
|
+
/** Price amount */
|
|
20
|
+
price: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
7
23
|
/**
|
|
8
24
|
* Auth module interface.
|
|
9
25
|
*/
|
|
@@ -36,6 +52,30 @@ export declare interface AuthModule {
|
|
|
36
52
|
introspect(options?: IntrospectOptions): Promise<IntrospectResponse>;
|
|
37
53
|
}
|
|
38
54
|
|
|
55
|
+
/**
|
|
56
|
+
* Optional configuration for checkout creation.
|
|
57
|
+
*/
|
|
58
|
+
export declare interface CheckoutCreateConfig {
|
|
59
|
+
/** Optional context to persist across 3DS redirects.
|
|
60
|
+
* Stored on the host side and returned in onResult after redirect. */
|
|
61
|
+
context?: unknown;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* @fileoverview Type definitions for the checkout module.
|
|
66
|
+
*/
|
|
67
|
+
/**
|
|
68
|
+
* Checkout item with type information.
|
|
69
|
+
*/
|
|
70
|
+
export declare interface CheckoutItem {
|
|
71
|
+
/** Item type */
|
|
72
|
+
type: "addon";
|
|
73
|
+
/** Item slug identifier */
|
|
74
|
+
slug: string;
|
|
75
|
+
/** Quantity (default: 1) */
|
|
76
|
+
quantity?: number;
|
|
77
|
+
}
|
|
78
|
+
|
|
39
79
|
/**
|
|
40
80
|
* Checkout module interface.
|
|
41
81
|
*/
|
|
@@ -43,36 +83,94 @@ export declare interface CheckoutModule {
|
|
|
43
83
|
/**
|
|
44
84
|
* Create/initiate a checkout flow.
|
|
45
85
|
*
|
|
46
|
-
*
|
|
86
|
+
* Accepts a single item or an array of items for multi-item checkout.
|
|
87
|
+
*
|
|
88
|
+
* @param input - Single checkout item or array of items
|
|
89
|
+
* @param config - Optional configuration
|
|
90
|
+
*
|
|
91
|
+
* @example
|
|
92
|
+
* ```typescript
|
|
93
|
+
* // Single item
|
|
94
|
+
* embedded.checkout.create(
|
|
95
|
+
* { type: "addon", slug: "premium-analytics", quantity: 1 },
|
|
96
|
+
* { context: { route: "/pricing", plan: "premium" } },
|
|
97
|
+
* );
|
|
98
|
+
*
|
|
99
|
+
* // Multiple items
|
|
100
|
+
* embedded.checkout.create([
|
|
101
|
+
* { type: "addon", slug: "premium-analytics", quantity: 2 },
|
|
102
|
+
* { type: "addon", slug: "extra-storage" },
|
|
103
|
+
* ]);
|
|
104
|
+
* ```
|
|
105
|
+
*/
|
|
106
|
+
create(input: CheckoutItem | CheckoutItem[], config?: CheckoutCreateConfig): void;
|
|
107
|
+
/**
|
|
108
|
+
* Subscribe to checkout results.
|
|
109
|
+
* Context from create() is returned here after 3DS redirect.
|
|
110
|
+
*
|
|
111
|
+
* @param callback - Function called when checkout completes
|
|
112
|
+
* @returns Unsubscribe function
|
|
47
113
|
*
|
|
48
114
|
* @example
|
|
49
115
|
* ```typescript
|
|
50
|
-
* embedded.checkout.
|
|
51
|
-
*
|
|
52
|
-
*
|
|
53
|
-
*
|
|
116
|
+
* const unsubscribe = embedded.checkout.onResult((result) => {
|
|
117
|
+
* if (result.context?.route) {
|
|
118
|
+
* router.push(result.context.route);
|
|
119
|
+
* }
|
|
120
|
+
* if (result.success) {
|
|
121
|
+
* console.log("Order:", result.order_id);
|
|
122
|
+
* }
|
|
54
123
|
* });
|
|
55
124
|
* ```
|
|
56
125
|
*/
|
|
57
|
-
|
|
126
|
+
onResult(callback: CheckoutResultCallback): () => void;
|
|
127
|
+
/**
|
|
128
|
+
* Get available addons for the current app.
|
|
129
|
+
* Results are cached on the host side (30 min TTL).
|
|
130
|
+
*
|
|
131
|
+
* @returns Promise resolving to addon list
|
|
132
|
+
*
|
|
133
|
+
* @example
|
|
134
|
+
* ```typescript
|
|
135
|
+
* const result = await embedded.checkout.getAddons();
|
|
136
|
+
* if (result.success) {
|
|
137
|
+
* console.log('Available addons:', result.addons);
|
|
138
|
+
* } else {
|
|
139
|
+
* console.error('Error:', result.error);
|
|
140
|
+
* }
|
|
141
|
+
* ```
|
|
142
|
+
*/
|
|
143
|
+
getAddons(): Promise<GetAddonsResult>;
|
|
144
|
+
/**
|
|
145
|
+
* Clean up module resources (message listeners, subscriptions).
|
|
146
|
+
* Called automatically by EmbeddedApp.destroy().
|
|
147
|
+
*/
|
|
148
|
+
destroy(): void;
|
|
58
149
|
}
|
|
59
150
|
|
|
60
151
|
/**
|
|
61
|
-
*
|
|
62
|
-
*/
|
|
152
|
+
* Checkout result returned after payment.
|
|
153
|
+
*/
|
|
154
|
+
export declare interface CheckoutResult {
|
|
155
|
+
/** Whether the checkout was successful */
|
|
156
|
+
success: boolean;
|
|
157
|
+
/** Order ID if successful */
|
|
158
|
+
order_id?: string;
|
|
159
|
+
/** Payment status */
|
|
160
|
+
status: "paid" | "pending" | "failed" | "cancelled" | "success";
|
|
161
|
+
/** Error details if failed */
|
|
162
|
+
error?: {
|
|
163
|
+
code: string;
|
|
164
|
+
message: string;
|
|
165
|
+
};
|
|
166
|
+
/** Developer context passed to create(), restored after 3DS redirect */
|
|
167
|
+
context?: unknown;
|
|
168
|
+
}
|
|
169
|
+
|
|
63
170
|
/**
|
|
64
|
-
*
|
|
171
|
+
* Callback for checkout result.
|
|
65
172
|
*/
|
|
66
|
-
export declare
|
|
67
|
-
/** Cart items or product IDs */
|
|
68
|
-
items?: unknown[];
|
|
69
|
-
/** Total amount */
|
|
70
|
-
amount?: number;
|
|
71
|
-
/** Currency code */
|
|
72
|
-
currency?: string;
|
|
73
|
-
/** Additional checkout data */
|
|
74
|
-
[key: string]: unknown;
|
|
75
|
-
}
|
|
173
|
+
export declare type CheckoutResultCallback = (result: CheckoutResult) => void;
|
|
76
174
|
|
|
77
175
|
/**
|
|
78
176
|
* Confirm dialog options.
|
|
@@ -110,11 +208,14 @@ export declare const embedded: EmbeddedApp;
|
|
|
110
208
|
* Provides the primary interface for third-party apps to communicate with the Salla host.
|
|
111
209
|
*/
|
|
112
210
|
export declare class EmbeddedApp {
|
|
113
|
-
private
|
|
114
|
-
private
|
|
115
|
-
private
|
|
116
|
-
private initCallbacks;
|
|
211
|
+
private initialized;
|
|
212
|
+
private initializing;
|
|
213
|
+
private debugMode;
|
|
117
214
|
private appReady;
|
|
215
|
+
private layout;
|
|
216
|
+
private themeSubscription;
|
|
217
|
+
private initSubscription;
|
|
218
|
+
private postInitHooks;
|
|
118
219
|
/** Auth module for token management */
|
|
119
220
|
auth: AuthModule;
|
|
120
221
|
/** Page module for navigation and resize */
|
|
@@ -127,113 +228,46 @@ export declare class EmbeddedApp {
|
|
|
127
228
|
checkout: CheckoutModule;
|
|
128
229
|
constructor();
|
|
129
230
|
/**
|
|
130
|
-
*
|
|
131
|
-
|
|
132
|
-
getState(): Readonly<EmbeddedState>;
|
|
133
|
-
/**
|
|
134
|
-
* Get current SDK configuration.
|
|
135
|
-
*/
|
|
136
|
-
getConfig(): Readonly<EmbeddedConfig>;
|
|
137
|
-
/**
|
|
138
|
-
* Check if SDK is initialized.
|
|
231
|
+
* Register a hook to run after successful init handshake.
|
|
232
|
+
* Used by modules to process context data.
|
|
139
233
|
*/
|
|
140
|
-
|
|
234
|
+
private registerPostInitHook;
|
|
141
235
|
/**
|
|
142
|
-
*
|
|
143
|
-
*
|
|
144
|
-
* @param type - Log type (log, warn, error, info, debug)
|
|
145
|
-
* @param args - Arguments to log
|
|
236
|
+
* Set up core event listeners.
|
|
146
237
|
*/
|
|
147
|
-
private
|
|
238
|
+
private setupListeners;
|
|
148
239
|
/**
|
|
149
|
-
*
|
|
240
|
+
* Get current SDK state.
|
|
150
241
|
*/
|
|
151
|
-
|
|
242
|
+
getState(): Readonly<EmbeddedState>;
|
|
152
243
|
/**
|
|
153
|
-
*
|
|
244
|
+
* Check if SDK is initialized and ready.
|
|
154
245
|
*/
|
|
155
|
-
|
|
246
|
+
isReady(): boolean;
|
|
156
247
|
/**
|
|
157
248
|
* Subscribe to theme changes.
|
|
158
|
-
*
|
|
159
|
-
* @param callback - Function called when theme changes
|
|
160
|
-
* @returns Unsubscribe function
|
|
161
|
-
*
|
|
162
|
-
* @example
|
|
163
|
-
* ```typescript
|
|
164
|
-
* const unsubscribe = embedded.onThemeChange((theme) => {
|
|
165
|
-
* document.body.classList.toggle('dark-mode', theme === 'dark');
|
|
166
|
-
* });
|
|
167
|
-
* ```
|
|
168
249
|
*/
|
|
169
|
-
onThemeChange(callback:
|
|
250
|
+
onThemeChange(callback: (theme: "light" | "dark") => void): () => void;
|
|
170
251
|
/**
|
|
171
|
-
* Subscribe to init completion.
|
|
172
|
-
*
|
|
173
|
-
* @param callback - Function called when init completes
|
|
174
|
-
* @returns Unsubscribe function
|
|
175
|
-
*
|
|
176
|
-
* @example
|
|
177
|
-
* ```typescript
|
|
178
|
-
* embedded.onInit((state) => {
|
|
179
|
-
* console.log('SDK initialized with layout:', state.layout);
|
|
180
|
-
* });
|
|
181
|
-
* ```
|
|
252
|
+
* Subscribe to init completion. Fires immediately if already initialized.
|
|
182
253
|
*/
|
|
183
254
|
onInit(callback: InitCallback): () => void;
|
|
184
255
|
/**
|
|
185
256
|
* Signal that the app is fully loaded and ready.
|
|
186
|
-
* This removes the host's loading overlay.
|
|
187
|
-
*
|
|
188
|
-
* @example
|
|
189
|
-
* ```typescript
|
|
190
|
-
* // After verifying token and loading initial data
|
|
191
|
-
* embedded.ready();
|
|
192
|
-
* ```
|
|
193
257
|
*/
|
|
194
258
|
ready(): void;
|
|
195
259
|
/**
|
|
196
260
|
* Initialize the SDK and establish connection with the host.
|
|
197
|
-
*
|
|
198
|
-
* @param options - Initialization options (optional)
|
|
199
|
-
* @returns Promise that resolves with layout info
|
|
200
|
-
*
|
|
201
|
-
* @example
|
|
202
|
-
* ```typescript
|
|
203
|
-
* const { layout } = await embedded.init({ debug: true });
|
|
204
|
-
* console.log('Theme:', layout.theme);
|
|
205
|
-
* console.log('Locale:', layout.locale);
|
|
206
|
-
* ```
|
|
207
261
|
*/
|
|
208
262
|
init(options?: InitOptions): Promise<{
|
|
209
263
|
layout: LayoutInfo;
|
|
210
264
|
}>;
|
|
211
|
-
/**
|
|
212
|
-
* Wait for initialization to complete.
|
|
213
|
-
* Useful when multiple calls to init() might happen.
|
|
214
|
-
*/
|
|
215
|
-
private waitForInit;
|
|
216
265
|
/**
|
|
217
266
|
* Destroy the SDK instance and clean up resources.
|
|
218
|
-
* Sends a destroy event to the host to navigate away from the embedded view.
|
|
219
|
-
*
|
|
220
|
-
* @example
|
|
221
|
-
* ```typescript
|
|
222
|
-
* // On auth failure or when app needs to exit
|
|
223
|
-
* embedded.destroy();
|
|
224
|
-
* ```
|
|
225
267
|
*/
|
|
226
268
|
destroy(): void;
|
|
227
269
|
}
|
|
228
270
|
|
|
229
|
-
/**
|
|
230
|
-
* Internal configuration after initialization.
|
|
231
|
-
*/
|
|
232
|
-
declare interface EmbeddedConfig {
|
|
233
|
-
debug: boolean;
|
|
234
|
-
initialized: boolean;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
271
|
/**
|
|
238
272
|
* Current state of the embedded SDK.
|
|
239
273
|
*/
|
|
@@ -257,6 +291,21 @@ export declare interface ExtendedAction {
|
|
|
257
291
|
disabled?: boolean;
|
|
258
292
|
}
|
|
259
293
|
|
|
294
|
+
/**
|
|
295
|
+
* Result of getAddons call.
|
|
296
|
+
*/
|
|
297
|
+
declare interface GetAddonsResult {
|
|
298
|
+
/** Whether the fetch was successful */
|
|
299
|
+
success: boolean;
|
|
300
|
+
/** List of available addons (if success) */
|
|
301
|
+
addons?: AddonInfo[];
|
|
302
|
+
/** Error details (if failed) */
|
|
303
|
+
error?: {
|
|
304
|
+
code: string;
|
|
305
|
+
message: string;
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
|
|
260
309
|
/**
|
|
261
310
|
* Get the singleton EmbeddedApp instance.
|
|
262
311
|
*/
|
|
@@ -538,23 +587,15 @@ export declare interface PrimaryActionConfig {
|
|
|
538
587
|
}
|
|
539
588
|
|
|
540
589
|
/**
|
|
541
|
-
* Reset the singleton (
|
|
590
|
+
* Reset the singleton (for testing).
|
|
542
591
|
*/
|
|
543
592
|
export declare function resetEmbeddedApp(): void;
|
|
544
593
|
|
|
545
|
-
/**
|
|
546
|
-
* @fileoverview Core type definitions for the Embedded SDK.
|
|
547
|
-
*/
|
|
548
594
|
/**
|
|
549
595
|
* Theme type for the SDK.
|
|
550
596
|
*/
|
|
551
597
|
declare type Theme = "light" | "dark";
|
|
552
598
|
|
|
553
|
-
/**
|
|
554
|
-
* Theme change callback type.
|
|
555
|
-
*/
|
|
556
|
-
declare type ThemeChangeCallback = (theme: "light" | "dark") => void;
|
|
557
|
-
|
|
558
599
|
/**
|
|
559
600
|
* Toast notification options.
|
|
560
601
|
*/
|
package/dist/umd/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
(function(l,s){typeof exports=="object"&&typeof module<"u"?s(exports):typeof define=="function"&&define.amd?define(["exports"],s):(l=typeof globalThis<"u"?globalThis:l||self,s(l.SallaEmbeddedSDK={}))})(this,function(l){"use strict";const s="embedded::",E={INIT:`${s}iframe.ready`,RESIZE:`${s}iframe.resize`,READY:`${s}ready`,DESTROY:`${s}destroy`},O={PROVIDE:`${s}context.provide`,THEME_CHANGE:`${s}theme.change`},h={LOADING:`${s}ui.loading`,TOAST:`${s}ui.toast`,CONFIRM:`${s}ui.confirm`,CONFIRM_RESPONSE:`${s}ui.confirm.response`},H={version:"0.1.0"},_={REFRESH:`${s}auth.refresh`},p={NAVIGATE:`${s}page.navigate`,REDIRECT:`${s}page.redirect`,SET_TITLE:`${s}page.setTitle`},v={SET_ACTION:`${s}nav.setAction`,CLEAR_ACTION:`${s}nav.clearAction`,ACTION_CLICK:`${s}nav.actionClick`},D={CREATE:`${s}checkout.create`},I=H.version,K=1e4,G=["localhost","merchants.workers.dev","s.salla.sa",".salla.group",".salla.sa"];let w={showVersion:!0,debug:!1};function X(e){w={...w,...e}}function x(e){return`%c${e}`}function M(e,t="#fff"){return`background-color: ${e}; color: ${t}; padding: 2px 6px; border-radius: 3px; font-weight: 500; font-size: 11px;`}function T(e,...t){if(e==="debug"&&!w.debug)return;const r=[],i=[];r.push(x("EmbeddedSDK")),i.push(M("#10b981","#fff")),w.showVersion&&(r.push(x(`v${I}`)),i.push(M("#6b7280","#fff")));const n=r.join("").trim();(console[e]||console.log)(n,...i,...t)}const u={log:(...e)=>{T("log",...e)},warn:(...e)=>{T("warn",...e)},error:(...e)=>{T("error",...e)},info:(...e)=>{T("info",...e)},debug:(...e)=>{T("debug",...e)}};function Y(e){try{const r=new URL(e).hostname;return G.some(i=>i.startsWith(".")?r.endsWith(i)||r===i.slice(1):r===i||r.startsWith(`${i}:`))}catch{return!1}}function W(){return typeof window>"u"||window.parent===window?null:window.parent}function o(e,t,r="*",i,n){const a=W();if(!a){u.warn("Not running in an iframe, cannot post to host");return}const d={event:e,payload:t||{},timestamp:Date.now(),source:"embedded-app",...i&&{requestId:i},metadata:{version:I}};a.postMessage(d,r)}const g=new Map;let A=!1;function U(e){if(process.env.NODE_ENV==="production"&&!Y(e.origin))return;const t=e.data;if(!t||typeof t.event!="string"||!t.payload||typeof t.timestamp!="number"||!t.source){u.warn("Invalid message structure received:",t);return}const r=g.get(t.event);r&&r.forEach(n=>{try{n(t)}catch(a){u.error("Error in message handler:",a)}});const i=g.get("*");i&&i.forEach(n=>{try{n(t)}catch(a){u.error("Error in wildcard handler:",a)}})}function B(){A||typeof window>"u"||(window.addEventListener("message",U),A=!0)}function R(e,t){B(),g.has(e)||g.set(e,new Set);const r=g.get(e);return r.add(t),()=>{r.delete(t),r.size===0&&g.delete(e)}}function Z(e,t=K){return new Promise((r,i)=>{const n=setTimeout(()=>{a(),i(new Error(`[EmbeddedSDK] Timeout waiting for "${e}" message`))},t),a=R(e,d=>{clearTimeout(n),a(),r(d)})})}function J(){g.clear(),A&&typeof window<"u"&&(window.removeEventListener("message",U),A=!1)}function Q(){return typeof window>"u"?!1:window.parent!==window}const m=new Map,ee=3e4;function te(){const e=Date.now(),t=Math.random().toString(36).slice(2,9);return`req_${e}_${t}`}function re(e,t={},r=ee){const i=te();return new Promise((n,a)=>{const d=setTimeout(()=>{m.get(i)&&(m.delete(i),a(new Error(`[EmbeddedSDK] Request "${e}" timed out after ${r}ms`)))},r);m.set(i,{resolve:n,reject:a,timeout:d,event:e}),o(e,t,"*",i)})}function ie(e,t,r){const i=m.get(e);if(!i){u.warn(`Received response for unknown request: ${e}`);return}clearTimeout(i.timeout),m.delete(e),i.resolve(t)}function ne(e="SDK cleanup"){m.forEach((t,r)=>{clearTimeout(t.timeout),t.reject(new Error(`[EmbeddedSDK] Request ${r} cancelled: ${e}`))}),m.clear()}const ae="https://api.salla.dev";class y extends Error{constructor(t,r,i){super(t),this.status=r,this.response=i,this.name="ApiError"}}async function se(e,t={}){const{method:r="GET",headers:i={},body:n,timeout:a=3e4}=t,d=`${ae}${e}`,$=new AbortController,j=setTimeout(()=>{$.abort()},a);try{const c=await fetch(d,{method:r,headers:{"Content-Type":"application/json",...i},body:n?JSON.stringify(n):void 0,signal:$.signal});clearTimeout(j);let S;const k=c.headers.get("content-type");if(k!=null&&k.includes("application/json")?S=await c.json():S=await c.text(),!c.ok)throw new y(`API request failed: ${c.statusText}`,c.status,S);return S}catch(c){throw clearTimeout(j),c instanceof y?c:c instanceof Error?c.name==="AbortError"?new y(`Request timeout after ${a}ms`):new y(`Request failed: ${c.message}`):new y("Unknown error occurred")}}function C(e){return{isVerified:!1,isError:!0,error:e,data:null}}async function oe(e){const{token:t,appId:r,refreshOnError:i=!0}=e;if(!t){const n="Token is required. Provide it as a parameter or in URL as ?token=XXX";return u.error("Error in introspect:",n),C(n)}if(!r){const n="App ID is required. Provide it as a parameter or in URL as ?app_id=XXX";return u.error("Error in introspect:",n),C(n)}try{const n=await se("/exchange-authority/v1/introspect",{method:"POST",headers:{"S-Source":r,"Content-Type":"application/json"},body:{env:"prod",token:t,iss:"merchant-dashboard",subject:"embedded-page"}}),a=n.success;return{isVerified:a,isError:!a,error:a?void 0:"API request failed",data:a?n.data:null}}catch(n){i&&(L().ui.toast.error((n==null?void 0:n.toString())??"Introspect error"),o(_.REFRESH,{})),u.error("Error in introspect:",n);const a=n instanceof Error?n.message:n;return C(a)}}function ue(e){return{getToken(){return new URLSearchParams(window.location.search).get("token")},getAppId(){return new URLSearchParams(window.location.search).get("app_id")},refresh(){o(_.REFRESH,{})},async introspect(t={}){const r=t.token??this.getToken()??"",i=t.appId??this.getAppId()??"";return oe({token:r,appId:i,refreshOnError:t.refreshOnError})}}}const q=["success","error","warning","info"];function ce(e){const t=[];return e.type===void 0||e.type===null?t.push("Toast type is required"):(typeof e.type!="string"||!q.includes(e.type))&&t.push(`Invalid toast type "${e.type}". Expected: ${q.join(" | ")}`),e.message===void 0||e.message===null?t.push("Toast message is required"):typeof e.message!="string"?t.push("Toast message must be a string"):e.message.trim()===""&&t.push("Toast message cannot be empty"),e.duration!==void 0&&e.duration!==null&&(typeof e.duration!="number"?t.push("Toast duration must be a number"):e.duration<0&&t.push("Toast duration cannot be negative")),{valid:t.length===0,errors:t}}function le(e){const t=[];return typeof e!="object"||e===null?(t.push("Checkout payload must be an object"),{valid:!1,errors:t}):(e.amount!==void 0&&e.amount!==null&&(typeof e.amount!="number"?t.push("Checkout amount must be a number"):e.amount<0&&t.push("Checkout amount cannot be negative")),e.currency!==void 0&&e.currency!==null&&(typeof e.currency!="string"?t.push("Checkout currency must be a string"):e.currency.trim()===""&&t.push("Checkout currency cannot be empty")),e.items!==void 0&&e.items!==null&&(Array.isArray(e.items)||t.push("Checkout items must be an array")),{valid:t.length===0,errors:t})}function de(e){const t=[];return e.path===void 0||e.path===null?t.push("Navigation path is required"):typeof e.path!="string"?t.push("Navigation path must be a string"):e.path.trim()===""&&t.push("Navigation path cannot be empty"),e.replace!==void 0&&typeof e.replace!="boolean"&&t.push("Navigation replace option must be a boolean"),{valid:t.length===0,errors:t}}function fe(e){const t=[];if(e.url===void 0||e.url===null)t.push("Redirect URL is required");else if(typeof e.url!="string")t.push("Redirect URL must be a string");else if(e.url.trim()==="")t.push("Redirect URL cannot be empty");else try{new URL(e.url)}catch{t.push(`Invalid redirect URL: "${e.url}"`)}return{valid:t.length===0,errors:t}}function he(e){const t=[];return e.title?typeof e.title!="string"&&t.push("Nav action title must be a string"):t.push("Nav action title is required"),e.value?typeof e.value!="string"&&t.push("Nav action value must be a string"):t.push("Nav action value is required"),e.subTitle!==void 0&&e.subTitle!==null&&typeof e.subTitle!="string"&&t.push("Nav action subTitle must be a string"),e.icon!==void 0&&e.icon!==null&&typeof e.icon!="string"&&t.push("Nav action icon must be a string"),e.disabled!==void 0&&e.disabled!==null&&typeof e.disabled!="boolean"&&t.push("Nav action disabled must be a boolean"),e.extendedActions!==void 0&&e.extendedActions!==null&&(Array.isArray(e.extendedActions)?e.extendedActions.forEach((r,i)=>{if(typeof r!="object"||r===null){t.push(`Extended action at index ${i} must be an object`);return}const n=r;(!n.title||typeof n.title!="string")&&t.push(`Extended action at index ${i} is missing required "title" property`),(!n.value||typeof n.value!="string")&&t.push(`Extended action at index ${i} is missing required "value" property`),n.subTitle!==void 0&&typeof n.subTitle!="string"&&t.push(`Extended action at index ${i} subTitle must be a string`),n.icon!==void 0&&typeof n.icon!="string"&&t.push(`Extended action at index ${i} icon must be a string`),n.disabled!==void 0&&typeof n.disabled!="boolean"&&t.push(`Extended action at index ${i} disabled must be a boolean`)}):t.push("Nav action extendedActions must be an array")),{valid:t.length===0,errors:t}}const V=["danger","warning","info"];function ge(e){const t=[];return e.title===void 0||e.title===null?t.push("Confirm dialog title is required"):typeof e.title!="string"?t.push("Confirm dialog title must be a string"):e.title.trim()===""&&t.push("Confirm dialog title cannot be empty"),e.message===void 0||e.message===null?t.push("Confirm dialog message is required"):typeof e.message!="string"?t.push("Confirm dialog message must be a string"):e.message.trim()===""&&t.push("Confirm dialog message cannot be empty"),e.confirmText!==void 0&&e.confirmText!==null&&typeof e.confirmText!="string"&&t.push("Confirm dialog confirmText must be a string"),e.cancelText!==void 0&&e.cancelText!==null&&typeof e.cancelText!="string"&&t.push("Confirm dialog cancelText must be a string"),e.variant!==void 0&&e.variant!==null&&(typeof e.variant!="string"||!V.includes(e.variant))&&t.push(`Invalid confirm variant "${e.variant}". Expected: ${V.join(" | ")}`),{valid:t.length===0,errors:t}}function f(e,t){u.error(`Validation failed for ${e}:
|
|
2
|
-
`+
|
|
3
|
-
`))}function
|
|
1
|
+
(function(l,o){typeof exports=="object"&&typeof module<"u"?o(exports):typeof define=="function"&&define.amd?define(["exports"],o):(l=typeof globalThis<"u"?globalThis:l||self,o(l.SallaEmbeddedSDK={}))})(this,function(l){"use strict";const o="embedded::",T={INIT:`${o}iframe.ready`,RESIZE:`${o}iframe.resize`,READY:`${o}ready`,DESTROY:`${o}destroy`},L={PROVIDE:`${o}context.provide`,THEME_CHANGE:`${o}theme.change`},g={LOADING:`${o}ui.loading`,TOAST:`${o}ui.toast`,CONFIRM:`${o}ui.confirm`,CONFIRM_RESPONSE:`${o}ui.confirm.response`},W={},k={REFRESH:`${o}auth.refresh`},p={NAVIGATE:`${o}page.navigate`,REDIRECT:`${o}page.redirect`,SET_TITLE:`${o}page.setTitle`},S={SET_ACTION:`${o}nav.setAction`,CLEAR_ACTION:`${o}nav.clearAction`,ACTION_CLICK:`${o}nav.actionClick`},E={CREATE:`${o}checkout.create`,RESPONSE:`${o}checkout.response`,GET_ADDONS:`${o}checkout.getAddons`,GET_ADDONS_RESPONSE:`${o}checkout.getAddons.response`},A=W.version||"",Y=1e4,B=["localhost","merchants.workers.dev","s.salla.sa",".salla.group",".salla.sa"];let R={showVersion:!0,debug:!1};function Z(t){R={...R,...t}}function q(t){return`%c${t}`}function M(t,e="#fff"){return`background-color: ${t}; color: ${e}; padding: 2px 6px; border-radius: 3px; font-weight: 500; font-size: 11px;`}function v(t,...e){if(t==="debug"&&!R.debug)return;const r=[],i=[];r.push(q("EmbeddedSDK")),i.push(M("#10b981","#fff")),R.showVersion&&(r.push(q(`v${A}`)),i.push(M("#6b7280","#fff")));const n=r.join("").trim();(console[t]||console.log)(n,...i,...e)}const a={log:(...t)=>{v("log",...t)},warn:(...t)=>{v("warn",...t)},error:(...t)=>{v("error",...t)},info:(...t)=>{v("info",...t)},debug:(...t)=>{v("debug",...t)}};function Q(t){try{const r=new URL(t).hostname;return B.some(i=>i.startsWith(".")?r.endsWith(i)||r===i.slice(1):r===i||r.startsWith(`${i}:`))}catch{return!1}}function J(){return typeof window>"u"||window.parent===window?null:window.parent}function u(t,e,r="*",i,n){const s=J();if(!s){a.warn("Not running in an iframe, cannot post to host");return}const c={event:t,payload:e||{},timestamp:Date.now(),source:"embedded-app",...i&&{requestId:i},metadata:{version:A}};s.postMessage(c,r)}const f=new Map;let I=!1;function z(t){if(process.env.NODE_ENV==="production"&&!Q(t.origin))return;const e=t.data;if(!e||typeof e.event!="string"||!e.payload||typeof e.timestamp!="number"||!e.source){a.warn("Invalid message structure received:",e);return}const r=f.get(e.event);r&&r.forEach(n=>{try{n(e)}catch(s){a.error("Error in message handler:",s)}});const i=f.get("*");i&&i.forEach(n=>{try{n(e)}catch(s){a.error("Error in wildcard handler:",s)}})}function ee(){I||typeof window>"u"||(window.addEventListener("message",z),I=!0)}function y(t,e){ee(),f.has(t)||f.set(t,new Set);const r=f.get(t);return r.add(e),()=>{r.delete(e),r.size===0&&f.delete(t)}}function te(t,e=Y){return new Promise((r,i)=>{const n=setTimeout(()=>{s(),i(new Error(`[EmbeddedSDK] Timeout waiting for "${t}" message`))},e),s=y(t,c=>{clearTimeout(n),s(),r(c)})})}function re(){f.clear(),I&&typeof window<"u"&&(window.removeEventListener("message",z),I=!1)}function ie(){return typeof window>"u"?!1:window.parent!==window}function ne(t,e,r){const i={event:t,payload:e,timestamp:Date.now(),source:"merchant-dashboard",...r,metadata:{version:A,synthetic:!0}},n=f.get(t);n==null||n.forEach(s=>{try{s(i)}catch(c){a.error("Error in message handler:",c)}})}const m=new Map,se=3e4;function P(){const t=Date.now(),e=Math.random().toString(36).slice(2,9);return`req_${t}_${e}`}function U(t,e={},r=se){const i=P();return new Promise((n,s)=>{const c=setTimeout(()=>{m.get(i)&&(m.delete(i),s(new Error(`[EmbeddedSDK] Request "${t}" timed out after ${r}ms`)))},r);m.set(i,{resolve:n,reject:s,timeout:c,event:t}),u(t,e,"*",i)})}function V(t,e,r){const i=m.get(t);if(!i){a.warn(`Received response for unknown request: ${t}`);return}clearTimeout(i.timeout),m.delete(t),i.resolve(e)}function ae(t="SDK cleanup"){m.forEach((e,r)=>{clearTimeout(e.timeout),e.reject(new Error(`[EmbeddedSDK] Request ${r} cancelled: ${t}`))}),m.clear()}function N(){const t=new Set;return{subscribe(e){return t.add(e),()=>{t.delete(e)}},notify(...e){t.forEach(r=>{try{r(...e)}catch(i){a.error("Error in subscription callback:",i)}})},clear(){t.clear()},size(){return t.size}}}const oe="https://api.salla.dev";class w extends Error{constructor(e,r,i){super(e),this.status=r,this.response=i,this.name="ApiError"}}async function ue(t,e={}){const{method:r="GET",headers:i={},body:n,timeout:s=3e4}=e,c=`${oe}${t}`,D=new AbortController,X=setTimeout(()=>{D.abort()},s);try{const d=await fetch(c,{method:r,headers:{"Content-Type":"application/json",...i},body:n?JSON.stringify(n):void 0,signal:D.signal});clearTimeout(X);let C;const x=d.headers.get("content-type");if(x!=null&&x.includes("application/json")?C=await d.json():C=await d.text(),!d.ok)throw new w(`API request failed: ${d.statusText}`,d.status,C);return C}catch(d){throw clearTimeout(X),d instanceof w?d:d instanceof Error?d.name==="AbortError"?new w(`Request timeout after ${s}ms`):new w(`Request failed: ${d.message}`):new w("Unknown error occurred")}}function $(t){return{isVerified:!1,isError:!0,error:t,data:null}}async function ce(t){const{token:e,appId:r,refreshOnError:i=!0}=t;if(!e){const n="Token is required. Provide it as a parameter or in URL as ?token=XXX";return a.error("Error in introspect:",n),$(n)}if(!r){const n="App ID is required. Provide it as a parameter or in URL as ?app_id=XXX";return a.error("Error in introspect:",n),$(n)}try{const n=await ue("/exchange-authority/v1/introspect",{method:"POST",headers:{"S-Source":r,"Content-Type":"application/json"},body:{env:"prod",token:e,iss:"merchant-dashboard",subject:"embedded-page"}}),s=n.success;return{isVerified:s,isError:!s,error:s?void 0:"API request failed",data:s?n.data:null}}catch(n){i&&(O().ui.toast.error((n==null?void 0:n.toString())??"Introspect error"),u(k.REFRESH,{})),a.error("Error in introspect:",n);const s=n instanceof Error?n.message:n;return $(s)}}function de(t){return{getToken(){return new URLSearchParams(window.location.search).get("token")},getAppId(){return new URLSearchParams(window.location.search).get("app_id")},refresh(){u(k.REFRESH,{})},async introspect(e={}){const r=e.token??this.getToken()??"",i=e.appId??this.getAppId()??"";return ce({token:r,appId:i,refreshOnError:e.refreshOnError})}}}const j=["success","error","warning","info"];function le(t){const e=[];return t.type===void 0||t.type===null?e.push("Toast type is required"):(typeof t.type!="string"||!j.includes(t.type))&&e.push(`Invalid toast type "${t.type}". Expected: ${j.join(" | ")}`),t.message===void 0||t.message===null?e.push("Toast message is required"):typeof t.message!="string"?e.push("Toast message must be a string"):t.message.trim()===""&&e.push("Toast message cannot be empty"),t.duration!==void 0&&t.duration!==null&&(typeof t.duration!="number"?e.push("Toast duration must be a number"):t.duration<0&&e.push("Toast duration cannot be negative")),{valid:e.length===0,errors:e}}function H(t,e){const r=[];return typeof t!="object"||t===null?(r.push(`${e} must be an object`),r):((typeof t.type!="string"||t.type.trim()==="")&&r.push(`${e} must have a valid type`),(typeof t.slug!="string"||t.slug.trim()==="")&&r.push(`${e} must have a valid slug`),t.quantity!==void 0&&(typeof t.quantity!="number"||t.quantity<1)&&r.push(`${e} must have a quantity >= 1`),r)}function fe(t){const e=[];if(typeof t!="object"||t===null)return e.push("Checkout options must be an object"),{valid:!1,errors:e};const r="item"in t,i="items"in t;if(!r&&!i)return e.push("Checkout requires either 'item' or 'items'"),{valid:!1,errors:e};if(r){const n=H(t.item,"Item");return e.push(...n),{valid:e.length===0,errors:e}}return Array.isArray(t.items)?t.items.length===0?(e.push("At least one item is required"),{valid:!1,errors:e}):(t.items.forEach((n,s)=>{const c=H(n,`Item at index ${s}`);e.push(...c)}),{valid:e.length===0,errors:e}):(e.push("Checkout items must be an array"),{valid:!1,errors:e})}function he(t){const e=[];return t.path===void 0||t.path===null?e.push("Navigation path is required"):typeof t.path!="string"?e.push("Navigation path must be a string"):t.path.trim()===""&&e.push("Navigation path cannot be empty"),t.replace!==void 0&&typeof t.replace!="boolean"&&e.push("Navigation replace option must be a boolean"),{valid:e.length===0,errors:e}}function ge(t){const e=[];if(t.url===void 0||t.url===null)e.push("Redirect URL is required");else if(typeof t.url!="string")e.push("Redirect URL must be a string");else if(t.url.trim()==="")e.push("Redirect URL cannot be empty");else try{new URL(t.url)}catch{e.push(`Invalid redirect URL: "${t.url}"`)}return{valid:e.length===0,errors:e}}function me(t){const e=[];return t.title?typeof t.title!="string"&&e.push("Nav action title must be a string"):e.push("Nav action title is required"),t.value?typeof t.value!="string"&&e.push("Nav action value must be a string"):e.push("Nav action value is required"),t.subTitle!==void 0&&t.subTitle!==null&&typeof t.subTitle!="string"&&e.push("Nav action subTitle must be a string"),t.icon!==void 0&&t.icon!==null&&typeof t.icon!="string"&&e.push("Nav action icon must be a string"),t.disabled!==void 0&&t.disabled!==null&&typeof t.disabled!="boolean"&&e.push("Nav action disabled must be a boolean"),t.extendedActions!==void 0&&t.extendedActions!==null&&(Array.isArray(t.extendedActions)?t.extendedActions.forEach((r,i)=>{if(typeof r!="object"||r===null){e.push(`Extended action at index ${i} must be an object`);return}const n=r;(!n.title||typeof n.title!="string")&&e.push(`Extended action at index ${i} is missing required "title" property`),(!n.value||typeof n.value!="string")&&e.push(`Extended action at index ${i} is missing required "value" property`),n.subTitle!==void 0&&typeof n.subTitle!="string"&&e.push(`Extended action at index ${i} subTitle must be a string`),n.icon!==void 0&&typeof n.icon!="string"&&e.push(`Extended action at index ${i} icon must be a string`),n.disabled!==void 0&&typeof n.disabled!="boolean"&&e.push(`Extended action at index ${i} disabled must be a boolean`)}):e.push("Nav action extendedActions must be an array")),{valid:e.length===0,errors:e}}const F=["danger","warning","info"];function pe(t){const e=[];return t.title===void 0||t.title===null?e.push("Confirm dialog title is required"):typeof t.title!="string"?e.push("Confirm dialog title must be a string"):t.title.trim()===""&&e.push("Confirm dialog title cannot be empty"),t.message===void 0||t.message===null?e.push("Confirm dialog message is required"):typeof t.message!="string"?e.push("Confirm dialog message must be a string"):t.message.trim()===""&&e.push("Confirm dialog message cannot be empty"),t.confirmText!==void 0&&t.confirmText!==null&&typeof t.confirmText!="string"&&e.push("Confirm dialog confirmText must be a string"),t.cancelText!==void 0&&t.cancelText!==null&&typeof t.cancelText!="string"&&e.push("Confirm dialog cancelText must be a string"),t.variant!==void 0&&t.variant!==null&&(typeof t.variant!="string"||!F.includes(t.variant))&&e.push(`Invalid confirm variant "${t.variant}". Expected: ${F.join(" | ")}`),{valid:e.length===0,errors:e}}function h(t,e){a.error(`Validation failed for ${t}:
|
|
2
|
+
`+e.map(r=>` • ${r}`).join(`
|
|
3
|
+
`))}function Ee(){return{navigate(t,e){const r=he({path:t,...e});if(!r.valid){h(p.NAVIGATE,r.errors);return}u(p.NAVIGATE,{path:t,state:e==null?void 0:e.state,replace:e==null?void 0:e.replace})},redirect(t){const e=ge({url:t});if(!e.valid){h(p.REDIRECT,e.errors);return}u(p.REDIRECT,{url:t})},navTo(t,e){if(t.startsWith("http://")||t.startsWith("https://")){this.redirect(t);return}this.navigate(t,e)},resize(t){if(typeof t!="number"||t<0){h(T.RESIZE,["Height must be a non-negative number"]);return}u(T.RESIZE,{height:t})},autoResize(){const t=document.documentElement.scrollHeight;this.resize(t)},setTitle(t){if(typeof t!="string"||!t.trim()){h(p.SET_TITLE,["Title must be a non-empty string"]);return}u(p.SET_TITLE,{title:t})}}}function ye(){const t=N();return y(S.ACTION_CLICK,e=>{t.notify(e.payload.value)}),{setAction(e){var i;const r=me(e);if(!r.valid){h(S.SET_ACTION,r.errors);return}u(S.SET_ACTION,{title:e.title,value:e.value,subTitle:e.subTitle,icon:e.icon,disabled:e.disabled,extendedActions:(i=e.extendedActions)==null?void 0:i.map(n=>({title:n.title,value:n.value,subTitle:n.subTitle,icon:n.icon,disabled:n.disabled}))})},clearAction(){u(S.CLEAR_ACTION,{})},onActionClick(e){return t.subscribe(e)}}}function be(){return{show(){u(g.LOADING,{action:"show"})},hide(){u(g.LOADING,{action:"hide"})}}}function Te(){const t=e=>{const r=le(e);if(!r.valid){h(g.TOAST,r.errors);return}u(g.TOAST,{type:e.type,message:e.message,duration:e.duration})};return{show:t,success(e,r){t({type:"success",message:e,duration:r})},error(e,r){t({type:"error",message:e,duration:r})},warning(e,r){t({type:"warning",message:e,duration:r})},info(e,r){t({type:"info",message:e,duration:r})}}}function ve(){return async t=>{const e=pe(t);return e.valid?U(g.CONFIRM,{title:t.title,message:t.message,confirmText:t.confirmText??"Confirm",cancelText:t.cancelText??"Cancel",variant:t.variant??"info"}):(h(g.CONFIRM,e.errors),Promise.reject(new Error(e.errors.join(", "))))}}function we(){return{loading:be(),toast:Te(),confirm:ve()}}function Se(){const t=N(),e=[];return e.push(y(E.RESPONSE,r=>{t.notify({success:r.payload.success,order_id:r.payload.order_id,status:r.payload.status,error:r.payload.error,context:r.payload.context})})),e.push(y(E.GET_ADDONS_RESPONSE,r=>{r.requestId&&V(r.requestId,{success:r.payload.success,addons:r.payload.addons,error:r.payload.error})})),{create(r,i){const n=Array.isArray(r)?r:[r],s=fe({items:n});if(!s.valid)throw h(E.CREATE,s.errors),new Error(s.errors[0]);u(E.CREATE,{items:n.map(c=>({type:c.type,slug:c.slug,quantity:c.quantity??1})),...(i==null?void 0:i.context)!==void 0&&{context:i.context}},"*",P())},onResult(r){return t.subscribe(r)},async getAddons(){try{return await U(E.GET_ADDONS,{},3e4)}catch(r){return{success:!1,error:{code:"REQUEST_FAILED",message:r instanceof Error?r.message:"Unknown error"}}}},destroy(){e.forEach(r=>r()),e.length=0,t.clear()}}}const G={theme:"light",width:0,locale:"ar",currency:"SAR"};class K{constructor(){this.initialized=!1,this.initializing=!1,this.debugMode=!1,this.appReady=!1,this.layout={...G},this.postInitHooks=[],this.themeSubscription=N(),this.initSubscription=N(),this.auth=de(),this.page=Ee(),this.nav=ye(),this.ui=we(),this.checkout=Se(),this.registerPostInitHook(e=>{const r=e.payload.pendingCheckoutResult;r&&(a.debug("Dispatching pending checkout result:",r),queueMicrotask(()=>{ne(E.RESPONSE,{success:r.success,status:r.status,error:r.error,context:r.context})}))}),this.setupListeners()}registerPostInitHook(e){this.postInitHooks.push(e)}setupListeners(){y(L.THEME_CHANGE,e=>{this.layout.theme=e.payload.theme,a.debug("Theme changed:",e.payload.theme),this.themeSubscription.notify(e.payload.theme)}),y(g.CONFIRM_RESPONSE,e=>{a.debug("Received confirm response:",e),e.requestId&&V(e.requestId,{confirmed:e.payload.confirmed})})}getState(){return{ready:this.initialized,initializing:this.initializing,layout:{...this.layout}}}isReady(){return this.initialized}onThemeChange(e){return this.themeSubscription.subscribe(e)}onInit(e){if(this.initialized)try{e(this.getState())}catch(r){a.error("Error in init callback:",r)}return this.initSubscription.subscribe(e)}ready(){if(this.appReady){a.debug("App already signaled as ready");return}if(!this.initialized){a.warn("Cannot signal ready before init() is called");return}this.appReady=!0,u(T.READY,{}),a.debug("Sent ready signal to host")}async init(e={}){if(this.initialized)return a.debug("Already initialized, returning current layout"),{layout:{...this.layout}};if(this.initializing)return a.warn("Initialization already in progress"),new Promise(r=>{const i=this.onInit(n=>{i(),r({layout:{...n.layout}})})});this.initializing=!0,this.debugMode=e.debug??!1,Z({debug:this.debugMode}),ie()||a.warn("Not running in an iframe. Some features may not work."),a.debug("Initializing SDK...");try{u(T.INIT,{height:document.documentElement.scrollHeight}),a.debug("Sent iframe.ready message, waiting for context...");const r=await te(L.PROVIDE);a.debug("Received context from host:",r);const{layout:i}=r.payload;return this.layout={theme:(i==null?void 0:i.theme)??"light",width:(i==null?void 0:i.width)??0,locale:(i==null?void 0:i.locale)??"ar",currency:(i==null?void 0:i.currency)??"SAR"},this.postInitHooks.forEach(n=>{try{n(r)}catch(s){a.error("Error in post-init hook:",s)}}),this.initialized=!0,this.initializing=!1,a.debug("Initialization complete. Layout:",this.layout),this.initSubscription.notify(this.getState()),{layout:{...this.layout}}}catch(r){throw this.initializing=!1,r}}destroy(){a.debug("Destroying SDK instance"),this.initialized&&(u(T.DESTROY,{}),a.debug("Sent destroy event to host")),this.checkout.destroy(),ae("SDK destroyed"),re(),this.themeSubscription.clear(),this.initSubscription.clear(),this.postInitHooks=[],this.initialized=!1,this.initializing=!1,this.appReady=!1,this.layout={...G}}}let b=null;function O(){return b||(b=new K),b}function Ae(){b&&(b.destroy(),b=null)}const _=O(),Re=A;typeof window<"u"&&(window.salla=window.salla||window.Salla||{},window.Salla=window.salla,window.salla.embedded||(window.salla.embedded=_),window.Salla.embedded||(window.Salla.embedded=_)),l.EmbeddedApp=K,l.embedded=_,l.getEmbeddedApp=O,l.resetEmbeddedApp=Ae,l.version=Re,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})});
|
|
4
4
|
//# sourceMappingURL=index.js.map
|