@salla.sa/embedded-sdk 0.1.0-beta.1 → 0.1.0-beta.10

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,267 +14,75 @@ 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
- // Initialize and connect to the Salla Dashboard
24
- const state = await embedded.init({
25
- app_id: "your-app-id",
26
- debug: true,
27
- });
28
-
29
- console.log("Connected! Store ID:", state.storeId);
30
- console.log("Theme:", state.isDarkMode ? "dark" : "light");
31
-
32
- // Hide loading indicator
33
- embedded.ui.loading.hide();
34
-
35
- // Set up breadcrumbs
36
- embedded.ui.breadcrumbs.set([
37
- { label: "Home", path: "/" },
38
- { label: "My App" },
39
- ]);
21
+ try {
22
+ // Initialize SDK
23
+ const { layout } = await embedded.init({ debug: true });
24
+
25
+ // Get token
26
+ const token = embedded.auth.getToken();
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);
35
+
36
+ if (!isValid) throw new Error("Token is invalid");
37
+
38
+ // Signal app is ready only after successful verification
39
+ embedded.ready();
40
+
41
+ // Set up your app
42
+ embedded.page.setTitle("My App");
43
+ } catch (err) {
44
+ embedded.destroy();
45
+ }
40
46
  }
41
47
 
42
48
  bootstrap();
43
49
  ```
44
50
 
45
- ### UMD (Browser Global)
51
+ ### Browser Global
46
52
 
47
53
  ```html
48
- <script src="https://unpkg.com/@salla.sa/embedded-sdk/dist/index.umd.js"></script>
54
+ <script src="https://unpkg.com/@salla.sa/embedded-sdk/dist/umd/index.js"></script>
49
55
  <script>
50
- SallaEmbeddedSDK.embedded
51
- .init({ app_id: "your-app-id" })
52
- .then(function (state) {
53
- console.log("Connected!", state);
54
- SallaEmbeddedSDK.embedded.ui.loading.hide();
55
- });
56
+ const embedded = Salla.embedded;
57
+ embedded.init({ debug: true }).then((result) => {
58
+ console.log("Layout:", result.layout);
59
+ embedded.ready();
60
+ });
56
61
  </script>
57
62
  ```
58
63
 
59
64
  ## API Overview
60
65
 
61
- ### Initialization
62
-
63
- ```typescript
64
- const state = await embedded.init({
65
- app_id: "your-app-id", // Required: Your app identifier
66
- env: "prod", // Optional: 'prod' | 'dev'
67
- debug: false, // Optional: Enable debug logging
68
- });
69
- ```
70
-
71
- Returns `EmbeddedState` with merchant context:
72
-
73
- ```typescript
74
- interface EmbeddedState {
75
- ready: boolean;
76
- token?: string;
77
- storeId?: number;
78
- userId?: number;
79
- merchantPlan?: string;
80
- isDarkMode?: boolean;
81
- locale?: string;
82
- baseUrl?: string;
83
- baseApiUrl?: string;
84
- }
85
- ```
86
-
87
- ### Auth Module
88
-
89
- ```typescript
90
- // Get authentication data
91
- const token = embedded.auth.getAccessToken();
92
- const storeId = embedded.auth.getStoreId();
93
- const userId = embedded.auth.getUserId();
94
- const plan = embedded.auth.getMerchantPlan();
95
- const locale = embedded.auth.getLocale();
96
-
97
- // Check authentication status
98
- if (embedded.auth.isAuthenticated()) {
99
- // Make authenticated API calls
100
- }
101
-
102
- // Auth actions
103
- embedded.auth.logout(); // Logout user
104
- embedded.auth.logout("/custom-redirect"); // With redirect
105
- embedded.auth.refreshToken(); // Refresh token (page reload)
106
- embedded.auth.verifyToken(); // Verify token
107
- ```
108
-
109
- ### UI Module
110
-
111
- ```typescript
112
- // Loading
113
- embedded.ui.loading.show(); // Show loading
114
- embedded.ui.loading.show("component"); // Component-level loading
115
- embedded.ui.loading.hide(); // Hide loading
116
- embedded.ui.loading.set(false); // Direct control
117
-
118
- // Overlay (fullscreen mode)
119
- embedded.ui.overlay.open();
120
- embedded.ui.overlay.close();
66
+ The SDK provides modules for authentication, UI components, navigation, and page management:
121
67
 
122
- // Breadcrumbs
123
- embedded.ui.breadcrumbs.set([
124
- { label: "Dashboard", path: "/" },
125
- { label: "Products", path: "/products" },
126
- { label: "Edit Product" },
127
- ]);
128
- embedded.ui.breadcrumbs.clear();
68
+ - **Auth**: `getToken()`, `getAppId()`, `refresh()`, `introspect()`
69
+ - **UI**: `loading`, `toast`, `modal`, `confirm`
70
+ - **Page**: `setTitle()`, `navigate()`, `redirect()`, `resize()`
71
+ - **Nav**: `setAction()`, `onActionClick()`, `clearAction()`
129
72
 
130
- // Toast Notifications
131
- embedded.ui.toast.success("Product saved!");
132
- embedded.ui.toast.error("Something went wrong");
133
- embedded.ui.toast.warning("Please review input");
134
- embedded.ui.toast.info("New features available");
135
- embedded.ui.toast.success("Saved!", 5000); // Custom duration
73
+ ## TypeScript
136
74
 
137
- // Generic toast
138
- embedded.ui.toast.show({
139
- type: "success",
140
- message: "Done!",
141
- duration: 3000,
142
- });
143
-
144
- // Modal
145
- embedded.ui.modal.open("my-modal", { data: 123 });
146
- embedded.ui.modal.close("my-modal");
147
-
148
- // Confirm Dialog (async)
149
- const result = await embedded.ui.confirm({
150
- title: "Delete Product?",
151
- message: "This action cannot be undone.",
152
- confirmText: "Delete",
153
- cancelText: "Cancel",
154
- variant: "danger", // 'danger' | 'warning' | 'info'
155
- });
156
-
157
- if (result.confirmed) {
158
- await deleteProduct();
159
- }
160
- ```
161
-
162
- ### Page Module
163
-
164
- ```typescript
165
- // SPA Navigation (React Router)
166
- embedded.page.navigate("/products");
167
- embedded.page.navigate("/orders", { replace: true });
168
- embedded.page.navigate("/item", { state: { id: 123 } });
169
-
170
- // Full Page Redirect
171
- embedded.page.redirect("https://external-site.com");
172
-
173
- // Auto-detect (internal → navigate, external → redirect)
174
- embedded.page.navTo("/products");
175
- embedded.page.navTo("https://external.com");
176
-
177
- // Iframe Resize
178
- embedded.page.resize(800);
179
- embedded.page.autoResize(); // Auto-detect content height
180
- ```
181
-
182
- ### Nav Module
183
-
184
- ```typescript
185
- // Set primary action button
186
- embedded.nav.setAction({
187
- title: "Create Product",
188
- url: "/products/new",
189
- });
190
-
191
- // With dropdown actions
192
- embedded.nav.setAction({
193
- title: "Actions",
194
- value: "main",
195
- extendedActions: [
196
- { title: "Import", url: "/import" },
197
- { title: "Export", value: "export" },
198
- ],
199
- });
200
-
201
- // Listen for action clicks
202
- const unsubscribe = embedded.nav.onActionClick((url, value) => {
203
- if (value === "export") {
204
- handleExport();
205
- }
206
- });
207
-
208
- // Clear action button
209
- embedded.nav.clearAction();
210
- ```
211
-
212
- ### Checkout Module
213
-
214
- ```typescript
215
- embedded.checkout.create({
216
- items: [{ productId: 123, quantity: 1 }],
217
- amount: 99.99,
218
- currency: "SAR",
219
- });
220
- ```
221
-
222
- ### Theme Support
223
-
224
- ```typescript
225
- // Get current theme
226
- const isDark = embedded.getState().isDarkMode;
227
-
228
- // Listen for theme changes
229
- const unsubscribe = embedded.onThemeChange((isDark) => {
230
- document.body.classList.toggle("dark", isDark);
231
- });
232
- ```
233
-
234
- ### Error Reporting
235
-
236
- ```typescript
237
- try {
238
- await riskyOperation();
239
- } catch (err) {
240
- embedded.reportError(err, { operation: "riskyOperation" });
241
- }
242
- ```
243
-
244
- ## TypeScript Support
245
-
246
- Full TypeScript support with exported types:
247
-
248
- ```typescript
249
- import type {
250
- EmbeddedState,
251
- InitOptions,
252
- BreadcrumbItem,
253
- ToastOptions,
254
- ToastType,
255
- ConfirmOptions,
256
- ConfirmResult,
257
- PrimaryActionConfig,
258
- ExtendedAction,
259
- CheckoutPayload,
260
- } from "@salla.sa/embedded-sdk";
261
- ```
75
+ Full TypeScript support with exported types. See [documentation](#) for complete type reference.
262
76
 
263
77
  ## Build Formats
264
78
 
265
- | Format | File | Usage |
266
- | -------- | ---------------------- | ---------------------------- |
267
- | ESM | `dist/index.es.js` | Modern bundlers (Vite, etc.) |
268
- | CommonJS | `dist/index.cjs.js` | Node.js |
269
- | UMD | `dist/index.umd.js` | Browser global |
270
- | SystemJS | `dist/index.system.js` | SystemJS/microfrontends |
79
+ | Format | File |
80
+ | -------- | ---------------------- |
81
+ | ESM | `dist/esm/index.js` |
82
+ | CommonJS | `dist/cjs/index.js` |
83
+ | UMD | `dist/umd/index.js` |
84
+ | SystemJS | `dist/system/index.js` |
271
85
 
272
- ## Development
86
+ ## Documentation
273
87
 
274
- ```bash
275
- pnpm install # Install dependencies
276
- pnpm dev # Development build with watch
277
- pnpm build # Production build
278
- pnpm lint # Lint code
279
- pnpm typecheck # Type check
280
- ```
88
+ 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 M="0.1.0-beta.1",L={version:M},s="embedded::",p={READY:`${s}iframe.ready`,RESIZE:`${s}iframe.resize`},A={PROVIDE:`${s}context.provide`,THEME_CHANGE:`${s}theme.change`},k={REPORT:`${s}error.report`},b={LOGOUT:`${s}auth.logout`,REFRESH:`${s}auth.refresh`,VERIFY:`${s}auth.verify`},h={NAVIGATE:`${s}page.navigate`,REDIRECT:`${s}page.redirect`},g={SET_ACTION:`${s}nav.setAction`,ACTION_CLICK:`${s}nav.actionClick`},o={LOADING:`${s}ui.loading`,BREADCRUMB:`${s}ui.breadcrumb`,OVERLAY:`${s}ui.overlay`,TOAST:`${s}ui.toast`,MODAL:`${s}ui.modal`,CONFIRM:`${s}ui.confirm`,CONFIRM_RESPONSE:`${s}ui.confirm.response`,MODAL_RESPONSE:`${s}ui.modal.response`},w={CREATE:`${s}checkout.create`},T=L.version,_=1e4,U=["localhost","merchants.workers.dev","s.salla.sa",".salla.group",".salla.sa"];function x(t){try{const r=new URL(t).hostname;return U.some(i=>i.startsWith(".")?r.endsWith(i)||r===i.slice(1):r===i||r.startsWith(`${i}:`))}catch{return!1}}function V(){return typeof window>"u"||window.parent===window?null:window.parent}function n(t,e,r="*"){const i=V();if(!i){console.warn("[EmbeddedSDK] Not running in an iframe, cannot post to host");return}const a={event:t,...e};i.postMessage(a,r)}const c=new Map;let E=!1;function O(t){if(process.env.NODE_ENV==="production"&&!x(t.origin))return;const e=t.data;if(!e||typeof e.event!="string")return;const r=c.get(e.event);r&&r.forEach(a=>{try{a(e)}catch(u){console.error("[EmbeddedSDK] Error in message handler:",u)}});const i=c.get("*");i&&i.forEach(a=>{try{a(e)}catch(u){console.error("[EmbeddedSDK] Error in wildcard handler:",u)}})}function P(){E||typeof window>"u"||(window.addEventListener("message",O),E=!0)}function m(t,e){P(),c.has(t)||c.set(t,new Set);const r=c.get(t);return r.add(e),()=>{r.delete(e),r.size===0&&c.delete(t)}}function F(t,e=_){return new Promise((r,i)=>{const a=setTimeout(()=>{u(),i(new Error(`[EmbeddedSDK] Timeout waiting for "${t}" message`))},e),u=m(t,v=>{clearTimeout(a),u(),r(v)})})}function K(){c.clear(),E&&typeof window<"u"&&(window.removeEventListener("message",O),E=!1)}function q(){return typeof window>"u"?!1:window.parent!==window}const d=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={},r=z){const i=B();return new Promise((a,u)=>{const v=setTimeout(()=>{d.get(i)&&(d.delete(i),u(new Error(`[EmbeddedSDK] Request "${t}" timed out after ${r}ms`)))},r);d.set(i,{resolve:a,reject:u,timeout:v,event:t}),n(t,{...e,requestId:i})})}function R(t,e,r){const i=d.get(t);if(!i){console.warn(`[EmbeddedSDK] Received response for unknown request: ${t}`);return}clearTimeout(i.timeout),d.delete(t),r?i.reject(new Error(r)):i.resolve(e)}function G(t="SDK cleanup"){d.forEach((e,r)=>{clearTimeout(e.timeout),e.reject(new Error(`[EmbeddedSDK] Request ${r} cancelled: ${t}`))}),d.clear()}function H(t){return{getAccessToken(){return t().token},isAuthenticated(){const e=t();return e.ready&&!!e.token},getStoreId(){return t().storeId},getUserId(){return t().userId},getMerchantPlan(){return t().merchantPlan},getLocale(){return t().locale},logout(e){n(b.LOGOUT,{redirectUrl:e})},refreshToken(){n(b.REFRESH)},verifyToken(e){n(b.VERIFY,{token:e})}}}const S=["success","error","warning","info"];function W(t){const e=[];return t.type===void 0||t.type===null?e.push("Toast type is required"):(typeof t.type!="string"||!S.includes(t.type))&&e.push(`Invalid toast type "${t.type}". Expected: ${S.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 Y(t){const e=[];return Array.isArray(t)?(t.forEach((r,i)=>{if(typeof r!="object"||r===null){e.push(`Breadcrumb item at index ${i} must be an object`);return}const a=r;a.label?typeof a.label!="string"&&e.push(`Breadcrumb item at index ${i} "label" must be a string`):e.push(`Breadcrumb item at index ${i} is missing required "label" property`),a.path!==void 0&&typeof a.path!="string"&&e.push(`Breadcrumb item at index ${i} "path" must be a string`)}),{valid:e.length===0,errors:e}):(e.push("Breadcrumbs must be an array"),{valid:!1,errors:e})}function Z(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 X(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 Q(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 J(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.url!==void 0&&t.url!==null&&typeof t.url!="string"&&e.push("Nav action URL must be a string"),t.value!==void 0&&t.value!==null&&typeof t.value!="string"&&e.push("Nav action value must be a string"),t.extendedActions!==void 0&&t.extendedActions!==null&&(Array.isArray(t.extendedActions)?t.extendedActions.forEach((r,i)=>{if(typeof r!="object"||r===null){e.push(`Extended action at index ${i} must be an object`);return}const a=r;(!a.title||typeof a.title!="string")&&e.push(`Extended action at index ${i} is missing required "title" property`)}):e.push("Nav action extendedActions must be an array")),{valid:e.length===0,errors:e}}const I=["danger","warning","info"];function ee(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"||!I.includes(t.variant))&&e.push(`Invalid confirm variant "${t.variant}". Expected: ${I.join(" | ")}`),{valid:e.length===0,errors:e}}function l(t,e){console.error(`[EmbeddedSDK] Validation failed for ${t}:
2
- `+e.map(r=>` • ${r}`).join(`
3
- `))}function te(){return{navigate(t,e){const r=X({path:t,...e});if(!r.valid){l(h.NAVIGATE,r.errors);return}n(h.NAVIGATE,{path:t,state:e==null?void 0:e.state,replace:e==null?void 0:e.replace})},redirect(t){const e=Q({url:t});if(!e.valid){l(h.REDIRECT,e.errors);return}n(h.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){l(p.RESIZE,["Height must be a non-negative number"]);return}n(p.RESIZE,{height:t})},autoResize(){const t=document.documentElement.scrollHeight;this.resize(t)}}}function re(){const t=new Set;return m(g.ACTION_CLICK,e=>{t.forEach(r=>{try{r(e.url,e.value)}catch(i){console.error("[EmbeddedSDK] Error in action click callback:",i)}})}),{setAction(e){const r=J(e);if(!r.valid){l(g.SET_ACTION,r.errors);return}n(g.SET_ACTION,{title:e.title,url:e.url,value:e.value,extendedActions:e.extendedActions})},clearAction(){n(g.SET_ACTION,{title:""})},onActionClick(e){return t.add(e),()=>{t.delete(e)}},primaryAction(e){this.setAction(e)},clearPrimaryAction(){this.clearAction()}}}function ie(){return{show(t="full"){n(o.LOADING,{status:!1,mode:t})},hide(){n(o.LOADING,{status:!0,mode:"full"})},set(t,e="full"){n(o.LOADING,{status:t,mode:e})}}}function ne(){return{open(){n(o.OVERLAY,{action:"open"})},close(){n(o.OVERLAY,{action:"close"})}}}function se(){return{set(t){const e=Y(t);if(!e.valid){l(o.BREADCRUMB,e.errors);return}n(o.BREADCRUMB,{items:t})},clear(){n(o.BREADCRUMB,{items:[]})}}}function ae(){const t=e=>{const r=W(e);if(!r.valid){l(o.TOAST,r.errors);return}n(o.TOAST,{type:e.type,message:e.message,duration:e.duration})};return{show:t,success(e,r){t({type:"success",message:e,duration:r})},error(e,r){t({type:"error",message:e,duration:r})},warning(e,r){t({type:"warning",message:e,duration:r})},info(e,r){t({type:"info",message:e,duration:r})}}}function oe(){return{open(t,e){n(o.MODAL,{action:"open",id:t,content:e})},close(t){n(o.MODAL,{action:"close",id:t})}}}function ue(){return async t=>{const e=ee(t);return e.valid?j(o.CONFIRM,{title:t.title,message:t.message,confirmText:t.confirmText??"Confirm",cancelText:t.cancelText??"Cancel",variant:t.variant??"info"}):(l(o.CONFIRM,e.errors),Promise.reject(new Error(e.errors.join(", "))))}}function le(){return{loading:ie(),overlay:ne(),breadcrumbs:se(),toast:ae(),modal:oe(),confirm:ue()}}function ce(){return{create(t){const e=Z(t);if(!e.valid){l(w.CREATE,e.errors);return}n(w.CREATE,{payload:t})}}}const C={appId:"",env:"prod",debug:!1,initialized:!1},N={ready:!1,initializing:!1};class D{constructor(){this.config={...C},this.state={...N},this.themeCallbacks=new Set,this.auth=H(()=>this.getState()),this.page=te(),this.nav=re(),this.ui=le(),this.checkout=ce(),this.setupThemeListener(),this.setupResponseListeners()}getState(){return{...this.state}}getConfig(){return{...this.config}}isReady(){return this.state.ready}log(...e){this.config.debug&&console.log(`[EmbeddedSDK v${T}]`,...e)}warn(...e){console.warn(`[EmbeddedSDK v${T}]`,...e)}setupThemeListener(){m(A.THEME_CHANGE,e=>{this.state.isDarkMode=e.dark,this.log("Theme changed:",e.dark?"dark":"light"),this.themeCallbacks.forEach(r=>{try{r(e.dark)}catch(i){console.error("[EmbeddedSDK] Error in theme callback:",i)}})})}setupResponseListeners(){m(o.CONFIRM_RESPONSE,e=>{this.log("Received confirm response:",e),R(e.requestId,{confirmed:e.confirmed})}),m(o.MODAL_RESPONSE,e=>{this.log("Received modal response:",e),R(e.requestId,e.result,e.error)})}onThemeChange(e){return this.themeCallbacks.add(e),()=>{this.themeCallbacks.delete(e)}}reportError(e,r){const i=typeof e=="string"?{message:e}:{message:e.message,stack:e.stack,code:e.code};n(k.REPORT,{error:i,context:r})}async init(e){if(this.config.initialized)return this.log("Already initialized, returning current state"),this.getState();if(this.state.initializing)return this.warn("Initialization already in progress"),this.waitForReady();if(!e.app_id)throw new Error("[EmbeddedSDK] app_id is required");q()||this.warn("Not running in an iframe. Some features may not work."),this.config={appId:e.app_id,env:e.env??"prod",debug:e.debug??!1,initialized:!1},this.state.initializing=!0,this.log("Initializing with options:",e);try{n(p.READY,{height:document.documentElement.scrollHeight}),this.log("Sent iframe.ready message, waiting for context...");const r=await F(A.PROVIDE);return this.log("Received context from host:",r),this.state={ready:!0,initializing:!1,token:r.token,storeId:r.storeId,userId:r.userId,merchantPlan:r.plan,isDarkMode:r.isDarkMode,parentWidth:r.parentWidth,baseUrl:r.baseUrl,baseApiUrl:r.baseApiUrl,locale:r.locale},this.config.initialized=!0,this.log("Initialization complete. State:",this.state),this.getState()}catch(r){throw this.state.initializing=!1,this.state.ready=!1,r}}waitForReady(){return new Promise(e=>{const r=()=>{this.state.ready?e(this.getState()):setTimeout(r,100)};r()})}destroy(){this.log("Destroying SDK instance"),G("SDK destroyed"),K(),this.themeCallbacks.clear(),this.config={...C},this.state={...N}}}let f=null;function $(){return f||(f=new D),f}function de(){f&&(f.destroy(),f=null)}const y=$(),fe=T;typeof window<"u"&&(window.salla=window.salla||window.Salla||{},window.Salla=window.salla,window.salla.embedded||(window.salla.embedded=y),window.Salla.embedded||(window.Salla.embedded=y));exports.EmbeddedApp=D;exports.embedded=y;exports.getEmbeddedApp=$;exports.resetEmbeddedApp=de;exports.version=fe;
1
+ "use strict";Object.defineProperty(exports,Symbol.toStringTag,{value:"Module"});const o="embedded::",T={INIT:`${o}iframe.ready`,RESIZE:`${o}iframe.resize`,READY:`${o}ready`,DESTROY:`${o}destroy`},$={PROVIDE:`${o}context.provide`,THEME_CHANGE:`${o}theme.change`},G={LOG:`${o}log`},d={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`},H="0.1.0-beta.10",K={version:H},q={REFRESH:`${o}auth.refresh`},m={NAVIGATE:`${o}page.navigate`,REDIRECT:`${o}page.redirect`,SET_TITLE:`${o}page.setTitle`},S={SET_ACTION:`${o}nav.setAction`,ACTION_CLICK:`${o}nav.actionClick`},O={CREATE:`${o}checkout.create`},z=K.version,X=1e4,W=["localhost","merchants.workers.dev","s.salla.sa",".salla.group",".salla.sa"];let R={showVersion:!0,debug:!1};function Y(e){R={...R,...e}}function _(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 p(e,...t){if(e==="debug"&&!R.debug)return;const r=[],i=[];r.push(_("EmbeddedSDK")),i.push(M("#10b981","#fff")),R.showVersion&&(r.push(_(`v${z}`)),i.push(M("#6b7280","#fff")));const n=r.join("").trim();(console[e]||console.log)(n,...i,...t)}const u={log:(...e)=>{p("log",...e)},warn:(...e)=>{p("warn",...e)},error:(...e)=>{p("error",...e)},info:(...e)=>{p("info",...e)},debug:(...e)=>{p("debug",...e)}};function B(e){try{const r=new URL(e).hostname;return W.some(i=>i.startsWith(".")?r.endsWith(i)||r===i.slice(1):r===i||r.startsWith(`${i}:`))}catch{return!1}}function Z(){return typeof window>"u"||window.parent===window?null:window.parent}function l(e,t,r="*"){const i=Z();if(!i){u.warn("Not running in an iframe, cannot post to host");return}const n={event:e,...t};i.postMessage(n,r)}const h=new Map;let C=!1;function F(e){if(process.env.NODE_ENV==="production"&&!B(e.origin))return;const t=e.data;if(!t||typeof t.event!="string")return;const r=h.get(t.event);r&&r.forEach(n=>{try{n(t)}catch(s){u.error("Error in message handler:",s)}});const i=h.get("*");i&&i.forEach(n=>{try{n(t)}catch(s){u.error("Error in wildcard handler:",s)}})}function J(){C||typeof window>"u"||(window.addEventListener("message",F),C=!0)}function v(e,t){J(),h.has(e)||h.set(e,new Set);const r=h.get(e);return r.add(t),()=>{r.delete(t),r.size===0&&h.delete(e)}}function Q(e,t=X){return new Promise((r,i)=>{const n=setTimeout(()=>{s(),i(new Error(`[EmbeddedSDK] Timeout waiting for "${e}" message`))},t),s=v(e,a=>{clearTimeout(n),s(),r(a)})})}function ee(){h.clear(),C&&typeof window<"u"&&(window.removeEventListener("message",F),C=!1)}function te(){return typeof window>"u"?!1:window.parent!==window}const g=new Map,re=3e4;function ie(){const e=Date.now(),t=Math.random().toString(36).slice(2,9);return`req_${e}_${t}`}function ne(e,t={},r=re){const i=ie();return new Promise((n,s)=>{const a=setTimeout(()=>{g.get(i)&&(g.delete(i),s(new Error(`[EmbeddedSDK] Request "${e}" timed out after ${r}ms`)))},r);g.set(i,{resolve:n,reject:s,timeout:a,event:e}),l(e,{...t,requestId:i})})}function D(e,t,r){const i=g.get(e);if(!i){u.warn(`Received response for unknown request: ${e}`);return}clearTimeout(i.timeout),g.delete(e),r?i.reject(new Error(r)):i.resolve(t)}function se(e="SDK cleanup"){g.forEach((t,r)=>{clearTimeout(t.timeout),t.reject(new Error(`[EmbeddedSDK] Request ${r} cancelled: ${e}`))}),g.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 oe(e,t={}){const{method:r="GET",headers:i={},body:n,timeout:s=3e4}=t,a=`${ae}${e}`,E=new AbortController,w=setTimeout(()=>{E.abort()},s);try{const c=await fetch(a,{method:r,headers:{"Content-Type":"application/json",...i},body:n?JSON.stringify(n):void 0,signal:E.signal});clearTimeout(w);let A;const I=c.headers.get("content-type");if(I!=null&&I.includes("application/json")?A=await c.json():A=await c.text(),!c.ok)throw new y(`API request failed: ${c.statusText}`,c.status,A);return A}catch(c){throw clearTimeout(w),c instanceof y?c:c instanceof Error?c.name==="AbortError"?new y(`Request timeout after ${s}ms`):new y(`Request failed: ${c.message}`):new y("Unknown error occurred")}}function L(e){return{isVerified:!1,isError:!0,error:e,data:null}}async function le(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),L(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),L(n)}try{const n=await oe("/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"}}),s=n.success;return{isVerified:s,isError:!s,error:s?void 0:"API request failed",data:s?n.data:null}}catch(n){i&&(k().ui.toast.error((n==null?void 0:n.toString())??"Introspect error"),l(q.REFRESH,{})),u.error("Error in introspect:",n);const s=n instanceof Error?n.message:n;return L(s)}}function ue(e){return{getToken(){return new URLSearchParams(window.location.search).get("token")},getAppId(){return new URLSearchParams(window.location.search).get("app_id")},refresh(){l(q.REFRESH,{})},async introspect(t={}){const r=t.token??this.getToken()??"",i=t.appId??this.getAppId()??"";return le({token:r,appId:i,refreshOnError:t.refreshOnError})}}}const x=["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"||!x.includes(e.type))&&t.push(`Invalid toast type "${e.type}". Expected: ${x.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 de(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 fe(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 he(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 ge(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((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.subTitle!==void 0&&typeof n.subTitle!="string"&&t.push(`Extended action at index ${i} subTitle must be a string`),n.url!==void 0&&typeof n.url!="string"&&t.push(`Extended action at index ${i} url must be a string`),n.value!==void 0&&typeof n.value!="string"&&t.push(`Extended action at index ${i} value 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 U=["danger","warning","info"];function me(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"||!U.includes(e.variant))&&t.push(`Invalid confirm variant "${e.variant}". Expected: ${U.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 be(){return{navigate(e,t){const r=fe({path:e,...t});if(!r.valid){f(m.NAVIGATE,r.errors);return}l(m.NAVIGATE,{path:e,state:t==null?void 0:t.state,replace:t==null?void 0:t.replace})},redirect(e){const t=he({url:e});if(!t.valid){f(m.REDIRECT,t.errors);return}l(m.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(T.RESIZE,["Height must be a non-negative number"]);return}l(T.RESIZE,{height:e})},autoResize(){const e=document.documentElement.scrollHeight;this.resize(e)},setTitle(e){if(typeof e!="string"||!e.trim()){f(m.SET_TITLE,["Title must be a non-empty string"]);return}l(m.SET_TITLE,{title:e})}}}function Ee(){const e=new Set;let t=null;return v(S.ACTION_CLICK,i=>{if(t)try{t()}catch(n){u.error("Error in onClick callback:",n)}e.forEach(n=>{try{n(i.url,i.value)}catch(s){u.error("Error in action click callback:",s)}})}),{setAction(i){var s;const n=ge(i);if(!n.valid){f(S.SET_ACTION,n.errors);return}i.onClick?t=i.onClick:t=null,l(S.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(){t=null,l(S.SET_ACTION,{title:""})},onActionClick(i){return e.add(i),()=>{e.delete(i)}},primaryAction(i){this.setAction(i)},clearPrimaryAction(){this.clearAction()}}}function pe(){return{show(e="full"){l(d.LOADING,{status:!1,mode:e})},hide(){l(d.LOADING,{status:!0,mode:"full"})}}}function ye(){const e=t=>{const r=ce(t);if(!r.valid){f(d.TOAST,r.errors);return}l(d.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{open(e,t){l(d.MODAL,{action:"open",id:e,content:t})},close(e){l(d.MODAL,{action:"close",id:e})}}}function ve(){return async e=>{const t=me(e);return t.valid?ne(d.CONFIRM,{title:e.title,message:e.message,confirmText:e.confirmText??"Confirm",cancelText:e.cancelText??"Cancel",variant:e.variant??"info"}):(f(d.CONFIRM,t.errors),Promise.reject(new Error(t.errors.join(", "))))}}function we(){return{loading:pe(),toast:ye(),modal:Te(),confirm:ve()}}function Ae(){return{create(e){const t=de(e);if(!t.valid){f(O.CREATE,t.errors);return}l(O.CREATE,{payload:e})}}}const V={debug:!1,initialized:!1},Se={theme:"light",width:0,locale:"ar",currency:"SAR"},P={ready:!1,initializing:!1,layout:{...Se}};class j{constructor(){this.config={...V},this.state={...P},this.themeCallbacks=new Set,this.initCallbacks=new Set,this.appReady=!1,this.auth=ue(),this.page=be(),this.nav=Ee(),this.ui=we(),this.checkout=Ae(),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(){v($.THEME_CHANGE,t=>{this.state.layout.theme=t.theme,this.internalLog("debug","Theme changed:",t.theme),this.themeCallbacks.forEach(r=>{try{r(t.theme)}catch(i){this.internalLog("error","Error in theme callback:",i)}})})}setupResponseListeners(){v(d.CONFIRM_RESPONSE,t=>{this.internalLog("debug","Received confirm response:",t),D(t.requestId,{confirmed:t.confirmed})}),v(d.MODAL_RESPONSE,t=>{this.internalLog("debug","Received modal response:",t),D(t.requestId,t.result,t.error)})}onThemeChange(t){return this.themeCallbacks.add(t),()=>{this.themeCallbacks.delete(t)}}onInit(t){if(this.config.initialized)try{t(this.getState())}catch(r){this.internalLog("error","Error in init callback:",r)}return this.initCallbacks.add(t),()=>{this.initCallbacks.delete(t)}}log(t,r,i){l(G.LOG,{level:t,message:r,context:i})}ready(){if(this.appReady){this.internalLog("debug","App already signaled as ready");return}if(!this.config.initialized){this.internalLog("warn","Cannot signal ready before init() is called");return}this.appReady=!0,l(T.READY,{}),this.internalLog("debug","Sent ready signal to host")}async init(t={}){var r,i,n,s;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();te()||this.internalLog("warn","Not running in an iframe. Some features may not work."),this.config={debug:t.debug??!1,initialized:!1},Y({debug:this.config.debug}),this.state.initializing=!0,this.internalLog("debug","Initializing SDK...");try{l(T.INIT,{height:document.documentElement.scrollHeight}),this.internalLog("debug","Sent iframe.ready message, waiting for context...");const a=await Q($.PROVIDE);this.internalLog("debug","Received context from host:",a),this.state={ready:!0,initializing:!1,layout:{theme:((r=a.layout)==null?void 0:r.theme)??"light",width:((i=a.layout)==null?void 0:i.width)??0,locale:((n=a.layout)==null?void 0:n.locale)??"ar",currency:((s=a.layout)==null?void 0:s.currency)??"SAR"}},this.config.initialized=!0,this.internalLog("debug","Initialization complete. Layout:",this.state.layout);const E=this.getState();return this.initCallbacks.forEach(w=>{try{w(E)}catch(c){this.internalLog("error","Error in init callback:",c)}}),{layout:{...this.state.layout}}}catch(a){throw this.state.initializing=!1,this.state.ready=!1,a}}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&&(l(T.DESTROY,{}),this.internalLog("debug","Sent destroy event to host")),se("SDK destroyed"),ee(),this.themeCallbacks.clear(),this.initCallbacks.clear(),this.config={...V},this.state={...P},this.appReady=!1}}let b=null;function k(){return b||(b=new j),b}function Re(){b&&(b.destroy(),b=null)}const N=k(),Ce=z;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));exports.EmbeddedApp=j;exports.embedded=N;exports.getEmbeddedApp=k;exports.resetEmbeddedApp=Re;exports.version=Ce;
4
4
  //# sourceMappingURL=index.js.map