@salla.sa/embedded-sdk 0.1.0-beta.5 → 0.1.0-beta.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -14,295 +14,80 @@ yarn add @salla.sa/embedded-sdk
14
14
 
15
15
  ## Quick Start
16
16
 
17
- ### ES Modules (Recommended)
18
-
19
17
  ```typescript
20
18
  import { embedded } from "@salla.sa/embedded-sdk";
21
19
 
22
20
  async function bootstrap() {
23
21
  try {
24
- // 1. Initialize SDK and get layout info
22
+ // Initialize SDK
25
23
  const { layout } = await embedded.init({ debug: true });
26
- console.log("Theme:", layout.theme);
27
- console.log("Locale:", layout.locale);
28
24
 
29
- // 2. Get token from URL and verify with your backend
25
+ // Get token
30
26
  const token = embedded.auth.getToken();
31
- if (!token) {
32
- throw new Error("No token found");
33
- }
27
+ if (!token) throw new Error("No token found");
28
+
29
+ // Verify token with your backend before signaling ready
30
+ const isValid = await fetch("/api/verify-token", {
31
+ method: "POST",
32
+ headers: { "Content-Type": "application/json" },
33
+ body: JSON.stringify({ token }),
34
+ }).then((res) => res.ok);
34
35
 
35
- const verified = await verifyWithBackend(token);
36
- if (!verified) {
37
- throw new Error("Token verification failed");
38
- }
36
+ if (!isValid) throw new Error("Token is invalid");
39
37
 
40
- // 3. Signal that app is ready (removes host loading overlay)
38
+ // Signal app is ready only after successful verification
41
39
  embedded.ready();
42
40
 
43
- // 4. Set up your app
41
+ // Set up your app
44
42
  embedded.page.setTitle("My App");
45
43
  } catch (err) {
46
- // Exit embedded view (navigates to apps page)
47
44
  embedded.destroy();
48
45
  }
49
46
  }
50
-
51
- bootstrap();
52
47
  ```
53
48
 
54
- ### UMD (Browser Global)
49
+ End of Selection
55
50
 
56
- ```html
57
- <script src="https://unpkg.com/@salla.sa/embedded-sdk/dist/index.umd.js"></script>
58
- <script>
59
- const embedded = SallaEmbeddedSDK.embedded;
60
-
61
- embedded.init({ debug: true }).then(function (result) {
62
- console.log("Layout:", result.layout);
63
-
64
- const token = embedded.auth.getToken();
65
- // ... verify token and call embedded.ready()
66
- });
67
- </script>
68
51
  ```
69
52
 
70
- ### Script Access via Salla Object
53
+ bootstrap();
54
+ ```
71
55
 
72
- The SDK is also accessible via the global `Salla` object:
56
+ ### Browser Global
73
57
 
74
58
  ```html
59
+ <script src="https://unpkg.com/@salla.sa/embedded-sdk/dist/umd/index.js"></script>
75
60
  <script>
76
- // Access SDK from Salla namespace
77
- Salla.embedded.init({ debug: true });
61
+ const embedded = Salla.embedded;
62
+ embedded.init({ debug: true }).then((result) => {
63
+ console.log("Layout:", result.layout);
64
+ embedded.ready();
65
+ });
78
66
  </script>
79
67
  ```
80
68
 
81
69
  ## API Overview
82
70
 
83
- ### Initialization
84
-
85
- ```typescript
86
- const { layout } = await embedded.init({
87
- debug: false, // Optional: Enable debug logging
88
- });
89
- ```
90
-
91
- Returns layout information from the host:
92
-
93
- ```typescript
94
- interface LayoutInfo {
95
- theme: "light" | "dark";
96
- width: number;
97
- locale: string;
98
- currency: string;
99
- }
100
- ```
101
-
102
- ### Core Methods
103
-
104
- ```typescript
105
- // Signal app is ready (removes host loading overlay)
106
- embedded.ready();
107
-
108
- // Get current state
109
- const state = embedded.getState();
110
-
111
- // Check if initialized
112
- const ready = embedded.isReady();
113
-
114
- // Subscribe to initialization
115
- embedded.onInit((state) => {
116
- console.log("Initialized with layout:", state.layout);
117
- });
118
-
119
- // Send log message to host
120
- embedded.log("error", "Something failed", { context: "data" });
121
- ```
122
-
123
- ### Auth Module
124
-
125
- ```typescript
126
- // Get token from URL (?token=XXX)
127
- const token = embedded.auth.getToken();
128
-
129
- // Request token refresh (re-renders iframe with new token)
130
- embedded.auth.refresh();
131
- ```
132
-
133
- ### Destroy
134
-
135
- ```typescript
136
- // Exit embedded view and navigate back to app page
137
- embedded.destroy();
138
- ```
139
-
140
- ### UI Module
141
-
142
- ```typescript
143
- // Loading (in-app loading states)
144
- embedded.ui.loading.show(); // Show loading
145
- embedded.ui.loading.show("component"); // Component-level
146
- embedded.ui.loading.hide(); // Hide loading
147
-
148
- // Toast Notifications
149
- embedded.ui.toast.success("Product saved!");
150
- embedded.ui.toast.error("Something went wrong");
151
- embedded.ui.toast.warning("Please review input");
152
- embedded.ui.toast.info("New features available");
153
- embedded.ui.toast.success("Saved!", 5000); // Custom duration
154
-
155
- // Generic toast
156
- embedded.ui.toast.show({
157
- type: "success",
158
- message: "Done!",
159
- duration: 3000,
160
- });
161
-
162
- // Modal
163
- embedded.ui.modal.open("my-modal", { data: 123 });
164
- embedded.ui.modal.close("my-modal");
165
-
166
- // Confirm Dialog (async)
167
- const result = await embedded.ui.confirm({
168
- title: "Delete Product?",
169
- message: "This action cannot be undone.",
170
- confirmText: "Delete",
171
- cancelText: "Cancel",
172
- variant: "danger", // 'danger' | 'warning' | 'info'
173
- });
174
-
175
- if (result.confirmed) {
176
- await deleteProduct();
177
- }
178
- ```
71
+ The SDK provides modules for authentication, UI components, navigation, and page management:
179
72
 
180
- ### Page Module
181
-
182
- ```typescript
183
- // Set page title in host
184
- embedded.page.setTitle("Product Details");
185
-
186
- // SPA Navigation (React Router)
187
- embedded.page.navigate("/products");
188
- embedded.page.navigate("/orders", { replace: true });
189
- embedded.page.navigate("/item", { state: { id: 123 } });
190
-
191
- // Full Page Redirect
192
- embedded.page.redirect("https://external-site.com");
193
-
194
- // Auto-detect (internal → navigate, external → redirect)
195
- embedded.page.navTo("/products");
196
- embedded.page.navTo("https://external.com");
197
-
198
- // Iframe Resize
199
- embedded.page.resize(800);
200
- embedded.page.autoResize(); // Auto-detect content height
201
- ```
202
-
203
- ### Nav Module
204
-
205
- ```typescript
206
- // Set primary action button with onClick
207
- embedded.nav.setAction({
208
- title: "Create Product",
209
- onClick: () => {
210
- // Handle click
211
- console.log("Create product clicked");
212
- },
213
- });
73
+ - **Auth**: `getToken()`, `getAppId()`, `refresh()`, `introspect()`
74
+ - **UI**: `loading`, `toast`, `modal`, `confirm`
75
+ - **Page**: `setTitle()`, `navigate()`, `redirect()`, `resize()`
76
+ - **Nav**: `setAction()`, `onActionClick()`, `clearAction()`
214
77
 
215
- // With optional props
216
- embedded.nav.setAction({
217
- title: "Save",
218
- subTitle: "Save changes",
219
- icon: "sicon-save",
220
- disabled: false,
221
- onClick: () => {
222
- handleSave();
223
- },
224
- });
78
+ ## TypeScript
225
79
 
226
- // With dropdown actions
227
- embedded.nav.setAction({
228
- title: "Actions",
229
- value: "main",
230
- onClick: () => {
231
- // Handle main action click
232
- },
233
- extendedActions: [
234
- { title: "Import", url: "/import" },
235
- { title: "Export", value: "export" },
236
- ],
237
- });
238
-
239
- // Listen for action clicks
240
- const unsubscribe = embedded.nav.onActionClick((url, value) => {
241
- if (value === "export") {
242
- handleExport();
243
- }
244
- });
245
-
246
- // Clear action button
247
- embedded.nav.clearAction();
248
- ```
249
-
250
- ### Checkout Module
251
-
252
- ```typescript
253
- embedded.checkout.create({
254
- items: [{ productId: 123, quantity: 1 }],
255
- amount: 99.99,
256
- currency: "SAR",
257
- });
258
- ```
259
-
260
- ### Theme Support
261
-
262
- ```typescript
263
- // Get current theme from state
264
- const theme = embedded.getState().layout.theme;
265
-
266
- // Listen for theme changes
267
- const unsubscribe = embedded.onThemeChange((theme) => {
268
- document.body.classList.toggle("dark", theme === "dark");
269
- });
270
- ```
271
-
272
- ## TypeScript Support
273
-
274
- Full TypeScript support with exported types:
275
-
276
- ```typescript
277
- import type {
278
- EmbeddedState,
279
- LayoutInfo,
280
- InitOptions,
281
- ToastOptions,
282
- ToastType,
283
- ConfirmOptions,
284
- ConfirmResult,
285
- PrimaryActionConfig,
286
- ExtendedAction,
287
- CheckoutPayload,
288
- } from "@salla.sa/embedded-sdk";
289
- ```
80
+ Full TypeScript support with exported types. See [documentation](#) for complete type reference.
290
81
 
291
82
  ## Build Formats
292
83
 
293
- | Format | File | Usage |
294
- | -------- | ---------------------- | ---------------------------- |
295
- | ESM | `dist/index.es.js` | Modern bundlers (Vite, etc.) |
296
- | CommonJS | `dist/index.cjs.js` | Node.js |
297
- | UMD | `dist/index.umd.js` | Browser global |
298
- | SystemJS | `dist/index.system.js` | SystemJS/microfrontends |
84
+ | Format | File |
85
+ | -------- | ---------------------- |
86
+ | ESM | `dist/esm/index.js` |
87
+ | CommonJS | `dist/cjs/index.js` |
88
+ | UMD | `dist/umd/index.js` |
89
+ | SystemJS | `dist/system/index.js` |
299
90
 
300
- ## Development
91
+ ## Documentation
301
92
 
302
- ```bash
303
- pnpm install # Install dependencies
304
- pnpm dev # Development build with watch
305
- pnpm build # Production build
306
- pnpm lint # Lint code
307
- pnpm typecheck # Type check
308
- ```
93
+ For complete API documentation, examples, and advanced usage, see the [full documentation](#).
package/dist/cjs/index.js CHANGED
@@ -1,4 +1,4 @@
1
- "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const U="0.1.0-beta.5",P={version:U},o="embedded::",p={INIT:`${o}iframe.ready`,RESIZE:`${o}iframe.resize`,READY:`${o}ready`,DESTROY:`${o}destroy`},I={PROVIDE:`${o}context.provide`,THEME_CHANGE:`${o}theme.change`},q={LOG:`${o}log`},z={REFRESH:`${o}auth.refresh`},m={NAVIGATE:`${o}page.navigate`,REDIRECT:`${o}page.redirect`,SET_TITLE:`${o}page.setTitle`},w={SET_ACTION:`${o}nav.setAction`,ACTION_CLICK:`${o}nav.actionClick`},c={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`},L={CREATE:`${o}checkout.create`},A=P.version,K=1e4,V=["localhost","merchants.workers.dev","s.salla.sa",".salla.group",".salla.sa"];function F(t){try{const n=new URL(t).hostname;return V.some(i=>i.startsWith(".")?n.endsWith(i)||n===i.slice(1):n===i||n.startsWith(`${i}:`))}catch{return!1}}function j(){return typeof window>"u"||window.parent===window?null:window.parent}function l(t,e,n="*"){const i=j();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 f=new Map;let S=!1;function _(t){if(process.env.NODE_ENV==="production"&&!F(t.origin))return;const e=t.data;if(!e||typeof e.event!="string")return;const n=f.get(e.event);n&&n.forEach(r=>{try{r(e)}catch(s){console.error("[EmbeddedSDK] Error in message handler:",s)}});const i=f.get("*");i&&i.forEach(r=>{try{r(e)}catch(s){console.error("[EmbeddedSDK] Error in wildcard handler:",s)}})}function G(){S||typeof window>"u"||(window.addEventListener("message",_),S=!0)}function y(t,e){G(),f.has(t)||f.set(t,new Set);const n=f.get(t);return n.add(e),()=>{n.delete(e),n.size===0&&f.delete(t)}}function H(t,e=K){return new Promise((n,i)=>{const r=setTimeout(()=>{s(),i(new Error(`[EmbeddedSDK] Timeout waiting for "${t}" message`))},e),s=y(t,a=>{clearTimeout(r),s(),n(a)})})}function X(){f.clear(),S&&typeof window<"u"&&(window.removeEventListener("message",_),S=!1)}function W(){return typeof window>"u"?!1:window.parent!==window}const h=new Map,Y=3e4;function Z(){const t=Date.now(),e=Math.random().toString(36).slice(2,9);return`req_${t}_${e}`}function B(t,e={},n=Y){const i=Z();return new Promise((r,s)=>{const a=setTimeout(()=>{h.get(i)&&(h.delete(i),s(new Error(`[EmbeddedSDK] Request "${t}" timed out after ${n}ms`)))},n);h.set(i,{resolve:r,reject:s,timeout:a,event:t}),l(t,{...e,requestId:i})})}function N(t,e,n){const i=h.get(t);if(!i){console.warn(`[EmbeddedSDK] Received response for unknown request: ${t}`);return}clearTimeout(i.timeout),h.delete(t),n?i.reject(new Error(n)):i.resolve(e)}function J(t="SDK cleanup"){h.forEach((e,n)=>{clearTimeout(e.timeout),e.reject(new Error(`[EmbeddedSDK] Request ${n} cancelled: ${t}`))}),h.clear()}const Q="https://api.salla.dev";class b extends Error{constructor(e,n,i){super(e),this.status=n,this.response=i,this.name="ApiError"}}async function ee(t,e={}){const{method:n="GET",headers:i={},body:r,timeout:s=3e4}=e,a=`${Q}${t}`,E=new AbortController,T=setTimeout(()=>E.abort(),s);try{const u=await fetch(a,{method:n,headers:{"Content-Type":"application/json",...i},body:r?JSON.stringify(r):void 0,signal:E.signal});clearTimeout(T);let v;const R=u.headers.get("content-type");if(R&&R.includes("application/json")?v=await u.json():v=await u.text(),!u.ok)throw new b(`API request failed: ${u.statusText}`,u.status,v);return v}catch(u){throw clearTimeout(T),u instanceof b?u:u instanceof Error?u.name==="AbortError"?new b(`Request timeout after ${s}ms`):new b(`Request failed: ${u.message}`):new b("Unknown error occurred")}}function te(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 ee("/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 $=["success","error","warning","info"];function ie(t){const e=[];return t.type===void 0||t.type===null?e.push("Toast type is required"):(typeof t.type!="string"||!$.includes(t.type))&&e.push(`Invalid toast type "${t.type}". Expected: ${$.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 ne(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 re(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 se(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 ae(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 D=["danger","warning","info"];function oe(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"||!D.includes(t.variant))&&e.push(`Invalid confirm variant "${t.variant}". Expected: ${D.join(" | ")}`),{valid:e.length===0,errors:e}}function d(t,e){console.error(`[EmbeddedSDK] Validation failed for ${t}:
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const U="0.1.0-beta.6",P={version:U},o="embedded::",p={INIT:`${o}iframe.ready`,RESIZE:`${o}iframe.resize`,READY:`${o}ready`,DESTROY:`${o}destroy`},I={PROVIDE:`${o}context.provide`,THEME_CHANGE:`${o}theme.change`},q={LOG:`${o}log`},z={REFRESH:`${o}auth.refresh`},m={NAVIGATE:`${o}page.navigate`,REDIRECT:`${o}page.redirect`,SET_TITLE:`${o}page.setTitle`},w={SET_ACTION:`${o}nav.setAction`,ACTION_CLICK:`${o}nav.actionClick`},c={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`},L={CREATE:`${o}checkout.create`},C=P.version,K=1e4,V=["localhost","merchants.workers.dev","s.salla.sa",".salla.group",".salla.sa"];function F(t){try{const n=new URL(t).hostname;return V.some(i=>i.startsWith(".")?n.endsWith(i)||n===i.slice(1):n===i||n.startsWith(`${i}:`))}catch{return!1}}function j(){return typeof window>"u"||window.parent===window?null:window.parent}function l(t,e,n="*"){const i=j();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 f=new Map;let S=!1;function _(t){if(process.env.NODE_ENV==="production"&&!F(t.origin))return;const e=t.data;if(!e||typeof e.event!="string")return;const n=f.get(e.event);n&&n.forEach(r=>{try{r(e)}catch(s){console.error("[EmbeddedSDK] Error in message handler:",s)}});const i=f.get("*");i&&i.forEach(r=>{try{r(e)}catch(s){console.error("[EmbeddedSDK] Error in wildcard handler:",s)}})}function G(){S||typeof window>"u"||(window.addEventListener("message",_),S=!0)}function y(t,e){G(),f.has(t)||f.set(t,new Set);const n=f.get(t);return n.add(e),()=>{n.delete(e),n.size===0&&f.delete(t)}}function H(t,e=K){return new Promise((n,i)=>{const r=setTimeout(()=>{s(),i(new Error(`[EmbeddedSDK] Timeout waiting for "${t}" message`))},e),s=y(t,a=>{clearTimeout(r),s(),n(a)})})}function X(){f.clear(),S&&typeof window<"u"&&(window.removeEventListener("message",_),S=!1)}function W(){return typeof window>"u"?!1:window.parent!==window}const h=new Map,Y=3e4;function Z(){const t=Date.now(),e=Math.random().toString(36).slice(2,9);return`req_${t}_${e}`}function B(t,e={},n=Y){const i=Z();return new Promise((r,s)=>{const a=setTimeout(()=>{h.get(i)&&(h.delete(i),s(new Error(`[EmbeddedSDK] Request "${t}" timed out after ${n}ms`)))},n);h.set(i,{resolve:r,reject:s,timeout:a,event:t}),l(t,{...e,requestId:i})})}function N(t,e,n){const i=h.get(t);if(!i){console.warn(`[EmbeddedSDK] Received response for unknown request: ${t}`);return}clearTimeout(i.timeout),h.delete(t),n?i.reject(new Error(n)):i.resolve(e)}function J(t="SDK cleanup"){h.forEach((e,n)=>{clearTimeout(e.timeout),e.reject(new Error(`[EmbeddedSDK] Request ${n} cancelled: ${t}`))}),h.clear()}const Q="https://api.salla.dev";class b extends Error{constructor(e,n,i){super(e),this.status=n,this.response=i,this.name="ApiError"}}async function ee(t,e={}){const{method:n="GET",headers:i={},body:r,timeout:s=3e4}=e,a=`${Q}${t}`,E=new AbortController,T=setTimeout(()=>{E.abort()},s);try{const u=await fetch(a,{method:n,headers:{"Content-Type":"application/json",...i},body:r?JSON.stringify(r):void 0,signal:E.signal});clearTimeout(T);let v;const A=u.headers.get("content-type");if(A!=null&&A.includes("application/json")?v=await u.json():v=await u.text(),!u.ok)throw new b(`API request failed: ${u.statusText}`,u.status,v);return v}catch(u){throw clearTimeout(T),u instanceof b?u:u instanceof Error?u.name==="AbortError"?new b(`Request timeout after ${s}ms`):new b(`Request failed: ${u.message}`):new b("Unknown error occurred")}}function te(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 ee("/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 $=["success","error","warning","info"];function ie(t){const e=[];return t.type===void 0||t.type===null?e.push("Toast type is required"):(typeof t.type!="string"||!$.includes(t.type))&&e.push(`Invalid toast type "${t.type}". Expected: ${$.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 ne(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 re(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 se(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 ae(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 D=["danger","warning","info"];function oe(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"||!D.includes(t.variant))&&e.push(`Invalid confirm variant "${t.variant}". Expected: ${D.join(" | ")}`),{valid:e.length===0,errors:e}}function d(t,e){console.error(`[EmbeddedSDK] Validation failed for ${t}:
2
2
  `+e.map(n=>` • ${n}`).join(`
3
- `))}function le(){return{navigate(t,e){const n=re({path:t,...e});if(!n.valid){d(m.NAVIGATE,n.errors);return}l(m.NAVIGATE,{path:t,state:e==null?void 0:e.state,replace:e==null?void 0:e.replace})},redirect(t){const e=se({url:t});if(!e.valid){d(m.REDIRECT,e.errors);return}l(m.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){d(p.RESIZE,["Height must be a non-negative number"]);return}l(p.RESIZE,{height:t})},autoResize(){const t=document.documentElement.scrollHeight;this.resize(t)},setTitle(t){if(typeof t!="string"||!t.trim()){d(m.SET_TITLE,["Title must be a non-empty string"]);return}l(m.SET_TITLE,{title:t})}}}function ue(){const t=new Set;let e=null;return y(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(s){console.error("[EmbeddedSDK] Error in action click callback:",s)}})}),{setAction(i){var s;const r=ae(i);if(!r.valid){d(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:(s=i.extendedActions)==null?void 0:s.map(a=>({title:a.title,subTitle:a.subTitle,url:a.url,value:a.value,icon:a.icon,disabled:a.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 de(){const t=e=>{const n=ie(e);if(!n.valid){d(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 fe(){return{open(t,e){l(c.MODAL,{action:"open",id:t,content:e})},close(t){l(c.MODAL,{action:"close",id:t})}}}function he(){return async t=>{const e=oe(t);return e.valid?B(c.CONFIRM,{title:t.title,message:t.message,confirmText:t.confirmText??"Confirm",cancelText:t.cancelText??"Cancel",variant:t.variant??"info"}):(d(c.CONFIRM,e.errors),Promise.reject(new Error(e.errors.join(", "))))}}function me(){return{loading:ce(),toast:de(),modal:fe(),confirm:he()}}function ge(){return{create(t){const e=ne(t);if(!e.valid){d(L.CREATE,e.errors);return}l(L.CREATE,{payload:t})}}}const k={debug:!1,initialized:!1},Ee={theme:"light",width:0,locale:"ar",currency:"SAR"},O={ready:!1,initializing:!1,layout:{...Ee}};class M{constructor(){this.config={...k},this.state={...O},this.themeCallbacks=new Set,this.initCallbacks=new Set,this.appReady=!1,this.auth=te(),this.page=le(),this.nav=ue(),this.ui=me(),this.checkout=ge(),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${A}]`,...e)}warn(...e){console.warn(`[EmbeddedSDK v${A}]`,...e)}setupThemeListener(){y(I.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(){y(c.CONFIRM_RESPONSE,e=>{this.debugLog("Received confirm response:",e),N(e.requestId,{confirmed:e.confirmed})}),y(c.MODAL_RESPONSE,e=>{this.debugLog("Received modal response:",e),N(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(p.READY,{}),this.debugLog("Sent ready signal to host")}async init(e={}){var n,i,r,s;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(p.INIT,{height:document.documentElement.scrollHeight}),this.debugLog("Sent iframe.ready message, waiting for context...");const a=await H(I.PROVIDE);this.debugLog("Received context from host:",a),this.state={ready:!0,initializing:!1,layout:{theme:((n=a.layout)==null?void 0:n.theme)??"light",width:((i=a.layout)==null?void 0:i.width)??0,locale:((r=a.layout)==null?void 0:r.locale)??"ar",currency:((s=a.layout)==null?void 0:s.currency)??"SAR"}},this.config.initialized=!0,this.debugLog("Initialization complete. Layout:",this.state.layout);const E=this.getState();return this.initCallbacks.forEach(T=>{try{T(E)}catch(u){console.error("[EmbeddedSDK] Error in init callback:",u)}}),{layout:{...this.state.layout}}}catch(a){throw this.state.initializing=!1,this.state.ready=!1,a}}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(p.DESTROY,{}),this.debugLog("Sent destroy event to host")),J("SDK destroyed"),X(),this.themeCallbacks.clear(),this.initCallbacks.clear(),this.config={...k},this.state={...O},this.appReady=!1}}let g=null;function x(){return g||(g=new M),g}function be(){g&&(g.destroy(),g=null)}const C=x(),pe=A;typeof window<"u"&&(window.salla=window.salla||window.Salla||{},window.Salla=window.salla,window.salla.embedded||(window.salla.embedded=C),window.Salla.embedded||(window.Salla.embedded=C));exports.EmbeddedApp=M;exports.embedded=C;exports.getEmbeddedApp=x;exports.resetEmbeddedApp=be;exports.version=pe;
3
+ `))}function le(){return{navigate(t,e){const n=re({path:t,...e});if(!n.valid){d(m.NAVIGATE,n.errors);return}l(m.NAVIGATE,{path:t,state:e==null?void 0:e.state,replace:e==null?void 0:e.replace})},redirect(t){const e=se({url:t});if(!e.valid){d(m.REDIRECT,e.errors);return}l(m.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){d(p.RESIZE,["Height must be a non-negative number"]);return}l(p.RESIZE,{height:t})},autoResize(){const t=document.documentElement.scrollHeight;this.resize(t)},setTitle(t){if(typeof t!="string"||!t.trim()){d(m.SET_TITLE,["Title must be a non-empty string"]);return}l(m.SET_TITLE,{title:t})}}}function ue(){const t=new Set;let e=null;return y(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(s){console.error("[EmbeddedSDK] Error in action click callback:",s)}})}),{setAction(i){var s;const r=ae(i);if(!r.valid){d(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:(s=i.extendedActions)==null?void 0:s.map(a=>({title:a.title,subTitle:a.subTitle,url:a.url,value:a.value,icon:a.icon,disabled:a.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 de(){const t=e=>{const n=ie(e);if(!n.valid){d(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 fe(){return{open(t,e){l(c.MODAL,{action:"open",id:t,content:e})},close(t){l(c.MODAL,{action:"close",id:t})}}}function he(){return async t=>{const e=oe(t);return e.valid?B(c.CONFIRM,{title:t.title,message:t.message,confirmText:t.confirmText??"Confirm",cancelText:t.cancelText??"Cancel",variant:t.variant??"info"}):(d(c.CONFIRM,e.errors),Promise.reject(new Error(e.errors.join(", "))))}}function me(){return{loading:ce(),toast:de(),modal:fe(),confirm:he()}}function ge(){return{create(t){const e=ne(t);if(!e.valid){d(L.CREATE,e.errors);return}l(L.CREATE,{payload:t})}}}const k={debug:!1,initialized:!1},Ee={theme:"light",width:0,locale:"ar",currency:"SAR"},O={ready:!1,initializing:!1,layout:{...Ee}};class M{constructor(){this.config={...k},this.state={...O},this.themeCallbacks=new Set,this.initCallbacks=new Set,this.appReady=!1,this.auth=te(),this.page=le(),this.nav=ue(),this.ui=me(),this.checkout=ge(),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${C}]`,...e)}warn(...e){console.warn(`[EmbeddedSDK v${C}]`,...e)}setupThemeListener(){y(I.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(){y(c.CONFIRM_RESPONSE,e=>{this.debugLog("Received confirm response:",e),N(e.requestId,{confirmed:e.confirmed})}),y(c.MODAL_RESPONSE,e=>{this.debugLog("Received modal response:",e),N(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(p.READY,{}),this.debugLog("Sent ready signal to host")}async init(e={}){var n,i,r,s;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(p.INIT,{height:document.documentElement.scrollHeight}),this.debugLog("Sent iframe.ready message, waiting for context...");const a=await H(I.PROVIDE);this.debugLog("Received context from host:",a),this.state={ready:!0,initializing:!1,layout:{theme:((n=a.layout)==null?void 0:n.theme)??"light",width:((i=a.layout)==null?void 0:i.width)??0,locale:((r=a.layout)==null?void 0:r.locale)??"ar",currency:((s=a.layout)==null?void 0:s.currency)??"SAR"}},this.config.initialized=!0,this.debugLog("Initialization complete. Layout:",this.state.layout);const E=this.getState();return this.initCallbacks.forEach(T=>{try{T(E)}catch(u){console.error("[EmbeddedSDK] Error in init callback:",u)}}),{layout:{...this.state.layout}}}catch(a){throw this.state.initializing=!1,this.state.ready=!1,a}}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(p.DESTROY,{}),this.debugLog("Sent destroy event to host")),J("SDK destroyed"),X(),this.themeCallbacks.clear(),this.initCallbacks.clear(),this.config={...k},this.state={...O},this.appReady=!1}}let g=null;function x(){return g||(g=new M),g}function be(){g&&(g.destroy(),g=null)}const R=x(),pe=C;typeof window<"u"&&(window.salla=window.salla||window.Salla||{},window.Salla=window.salla,window.salla.embedded||(window.salla.embedded=R),window.Salla.embedded||(window.Salla.embedded=R));exports.EmbeddedApp=M;exports.embedded=R;exports.getEmbeddedApp=x;exports.resetEmbeddedApp=be;exports.version=pe;
4
4
  //# sourceMappingURL=index.js.map