@salla.sa/embedded-sdk 0.1.0-beta.12 → 0.1.0-beta.3

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