@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.
@@ -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,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 parameters (appId and token). If not provided, will be extracted from URL params.
32
- * @returns Promise that resolves with the introspect response
33
- * @throws {Error} If appId or token is missing, or if the API request fails
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
- * Log debug messages if debug mode is enabled.
154
- */
155
- private debugLog;
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 warn;
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
- /** HTTP status code */
319
- status: number;
320
- /** Whether the request was successful */
321
- success: boolean;
322
- /** Response data */
323
- data: IntrospectResponseData;
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
- /** Token ID */
331
- id: number;
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(mode?: LoadingMode): void;
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
- * onClick: () => {
442
- * // Handle click
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', url: '/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((url, value) => {
487
- * if (value === 'export') {
488
- * 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;
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
- /** Callback function to execute when clicked (replaces url) */
603
- onClick?: () => void;
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
- `+e.map(n=>` • ${n}`).join(`
3
- `))}function ue(){return{navigate(t,e){const n=se({path:t,...e});if(!n.valid){f(g.NAVIGATE,n.errors);return}l(g.NAVIGATE,{path:t,state:e==null?void 0:e.state,replace:e==null?void 0:e.replace})},redirect(t){const e=ae({url:t});if(!e.valid){f(g.REDIRECT,e.errors);return}l(g.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){f(b.RESIZE,["Height must be a non-negative number"]);return}l(b.RESIZE,{height:t})},autoResize(){const t=document.documentElement.scrollHeight;this.resize(t)},setTitle(t){if(typeof t!="string"||!t.trim()){f(g.SET_TITLE,["Title must be a non-empty string"]);return}l(g.SET_TITLE,{title:t})}}}function de(){const t=new Set;let e=null;return T(w.ACTION_CLICK,i=>{if(e)try{e()}catch(r){console.error("[EmbeddedSDK] Error in onClick callback:",r)}t.forEach(r=>{try{r(i.url,i.value)}catch(a){console.error("[EmbeddedSDK] Error in action click callback:",a)}})}),{setAction(i){var a;const r=oe(i);if(!r.valid){f(w.SET_ACTION,r.errors);return}i.onClick?e=i.onClick:e=null,l(w.SET_ACTION,{title:i.title,onClick:i.onClick?!0:void 0,value:i.value,subTitle:i.subTitle,icon:i.icon,disabled:i.disabled,extendedActions:(a=i.extendedActions)==null?void 0:a.map(o=>({title:o.title,subTitle:o.subTitle,url:o.url,value:o.value,icon:o.icon,disabled:o.disabled}))})},clearAction(){e=null,l(w.SET_ACTION,{title:""})},onActionClick(i){return t.add(i),()=>{t.delete(i)}},primaryAction(i){this.setAction(i)},clearPrimaryAction(){this.clearAction()}}}function ce(){return{show(t="full"){l(c.LOADING,{status:!1,mode:t})},hide(){l(c.LOADING,{status:!0,mode:"full"})}}}function fe(){const t=e=>{const n=ne(e);if(!n.valid){f(c.TOAST,n.errors);return}l(c.TOAST,{type:e.type,message:e.message,duration:e.duration})};return{show:t,success(e,n){t({type:"success",message:e,duration:n})},error(e,n){t({type:"error",message:e,duration:n})},warning(e,n){t({type:"warning",message:e,duration:n})},info(e,n){t({type:"info",message:e,duration:n})}}}function he(){return{open(t,e){l(c.MODAL,{action:"open",id:t,content:e})},close(t){l(c.MODAL,{action:"close",id:t})}}}function me(){return async t=>{const e=le(t);return e.valid?J(c.CONFIRM,{title:t.title,message:t.message,confirmText:t.confirmText??"Confirm",cancelText:t.cancelText??"Cancel",variant:t.variant??"info"}):(f(c.CONFIRM,e.errors),Promise.reject(new Error(e.errors.join(", "))))}}function ge(){return{loading:ce(),toast:fe(),modal:he(),confirm:me()}}function Ee(){return{create(t){const e=re(t);if(!e.valid){f(D.CREATE,e.errors);return}l(D.CREATE,{payload:t})}}}const M={debug:!1,initialized:!1},x={ready:!1,initializing:!1,layout:{...{theme:"light",width:0,locale:"ar",currency:"SAR"}}};class U{constructor(){this.config={...M},this.state={...x},this.themeCallbacks=new Set,this.initCallbacks=new Set,this.appReady=!1,this.auth=ie(),this.page=ue(),this.nav=de(),this.ui=ge(),this.checkout=Ee(),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}debugLog(...e){this.config.debug&&console.log(`[EmbeddedSDK v${R}]`,...e)}warn(...e){console.warn(`[EmbeddedSDK v${R}]`,...e)}setupThemeListener(){T(N.THEME_CHANGE,e=>{this.state.layout.theme=e.theme,this.debugLog("Theme changed:",e.theme),this.themeCallbacks.forEach(n=>{try{n(e.theme)}catch(i){console.error("[EmbeddedSDK] Error in theme callback:",i)}})})}setupResponseListeners(){T(c.CONFIRM_RESPONSE,e=>{this.debugLog("Received confirm response:",e),O(e.requestId,{confirmed:e.confirmed})}),T(c.MODAL_RESPONSE,e=>{this.debugLog("Received modal response:",e),O(e.requestId,e.result,e.error)})}onThemeChange(e){return this.themeCallbacks.add(e),()=>{this.themeCallbacks.delete(e)}}onInit(e){if(this.config.initialized)try{e(this.getState())}catch(n){console.error("[EmbeddedSDK] Error in init callback:",n)}return this.initCallbacks.add(e),()=>{this.initCallbacks.delete(e)}}log(e,n,i){l(q.LOG,{level:e,message:n,context:i})}ready(){if(this.appReady){this.debugLog("App already signaled as ready");return}if(!this.config.initialized){this.warn("Cannot signal ready before init() is called");return}this.appReady=!0,l(b.READY,{}),this.debugLog("Sent ready signal to host")}async init(e={}){var n,i,r,a;if(this.config.initialized)return this.debugLog("Already initialized, returning current layout"),{layout:{...this.state.layout}};if(this.state.initializing)return this.warn("Initialization already in progress"),this.waitForInit();W()||this.warn("Not running in an iframe. Some features may not work."),this.config={debug:e.debug??!1,initialized:!1},this.state.initializing=!0,this.debugLog("Initializing SDK...");try{l(b.INIT,{height:document.documentElement.scrollHeight}),this.debugLog("Sent iframe.ready message, waiting for context...");const o=await X(N.PROVIDE);this.debugLog("Received context from host:",o),this.state={ready:!0,initializing:!1,layout:{theme:((n=o.layout)==null?void 0:n.theme)??"light",width:((i=o.layout)==null?void 0:i.width)??0,locale:((r=o.layout)==null?void 0:r.locale)??"ar",currency:((a=o.layout)==null?void 0:a.currency)??"SAR"}},this.config.initialized=!0,this.debugLog("Initialization complete. Layout:",this.state.layout);const y=this.getState();return this.initCallbacks.forEach(A=>{try{A(y)}catch(u){console.error("[EmbeddedSDK] Error in init callback:",u)}}),{layout:{...this.state.layout}}}catch(o){throw this.state.initializing=!1,this.state.ready=!1,o}}waitForInit(){return new Promise(e=>{const n=this.onInit(i=>{n(),e({layout:{...i.layout}})})})}destroy(){this.debugLog("Destroying SDK instance"),this.config.initialized&&(l(b.DESTROY,{}),this.debugLog("Sent destroy event to host")),Q("SDK destroyed"),Y(),this.themeCallbacks.clear(),this.initCallbacks.clear(),this.config={...M},this.state={...x},this.appReady=!1}}let E=null;function K(){return E||(E=new U),E}function be(){E&&(E.destroy(),E=null)}const I=K(),Te=R;typeof window<"u"&&(window.salla=window.salla||window.Salla||{},window.Salla=window.salla,window.salla.embedded||(window.salla.embedded=I),window.Salla.embedded||(window.Salla.embedded=I)),d.EmbeddedApp=U,d.embedded=I,d.getEmbeddedApp=K,d.resetEmbeddedApp=be,d.version=Te,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