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