@salla.sa/embedded-sdk 0.1.0-beta.9 → 0.2.1
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 +312 -369
- 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 +40 -126
- package/dist/umd/index.js +3 -3
- package/dist/umd/index.js.map +1 -1
- package/package.json +36 -29
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,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
|
|
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, 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
|
-
|
|
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
|
-
/**
|
|
337
|
-
|
|
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(
|
|
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
|
-
*
|
|
448
|
-
*
|
|
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',
|
|
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((
|
|
493
|
-
*
|
|
494
|
-
*
|
|
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
|
-
/**
|
|
609
|
-
|
|
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(
|
|
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"},_={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
|