@salla.sa/embedded-sdk 0.1.0-beta.8 → 0.1.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/dist/cjs/index.js +3 -3
- package/dist/cjs/index.js.map +1 -1
- package/dist/esm/index.js +471 -442
- 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 +52 -132
- package/dist/umd/index.js +3 -3
- package/dist/umd/index.js.map +1 -1
- package/package.json +35 -28
package/dist/types/index.d.ts
CHANGED
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Callback for action button clicks.
|
|
3
|
+
* @param value - The value identifier of the clicked action
|
|
3
4
|
*/
|
|
4
|
-
declare type ActionClickCallback = (
|
|
5
|
+
declare type ActionClickCallback = (value: string) => void;
|
|
5
6
|
|
|
6
7
|
/**
|
|
7
8
|
* Auth module interface.
|
|
@@ -28,21 +29,9 @@ export declare interface AuthModule {
|
|
|
28
29
|
* Introspect (verify) a short-lived token with Salla's API.
|
|
29
30
|
* This method verifies the token and returns token information.
|
|
30
31
|
*
|
|
31
|
-
* @param options - Optional
|
|
32
|
-
* @returns Promise
|
|
33
|
-
* @throws {Error} If
|
|
34
|
-
*
|
|
35
|
-
* @example
|
|
36
|
-
* ```typescript
|
|
37
|
-
* // With explicit parameters
|
|
38
|
-
* const result = await embedded.auth.introspect({
|
|
39
|
-
* appId: 'my-app-id',
|
|
40
|
-
* token: 'short-lived-token'
|
|
41
|
-
* });
|
|
42
|
-
*
|
|
43
|
-
* // Using URL params (fallback)
|
|
44
|
-
* const result = await embedded.auth.introspect();
|
|
45
|
-
* ```
|
|
32
|
+
* @param options - Optional params (appId, token, refreshOnError); auto extracted from URL if not provided.
|
|
33
|
+
* @returns Promise with introspect result.
|
|
34
|
+
* @throws {Error} If required params missing.
|
|
46
35
|
*/
|
|
47
36
|
introspect(options?: IntrospectOptions): Promise<IntrospectResponse>;
|
|
48
37
|
}
|
|
@@ -150,13 +139,12 @@ export declare class EmbeddedApp {
|
|
|
150
139
|
*/
|
|
151
140
|
isReady(): boolean;
|
|
152
141
|
/**
|
|
153
|
-
*
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
* Log warnings.
|
|
142
|
+
* Unified internal logging function that supports all console log types.
|
|
143
|
+
*
|
|
144
|
+
* @param type - Log type (log, warn, error, info, debug)
|
|
145
|
+
* @param args - Arguments to log
|
|
158
146
|
*/
|
|
159
|
-
private
|
|
147
|
+
private internalLog;
|
|
160
148
|
/**
|
|
161
149
|
* Set up listener for theme changes from host.
|
|
162
150
|
*/
|
|
@@ -193,19 +181,6 @@ export declare class EmbeddedApp {
|
|
|
193
181
|
* ```
|
|
194
182
|
*/
|
|
195
183
|
onInit(callback: InitCallback): () => void;
|
|
196
|
-
/**
|
|
197
|
-
* Send log message to host for debugging/monitoring.
|
|
198
|
-
*
|
|
199
|
-
* @param level - Log level (info, warn, error)
|
|
200
|
-
* @param message - Log message
|
|
201
|
-
* @param context - Additional context
|
|
202
|
-
*
|
|
203
|
-
* @example
|
|
204
|
-
* ```typescript
|
|
205
|
-
* embedded.log('error', 'Failed to load data', { endpoint: '/api/data' });
|
|
206
|
-
* ```
|
|
207
|
-
*/
|
|
208
|
-
log(level: LogLevel, message: string, context?: Record<string, unknown>): void;
|
|
209
184
|
/**
|
|
210
185
|
* Signal that the app is fully loaded and ready.
|
|
211
186
|
* This removes the host's loading overlay.
|
|
@@ -276,9 +251,8 @@ export declare interface EmbeddedState {
|
|
|
276
251
|
*/
|
|
277
252
|
export declare interface ExtendedAction {
|
|
278
253
|
title: string;
|
|
254
|
+
value: string;
|
|
279
255
|
subTitle?: string;
|
|
280
|
-
url?: string;
|
|
281
|
-
value?: string;
|
|
282
256
|
icon?: string;
|
|
283
257
|
disabled?: boolean;
|
|
284
258
|
}
|
|
@@ -309,26 +283,30 @@ declare interface IntrospectOptions {
|
|
|
309
283
|
appId?: string;
|
|
310
284
|
/** Short-lived token. If not provided, will be extracted from URL params. */
|
|
311
285
|
token?: string;
|
|
286
|
+
/** Automatically refresh token on error (default: true) */
|
|
287
|
+
refreshOnError?: boolean;
|
|
312
288
|
}
|
|
313
289
|
|
|
314
290
|
/**
|
|
315
291
|
* Response from the introspect API.
|
|
316
292
|
*/
|
|
317
293
|
declare interface IntrospectResponse {
|
|
318
|
-
/**
|
|
319
|
-
|
|
320
|
-
/** Whether
|
|
321
|
-
|
|
322
|
-
/**
|
|
323
|
-
|
|
294
|
+
/** Whether the token was verified successfully */
|
|
295
|
+
isVerified: boolean;
|
|
296
|
+
/** Whether an error occurred */
|
|
297
|
+
isError: boolean;
|
|
298
|
+
/** Error content if an error occurred */
|
|
299
|
+
error?: unknown;
|
|
300
|
+
/** Response data (null on error) */
|
|
301
|
+
data: IntrospectResponseData | null;
|
|
324
302
|
}
|
|
325
303
|
|
|
326
304
|
/**
|
|
327
305
|
* Response data from the introspect API.
|
|
328
306
|
*/
|
|
329
307
|
declare interface IntrospectResponseData {
|
|
330
|
-
/**
|
|
331
|
-
|
|
308
|
+
/** Merchant ID (Store ID) */
|
|
309
|
+
merchant_id: number;
|
|
332
310
|
/** User ID */
|
|
333
311
|
user_id: number;
|
|
334
312
|
/** Expiration time in ISO 8601 format */
|
|
@@ -360,7 +338,6 @@ export declare type LoadingMode = "full" | "component";
|
|
|
360
338
|
declare interface LoadingSubModule {
|
|
361
339
|
/**
|
|
362
340
|
* Show loading indicator.
|
|
363
|
-
* @param mode - Loading display mode
|
|
364
341
|
*
|
|
365
342
|
* @example
|
|
366
343
|
* ```typescript
|
|
@@ -369,62 +346,13 @@ declare interface LoadingSubModule {
|
|
|
369
346
|
* embedded.ui.loading.hide();
|
|
370
347
|
* ```
|
|
371
348
|
*/
|
|
372
|
-
show(
|
|
349
|
+
show(): void;
|
|
373
350
|
/**
|
|
374
351
|
* Hide loading indicator.
|
|
375
352
|
*/
|
|
376
353
|
hide(): void;
|
|
377
354
|
}
|
|
378
355
|
|
|
379
|
-
/**
|
|
380
|
-
* Log level type.
|
|
381
|
-
*/
|
|
382
|
-
declare type LogLevel = "info" | "warn" | "error";
|
|
383
|
-
|
|
384
|
-
/**
|
|
385
|
-
* Modal action types.
|
|
386
|
-
*/
|
|
387
|
-
export declare type ModalAction = "open" | "close";
|
|
388
|
-
|
|
389
|
-
/**
|
|
390
|
-
* Modal action type.
|
|
391
|
-
*/
|
|
392
|
-
declare type ModalAction_2 = "open" | "close";
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* Modal control options.
|
|
396
|
-
*/
|
|
397
|
-
export declare interface ModalOptions {
|
|
398
|
-
/** Modal action */
|
|
399
|
-
action: ModalAction_2;
|
|
400
|
-
/** Modal identifier */
|
|
401
|
-
id?: string;
|
|
402
|
-
/** Modal content/data */
|
|
403
|
-
content?: unknown;
|
|
404
|
-
}
|
|
405
|
-
|
|
406
|
-
/**
|
|
407
|
-
* Modal sub-module interface.
|
|
408
|
-
*/
|
|
409
|
-
declare interface ModalSubModule {
|
|
410
|
-
/**
|
|
411
|
-
* Open a modal.
|
|
412
|
-
* @param id - Modal identifier
|
|
413
|
-
* @param content - Modal content/data
|
|
414
|
-
*
|
|
415
|
-
* @example
|
|
416
|
-
* ```typescript
|
|
417
|
-
* embedded.ui.modal.open('confirm-delete', { itemId: 123 });
|
|
418
|
-
* ```
|
|
419
|
-
*/
|
|
420
|
-
open(id?: string, content?: unknown): void;
|
|
421
|
-
/**
|
|
422
|
-
* Close a modal.
|
|
423
|
-
* @param id - Modal identifier
|
|
424
|
-
*/
|
|
425
|
-
close(id?: string): void;
|
|
426
|
-
}
|
|
427
|
-
|
|
428
356
|
/**
|
|
429
357
|
* Nav module interface.
|
|
430
358
|
*/
|
|
@@ -436,22 +364,11 @@ export declare interface NavModule {
|
|
|
436
364
|
*
|
|
437
365
|
* @example
|
|
438
366
|
* ```typescript
|
|
367
|
+
* // Simple action button
|
|
439
368
|
* embedded.nav.setAction({
|
|
440
369
|
* title: 'Create Product',
|
|
441
|
-
*
|
|
442
|
-
*
|
|
443
|
-
* }
|
|
444
|
-
* });
|
|
445
|
-
*
|
|
446
|
-
* // With optional props
|
|
447
|
-
* embedded.nav.setAction({
|
|
448
|
-
* title: 'Save',
|
|
449
|
-
* subTitle: 'Save changes',
|
|
450
|
-
* icon: 'sicon-save',
|
|
451
|
-
* disabled: false,
|
|
452
|
-
* onClick: () => {
|
|
453
|
-
* handleSave();
|
|
454
|
-
* }
|
|
370
|
+
* value: 'create-product',
|
|
371
|
+
* icon: 'sicon-plus',
|
|
455
372
|
* });
|
|
456
373
|
*
|
|
457
374
|
* // With extended actions dropdown
|
|
@@ -459,10 +376,19 @@ export declare interface NavModule {
|
|
|
459
376
|
* title: 'Actions',
|
|
460
377
|
* value: 'main-action',
|
|
461
378
|
* extendedActions: [
|
|
462
|
-
* { title: 'Import',
|
|
463
|
-
* { title: 'Export', value: 'export' }
|
|
379
|
+
* { title: 'Import', value: 'import' },
|
|
380
|
+
* { title: 'Export', value: 'export' },
|
|
464
381
|
* ]
|
|
465
382
|
* });
|
|
383
|
+
*
|
|
384
|
+
* // Handle clicks via onActionClick
|
|
385
|
+
* embedded.nav.onActionClick((value) => {
|
|
386
|
+
* if (value === 'create-product') {
|
|
387
|
+
* openCreateForm();
|
|
388
|
+
* } else if (value === 'import') {
|
|
389
|
+
* embedded.page.navigate('/import');
|
|
390
|
+
* }
|
|
391
|
+
* });
|
|
466
392
|
* ```
|
|
467
393
|
*/
|
|
468
394
|
setAction(config: PrimaryActionConfig): void;
|
|
@@ -478,27 +404,27 @@ export declare interface NavModule {
|
|
|
478
404
|
/**
|
|
479
405
|
* Subscribe to action button click events.
|
|
480
406
|
*
|
|
481
|
-
* @param callback - Function called when action is clicked
|
|
407
|
+
* @param callback - Function called when action is clicked, receives the action value
|
|
482
408
|
* @returns Unsubscribe function
|
|
483
409
|
*
|
|
484
410
|
* @example
|
|
485
411
|
* ```typescript
|
|
486
|
-
* const unsubscribe = embedded.nav.onActionClick((
|
|
487
|
-
*
|
|
488
|
-
*
|
|
412
|
+
* const unsubscribe = embedded.nav.onActionClick((value) => {
|
|
413
|
+
* switch (value) {
|
|
414
|
+
* case 'save':
|
|
415
|
+
* handleSave();
|
|
416
|
+
* break;
|
|
417
|
+
* case 'export':
|
|
418
|
+
* handleExport();
|
|
419
|
+
* break;
|
|
420
|
+
* case 'settings':
|
|
421
|
+
* embedded.page.navigate('/settings');
|
|
422
|
+
* break;
|
|
489
423
|
* }
|
|
490
424
|
* });
|
|
491
425
|
* ```
|
|
492
426
|
*/
|
|
493
427
|
onActionClick(callback: ActionClickCallback): Unsubscribe;
|
|
494
|
-
/**
|
|
495
|
-
* @deprecated Use setAction instead
|
|
496
|
-
*/
|
|
497
|
-
primaryAction(config: PrimaryActionConfig): void;
|
|
498
|
-
/**
|
|
499
|
-
* @deprecated Use clearAction instead
|
|
500
|
-
*/
|
|
501
|
-
clearPrimaryAction(): void;
|
|
502
428
|
}
|
|
503
429
|
|
|
504
430
|
/**
|
|
@@ -599,10 +525,8 @@ export declare interface PageModule {
|
|
|
599
525
|
export declare interface PrimaryActionConfig {
|
|
600
526
|
/** Button title */
|
|
601
527
|
title: string;
|
|
602
|
-
/**
|
|
603
|
-
|
|
604
|
-
/** Custom value for identifying the action (passed to onClick callback) */
|
|
605
|
-
value?: string;
|
|
528
|
+
/** Value identifier for the action (passed to onActionClick callback) */
|
|
529
|
+
value: string;
|
|
606
530
|
/** Optional subtitle */
|
|
607
531
|
subTitle?: string;
|
|
608
532
|
/** Optional icon class name */
|
|
@@ -709,10 +633,6 @@ export declare interface UIModule {
|
|
|
709
633
|
* Toast notifications.
|
|
710
634
|
*/
|
|
711
635
|
toast: ToastSubModule;
|
|
712
|
-
/**
|
|
713
|
-
* Modal control.
|
|
714
|
-
*/
|
|
715
|
-
modal: ModalSubModule;
|
|
716
636
|
/**
|
|
717
637
|
* Show a confirmation dialog and wait for user response.
|
|
718
638
|
*
|
package/dist/umd/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
(function(d,v){typeof exports=="object"&&typeof module<"u"?v(exports):typeof define=="function"&&define.amd?define(["exports"],v):(d=typeof globalThis<"u"?globalThis:d||self,v(d.SallaEmbeddedSDK={}))})(this,function(d){"use strict";const P={version:"0.1.0-beta.8"},s="embedded::",b={INIT:`${s}iframe.ready`,RESIZE:`${s}iframe.resize`,READY:`${s}ready`,DESTROY:`${s}destroy`},N={PROVIDE:`${s}context.provide`,THEME_CHANGE:`${s}theme.change`},q={LOG:`${s}log`},z={REFRESH:`${s}auth.refresh`},g={NAVIGATE:`${s}page.navigate`,REDIRECT:`${s}page.redirect`,SET_TITLE:`${s}page.setTitle`},w={SET_ACTION:`${s}nav.setAction`,ACTION_CLICK:`${s}nav.actionClick`},c={LOADING:`${s}ui.loading`,TOAST:`${s}ui.toast`,MODAL:`${s}ui.modal`,CONFIRM:`${s}ui.confirm`,CONFIRM_RESPONSE:`${s}ui.confirm.response`,MODAL_RESPONSE:`${s}ui.modal.response`},D={CREATE:`${s}checkout.create`},R=P.version,V=1e4,F=["localhost","merchants.workers.dev","s.salla.sa",".salla.group",".salla.sa"];function j(t){try{const n=new URL(t).hostname;return F.some(i=>i.startsWith(".")?n.endsWith(i)||n===i.slice(1):n===i||n.startsWith(`${i}:`))}catch{return!1}}function G(){return typeof window>"u"||window.parent===window?null:window.parent}function l(t,e,n="*"){const i=G();if(!i){console.warn("[EmbeddedSDK] Not running in an iframe, cannot post to host");return}const r={event:t,...e};i.postMessage(r,n)}const h=new Map;let S=!1;function $(t){if(process.env.NODE_ENV==="production"&&!j(t.origin))return;const e=t.data;if(!e||typeof e.event!="string")return;const n=h.get(e.event);n&&n.forEach(r=>{try{r(e)}catch(a){console.error("[EmbeddedSDK] Error in message handler:",a)}});const i=h.get("*");i&&i.forEach(r=>{try{r(e)}catch(a){console.error("[EmbeddedSDK] Error in wildcard handler:",a)}})}function H(){S||typeof window>"u"||(window.addEventListener("message",$),S=!0)}function T(t,e){H(),h.has(t)||h.set(t,new Set);const n=h.get(t);return n.add(e),()=>{n.delete(e),n.size===0&&h.delete(t)}}function X(t,e=V){return new Promise((n,i)=>{const r=setTimeout(()=>{a(),i(new Error(`[EmbeddedSDK] Timeout waiting for "${t}" message`))},e),a=T(t,o=>{clearTimeout(r),a(),n(o)})})}function Y(){h.clear(),S&&typeof window<"u"&&(window.removeEventListener("message",$),S=!1)}function W(){return typeof window>"u"?!1:window.parent!==window}const m=new Map,Z=3e4;function B(){const t=Date.now(),e=Math.random().toString(36).slice(2,9);return`req_${t}_${e}`}function J(t,e={},n=Z){const i=B();return new Promise((r,a)=>{const o=setTimeout(()=>{m.get(i)&&(m.delete(i),a(new Error(`[EmbeddedSDK] Request "${t}" timed out after ${n}ms`)))},n);m.set(i,{resolve:r,reject:a,timeout:o,event:t}),l(t,{...e,requestId:i})})}function O(t,e,n){const i=m.get(t);if(!i){console.warn(`[EmbeddedSDK] Received response for unknown request: ${t}`);return}clearTimeout(i.timeout),m.delete(t),n?i.reject(new Error(n)):i.resolve(e)}function Q(t="SDK cleanup"){m.forEach((e,n)=>{clearTimeout(e.timeout),e.reject(new Error(`[EmbeddedSDK] Request ${n} cancelled: ${t}`))}),m.clear()}const ee="https://api.salla.dev";class p extends Error{constructor(e,n,i){super(e),this.status=n,this.response=i,this.name="ApiError"}}async function te(t,e={}){const{method:n="GET",headers:i={},body:r,timeout:a=3e4}=e,o=`${ee}${t}`,y=new AbortController,A=setTimeout(()=>{y.abort()},a);try{const u=await fetch(o,{method:n,headers:{"Content-Type":"application/json",...i},body:r?JSON.stringify(r):void 0,signal:y.signal});clearTimeout(A);let C;const L=u.headers.get("content-type");if(L!=null&&L.includes("application/json")?C=await u.json():C=await u.text(),!u.ok)throw new p(`API request failed: ${u.statusText}`,u.status,C);return C}catch(u){throw clearTimeout(A),u instanceof p?u:u instanceof Error?u.name==="AbortError"?new p(`Request timeout after ${a}ms`):new p(`Request failed: ${u.message}`):new p("Unknown error occurred")}}function ie(t){return{getToken(){return new URLSearchParams(window.location.search).get("token")},getAppId(){return new URLSearchParams(window.location.search).get("app_id")},refresh(){l(z.REFRESH,{})},async introspect(e={}){const n=e.token??this.getToken();if(!n)throw new Error("Token is required. Provide it as a parameter or in URL as ?token=XXX");const i=e.appId??this.getAppId();if(!i)throw new Error("App ID is required. Provide it as a parameter or in URL as ?app_id=XXX");return await te("/exchange-authority/v1/introspect",{method:"POST",headers:{"S-Source":i,"Content-Type":"application/json"},body:{env:"prod",token:n,iss:"merchant-dashboard",subject:"embedded-page"}})}}}const k=["success","error","warning","info"];function ne(t){const e=[];return t.type===void 0||t.type===null?e.push("Toast type is required"):(typeof t.type!="string"||!k.includes(t.type))&&e.push(`Invalid toast type "${t.type}". Expected: ${k.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 re(t){const e=[];return typeof t!="object"||t===null?(e.push("Checkout payload must be an object"),{valid:!1,errors:e}):(t.amount!==void 0&&t.amount!==null&&(typeof t.amount!="number"?e.push("Checkout amount must be a number"):t.amount<0&&e.push("Checkout amount cannot be negative")),t.currency!==void 0&&t.currency!==null&&(typeof t.currency!="string"?e.push("Checkout currency must be a string"):t.currency.trim()===""&&e.push("Checkout currency cannot be empty")),t.items!==void 0&&t.items!==null&&(Array.isArray(t.items)||e.push("Checkout items must be an array")),{valid:e.length===0,errors:e})}function se(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 ae(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 oe(t){const e=[];return t.title===void 0||t.title===null?e.push("Nav action title is required"):typeof t.title!="string"&&e.push("Nav action title must be a string"),t.onClick!==void 0&&t.onClick!==null&&typeof t.onClick!="function"&&e.push("Nav action onClick must be a function"),t.value!==void 0&&t.value!==null&&typeof t.value!="string"&&e.push("Nav action value must be a string"),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((n,i)=>{if(typeof n!="object"||n===null){e.push(`Extended action at index ${i} must be an object`);return}const r=n;(!r.title||typeof r.title!="string")&&e.push(`Extended action at index ${i} is missing required "title" property`),r.subTitle!==void 0&&typeof r.subTitle!="string"&&e.push(`Extended action at index ${i} subTitle must be a string`),r.url!==void 0&&typeof r.url!="string"&&e.push(`Extended action at index ${i} url must be a string`),r.value!==void 0&&typeof r.value!="string"&&e.push(`Extended action at index ${i} value must be a string`),r.icon!==void 0&&typeof r.icon!="string"&&e.push(`Extended action at index ${i} icon must be a string`),r.disabled!==void 0&&typeof r.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 _=["danger","warning","info"];function le(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"||!_.includes(t.variant))&&e.push(`Invalid confirm variant "${t.variant}". Expected: ${_.join(" | ")}`),{valid:e.length===0,errors:e}}function f(t,e){console.error(`[EmbeddedSDK] Validation failed for ${t}:
|
|
2
|
-
`+
|
|
3
|
-
`))}function
|
|
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-beta.12"},_={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
|
+
`+t.map(r=>` • ${r}`).join(`
|
|
3
|
+
`))}function me(){return{navigate(e,t){const r=de({path:e,...t});if(!r.valid){f(p.NAVIGATE,r.errors);return}o(p.NAVIGATE,{path:e,state:t==null?void 0:t.state,replace:t==null?void 0:t.replace})},redirect(e){const t=fe({url:e});if(!t.valid){f(p.REDIRECT,t.errors);return}o(p.REDIRECT,{url:e})},navTo(e,t){if(e.startsWith("http://")||e.startsWith("https://")){this.redirect(e);return}this.navigate(e,t)},resize(e){if(typeof e!="number"||e<0){f(E.RESIZE,["Height must be a non-negative number"]);return}o(E.RESIZE,{height:e})},autoResize(){const e=document.documentElement.scrollHeight;this.resize(e)},setTitle(e){if(typeof e!="string"||!e.trim()){f(p.SET_TITLE,["Title must be a non-empty string"]);return}o(p.SET_TITLE,{title:e})}}}function pe(){const e=new Set;return R(v.ACTION_CLICK,r=>{e.forEach(i=>{try{i(r.payload.value)}catch(n){u.error("Error in action click callback:",n)}})}),{setAction(r){var n;const i=he(r);if(!i.valid){f(v.SET_ACTION,i.errors);return}o(v.SET_ACTION,{title:r.title,value:r.value,subTitle:r.subTitle,icon:r.icon,disabled:r.disabled,extendedActions:(n=r.extendedActions)==null?void 0:n.map(a=>({title:a.title,value:a.value,subTitle:a.subTitle,icon:a.icon,disabled:a.disabled}))})},clearAction(){o(v.CLEAR_ACTION,{})},onActionClick(r){return e.add(r),()=>{e.delete(r)}}}}function be(){return{show(){o(h.LOADING,{action:"show"})},hide(){o(h.LOADING,{action:"hide"})}}}function Ee(){const e=t=>{const r=ce(t);if(!r.valid){f(h.TOAST,r.errors);return}o(h.TOAST,{type:t.type,message:t.message,duration:t.duration})};return{show:e,success(t,r){e({type:"success",message:t,duration:r})},error(t,r){e({type:"error",message:t,duration:r})},warning(t,r){e({type:"warning",message:t,duration:r})},info(t,r){e({type:"info",message:t,duration:r})}}}function Te(){return async e=>{const t=ge(e);return t.valid?re(h.CONFIRM,{title:e.title,message:e.message,confirmText:e.confirmText??"Confirm",cancelText:e.cancelText??"Cancel",variant:e.variant??"info"}):(f(h.CONFIRM,t.errors),Promise.reject(new Error(t.errors.join(", "))))}}function ye(){return{loading:be(),toast:Ee(),confirm:Te()}}function ve(){return{create(e){const t=le(e);if(!t.valid){f(D.CREATE,t.errors);return}o(D.CREATE,e)}}}const z={debug:!1,initialized:!1},F={ready:!1,initializing:!1,layout:{...{theme:"light",width:0,locale:"ar",currency:"SAR"}}};class P{constructor(){this.config={...z},this.state={...F},this.themeCallbacks=new Set,this.initCallbacks=new Set,this.appReady=!1,this.auth=ue(),this.page=me(),this.nav=pe(),this.ui=ye(),this.checkout=ve(),this.setupThemeListener(),this.setupResponseListeners()}getState(){return{ready:this.state.ready,initializing:this.state.initializing,layout:{...this.state.layout}}}getConfig(){return{...this.config}}isReady(){return this.state.ready}internalLog(t,...r){switch(t){case"log":u.log(...r);break;case"warn":u.warn(...r);break;case"error":u.error(...r);break;case"info":u.info(...r);break;case"debug":u.debug(...r);break}}setupThemeListener(){R(O.THEME_CHANGE,t=>{this.state.layout.theme=t.payload.theme,this.internalLog("debug","Theme changed:",t.payload.theme),this.themeCallbacks.forEach(r=>{try{r(t.payload.theme)}catch(i){this.internalLog("error","Error in theme callback:",i)}})})}setupResponseListeners(){R(h.CONFIRM_RESPONSE,t=>{this.internalLog("debug","Received confirm response:",t),t.requestId&&ie(t.requestId,{confirmed:t.payload.confirmed})})}onThemeChange(t){return this.themeCallbacks.add(t),()=>{this.themeCallbacks.delete(t)}}onInit(t){if(this.config.initialized)try{t(this.getState())}catch(r){this.internalLog("error","Error in init callback:",r)}return this.initCallbacks.add(t),()=>{this.initCallbacks.delete(t)}}ready(){if(this.appReady){this.internalLog("debug","App already signaled as ready");return}if(!this.config.initialized){this.internalLog("warn","Cannot signal ready before init() is called");return}this.appReady=!0,o(E.READY,{}),this.internalLog("debug","Sent ready signal to host")}async init(t={}){if(this.config.initialized)return this.internalLog("debug","Already initialized, returning current layout"),{layout:{...this.state.layout}};if(this.state.initializing)return this.internalLog("warn","Initialization already in progress"),this.waitForInit();Q()||this.internalLog("warn","Not running in an iframe. Some features may not work."),this.config={debug:t.debug??!1,initialized:!1},X({debug:this.config.debug}),this.state.initializing=!0,this.internalLog("debug","Initializing SDK...");try{o(E.INIT,{height:document.documentElement.scrollHeight}),this.internalLog("debug","Sent iframe.ready message, waiting for context...");const r=await Z(O.PROVIDE);this.internalLog("debug","Received context from host:",r);const i=r.payload.layout;this.state={ready:!0,initializing:!1,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.config.initialized=!0,this.internalLog("debug","Initialization complete. Layout:",this.state.layout);const n=this.getState();return this.initCallbacks.forEach(a=>{try{a(n)}catch(d){this.internalLog("error","Error in init callback:",d)}}),{layout:{...this.state.layout}}}catch(r){throw this.state.initializing=!1,this.state.ready=!1,r}}waitForInit(){return new Promise(t=>{const r=this.onInit(i=>{r(),t({layout:{...i.layout}})})})}destroy(){this.internalLog("debug","Destroying SDK instance"),this.config.initialized&&(o(E.DESTROY,{}),this.internalLog("debug","Sent destroy event to host")),ne("SDK destroyed"),J(),this.themeCallbacks.clear(),this.initCallbacks.clear(),this.config={...z},this.state={...F},this.appReady=!1}}let b=null;function L(){return b||(b=new P),b}function we(){b&&(b.destroy(),b=null)}const N=L(),Ae=I;typeof window<"u"&&(window.salla=window.salla||window.Salla||{},window.Salla=window.salla,window.salla.embedded||(window.salla.embedded=N),window.Salla.embedded||(window.Salla.embedded=N)),l.EmbeddedApp=P,l.embedded=N,l.getEmbeddedApp=L,l.resetEmbeddedApp=we,l.version=Ae,Object.defineProperty(l,Symbol.toStringTag,{value:"Module"})});
|
|
4
4
|
//# sourceMappingURL=index.js.map
|