@commercengine/js 0.2.1 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -19
- package/dist/index.cjs +21 -12
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +78 -13
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.mts +78 -13
- package/dist/index.d.mts.map +1 -1
- package/dist/index.iife.js +21 -12
- package/dist/index.iife.js.map +1 -1
- package/dist/index.mjs +21 -12
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -76,7 +76,11 @@ interface CheckoutConfig {
|
|
|
76
76
|
onClose?: () => void;
|
|
77
77
|
onComplete?: (order: OrderData) => void;
|
|
78
78
|
onCartUpdate?: (cart: CartData) => void;
|
|
79
|
-
|
|
79
|
+
onLogin?: (data: AuthLoginData) => void;
|
|
80
|
+
onLogout?: (data: AuthLogoutData) => void;
|
|
81
|
+
onTokenRefresh?: (data: AuthRefreshData) => void;
|
|
82
|
+
onSessionError?: () => void;
|
|
83
|
+
onError?: (error: ErrorData) => void;
|
|
80
84
|
}
|
|
81
85
|
```
|
|
82
86
|
|
|
@@ -102,13 +106,18 @@ const checkout = await Commercengine.init({
|
|
|
102
106
|
onCartUpdate: (cart) => {
|
|
103
107
|
updateCartBadge(cart.count);
|
|
104
108
|
},
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
109
|
+
onLogin: ({ accessToken, user, loginMethod }) => {
|
|
110
|
+
// User logged in via checkout - sync to your auth system
|
|
111
|
+
console.log("User logged in via", loginMethod, ":", user?.email || user?.phone);
|
|
112
|
+
saveUserSession(accessToken);
|
|
113
|
+
},
|
|
114
|
+
onLogout: () => {
|
|
115
|
+
// User logged out - they now have anonymous tokens
|
|
116
|
+
clearUserSession();
|
|
117
|
+
},
|
|
118
|
+
onSessionError: () => {
|
|
119
|
+
// Session corrupted - SDK cleared all tokens, user needs to re-authenticate
|
|
120
|
+
clearUserSession();
|
|
112
121
|
},
|
|
113
122
|
});
|
|
114
123
|
```
|
|
@@ -214,7 +223,11 @@ Subscribe via callbacks (in config) or the event emitter.
|
|
|
214
223
|
| `close` | `onClose` | - | All drawers closed |
|
|
215
224
|
| `complete` | `onComplete` | `OrderData` | Order placed successfully |
|
|
216
225
|
| `cart:updated` | `onCartUpdate` | `CartData` | Cart state changed |
|
|
217
|
-
| `auth:
|
|
226
|
+
| `auth:login` | `onLogin` | `AuthLoginData` | User logged in |
|
|
227
|
+
| `auth:logout` | `onLogout` | `AuthLogoutData` | User logged out |
|
|
228
|
+
| `auth:refresh` | `onTokenRefresh` | `AuthRefreshData` | Tokens auto-refreshed |
|
|
229
|
+
| `auth:session-error` | `onSessionError` | - | Session corrupted, tokens cleared |
|
|
230
|
+
| `error` | `onError` | `ErrorData` | Configuration error |
|
|
218
231
|
|
|
219
232
|
### Event Data Types
|
|
220
233
|
|
|
@@ -230,10 +243,52 @@ interface CartData {
|
|
|
230
243
|
currency: string; // Currency code (e.g., "USD", "INR")
|
|
231
244
|
}
|
|
232
245
|
|
|
233
|
-
interface
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
246
|
+
interface AuthLoginData {
|
|
247
|
+
accessToken: string;
|
|
248
|
+
refreshToken?: string;
|
|
249
|
+
user?: UserInfo;
|
|
250
|
+
loginMethod?: LoginMethod;
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
interface AuthLogoutData {
|
|
254
|
+
accessToken: string; // New anonymous token
|
|
255
|
+
refreshToken?: string;
|
|
256
|
+
user?: UserInfo; // Anonymous user info
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
interface AuthRefreshData {
|
|
260
|
+
accessToken: string;
|
|
261
|
+
refreshToken?: string;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
interface ErrorData {
|
|
265
|
+
message: string;
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
type LoginMethod = "whatsapp" | "phone" | "email";
|
|
269
|
+
|
|
270
|
+
interface Channel {
|
|
271
|
+
id: string;
|
|
272
|
+
name: string;
|
|
273
|
+
type: string;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
interface UserInfo {
|
|
277
|
+
id: string;
|
|
278
|
+
email: string | null;
|
|
279
|
+
phone: string | null;
|
|
280
|
+
username: string;
|
|
281
|
+
firstName: string | null;
|
|
282
|
+
lastName: string | null;
|
|
283
|
+
storeId: string;
|
|
284
|
+
isLoggedIn: boolean;
|
|
285
|
+
isAnonymous: boolean;
|
|
286
|
+
customerId: string | null;
|
|
287
|
+
customerGroupId: string | null;
|
|
288
|
+
anonymousId: string;
|
|
289
|
+
channel: Channel;
|
|
290
|
+
tokenExpiry: Date;
|
|
291
|
+
tokenIssuedAt: Date;
|
|
237
292
|
}
|
|
238
293
|
```
|
|
239
294
|
|
|
@@ -266,11 +321,9 @@ User will be prompted to login within checkout (WhatsApp, Phone, or Email OTP).
|
|
|
266
321
|
const checkout = await Commercengine.init({
|
|
267
322
|
storeId: "store_xxx",
|
|
268
323
|
apiKey: "ak_xxx",
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
saveSession({ accessToken, refreshToken });
|
|
273
|
-
}
|
|
324
|
+
onLogin: ({ accessToken, refreshToken }) => {
|
|
325
|
+
// Save tokens to your auth system
|
|
326
|
+
saveSession({ accessToken, refreshToken });
|
|
274
327
|
},
|
|
275
328
|
});
|
|
276
329
|
```
|
|
@@ -317,7 +370,13 @@ import {
|
|
|
317
370
|
type Environment,
|
|
318
371
|
type CartData,
|
|
319
372
|
type OrderData,
|
|
320
|
-
type
|
|
373
|
+
type AuthLoginData,
|
|
374
|
+
type AuthLogoutData,
|
|
375
|
+
type AuthRefreshData,
|
|
376
|
+
type ErrorData,
|
|
377
|
+
type UserInfo,
|
|
378
|
+
type Channel,
|
|
379
|
+
type LoginMethod,
|
|
321
380
|
} from "@commercengine/js";
|
|
322
381
|
```
|
|
323
382
|
|
package/dist/index.cjs
CHANGED
|
@@ -275,19 +275,16 @@ var Checkout = class extends EventEmitter {
|
|
|
275
275
|
this.handleCartUpdate(data);
|
|
276
276
|
break;
|
|
277
277
|
case "auth:login":
|
|
278
|
-
this.
|
|
279
|
-
type: "login",
|
|
280
|
-
...data
|
|
281
|
-
});
|
|
278
|
+
this.handleLogin(data);
|
|
282
279
|
break;
|
|
283
280
|
case "auth:logout":
|
|
284
|
-
this.
|
|
281
|
+
this.handleLogout(data);
|
|
285
282
|
break;
|
|
286
283
|
case "auth:refresh":
|
|
287
|
-
this.
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
284
|
+
this.handleTokenRefresh(data);
|
|
285
|
+
break;
|
|
286
|
+
case "auth:session-error":
|
|
287
|
+
this.handleSessionError();
|
|
291
288
|
break;
|
|
292
289
|
}
|
|
293
290
|
}
|
|
@@ -323,9 +320,21 @@ var Checkout = class extends EventEmitter {
|
|
|
323
320
|
this.config.onCartUpdate?.(data);
|
|
324
321
|
this.emit("cart:updated", data);
|
|
325
322
|
}
|
|
326
|
-
|
|
327
|
-
this.config.
|
|
328
|
-
this.emit("auth:
|
|
323
|
+
handleLogin(data) {
|
|
324
|
+
this.config.onLogin?.(data);
|
|
325
|
+
this.emit("auth:login", data);
|
|
326
|
+
}
|
|
327
|
+
handleLogout(data) {
|
|
328
|
+
this.config.onLogout?.(data);
|
|
329
|
+
this.emit("auth:logout", data);
|
|
330
|
+
}
|
|
331
|
+
handleTokenRefresh(data) {
|
|
332
|
+
this.config.onTokenRefresh?.(data);
|
|
333
|
+
this.emit("auth:refresh", data);
|
|
334
|
+
}
|
|
335
|
+
handleSessionError() {
|
|
336
|
+
this.config.onSessionError?.();
|
|
337
|
+
this.emit("auth:session-error");
|
|
329
338
|
}
|
|
330
339
|
/**
|
|
331
340
|
* Open the cart drawer
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["onceListener: EventListener<T>","CHECKOUT_URLS: Record<Environment, string>","baseUrl: string","Commercengine: CommercengineGlobal"],"sources":["../src/events.ts","../src/iframe-manager.ts","../src/checkout.ts","../src/index.ts"],"sourcesContent":["/**\n * Simple Event Emitter\n *\n * Provides pub/sub functionality for checkout events.\n */\n\nimport type { CheckoutEventType, EventListener } from \"./types\";\n\nexport class EventEmitter {\n private listeners: Map<CheckoutEventType, Set<EventListener>> = new Map();\n\n /**\n * Subscribe to an event\n */\n on<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)?.add(listener as EventListener);\n }\n\n /**\n * Unsubscribe from an event\n */\n off<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n const eventListeners = this.listeners.get(event);\n if (eventListeners) {\n eventListeners.delete(listener as EventListener);\n }\n }\n\n /**\n * Subscribe to an event (once)\n */\n once<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n const onceListener: EventListener<T> = (data) => {\n this.off(event, onceListener);\n listener(data);\n };\n this.on(event, onceListener);\n }\n\n /**\n * Emit an event to all subscribers\n */\n protected emit<T = unknown>(event: CheckoutEventType, data?: T): void {\n const eventListeners = this.listeners.get(event);\n if (eventListeners) {\n for (const listener of eventListeners) {\n try {\n listener(data);\n } catch (error) {\n // biome-ignore lint/suspicious/noConsole: Intentional error logging\n console.error(`[Commercengine] Error in ${event} listener:`, error);\n }\n }\n }\n }\n\n /**\n * Remove all listeners\n */\n protected removeAllListeners(): void {\n this.listeners.clear();\n }\n}\n","/**\n * Iframe Manager\n *\n * Handles iframe creation, lifecycle, and visibility management.\n * The iframe is pre-mounted (hidden) on init and shown on demand.\n */\n\nimport type { OutgoingEventType } from \"./types\";\n\nexport class IframeManager {\n private iframe: HTMLIFrameElement | null = null;\n private container: HTMLDivElement | null = null;\n private checkoutOrigin: string | null = null;\n\n /**\n * Create and mount the iframe (hidden initially)\n */\n create(url: string, zIndex: number): void {\n if (this.container) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Iframe already created\");\n return;\n }\n\n // Store origin for postMessage security\n this.checkoutOrigin = new URL(url).origin;\n\n // Create container (covers viewport, initially non-interactive)\n this.container = document.createElement(\"div\");\n this.container.id = \"commercengine-checkout\";\n this.container.style.cssText = `\n position: fixed;\n inset: 0;\n z-index: ${zIndex};\n pointer-events: none;\n `;\n\n // Create iframe\n this.iframe = document.createElement(\"iframe\");\n this.iframe.id = \"commercengine-checkout-iframe\";\n this.iframe.title = \"Shopping cart and checkout\";\n this.iframe.src = url;\n this.iframe.allow = \"payment\";\n this.iframe.style.cssText = `\n width: 100%;\n height: 100%;\n border: none;\n pointer-events: inherit;\n background-color: transparent;\n `;\n\n this.container.appendChild(this.iframe);\n document.body.appendChild(this.container);\n }\n\n /**\n * Show the checkout overlay (enable pointer events)\n */\n show(): void {\n if (this.container) {\n this.container.style.pointerEvents = \"auto\";\n document.body.style.overflow = \"hidden\";\n }\n }\n\n /**\n * Hide the checkout overlay (disable pointer events)\n */\n hide(): void {\n if (this.container) {\n this.container.style.pointerEvents = \"none\";\n document.body.style.overflow = \"\";\n }\n }\n\n /**\n * Check if iframe is visible\n */\n isVisible(): boolean {\n return this.container?.style.pointerEvents === \"auto\";\n }\n\n /**\n * Send a message to the checkout iframe\n */\n postMessage(type: OutgoingEventType, data?: unknown): void {\n if (!this.iframe?.contentWindow) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Cannot send message - iframe not ready\");\n return;\n }\n\n // Use specific origin for security - never use \"*\"\n if (!this.checkoutOrigin) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning for security\n console.warn(\"[Commercengine] Cannot send message - checkout origin not established\");\n return;\n }\n this.iframe.contentWindow.postMessage({ type, data }, this.checkoutOrigin);\n }\n\n /**\n * Get the checkout origin for message validation\n */\n getCheckoutOrigin(): string | null {\n return this.checkoutOrigin;\n }\n\n /**\n * Destroy the iframe and clean up\n */\n destroy(): void {\n if (this.container) {\n this.container.remove();\n this.container = null;\n }\n this.iframe = null;\n this.checkoutOrigin = null;\n\n // Restore body scroll\n document.body.style.overflow = \"\";\n }\n}\n","/**\n * Checkout Class\n *\n * Main entry point for Commerce Engine Checkout integration.\n * Manages iframe lifecycle, event handling, and provides public API.\n */\n\nimport { EventEmitter } from \"./events\";\nimport { IframeManager } from \"./iframe-manager\";\nimport type {\n AuthChangeData,\n CartData,\n CheckoutConfig,\n CheckoutEventType,\n Environment,\n EventListener,\n IncomingEventType,\n OrderData,\n} from \"./types\";\n\n// =============================================================================\n// URL RESOLUTION\n// =============================================================================\n\n/** Checkout URLs by environment */\nconst CHECKOUT_URLS: Record<Environment, string> = {\n production: \"https://checkout.commercengine.com\",\n staging: \"https://staging.checkout.commercengine.com\",\n};\n\n/**\n * Build checkout iframe URL\n */\nfunction buildCheckoutUrl(storeId: string, apiKey: string, environment: Environment): string {\n const baseUrl = CHECKOUT_URLS[environment];\n return `${baseUrl}?store_id=${storeId}&api_key=${apiKey}&environment=${environment}&mode=iframe`;\n}\n\n// =============================================================================\n// CHECKOUT CLASS\n// =============================================================================\n\nexport class Checkout extends EventEmitter {\n private config: CheckoutConfig;\n private iframe: IframeManager;\n private isReady = false;\n private isOpen = false;\n private cartState: CartData = { count: 0, total: 0, currency: \"INR\" };\n private boundMessageHandler: (event: MessageEvent) => void;\n private hasQuickBuyParams = false; // Track if we detected quick buy params\n\n constructor(config: CheckoutConfig) {\n super();\n this.config = config;\n this.iframe = new IframeManager();\n this.boundMessageHandler = this.handleMessage.bind(this);\n this.initialize();\n }\n\n // ===========================================================================\n // INITIALIZATION\n // ===========================================================================\n\n private initialize(): void {\n // Build checkout URL - use direct URL if provided, otherwise resolve from credentials\n let baseUrl: string;\n if (this.config.url) {\n // Direct URL provided (local development)\n baseUrl = this.config.url;\n } else if (this.config.storeId && this.config.apiKey) {\n // Build URL from credentials + environment\n const env = this.config.environment || \"production\";\n baseUrl = buildCheckoutUrl(this.config.storeId, this.config.apiKey, env);\n } else {\n throw new Error(\n \"[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided.\"\n );\n }\n\n const url = new URL(baseUrl);\n\n // Always set iframe mode\n url.searchParams.set(\"mode\", \"iframe\");\n\n // Add store credentials if provided and not already in URL\n if (this.config.storeId && !url.searchParams.has(\"store_id\")) {\n url.searchParams.set(\"store_id\", this.config.storeId);\n }\n if (this.config.apiKey && !url.searchParams.has(\"api_key\")) {\n url.searchParams.set(\"api_key\", this.config.apiKey);\n }\n if (this.config.environment && !url.searchParams.has(\"environment\")) {\n url.searchParams.set(\"environment\", this.config.environment);\n }\n\n // Add theme preference\n if (this.config.theme) {\n url.searchParams.set(\"theme\", this.config.theme);\n }\n\n // Add auth config\n if (this.config.authMode) {\n url.searchParams.set(\"auth_mode\", this.config.authMode);\n }\n if (this.config.accessToken) {\n url.searchParams.set(\"token\", this.config.accessToken);\n }\n if (this.config.refreshToken) {\n url.searchParams.set(\"refresh_token\", this.config.refreshToken);\n }\n\n // Auto-detect quick buy params from parent URL (if enabled and no explicit quickBuy)\n let quickBuy = this.config.quickBuy;\n let sessionMode = this.config.sessionMode;\n\n if (this.config.autoDetectQuickBuy && !quickBuy) {\n const parentParams = new URLSearchParams(window.location.search);\n const productId = parentParams.get(\"product_id\");\n const variantId = parentParams.get(\"variant_id\");\n\n if (productId) {\n quickBuy = {\n productId,\n variantId: variantId ?? null,\n quantity: parseInt(parentParams.get(\"qty\") || parentParams.get(\"quantity\") || \"1\", 10),\n };\n\n // Also check for session_mode in parent URL\n const parentSessionMode = parentParams.get(\"session_mode\");\n if (parentSessionMode === \"force-new\" || parentSessionMode === \"continue-existing\") {\n sessionMode = parentSessionMode;\n }\n\n // Clean quick buy params from URL to prevent duplicate adds on refresh\n // Deferred to next tick to handle React StrictMode double-mounting\n setTimeout(() => this.cleanQuickBuyParamsFromUrl(), 0);\n }\n }\n\n // Add quick buy params\n if (quickBuy) {\n this.hasQuickBuyParams = true;\n url.searchParams.set(\"product_id\", quickBuy.productId);\n if (quickBuy.variantId) {\n url.searchParams.set(\"variant_id\", quickBuy.variantId);\n }\n if (quickBuy.quantity && quickBuy.quantity !== 1) {\n url.searchParams.set(\"qty\", String(quickBuy.quantity));\n }\n }\n\n // Add session mode\n if (sessionMode) {\n url.searchParams.set(\"session_mode\", sessionMode);\n }\n\n // Pass parent origin for postMessage security\n // This allows the iframe to validate incoming messages and send responses to the correct origin\n if (typeof window !== \"undefined\") {\n url.searchParams.set(\"parent_origin\", window.location.origin);\n }\n\n // Create iframe (hidden initially, will show when ready + opened)\n const zIndex = this.config.appearance?.zIndex ?? 99999;\n this.iframe.create(url.toString(), zIndex);\n\n // Listen for messages from checkout iframe\n window.addEventListener(\"message\", this.boundMessageHandler);\n }\n\n // ===========================================================================\n // URL CLEANUP\n // ===========================================================================\n\n /**\n * Remove quick buy params from the parent URL to prevent duplicate adds on refresh\n * Uses replaceState to avoid adding to browser history\n */\n private cleanQuickBuyParamsFromUrl(): void {\n if (typeof window === \"undefined\") return;\n\n const url = new URL(window.location.href);\n const paramsToRemove = [\"product_id\", \"variant_id\", \"qty\", \"quantity\", \"session_mode\"];\n\n let hasChanges = false;\n for (const param of paramsToRemove) {\n if (url.searchParams.has(param)) {\n url.searchParams.delete(param);\n hasChanges = true;\n }\n }\n\n if (hasChanges) {\n window.history.replaceState({}, \"\", url.toString());\n }\n }\n\n // ===========================================================================\n // MESSAGE HANDLING\n // ===========================================================================\n\n private handleMessage(event: MessageEvent): void {\n // Validate origin for security - reject if no expected origin or mismatch\n const expectedOrigin = this.iframe.getCheckoutOrigin();\n if (!expectedOrigin || event.origin !== expectedOrigin) {\n return;\n }\n\n const { type, data } = event.data || {};\n if (!type) return;\n\n switch (type as IncomingEventType) {\n case \"checkout:ready\":\n this.handleReady();\n break;\n\n case \"checkout:open\":\n this.handleOpen();\n break;\n\n case \"checkout:close\":\n this.handleClose();\n break;\n\n case \"checkout:complete\":\n this.handleComplete(data as OrderData);\n break;\n\n case \"checkout:error\":\n this.handleError(data as { message: string });\n break;\n\n case \"cart:updated\":\n this.handleCartUpdate(data as CartData);\n break;\n\n case \"auth:login\":\n this.handleAuthChange({ type: \"login\", ...data });\n break;\n\n case \"auth:logout\":\n this.handleAuthChange({ type: \"logout\" });\n break;\n\n case \"auth:refresh\":\n this.handleAuthChange({ type: \"refresh\", ...data });\n break;\n }\n }\n\n private handleReady(): void {\n this.isReady = true;\n this.config.onReady?.();\n this.emit(\"ready\");\n\n // Auto-open cart if we have quick buy params (from config or auto-detected URL)\n if (this.hasQuickBuyParams) {\n this.openCart();\n }\n }\n\n private handleError(data: { message: string }): void {\n // Set ready so user can still interact (they'll see the error drawer)\n this.isReady = true;\n this.config.onError?.(data);\n this.emit(\"error\", data);\n }\n\n private handleOpen(): void {\n this.iframe.show();\n this.isOpen = true;\n this.config.onOpen?.();\n this.emit(\"open\");\n }\n\n private handleClose(): void {\n this.iframe.hide();\n this.isOpen = false;\n this.config.onClose?.();\n this.emit(\"close\");\n }\n\n private handleComplete(data: OrderData): void {\n this.config.onComplete?.(data);\n this.emit(\"complete\", data);\n }\n\n private handleCartUpdate(data: CartData): void {\n this.cartState = data;\n this.config.onCartUpdate?.(data);\n this.emit(\"cart:updated\", data);\n }\n\n private handleAuthChange(data: AuthChangeData): void {\n this.config.onAuthChange?.(data);\n this.emit(\"auth:change\", data);\n }\n\n // ===========================================================================\n // PUBLIC API\n // ===========================================================================\n\n /**\n * Open the cart drawer\n */\n openCart(): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.show();\n this.isOpen = true;\n this.iframe.postMessage(\"checkout:open-cart\");\n }\n\n /**\n * Open the checkout drawer directly (e.g., for Buy Now flow)\n */\n openCheckout(): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.show();\n this.isOpen = true;\n this.iframe.postMessage(\"checkout:open-checkout\");\n }\n\n /**\n * Close the checkout overlay\n */\n close(): void {\n this.iframe.hide();\n this.isOpen = false;\n this.iframe.postMessage(\"checkout:close\");\n }\n\n /**\n * Update authentication tokens\n * Use this when user logs in/out on the parent site\n */\n updateTokens(accessToken: string, refreshToken?: string): void {\n if (!this.isReady) {\n // Store for when ready\n this.config.accessToken = accessToken;\n this.config.refreshToken = refreshToken;\n return;\n }\n\n this.iframe.postMessage(\"checkout:update-tokens\", { accessToken, refreshToken });\n }\n\n /**\n * Add an item to cart and open the cart drawer\n *\n * @param productId - Product ID (required)\n * @param variantId - Variant ID (required, null for non-variant products)\n * @param quantity - Quantity to add (default: 1)\n */\n addToCart(productId: string, variantId: string | null, quantity = 1): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.postMessage(\"checkout:add-item\", {\n productId,\n variantId,\n quantity,\n });\n\n // Auto-open cart to show the added item\n this.openCart();\n }\n\n /**\n * Get current cart state\n */\n getCart(): CartData {\n return { ...this.cartState };\n }\n\n /**\n * Check if checkout is ready\n */\n get ready(): boolean {\n return this.isReady;\n }\n\n /**\n * Check if checkout overlay is currently open\n */\n get open(): boolean {\n return this.isOpen;\n }\n\n /**\n * Subscribe to an event\n * @override EventEmitter.on with typed overloads\n */\n on(event: \"ready\", listener: EventListener<void>): void;\n on(event: \"open\", listener: EventListener<void>): void;\n on(event: \"close\", listener: EventListener<void>): void;\n on(event: \"complete\", listener: EventListener<OrderData>): void;\n on(event: \"cart:updated\", listener: EventListener<CartData>): void;\n on(event: \"auth:change\", listener: EventListener<AuthChangeData>): void;\n on<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n super.on(event, listener);\n }\n\n /**\n * Destroy the checkout instance\n * Removes iframe and cleans up event listeners\n */\n destroy(): void {\n window.removeEventListener(\"message\", this.boundMessageHandler);\n this.iframe.destroy();\n this.removeAllListeners();\n this.isReady = false;\n this.isOpen = false;\n }\n}\n","/**\n * Commerce Engine Checkout\n *\n * @commercengine/js - Embeddable checkout SDK\n *\n * @example CDN Usage (Vanilla JS):\n * ```html\n * <script src=\"https://cdn.commercengine.com/v1.js\" async></script>\n * <script>\n * window.Commercengine.onLoad = async () => {\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * theme: \"dark\",\n * onComplete: (order) => console.log(\"Order:\", order.orderNumber),\n * });\n *\n * document.getElementById(\"cart-btn\").onclick = () => checkout.openCart();\n * };\n * </script>\n * ```\n *\n * @example ESM Import:\n * ```typescript\n * import { Commercengine } from \"@commercengine/js\";\n *\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * });\n *\n * checkout.on(\"cart:updated\", (cart) => updateBadge(cart.count));\n * checkout.openCart();\n * ```\n */\n\nimport { Checkout } from \"./checkout\";\nimport type { CheckoutConfig } from \"./types\";\n\n// Re-export types for consumers\nexport type {\n AddToCartItem,\n AuthChangeData,\n AuthMode,\n CartData,\n CheckoutConfig,\n CheckoutEventType,\n Environment,\n ErrorData,\n OrderData,\n QuickBuyConfig,\n SessionMode,\n} from \"./types\";\n\nexport { Checkout };\n\n// =============================================================================\n// GLOBAL API\n// =============================================================================\n\n/**\n * Global Commercengine object interface\n */\ninterface CommercengineGlobal {\n /**\n * Initialize checkout with configuration\n * @returns Promise that resolves to Checkout instance\n */\n init: (config: CheckoutConfig) => Promise<Checkout>;\n\n /**\n * Callback invoked when script has loaded\n * Set this BEFORE including the script to be notified when ready\n */\n onLoad?: () => void;\n\n /**\n * Current checkout instance (if initialized)\n */\n instance?: Checkout;\n}\n\n/**\n * Global Commercengine namespace\n */\nconst Commercengine: CommercengineGlobal = {\n /**\n * Initialize checkout\n *\n * @param config - Checkout configuration\n * @returns Checkout instance\n *\n * @example\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * theme: \"system\",\n * });\n */\n init: async (config: CheckoutConfig): Promise<Checkout> => {\n // Validate required fields - either url (for dev) or storeId+apiKey (for prod)\n if (!config.url && (!config.storeId || !config.apiKey)) {\n throw new Error(\n \"[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided\"\n );\n }\n\n // Destroy existing instance if any\n if (Commercengine.instance) {\n Commercengine.instance.destroy();\n }\n\n // Create new instance\n const checkout = new Checkout(config);\n Commercengine.instance = checkout;\n\n // Wait for ready or error with timeout\n return new Promise((resolve, reject) => {\n if (checkout.ready) {\n resolve(checkout);\n return;\n }\n\n const timeoutMs = 30000; // 30 second timeout\n const timeout = setTimeout(() => {\n reject(new Error(\"Checkout initialization timed out\"));\n }, timeoutMs);\n\n // Resolve on ready\n checkout.once(\"ready\", () => {\n clearTimeout(timeout);\n resolve(checkout);\n });\n\n // Also resolve on error (checkout is still usable, will show error drawer)\n checkout.once(\"error\", () => {\n clearTimeout(timeout);\n resolve(checkout);\n });\n });\n },\n};\n\n// =============================================================================\n// GLOBAL SETUP\n// =============================================================================\n\n// Expose on window for CDN usage\nif (typeof window !== \"undefined\") {\n // Preserve any existing onLoad callback set before script loaded\n const existingOnLoad = (window as unknown as { Commercengine?: CommercengineGlobal })\n .Commercengine?.onLoad;\n\n // Set global\n (window as unknown as { Commercengine: CommercengineGlobal }).Commercengine = Commercengine;\n\n // Restore and call onLoad if it was set\n if (existingOnLoad) {\n Commercengine.onLoad = existingOnLoad;\n try {\n existingOnLoad();\n } catch (error) {\n // biome-ignore lint/suspicious/noConsole: Intentional error logging\n console.error(\"[Commercengine] Error in onLoad callback:\", error);\n }\n }\n}\n\nexport { Commercengine };\nexport default Commercengine;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["onceListener: EventListener<T>","CHECKOUT_URLS: Record<Environment, string>","baseUrl: string","Commercengine: CommercengineGlobal"],"sources":["../src/events.ts","../src/iframe-manager.ts","../src/checkout.ts","../src/index.ts"],"sourcesContent":["/**\n * Simple Event Emitter\n *\n * Provides pub/sub functionality for checkout events.\n */\n\nimport type { CheckoutEventType, EventListener } from \"./types\";\n\nexport class EventEmitter {\n private listeners: Map<CheckoutEventType, Set<EventListener>> = new Map();\n\n /**\n * Subscribe to an event\n */\n on<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)?.add(listener as EventListener);\n }\n\n /**\n * Unsubscribe from an event\n */\n off<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n const eventListeners = this.listeners.get(event);\n if (eventListeners) {\n eventListeners.delete(listener as EventListener);\n }\n }\n\n /**\n * Subscribe to an event (once)\n */\n once<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n const onceListener: EventListener<T> = (data) => {\n this.off(event, onceListener);\n listener(data);\n };\n this.on(event, onceListener);\n }\n\n /**\n * Emit an event to all subscribers\n */\n protected emit<T = unknown>(event: CheckoutEventType, data?: T): void {\n const eventListeners = this.listeners.get(event);\n if (eventListeners) {\n for (const listener of eventListeners) {\n try {\n listener(data);\n } catch (error) {\n // biome-ignore lint/suspicious/noConsole: Intentional error logging\n console.error(`[Commercengine] Error in ${event} listener:`, error);\n }\n }\n }\n }\n\n /**\n * Remove all listeners\n */\n protected removeAllListeners(): void {\n this.listeners.clear();\n }\n}\n","/**\n * Iframe Manager\n *\n * Handles iframe creation, lifecycle, and visibility management.\n * The iframe is pre-mounted (hidden) on init and shown on demand.\n */\n\nimport type { OutgoingEventType } from \"./types\";\n\nexport class IframeManager {\n private iframe: HTMLIFrameElement | null = null;\n private container: HTMLDivElement | null = null;\n private checkoutOrigin: string | null = null;\n\n /**\n * Create and mount the iframe (hidden initially)\n */\n create(url: string, zIndex: number): void {\n if (this.container) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Iframe already created\");\n return;\n }\n\n // Store origin for postMessage security\n this.checkoutOrigin = new URL(url).origin;\n\n // Create container (covers viewport, initially non-interactive)\n this.container = document.createElement(\"div\");\n this.container.id = \"commercengine-checkout\";\n this.container.style.cssText = `\n position: fixed;\n inset: 0;\n z-index: ${zIndex};\n pointer-events: none;\n `;\n\n // Create iframe\n this.iframe = document.createElement(\"iframe\");\n this.iframe.id = \"commercengine-checkout-iframe\";\n this.iframe.title = \"Shopping cart and checkout\";\n this.iframe.src = url;\n this.iframe.allow = \"payment\";\n this.iframe.style.cssText = `\n width: 100%;\n height: 100%;\n border: none;\n pointer-events: inherit;\n background-color: transparent;\n `;\n\n this.container.appendChild(this.iframe);\n document.body.appendChild(this.container);\n }\n\n /**\n * Show the checkout overlay (enable pointer events)\n */\n show(): void {\n if (this.container) {\n this.container.style.pointerEvents = \"auto\";\n document.body.style.overflow = \"hidden\";\n }\n }\n\n /**\n * Hide the checkout overlay (disable pointer events)\n */\n hide(): void {\n if (this.container) {\n this.container.style.pointerEvents = \"none\";\n document.body.style.overflow = \"\";\n }\n }\n\n /**\n * Check if iframe is visible\n */\n isVisible(): boolean {\n return this.container?.style.pointerEvents === \"auto\";\n }\n\n /**\n * Send a message to the checkout iframe\n */\n postMessage(type: OutgoingEventType, data?: unknown): void {\n if (!this.iframe?.contentWindow) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Cannot send message - iframe not ready\");\n return;\n }\n\n // Use specific origin for security - never use \"*\"\n if (!this.checkoutOrigin) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning for security\n console.warn(\"[Commercengine] Cannot send message - checkout origin not established\");\n return;\n }\n this.iframe.contentWindow.postMessage({ type, data }, this.checkoutOrigin);\n }\n\n /**\n * Get the checkout origin for message validation\n */\n getCheckoutOrigin(): string | null {\n return this.checkoutOrigin;\n }\n\n /**\n * Destroy the iframe and clean up\n */\n destroy(): void {\n if (this.container) {\n this.container.remove();\n this.container = null;\n }\n this.iframe = null;\n this.checkoutOrigin = null;\n\n // Restore body scroll\n document.body.style.overflow = \"\";\n }\n}\n","/**\n * Checkout Class\n *\n * Main entry point for Commerce Engine Checkout integration.\n * Manages iframe lifecycle, event handling, and provides public API.\n */\n\nimport { EventEmitter } from \"./events\";\nimport { IframeManager } from \"./iframe-manager\";\nimport type {\n AuthLoginData,\n AuthLogoutData,\n AuthRefreshData,\n CartData,\n CheckoutConfig,\n CheckoutEventType,\n Environment,\n EventListener,\n IncomingEventType,\n OrderData,\n} from \"./types\";\n\n// =============================================================================\n// URL RESOLUTION\n// =============================================================================\n\n/** Checkout URLs by environment */\nconst CHECKOUT_URLS: Record<Environment, string> = {\n production: \"https://checkout.commercengine.com\",\n staging: \"https://staging.checkout.commercengine.com\",\n};\n\n/**\n * Build checkout iframe URL\n */\nfunction buildCheckoutUrl(storeId: string, apiKey: string, environment: Environment): string {\n const baseUrl = CHECKOUT_URLS[environment];\n return `${baseUrl}?store_id=${storeId}&api_key=${apiKey}&environment=${environment}&mode=iframe`;\n}\n\n// =============================================================================\n// CHECKOUT CLASS\n// =============================================================================\n\nexport class Checkout extends EventEmitter {\n private config: CheckoutConfig;\n private iframe: IframeManager;\n private isReady = false;\n private isOpen = false;\n private cartState: CartData = { count: 0, total: 0, currency: \"INR\" };\n private boundMessageHandler: (event: MessageEvent) => void;\n private hasQuickBuyParams = false; // Track if we detected quick buy params\n\n constructor(config: CheckoutConfig) {\n super();\n this.config = config;\n this.iframe = new IframeManager();\n this.boundMessageHandler = this.handleMessage.bind(this);\n this.initialize();\n }\n\n // ===========================================================================\n // INITIALIZATION\n // ===========================================================================\n\n private initialize(): void {\n // Build checkout URL - use direct URL if provided, otherwise resolve from credentials\n let baseUrl: string;\n if (this.config.url) {\n // Direct URL provided (local development)\n baseUrl = this.config.url;\n } else if (this.config.storeId && this.config.apiKey) {\n // Build URL from credentials + environment\n const env = this.config.environment || \"production\";\n baseUrl = buildCheckoutUrl(this.config.storeId, this.config.apiKey, env);\n } else {\n throw new Error(\n \"[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided.\"\n );\n }\n\n const url = new URL(baseUrl);\n\n // Always set iframe mode\n url.searchParams.set(\"mode\", \"iframe\");\n\n // Add store credentials if provided and not already in URL\n if (this.config.storeId && !url.searchParams.has(\"store_id\")) {\n url.searchParams.set(\"store_id\", this.config.storeId);\n }\n if (this.config.apiKey && !url.searchParams.has(\"api_key\")) {\n url.searchParams.set(\"api_key\", this.config.apiKey);\n }\n if (this.config.environment && !url.searchParams.has(\"environment\")) {\n url.searchParams.set(\"environment\", this.config.environment);\n }\n\n // Add theme preference\n if (this.config.theme) {\n url.searchParams.set(\"theme\", this.config.theme);\n }\n\n // Add auth config\n if (this.config.authMode) {\n url.searchParams.set(\"auth_mode\", this.config.authMode);\n }\n if (this.config.accessToken) {\n url.searchParams.set(\"token\", this.config.accessToken);\n }\n if (this.config.refreshToken) {\n url.searchParams.set(\"refresh_token\", this.config.refreshToken);\n }\n\n // Auto-detect quick buy params from parent URL (if enabled and no explicit quickBuy)\n let quickBuy = this.config.quickBuy;\n let sessionMode = this.config.sessionMode;\n\n if (this.config.autoDetectQuickBuy && !quickBuy) {\n const parentParams = new URLSearchParams(window.location.search);\n const productId = parentParams.get(\"product_id\");\n const variantId = parentParams.get(\"variant_id\");\n\n if (productId) {\n quickBuy = {\n productId,\n variantId: variantId ?? null,\n quantity: parseInt(parentParams.get(\"qty\") || parentParams.get(\"quantity\") || \"1\", 10),\n };\n\n // Also check for session_mode in parent URL\n const parentSessionMode = parentParams.get(\"session_mode\");\n if (parentSessionMode === \"force-new\" || parentSessionMode === \"continue-existing\") {\n sessionMode = parentSessionMode;\n }\n\n // Clean quick buy params from URL to prevent duplicate adds on refresh\n // Deferred to next tick to handle React StrictMode double-mounting\n setTimeout(() => this.cleanQuickBuyParamsFromUrl(), 0);\n }\n }\n\n // Add quick buy params\n if (quickBuy) {\n this.hasQuickBuyParams = true;\n url.searchParams.set(\"product_id\", quickBuy.productId);\n if (quickBuy.variantId) {\n url.searchParams.set(\"variant_id\", quickBuy.variantId);\n }\n if (quickBuy.quantity && quickBuy.quantity !== 1) {\n url.searchParams.set(\"qty\", String(quickBuy.quantity));\n }\n }\n\n // Add session mode\n if (sessionMode) {\n url.searchParams.set(\"session_mode\", sessionMode);\n }\n\n // Pass parent origin for postMessage security\n // This allows the iframe to validate incoming messages and send responses to the correct origin\n if (typeof window !== \"undefined\") {\n url.searchParams.set(\"parent_origin\", window.location.origin);\n }\n\n // Create iframe (hidden initially, will show when ready + opened)\n const zIndex = this.config.appearance?.zIndex ?? 99999;\n this.iframe.create(url.toString(), zIndex);\n\n // Listen for messages from checkout iframe\n window.addEventListener(\"message\", this.boundMessageHandler);\n }\n\n // ===========================================================================\n // URL CLEANUP\n // ===========================================================================\n\n /**\n * Remove quick buy params from the parent URL to prevent duplicate adds on refresh\n * Uses replaceState to avoid adding to browser history\n */\n private cleanQuickBuyParamsFromUrl(): void {\n if (typeof window === \"undefined\") return;\n\n const url = new URL(window.location.href);\n const paramsToRemove = [\"product_id\", \"variant_id\", \"qty\", \"quantity\", \"session_mode\"];\n\n let hasChanges = false;\n for (const param of paramsToRemove) {\n if (url.searchParams.has(param)) {\n url.searchParams.delete(param);\n hasChanges = true;\n }\n }\n\n if (hasChanges) {\n window.history.replaceState({}, \"\", url.toString());\n }\n }\n\n // ===========================================================================\n // MESSAGE HANDLING\n // ===========================================================================\n\n private handleMessage(event: MessageEvent): void {\n // Validate origin for security - reject if no expected origin or mismatch\n const expectedOrigin = this.iframe.getCheckoutOrigin();\n if (!expectedOrigin || event.origin !== expectedOrigin) {\n return;\n }\n\n const { type, data } = event.data || {};\n if (!type) return;\n\n switch (type as IncomingEventType) {\n case \"checkout:ready\":\n this.handleReady();\n break;\n\n case \"checkout:open\":\n this.handleOpen();\n break;\n\n case \"checkout:close\":\n this.handleClose();\n break;\n\n case \"checkout:complete\":\n this.handleComplete(data as OrderData);\n break;\n\n case \"checkout:error\":\n this.handleError(data as { message: string });\n break;\n\n case \"cart:updated\":\n this.handleCartUpdate(data as CartData);\n break;\n\n case \"auth:login\":\n this.handleLogin(data as AuthLoginData);\n break;\n\n case \"auth:logout\":\n this.handleLogout(data as AuthLogoutData);\n break;\n\n case \"auth:refresh\":\n this.handleTokenRefresh(data as AuthRefreshData);\n break;\n\n case \"auth:session-error\":\n this.handleSessionError();\n break;\n }\n }\n\n private handleReady(): void {\n this.isReady = true;\n this.config.onReady?.();\n this.emit(\"ready\");\n\n // Auto-open cart if we have quick buy params (from config or auto-detected URL)\n if (this.hasQuickBuyParams) {\n this.openCart();\n }\n }\n\n private handleError(data: { message: string }): void {\n // Set ready so user can still interact (they'll see the error drawer)\n this.isReady = true;\n this.config.onError?.(data);\n this.emit(\"error\", data);\n }\n\n private handleOpen(): void {\n this.iframe.show();\n this.isOpen = true;\n this.config.onOpen?.();\n this.emit(\"open\");\n }\n\n private handleClose(): void {\n this.iframe.hide();\n this.isOpen = false;\n this.config.onClose?.();\n this.emit(\"close\");\n }\n\n private handleComplete(data: OrderData): void {\n this.config.onComplete?.(data);\n this.emit(\"complete\", data);\n }\n\n private handleCartUpdate(data: CartData): void {\n this.cartState = data;\n this.config.onCartUpdate?.(data);\n this.emit(\"cart:updated\", data);\n }\n\n private handleLogin(data: AuthLoginData): void {\n this.config.onLogin?.(data);\n this.emit(\"auth:login\", data);\n }\n\n private handleLogout(data: AuthLogoutData): void {\n this.config.onLogout?.(data);\n this.emit(\"auth:logout\", data);\n }\n\n private handleTokenRefresh(data: AuthRefreshData): void {\n this.config.onTokenRefresh?.(data);\n this.emit(\"auth:refresh\", data);\n }\n\n private handleSessionError(): void {\n this.config.onSessionError?.();\n this.emit(\"auth:session-error\");\n }\n\n // ===========================================================================\n // PUBLIC API\n // ===========================================================================\n\n /**\n * Open the cart drawer\n */\n openCart(): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.show();\n this.isOpen = true;\n this.iframe.postMessage(\"checkout:open-cart\");\n }\n\n /**\n * Open the checkout drawer directly (e.g., for Buy Now flow)\n */\n openCheckout(): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.show();\n this.isOpen = true;\n this.iframe.postMessage(\"checkout:open-checkout\");\n }\n\n /**\n * Close the checkout overlay\n */\n close(): void {\n this.iframe.hide();\n this.isOpen = false;\n this.iframe.postMessage(\"checkout:close\");\n }\n\n /**\n * Update authentication tokens\n * Use this when user logs in/out on the parent site\n */\n updateTokens(accessToken: string, refreshToken?: string): void {\n if (!this.isReady) {\n // Store for when ready\n this.config.accessToken = accessToken;\n this.config.refreshToken = refreshToken;\n return;\n }\n\n this.iframe.postMessage(\"checkout:update-tokens\", { accessToken, refreshToken });\n }\n\n /**\n * Add an item to cart and open the cart drawer\n *\n * @param productId - Product ID (required)\n * @param variantId - Variant ID (required, null for non-variant products)\n * @param quantity - Quantity to add (default: 1)\n */\n addToCart(productId: string, variantId: string | null, quantity = 1): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.postMessage(\"checkout:add-item\", {\n productId,\n variantId,\n quantity,\n });\n\n // Auto-open cart to show the added item\n this.openCart();\n }\n\n /**\n * Get current cart state\n */\n getCart(): CartData {\n return { ...this.cartState };\n }\n\n /**\n * Check if checkout is ready\n */\n get ready(): boolean {\n return this.isReady;\n }\n\n /**\n * Check if checkout overlay is currently open\n */\n get open(): boolean {\n return this.isOpen;\n }\n\n /**\n * Subscribe to an event\n * @override EventEmitter.on with typed overloads\n */\n on(event: \"ready\", listener: EventListener<void>): void;\n on(event: \"open\", listener: EventListener<void>): void;\n on(event: \"close\", listener: EventListener<void>): void;\n on(event: \"complete\", listener: EventListener<OrderData>): void;\n on(event: \"cart:updated\", listener: EventListener<CartData>): void;\n on(event: \"auth:login\", listener: EventListener<AuthLoginData>): void;\n on(event: \"auth:logout\", listener: EventListener<AuthLogoutData>): void;\n on(event: \"auth:refresh\", listener: EventListener<AuthRefreshData>): void;\n on(event: \"auth:session-error\", listener: EventListener<void>): void;\n on<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n super.on(event, listener);\n }\n\n /**\n * Destroy the checkout instance\n * Removes iframe and cleans up event listeners\n */\n destroy(): void {\n window.removeEventListener(\"message\", this.boundMessageHandler);\n this.iframe.destroy();\n this.removeAllListeners();\n this.isReady = false;\n this.isOpen = false;\n }\n}\n","/**\n * Commerce Engine Checkout\n *\n * @commercengine/js - Embeddable checkout SDK\n *\n * @example CDN Usage (Vanilla JS):\n * ```html\n * <script src=\"https://cdn.commercengine.com/v1.js\" async></script>\n * <script>\n * window.Commercengine.onLoad = async () => {\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * theme: \"dark\",\n * onComplete: (order) => console.log(\"Order:\", order.orderNumber),\n * });\n *\n * document.getElementById(\"cart-btn\").onclick = () => checkout.openCart();\n * };\n * </script>\n * ```\n *\n * @example ESM Import:\n * ```typescript\n * import { Commercengine } from \"@commercengine/js\";\n *\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * });\n *\n * checkout.on(\"cart:updated\", (cart) => updateBadge(cart.count));\n * checkout.openCart();\n * ```\n */\n\nimport { Checkout } from \"./checkout\";\nimport type { CheckoutConfig } from \"./types\";\n\n// Re-export types for consumers\nexport type {\n AddToCartItem,\n AuthLoginData,\n AuthLogoutData,\n AuthMode,\n AuthRefreshData,\n CartData,\n Channel,\n CheckoutConfig,\n CheckoutEventType,\n Environment,\n ErrorData,\n LoginMethod,\n OrderData,\n QuickBuyConfig,\n SessionMode,\n UserInfo,\n} from \"./types\";\n\nexport { Checkout };\n\n// =============================================================================\n// GLOBAL API\n// =============================================================================\n\n/**\n * Global Commercengine object interface\n */\ninterface CommercengineGlobal {\n /**\n * Initialize checkout with configuration\n * @returns Promise that resolves to Checkout instance\n */\n init: (config: CheckoutConfig) => Promise<Checkout>;\n\n /**\n * Callback invoked when script has loaded\n * Set this BEFORE including the script to be notified when ready\n */\n onLoad?: () => void;\n\n /**\n * Current checkout instance (if initialized)\n */\n instance?: Checkout;\n}\n\n/**\n * Global Commercengine namespace\n */\nconst Commercengine: CommercengineGlobal = {\n /**\n * Initialize checkout\n *\n * @param config - Checkout configuration\n * @returns Checkout instance\n *\n * @example\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * theme: \"system\",\n * });\n */\n init: async (config: CheckoutConfig): Promise<Checkout> => {\n // Validate required fields - either url (for dev) or storeId+apiKey (for prod)\n if (!config.url && (!config.storeId || !config.apiKey)) {\n throw new Error(\n \"[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided\"\n );\n }\n\n // Destroy existing instance if any\n if (Commercengine.instance) {\n Commercengine.instance.destroy();\n }\n\n // Create new instance\n const checkout = new Checkout(config);\n Commercengine.instance = checkout;\n\n // Wait for ready or error with timeout\n return new Promise((resolve, reject) => {\n if (checkout.ready) {\n resolve(checkout);\n return;\n }\n\n const timeoutMs = 30000; // 30 second timeout\n const timeout = setTimeout(() => {\n reject(new Error(\"Checkout initialization timed out\"));\n }, timeoutMs);\n\n // Resolve on ready\n checkout.once(\"ready\", () => {\n clearTimeout(timeout);\n resolve(checkout);\n });\n\n // Also resolve on error (checkout is still usable, will show error drawer)\n checkout.once(\"error\", () => {\n clearTimeout(timeout);\n resolve(checkout);\n });\n });\n },\n};\n\n// =============================================================================\n// GLOBAL SETUP\n// =============================================================================\n\n// Expose on window for CDN usage\nif (typeof window !== \"undefined\") {\n // Preserve any existing onLoad callback set before script loaded\n const existingOnLoad = (window as unknown as { Commercengine?: CommercengineGlobal })\n .Commercengine?.onLoad;\n\n // Set global\n (window as unknown as { Commercengine: CommercengineGlobal }).Commercengine = Commercengine;\n\n // Restore and call onLoad if it was set\n if (existingOnLoad) {\n Commercengine.onLoad = existingOnLoad;\n try {\n existingOnLoad();\n } catch (error) {\n // biome-ignore lint/suspicious/noConsole: Intentional error logging\n console.error(\"[Commercengine] Error in onLoad callback:\", error);\n }\n }\n}\n\nexport { Commercengine };\nexport default Commercengine;\n"],"mappings":"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"}
|
package/dist/index.d.cts
CHANGED
|
@@ -165,9 +165,22 @@ interface CheckoutConfig {
|
|
|
165
165
|
*/
|
|
166
166
|
onCartUpdate?: (cart: CartData) => void;
|
|
167
167
|
/**
|
|
168
|
-
* Called when
|
|
168
|
+
* Called when user logs in via checkout
|
|
169
169
|
*/
|
|
170
|
-
|
|
170
|
+
onLogin?: (data: AuthLoginData) => void;
|
|
171
|
+
/**
|
|
172
|
+
* Called when user logs out (receives new anonymous tokens)
|
|
173
|
+
*/
|
|
174
|
+
onLogout?: (data: AuthLogoutData) => void;
|
|
175
|
+
/**
|
|
176
|
+
* Called when tokens are auto-refreshed by SDK
|
|
177
|
+
*/
|
|
178
|
+
onTokenRefresh?: (data: AuthRefreshData) => void;
|
|
179
|
+
/**
|
|
180
|
+
* Called when session is corrupted and SDK clears all tokens.
|
|
181
|
+
* User needs to re-authenticate.
|
|
182
|
+
*/
|
|
183
|
+
onSessionError?: () => void;
|
|
171
184
|
/**
|
|
172
185
|
* Called when checkout encounters a configuration error
|
|
173
186
|
* This happens when credentials are missing or invalid.
|
|
@@ -176,6 +189,39 @@ interface CheckoutConfig {
|
|
|
176
189
|
*/
|
|
177
190
|
onError?: (error: ErrorData) => void;
|
|
178
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* Channel information from token
|
|
194
|
+
*/
|
|
195
|
+
interface Channel {
|
|
196
|
+
id: string;
|
|
197
|
+
name: string;
|
|
198
|
+
type: string;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* User information decoded from JWT token
|
|
202
|
+
* Matches @commercengine/storefront-sdk UserInfo 1:1
|
|
203
|
+
*/
|
|
204
|
+
interface UserInfo {
|
|
205
|
+
id: string;
|
|
206
|
+
email: string | null;
|
|
207
|
+
phone: string | null;
|
|
208
|
+
username: string;
|
|
209
|
+
firstName: string | null;
|
|
210
|
+
lastName: string | null;
|
|
211
|
+
storeId: string;
|
|
212
|
+
isLoggedIn: boolean;
|
|
213
|
+
isAnonymous: boolean;
|
|
214
|
+
customerId: string | null;
|
|
215
|
+
customerGroupId: string | null;
|
|
216
|
+
anonymousId: string;
|
|
217
|
+
channel: Channel;
|
|
218
|
+
tokenExpiry: Date;
|
|
219
|
+
tokenIssuedAt: Date;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Login method used for authentication
|
|
223
|
+
*/
|
|
224
|
+
type LoginMethod = "whatsapp" | "phone" | "email";
|
|
179
225
|
/**
|
|
180
226
|
* Cart state data emitted on cart:updated event
|
|
181
227
|
*/
|
|
@@ -197,14 +243,27 @@ interface OrderData {
|
|
|
197
243
|
orderNumber: string;
|
|
198
244
|
}
|
|
199
245
|
/**
|
|
200
|
-
*
|
|
246
|
+
* Data emitted on login event
|
|
201
247
|
*/
|
|
202
|
-
interface
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
248
|
+
interface AuthLoginData {
|
|
249
|
+
accessToken: string;
|
|
250
|
+
refreshToken?: string;
|
|
251
|
+
user?: UserInfo;
|
|
252
|
+
loginMethod?: LoginMethod;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Data emitted on logout event
|
|
256
|
+
*/
|
|
257
|
+
interface AuthLogoutData {
|
|
258
|
+
accessToken: string;
|
|
259
|
+
refreshToken?: string;
|
|
260
|
+
user?: UserInfo;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Data emitted on token refresh event
|
|
264
|
+
*/
|
|
265
|
+
interface AuthRefreshData {
|
|
266
|
+
accessToken: string;
|
|
208
267
|
refreshToken?: string;
|
|
209
268
|
}
|
|
210
269
|
/**
|
|
@@ -217,7 +276,7 @@ interface ErrorData {
|
|
|
217
276
|
/**
|
|
218
277
|
* Events emitted by checkout
|
|
219
278
|
*/
|
|
220
|
-
type CheckoutEventType = "ready" | "open" | "close" | "complete" | "error" | "cart:updated" | "auth:
|
|
279
|
+
type CheckoutEventType = "ready" | "open" | "close" | "complete" | "error" | "cart:updated" | "auth:login" | "auth:logout" | "auth:refresh" | "auth:session-error";
|
|
221
280
|
/**
|
|
222
281
|
* Event listener function type
|
|
223
282
|
*/
|
|
@@ -271,7 +330,10 @@ declare class Checkout extends EventEmitter {
|
|
|
271
330
|
private handleClose;
|
|
272
331
|
private handleComplete;
|
|
273
332
|
private handleCartUpdate;
|
|
274
|
-
private
|
|
333
|
+
private handleLogin;
|
|
334
|
+
private handleLogout;
|
|
335
|
+
private handleTokenRefresh;
|
|
336
|
+
private handleSessionError;
|
|
275
337
|
/**
|
|
276
338
|
* Open the cart drawer
|
|
277
339
|
*/
|
|
@@ -318,7 +380,10 @@ declare class Checkout extends EventEmitter {
|
|
|
318
380
|
on(event: "close", listener: EventListener<void>): void;
|
|
319
381
|
on(event: "complete", listener: EventListener<OrderData>): void;
|
|
320
382
|
on(event: "cart:updated", listener: EventListener<CartData>): void;
|
|
321
|
-
on(event: "auth:
|
|
383
|
+
on(event: "auth:login", listener: EventListener<AuthLoginData>): void;
|
|
384
|
+
on(event: "auth:logout", listener: EventListener<AuthLogoutData>): void;
|
|
385
|
+
on(event: "auth:refresh", listener: EventListener<AuthRefreshData>): void;
|
|
386
|
+
on(event: "auth:session-error", listener: EventListener<void>): void;
|
|
322
387
|
/**
|
|
323
388
|
* Destroy the checkout instance
|
|
324
389
|
* Removes iframe and cleans up event listeners
|
|
@@ -351,5 +416,5 @@ interface CommercengineGlobal {
|
|
|
351
416
|
*/
|
|
352
417
|
declare const Commercengine: CommercengineGlobal;
|
|
353
418
|
//#endregion
|
|
354
|
-
export { type AddToCartItem, type
|
|
419
|
+
export { type AddToCartItem, type AuthLoginData, type AuthLogoutData, type AuthMode, type AuthRefreshData, type CartData, type Channel, Checkout, type CheckoutConfig, type CheckoutEventType, Commercengine, Commercengine as default, type Environment, type ErrorData, type LoginMethod, type OrderData, type QuickBuyConfig, type SessionMode, type UserInfo };
|
|
355
420
|
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.cts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/events.ts","../src/checkout.ts","../src/index.ts"],"sourcesContent":[],"mappings":";;AAkBA;AASA;AAsBA;AA2BA;AAWA;AAQA;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.cts","names":[],"sources":["../src/types.ts","../src/events.ts","../src/checkout.ts","../src/index.ts"],"sourcesContent":[],"mappings":"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"}
|
package/dist/index.d.mts
CHANGED
|
@@ -165,9 +165,22 @@ interface CheckoutConfig {
|
|
|
165
165
|
*/
|
|
166
166
|
onCartUpdate?: (cart: CartData) => void;
|
|
167
167
|
/**
|
|
168
|
-
* Called when
|
|
168
|
+
* Called when user logs in via checkout
|
|
169
169
|
*/
|
|
170
|
-
|
|
170
|
+
onLogin?: (data: AuthLoginData) => void;
|
|
171
|
+
/**
|
|
172
|
+
* Called when user logs out (receives new anonymous tokens)
|
|
173
|
+
*/
|
|
174
|
+
onLogout?: (data: AuthLogoutData) => void;
|
|
175
|
+
/**
|
|
176
|
+
* Called when tokens are auto-refreshed by SDK
|
|
177
|
+
*/
|
|
178
|
+
onTokenRefresh?: (data: AuthRefreshData) => void;
|
|
179
|
+
/**
|
|
180
|
+
* Called when session is corrupted and SDK clears all tokens.
|
|
181
|
+
* User needs to re-authenticate.
|
|
182
|
+
*/
|
|
183
|
+
onSessionError?: () => void;
|
|
171
184
|
/**
|
|
172
185
|
* Called when checkout encounters a configuration error
|
|
173
186
|
* This happens when credentials are missing or invalid.
|
|
@@ -176,6 +189,39 @@ interface CheckoutConfig {
|
|
|
176
189
|
*/
|
|
177
190
|
onError?: (error: ErrorData) => void;
|
|
178
191
|
}
|
|
192
|
+
/**
|
|
193
|
+
* Channel information from token
|
|
194
|
+
*/
|
|
195
|
+
interface Channel {
|
|
196
|
+
id: string;
|
|
197
|
+
name: string;
|
|
198
|
+
type: string;
|
|
199
|
+
}
|
|
200
|
+
/**
|
|
201
|
+
* User information decoded from JWT token
|
|
202
|
+
* Matches @commercengine/storefront-sdk UserInfo 1:1
|
|
203
|
+
*/
|
|
204
|
+
interface UserInfo {
|
|
205
|
+
id: string;
|
|
206
|
+
email: string | null;
|
|
207
|
+
phone: string | null;
|
|
208
|
+
username: string;
|
|
209
|
+
firstName: string | null;
|
|
210
|
+
lastName: string | null;
|
|
211
|
+
storeId: string;
|
|
212
|
+
isLoggedIn: boolean;
|
|
213
|
+
isAnonymous: boolean;
|
|
214
|
+
customerId: string | null;
|
|
215
|
+
customerGroupId: string | null;
|
|
216
|
+
anonymousId: string;
|
|
217
|
+
channel: Channel;
|
|
218
|
+
tokenExpiry: Date;
|
|
219
|
+
tokenIssuedAt: Date;
|
|
220
|
+
}
|
|
221
|
+
/**
|
|
222
|
+
* Login method used for authentication
|
|
223
|
+
*/
|
|
224
|
+
type LoginMethod = "whatsapp" | "phone" | "email";
|
|
179
225
|
/**
|
|
180
226
|
* Cart state data emitted on cart:updated event
|
|
181
227
|
*/
|
|
@@ -197,14 +243,27 @@ interface OrderData {
|
|
|
197
243
|
orderNumber: string;
|
|
198
244
|
}
|
|
199
245
|
/**
|
|
200
|
-
*
|
|
246
|
+
* Data emitted on login event
|
|
201
247
|
*/
|
|
202
|
-
interface
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
248
|
+
interface AuthLoginData {
|
|
249
|
+
accessToken: string;
|
|
250
|
+
refreshToken?: string;
|
|
251
|
+
user?: UserInfo;
|
|
252
|
+
loginMethod?: LoginMethod;
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* Data emitted on logout event
|
|
256
|
+
*/
|
|
257
|
+
interface AuthLogoutData {
|
|
258
|
+
accessToken: string;
|
|
259
|
+
refreshToken?: string;
|
|
260
|
+
user?: UserInfo;
|
|
261
|
+
}
|
|
262
|
+
/**
|
|
263
|
+
* Data emitted on token refresh event
|
|
264
|
+
*/
|
|
265
|
+
interface AuthRefreshData {
|
|
266
|
+
accessToken: string;
|
|
208
267
|
refreshToken?: string;
|
|
209
268
|
}
|
|
210
269
|
/**
|
|
@@ -217,7 +276,7 @@ interface ErrorData {
|
|
|
217
276
|
/**
|
|
218
277
|
* Events emitted by checkout
|
|
219
278
|
*/
|
|
220
|
-
type CheckoutEventType = "ready" | "open" | "close" | "complete" | "error" | "cart:updated" | "auth:
|
|
279
|
+
type CheckoutEventType = "ready" | "open" | "close" | "complete" | "error" | "cart:updated" | "auth:login" | "auth:logout" | "auth:refresh" | "auth:session-error";
|
|
221
280
|
/**
|
|
222
281
|
* Event listener function type
|
|
223
282
|
*/
|
|
@@ -271,7 +330,10 @@ declare class Checkout extends EventEmitter {
|
|
|
271
330
|
private handleClose;
|
|
272
331
|
private handleComplete;
|
|
273
332
|
private handleCartUpdate;
|
|
274
|
-
private
|
|
333
|
+
private handleLogin;
|
|
334
|
+
private handleLogout;
|
|
335
|
+
private handleTokenRefresh;
|
|
336
|
+
private handleSessionError;
|
|
275
337
|
/**
|
|
276
338
|
* Open the cart drawer
|
|
277
339
|
*/
|
|
@@ -318,7 +380,10 @@ declare class Checkout extends EventEmitter {
|
|
|
318
380
|
on(event: "close", listener: EventListener<void>): void;
|
|
319
381
|
on(event: "complete", listener: EventListener<OrderData>): void;
|
|
320
382
|
on(event: "cart:updated", listener: EventListener<CartData>): void;
|
|
321
|
-
on(event: "auth:
|
|
383
|
+
on(event: "auth:login", listener: EventListener<AuthLoginData>): void;
|
|
384
|
+
on(event: "auth:logout", listener: EventListener<AuthLogoutData>): void;
|
|
385
|
+
on(event: "auth:refresh", listener: EventListener<AuthRefreshData>): void;
|
|
386
|
+
on(event: "auth:session-error", listener: EventListener<void>): void;
|
|
322
387
|
/**
|
|
323
388
|
* Destroy the checkout instance
|
|
324
389
|
* Removes iframe and cleans up event listeners
|
|
@@ -351,5 +416,5 @@ interface CommercengineGlobal {
|
|
|
351
416
|
*/
|
|
352
417
|
declare const Commercengine: CommercengineGlobal;
|
|
353
418
|
//#endregion
|
|
354
|
-
export { type AddToCartItem, type
|
|
419
|
+
export { type AddToCartItem, type AuthLoginData, type AuthLogoutData, type AuthMode, type AuthRefreshData, type CartData, type Channel, Checkout, type CheckoutConfig, type CheckoutEventType, Commercengine, Commercengine as default, type Environment, type ErrorData, type LoginMethod, type OrderData, type QuickBuyConfig, type SessionMode, type UserInfo };
|
|
355
420
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/events.ts","../src/checkout.ts","../src/index.ts"],"sourcesContent":[],"mappings":";;AAkBA;AASA;AAsBA;AA2BA;AAWA;AAQA;;;;;;;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/events.ts","../src/checkout.ts","../src/index.ts"],"sourcesContent":[],"mappings":"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"}
|
package/dist/index.iife.js
CHANGED
|
@@ -277,19 +277,16 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
277
277
|
this.handleCartUpdate(data);
|
|
278
278
|
break;
|
|
279
279
|
case "auth:login":
|
|
280
|
-
this.
|
|
281
|
-
type: "login",
|
|
282
|
-
...data
|
|
283
|
-
});
|
|
280
|
+
this.handleLogin(data);
|
|
284
281
|
break;
|
|
285
282
|
case "auth:logout":
|
|
286
|
-
this.
|
|
283
|
+
this.handleLogout(data);
|
|
287
284
|
break;
|
|
288
285
|
case "auth:refresh":
|
|
289
|
-
this.
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
286
|
+
this.handleTokenRefresh(data);
|
|
287
|
+
break;
|
|
288
|
+
case "auth:session-error":
|
|
289
|
+
this.handleSessionError();
|
|
293
290
|
break;
|
|
294
291
|
}
|
|
295
292
|
}
|
|
@@ -325,9 +322,21 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
325
322
|
this.config.onCartUpdate?.(data);
|
|
326
323
|
this.emit("cart:updated", data);
|
|
327
324
|
}
|
|
328
|
-
|
|
329
|
-
this.config.
|
|
330
|
-
this.emit("auth:
|
|
325
|
+
handleLogin(data) {
|
|
326
|
+
this.config.onLogin?.(data);
|
|
327
|
+
this.emit("auth:login", data);
|
|
328
|
+
}
|
|
329
|
+
handleLogout(data) {
|
|
330
|
+
this.config.onLogout?.(data);
|
|
331
|
+
this.emit("auth:logout", data);
|
|
332
|
+
}
|
|
333
|
+
handleTokenRefresh(data) {
|
|
334
|
+
this.config.onTokenRefresh?.(data);
|
|
335
|
+
this.emit("auth:refresh", data);
|
|
336
|
+
}
|
|
337
|
+
handleSessionError() {
|
|
338
|
+
this.config.onSessionError?.();
|
|
339
|
+
this.emit("auth:session-error");
|
|
331
340
|
}
|
|
332
341
|
/**
|
|
333
342
|
* Open the cart drawer
|
package/dist/index.iife.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.iife.js","names":["onceListener: EventListener<T>","CHECKOUT_URLS: Record<Environment, string>","baseUrl: string","Commercengine: CommercengineGlobal"],"sources":["../src/events.ts","../src/iframe-manager.ts","../src/checkout.ts","../src/index.ts"],"sourcesContent":["/**\n * Simple Event Emitter\n *\n * Provides pub/sub functionality for checkout events.\n */\n\nimport type { CheckoutEventType, EventListener } from \"./types\";\n\nexport class EventEmitter {\n private listeners: Map<CheckoutEventType, Set<EventListener>> = new Map();\n\n /**\n * Subscribe to an event\n */\n on<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)?.add(listener as EventListener);\n }\n\n /**\n * Unsubscribe from an event\n */\n off<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n const eventListeners = this.listeners.get(event);\n if (eventListeners) {\n eventListeners.delete(listener as EventListener);\n }\n }\n\n /**\n * Subscribe to an event (once)\n */\n once<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n const onceListener: EventListener<T> = (data) => {\n this.off(event, onceListener);\n listener(data);\n };\n this.on(event, onceListener);\n }\n\n /**\n * Emit an event to all subscribers\n */\n protected emit<T = unknown>(event: CheckoutEventType, data?: T): void {\n const eventListeners = this.listeners.get(event);\n if (eventListeners) {\n for (const listener of eventListeners) {\n try {\n listener(data);\n } catch (error) {\n // biome-ignore lint/suspicious/noConsole: Intentional error logging\n console.error(`[Commercengine] Error in ${event} listener:`, error);\n }\n }\n }\n }\n\n /**\n * Remove all listeners\n */\n protected removeAllListeners(): void {\n this.listeners.clear();\n }\n}\n","/**\n * Iframe Manager\n *\n * Handles iframe creation, lifecycle, and visibility management.\n * The iframe is pre-mounted (hidden) on init and shown on demand.\n */\n\nimport type { OutgoingEventType } from \"./types\";\n\nexport class IframeManager {\n private iframe: HTMLIFrameElement | null = null;\n private container: HTMLDivElement | null = null;\n private checkoutOrigin: string | null = null;\n\n /**\n * Create and mount the iframe (hidden initially)\n */\n create(url: string, zIndex: number): void {\n if (this.container) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Iframe already created\");\n return;\n }\n\n // Store origin for postMessage security\n this.checkoutOrigin = new URL(url).origin;\n\n // Create container (covers viewport, initially non-interactive)\n this.container = document.createElement(\"div\");\n this.container.id = \"commercengine-checkout\";\n this.container.style.cssText = `\n position: fixed;\n inset: 0;\n z-index: ${zIndex};\n pointer-events: none;\n `;\n\n // Create iframe\n this.iframe = document.createElement(\"iframe\");\n this.iframe.id = \"commercengine-checkout-iframe\";\n this.iframe.title = \"Shopping cart and checkout\";\n this.iframe.src = url;\n this.iframe.allow = \"payment\";\n this.iframe.style.cssText = `\n width: 100%;\n height: 100%;\n border: none;\n pointer-events: inherit;\n background-color: transparent;\n `;\n\n this.container.appendChild(this.iframe);\n document.body.appendChild(this.container);\n }\n\n /**\n * Show the checkout overlay (enable pointer events)\n */\n show(): void {\n if (this.container) {\n this.container.style.pointerEvents = \"auto\";\n document.body.style.overflow = \"hidden\";\n }\n }\n\n /**\n * Hide the checkout overlay (disable pointer events)\n */\n hide(): void {\n if (this.container) {\n this.container.style.pointerEvents = \"none\";\n document.body.style.overflow = \"\";\n }\n }\n\n /**\n * Check if iframe is visible\n */\n isVisible(): boolean {\n return this.container?.style.pointerEvents === \"auto\";\n }\n\n /**\n * Send a message to the checkout iframe\n */\n postMessage(type: OutgoingEventType, data?: unknown): void {\n if (!this.iframe?.contentWindow) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Cannot send message - iframe not ready\");\n return;\n }\n\n // Use specific origin for security - never use \"*\"\n if (!this.checkoutOrigin) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning for security\n console.warn(\"[Commercengine] Cannot send message - checkout origin not established\");\n return;\n }\n this.iframe.contentWindow.postMessage({ type, data }, this.checkoutOrigin);\n }\n\n /**\n * Get the checkout origin for message validation\n */\n getCheckoutOrigin(): string | null {\n return this.checkoutOrigin;\n }\n\n /**\n * Destroy the iframe and clean up\n */\n destroy(): void {\n if (this.container) {\n this.container.remove();\n this.container = null;\n }\n this.iframe = null;\n this.checkoutOrigin = null;\n\n // Restore body scroll\n document.body.style.overflow = \"\";\n }\n}\n","/**\n * Checkout Class\n *\n * Main entry point for Commerce Engine Checkout integration.\n * Manages iframe lifecycle, event handling, and provides public API.\n */\n\nimport { EventEmitter } from \"./events\";\nimport { IframeManager } from \"./iframe-manager\";\nimport type {\n AuthChangeData,\n CartData,\n CheckoutConfig,\n CheckoutEventType,\n Environment,\n EventListener,\n IncomingEventType,\n OrderData,\n} from \"./types\";\n\n// =============================================================================\n// URL RESOLUTION\n// =============================================================================\n\n/** Checkout URLs by environment */\nconst CHECKOUT_URLS: Record<Environment, string> = {\n production: \"https://checkout.commercengine.com\",\n staging: \"https://staging.checkout.commercengine.com\",\n};\n\n/**\n * Build checkout iframe URL\n */\nfunction buildCheckoutUrl(storeId: string, apiKey: string, environment: Environment): string {\n const baseUrl = CHECKOUT_URLS[environment];\n return `${baseUrl}?store_id=${storeId}&api_key=${apiKey}&environment=${environment}&mode=iframe`;\n}\n\n// =============================================================================\n// CHECKOUT CLASS\n// =============================================================================\n\nexport class Checkout extends EventEmitter {\n private config: CheckoutConfig;\n private iframe: IframeManager;\n private isReady = false;\n private isOpen = false;\n private cartState: CartData = { count: 0, total: 0, currency: \"INR\" };\n private boundMessageHandler: (event: MessageEvent) => void;\n private hasQuickBuyParams = false; // Track if we detected quick buy params\n\n constructor(config: CheckoutConfig) {\n super();\n this.config = config;\n this.iframe = new IframeManager();\n this.boundMessageHandler = this.handleMessage.bind(this);\n this.initialize();\n }\n\n // ===========================================================================\n // INITIALIZATION\n // ===========================================================================\n\n private initialize(): void {\n // Build checkout URL - use direct URL if provided, otherwise resolve from credentials\n let baseUrl: string;\n if (this.config.url) {\n // Direct URL provided (local development)\n baseUrl = this.config.url;\n } else if (this.config.storeId && this.config.apiKey) {\n // Build URL from credentials + environment\n const env = this.config.environment || \"production\";\n baseUrl = buildCheckoutUrl(this.config.storeId, this.config.apiKey, env);\n } else {\n throw new Error(\n \"[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided.\"\n );\n }\n\n const url = new URL(baseUrl);\n\n // Always set iframe mode\n url.searchParams.set(\"mode\", \"iframe\");\n\n // Add store credentials if provided and not already in URL\n if (this.config.storeId && !url.searchParams.has(\"store_id\")) {\n url.searchParams.set(\"store_id\", this.config.storeId);\n }\n if (this.config.apiKey && !url.searchParams.has(\"api_key\")) {\n url.searchParams.set(\"api_key\", this.config.apiKey);\n }\n if (this.config.environment && !url.searchParams.has(\"environment\")) {\n url.searchParams.set(\"environment\", this.config.environment);\n }\n\n // Add theme preference\n if (this.config.theme) {\n url.searchParams.set(\"theme\", this.config.theme);\n }\n\n // Add auth config\n if (this.config.authMode) {\n url.searchParams.set(\"auth_mode\", this.config.authMode);\n }\n if (this.config.accessToken) {\n url.searchParams.set(\"token\", this.config.accessToken);\n }\n if (this.config.refreshToken) {\n url.searchParams.set(\"refresh_token\", this.config.refreshToken);\n }\n\n // Auto-detect quick buy params from parent URL (if enabled and no explicit quickBuy)\n let quickBuy = this.config.quickBuy;\n let sessionMode = this.config.sessionMode;\n\n if (this.config.autoDetectQuickBuy && !quickBuy) {\n const parentParams = new URLSearchParams(window.location.search);\n const productId = parentParams.get(\"product_id\");\n const variantId = parentParams.get(\"variant_id\");\n\n if (productId) {\n quickBuy = {\n productId,\n variantId: variantId ?? null,\n quantity: parseInt(parentParams.get(\"qty\") || parentParams.get(\"quantity\") || \"1\", 10),\n };\n\n // Also check for session_mode in parent URL\n const parentSessionMode = parentParams.get(\"session_mode\");\n if (parentSessionMode === \"force-new\" || parentSessionMode === \"continue-existing\") {\n sessionMode = parentSessionMode;\n }\n\n // Clean quick buy params from URL to prevent duplicate adds on refresh\n // Deferred to next tick to handle React StrictMode double-mounting\n setTimeout(() => this.cleanQuickBuyParamsFromUrl(), 0);\n }\n }\n\n // Add quick buy params\n if (quickBuy) {\n this.hasQuickBuyParams = true;\n url.searchParams.set(\"product_id\", quickBuy.productId);\n if (quickBuy.variantId) {\n url.searchParams.set(\"variant_id\", quickBuy.variantId);\n }\n if (quickBuy.quantity && quickBuy.quantity !== 1) {\n url.searchParams.set(\"qty\", String(quickBuy.quantity));\n }\n }\n\n // Add session mode\n if (sessionMode) {\n url.searchParams.set(\"session_mode\", sessionMode);\n }\n\n // Pass parent origin for postMessage security\n // This allows the iframe to validate incoming messages and send responses to the correct origin\n if (typeof window !== \"undefined\") {\n url.searchParams.set(\"parent_origin\", window.location.origin);\n }\n\n // Create iframe (hidden initially, will show when ready + opened)\n const zIndex = this.config.appearance?.zIndex ?? 99999;\n this.iframe.create(url.toString(), zIndex);\n\n // Listen for messages from checkout iframe\n window.addEventListener(\"message\", this.boundMessageHandler);\n }\n\n // ===========================================================================\n // URL CLEANUP\n // ===========================================================================\n\n /**\n * Remove quick buy params from the parent URL to prevent duplicate adds on refresh\n * Uses replaceState to avoid adding to browser history\n */\n private cleanQuickBuyParamsFromUrl(): void {\n if (typeof window === \"undefined\") return;\n\n const url = new URL(window.location.href);\n const paramsToRemove = [\"product_id\", \"variant_id\", \"qty\", \"quantity\", \"session_mode\"];\n\n let hasChanges = false;\n for (const param of paramsToRemove) {\n if (url.searchParams.has(param)) {\n url.searchParams.delete(param);\n hasChanges = true;\n }\n }\n\n if (hasChanges) {\n window.history.replaceState({}, \"\", url.toString());\n }\n }\n\n // ===========================================================================\n // MESSAGE HANDLING\n // ===========================================================================\n\n private handleMessage(event: MessageEvent): void {\n // Validate origin for security - reject if no expected origin or mismatch\n const expectedOrigin = this.iframe.getCheckoutOrigin();\n if (!expectedOrigin || event.origin !== expectedOrigin) {\n return;\n }\n\n const { type, data } = event.data || {};\n if (!type) return;\n\n switch (type as IncomingEventType) {\n case \"checkout:ready\":\n this.handleReady();\n break;\n\n case \"checkout:open\":\n this.handleOpen();\n break;\n\n case \"checkout:close\":\n this.handleClose();\n break;\n\n case \"checkout:complete\":\n this.handleComplete(data as OrderData);\n break;\n\n case \"checkout:error\":\n this.handleError(data as { message: string });\n break;\n\n case \"cart:updated\":\n this.handleCartUpdate(data as CartData);\n break;\n\n case \"auth:login\":\n this.handleAuthChange({ type: \"login\", ...data });\n break;\n\n case \"auth:logout\":\n this.handleAuthChange({ type: \"logout\" });\n break;\n\n case \"auth:refresh\":\n this.handleAuthChange({ type: \"refresh\", ...data });\n break;\n }\n }\n\n private handleReady(): void {\n this.isReady = true;\n this.config.onReady?.();\n this.emit(\"ready\");\n\n // Auto-open cart if we have quick buy params (from config or auto-detected URL)\n if (this.hasQuickBuyParams) {\n this.openCart();\n }\n }\n\n private handleError(data: { message: string }): void {\n // Set ready so user can still interact (they'll see the error drawer)\n this.isReady = true;\n this.config.onError?.(data);\n this.emit(\"error\", data);\n }\n\n private handleOpen(): void {\n this.iframe.show();\n this.isOpen = true;\n this.config.onOpen?.();\n this.emit(\"open\");\n }\n\n private handleClose(): void {\n this.iframe.hide();\n this.isOpen = false;\n this.config.onClose?.();\n this.emit(\"close\");\n }\n\n private handleComplete(data: OrderData): void {\n this.config.onComplete?.(data);\n this.emit(\"complete\", data);\n }\n\n private handleCartUpdate(data: CartData): void {\n this.cartState = data;\n this.config.onCartUpdate?.(data);\n this.emit(\"cart:updated\", data);\n }\n\n private handleAuthChange(data: AuthChangeData): void {\n this.config.onAuthChange?.(data);\n this.emit(\"auth:change\", data);\n }\n\n // ===========================================================================\n // PUBLIC API\n // ===========================================================================\n\n /**\n * Open the cart drawer\n */\n openCart(): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.show();\n this.isOpen = true;\n this.iframe.postMessage(\"checkout:open-cart\");\n }\n\n /**\n * Open the checkout drawer directly (e.g., for Buy Now flow)\n */\n openCheckout(): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.show();\n this.isOpen = true;\n this.iframe.postMessage(\"checkout:open-checkout\");\n }\n\n /**\n * Close the checkout overlay\n */\n close(): void {\n this.iframe.hide();\n this.isOpen = false;\n this.iframe.postMessage(\"checkout:close\");\n }\n\n /**\n * Update authentication tokens\n * Use this when user logs in/out on the parent site\n */\n updateTokens(accessToken: string, refreshToken?: string): void {\n if (!this.isReady) {\n // Store for when ready\n this.config.accessToken = accessToken;\n this.config.refreshToken = refreshToken;\n return;\n }\n\n this.iframe.postMessage(\"checkout:update-tokens\", { accessToken, refreshToken });\n }\n\n /**\n * Add an item to cart and open the cart drawer\n *\n * @param productId - Product ID (required)\n * @param variantId - Variant ID (required, null for non-variant products)\n * @param quantity - Quantity to add (default: 1)\n */\n addToCart(productId: string, variantId: string | null, quantity = 1): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.postMessage(\"checkout:add-item\", {\n productId,\n variantId,\n quantity,\n });\n\n // Auto-open cart to show the added item\n this.openCart();\n }\n\n /**\n * Get current cart state\n */\n getCart(): CartData {\n return { ...this.cartState };\n }\n\n /**\n * Check if checkout is ready\n */\n get ready(): boolean {\n return this.isReady;\n }\n\n /**\n * Check if checkout overlay is currently open\n */\n get open(): boolean {\n return this.isOpen;\n }\n\n /**\n * Subscribe to an event\n * @override EventEmitter.on with typed overloads\n */\n on(event: \"ready\", listener: EventListener<void>): void;\n on(event: \"open\", listener: EventListener<void>): void;\n on(event: \"close\", listener: EventListener<void>): void;\n on(event: \"complete\", listener: EventListener<OrderData>): void;\n on(event: \"cart:updated\", listener: EventListener<CartData>): void;\n on(event: \"auth:change\", listener: EventListener<AuthChangeData>): void;\n on<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n super.on(event, listener);\n }\n\n /**\n * Destroy the checkout instance\n * Removes iframe and cleans up event listeners\n */\n destroy(): void {\n window.removeEventListener(\"message\", this.boundMessageHandler);\n this.iframe.destroy();\n this.removeAllListeners();\n this.isReady = false;\n this.isOpen = false;\n }\n}\n","/**\n * Commerce Engine Checkout\n *\n * @commercengine/js - Embeddable checkout SDK\n *\n * @example CDN Usage (Vanilla JS):\n * ```html\n * <script src=\"https://cdn.commercengine.com/v1.js\" async></script>\n * <script>\n * window.Commercengine.onLoad = async () => {\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * theme: \"dark\",\n * onComplete: (order) => console.log(\"Order:\", order.orderNumber),\n * });\n *\n * document.getElementById(\"cart-btn\").onclick = () => checkout.openCart();\n * };\n * </script>\n * ```\n *\n * @example ESM Import:\n * ```typescript\n * import { Commercengine } from \"@commercengine/js\";\n *\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * });\n *\n * checkout.on(\"cart:updated\", (cart) => updateBadge(cart.count));\n * checkout.openCart();\n * ```\n */\n\nimport { Checkout } from \"./checkout\";\nimport type { CheckoutConfig } from \"./types\";\n\n// Re-export types for consumers\nexport type {\n AddToCartItem,\n AuthChangeData,\n AuthMode,\n CartData,\n CheckoutConfig,\n CheckoutEventType,\n Environment,\n ErrorData,\n OrderData,\n QuickBuyConfig,\n SessionMode,\n} from \"./types\";\n\nexport { Checkout };\n\n// =============================================================================\n// GLOBAL API\n// =============================================================================\n\n/**\n * Global Commercengine object interface\n */\ninterface CommercengineGlobal {\n /**\n * Initialize checkout with configuration\n * @returns Promise that resolves to Checkout instance\n */\n init: (config: CheckoutConfig) => Promise<Checkout>;\n\n /**\n * Callback invoked when script has loaded\n * Set this BEFORE including the script to be notified when ready\n */\n onLoad?: () => void;\n\n /**\n * Current checkout instance (if initialized)\n */\n instance?: Checkout;\n}\n\n/**\n * Global Commercengine namespace\n */\nconst Commercengine: CommercengineGlobal = {\n /**\n * Initialize checkout\n *\n * @param config - Checkout configuration\n * @returns Checkout instance\n *\n * @example\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * theme: \"system\",\n * });\n */\n init: async (config: CheckoutConfig): Promise<Checkout> => {\n // Validate required fields - either url (for dev) or storeId+apiKey (for prod)\n if (!config.url && (!config.storeId || !config.apiKey)) {\n throw new Error(\n \"[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided\"\n );\n }\n\n // Destroy existing instance if any\n if (Commercengine.instance) {\n Commercengine.instance.destroy();\n }\n\n // Create new instance\n const checkout = new Checkout(config);\n Commercengine.instance = checkout;\n\n // Wait for ready or error with timeout\n return new Promise((resolve, reject) => {\n if (checkout.ready) {\n resolve(checkout);\n return;\n }\n\n const timeoutMs = 30000; // 30 second timeout\n const timeout = setTimeout(() => {\n reject(new Error(\"Checkout initialization timed out\"));\n }, timeoutMs);\n\n // Resolve on ready\n checkout.once(\"ready\", () => {\n clearTimeout(timeout);\n resolve(checkout);\n });\n\n // Also resolve on error (checkout is still usable, will show error drawer)\n checkout.once(\"error\", () => {\n clearTimeout(timeout);\n resolve(checkout);\n });\n });\n },\n};\n\n// =============================================================================\n// GLOBAL SETUP\n// =============================================================================\n\n// Expose on window for CDN usage\nif (typeof window !== \"undefined\") {\n // Preserve any existing onLoad callback set before script loaded\n const existingOnLoad = (window as unknown as { Commercengine?: CommercengineGlobal })\n .Commercengine?.onLoad;\n\n // Set global\n (window as unknown as { Commercengine: CommercengineGlobal }).Commercengine = Commercengine;\n\n // Restore and call onLoad if it was set\n if (existingOnLoad) {\n Commercengine.onLoad = existingOnLoad;\n try {\n existingOnLoad();\n } catch (error) {\n // biome-ignore lint/suspicious/noConsole: Intentional error logging\n console.error(\"[Commercengine] Error in onLoad callback:\", error);\n }\n }\n}\n\nexport { Commercengine };\nexport default Commercengine;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.iife.js","names":["onceListener: EventListener<T>","CHECKOUT_URLS: Record<Environment, string>","baseUrl: string","Commercengine: CommercengineGlobal"],"sources":["../src/events.ts","../src/iframe-manager.ts","../src/checkout.ts","../src/index.ts"],"sourcesContent":["/**\n * Simple Event Emitter\n *\n * Provides pub/sub functionality for checkout events.\n */\n\nimport type { CheckoutEventType, EventListener } from \"./types\";\n\nexport class EventEmitter {\n private listeners: Map<CheckoutEventType, Set<EventListener>> = new Map();\n\n /**\n * Subscribe to an event\n */\n on<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)?.add(listener as EventListener);\n }\n\n /**\n * Unsubscribe from an event\n */\n off<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n const eventListeners = this.listeners.get(event);\n if (eventListeners) {\n eventListeners.delete(listener as EventListener);\n }\n }\n\n /**\n * Subscribe to an event (once)\n */\n once<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n const onceListener: EventListener<T> = (data) => {\n this.off(event, onceListener);\n listener(data);\n };\n this.on(event, onceListener);\n }\n\n /**\n * Emit an event to all subscribers\n */\n protected emit<T = unknown>(event: CheckoutEventType, data?: T): void {\n const eventListeners = this.listeners.get(event);\n if (eventListeners) {\n for (const listener of eventListeners) {\n try {\n listener(data);\n } catch (error) {\n // biome-ignore lint/suspicious/noConsole: Intentional error logging\n console.error(`[Commercengine] Error in ${event} listener:`, error);\n }\n }\n }\n }\n\n /**\n * Remove all listeners\n */\n protected removeAllListeners(): void {\n this.listeners.clear();\n }\n}\n","/**\n * Iframe Manager\n *\n * Handles iframe creation, lifecycle, and visibility management.\n * The iframe is pre-mounted (hidden) on init and shown on demand.\n */\n\nimport type { OutgoingEventType } from \"./types\";\n\nexport class IframeManager {\n private iframe: HTMLIFrameElement | null = null;\n private container: HTMLDivElement | null = null;\n private checkoutOrigin: string | null = null;\n\n /**\n * Create and mount the iframe (hidden initially)\n */\n create(url: string, zIndex: number): void {\n if (this.container) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Iframe already created\");\n return;\n }\n\n // Store origin for postMessage security\n this.checkoutOrigin = new URL(url).origin;\n\n // Create container (covers viewport, initially non-interactive)\n this.container = document.createElement(\"div\");\n this.container.id = \"commercengine-checkout\";\n this.container.style.cssText = `\n position: fixed;\n inset: 0;\n z-index: ${zIndex};\n pointer-events: none;\n `;\n\n // Create iframe\n this.iframe = document.createElement(\"iframe\");\n this.iframe.id = \"commercengine-checkout-iframe\";\n this.iframe.title = \"Shopping cart and checkout\";\n this.iframe.src = url;\n this.iframe.allow = \"payment\";\n this.iframe.style.cssText = `\n width: 100%;\n height: 100%;\n border: none;\n pointer-events: inherit;\n background-color: transparent;\n `;\n\n this.container.appendChild(this.iframe);\n document.body.appendChild(this.container);\n }\n\n /**\n * Show the checkout overlay (enable pointer events)\n */\n show(): void {\n if (this.container) {\n this.container.style.pointerEvents = \"auto\";\n document.body.style.overflow = \"hidden\";\n }\n }\n\n /**\n * Hide the checkout overlay (disable pointer events)\n */\n hide(): void {\n if (this.container) {\n this.container.style.pointerEvents = \"none\";\n document.body.style.overflow = \"\";\n }\n }\n\n /**\n * Check if iframe is visible\n */\n isVisible(): boolean {\n return this.container?.style.pointerEvents === \"auto\";\n }\n\n /**\n * Send a message to the checkout iframe\n */\n postMessage(type: OutgoingEventType, data?: unknown): void {\n if (!this.iframe?.contentWindow) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Cannot send message - iframe not ready\");\n return;\n }\n\n // Use specific origin for security - never use \"*\"\n if (!this.checkoutOrigin) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning for security\n console.warn(\"[Commercengine] Cannot send message - checkout origin not established\");\n return;\n }\n this.iframe.contentWindow.postMessage({ type, data }, this.checkoutOrigin);\n }\n\n /**\n * Get the checkout origin for message validation\n */\n getCheckoutOrigin(): string | null {\n return this.checkoutOrigin;\n }\n\n /**\n * Destroy the iframe and clean up\n */\n destroy(): void {\n if (this.container) {\n this.container.remove();\n this.container = null;\n }\n this.iframe = null;\n this.checkoutOrigin = null;\n\n // Restore body scroll\n document.body.style.overflow = \"\";\n }\n}\n","/**\n * Checkout Class\n *\n * Main entry point for Commerce Engine Checkout integration.\n * Manages iframe lifecycle, event handling, and provides public API.\n */\n\nimport { EventEmitter } from \"./events\";\nimport { IframeManager } from \"./iframe-manager\";\nimport type {\n AuthLoginData,\n AuthLogoutData,\n AuthRefreshData,\n CartData,\n CheckoutConfig,\n CheckoutEventType,\n Environment,\n EventListener,\n IncomingEventType,\n OrderData,\n} from \"./types\";\n\n// =============================================================================\n// URL RESOLUTION\n// =============================================================================\n\n/** Checkout URLs by environment */\nconst CHECKOUT_URLS: Record<Environment, string> = {\n production: \"https://checkout.commercengine.com\",\n staging: \"https://staging.checkout.commercengine.com\",\n};\n\n/**\n * Build checkout iframe URL\n */\nfunction buildCheckoutUrl(storeId: string, apiKey: string, environment: Environment): string {\n const baseUrl = CHECKOUT_URLS[environment];\n return `${baseUrl}?store_id=${storeId}&api_key=${apiKey}&environment=${environment}&mode=iframe`;\n}\n\n// =============================================================================\n// CHECKOUT CLASS\n// =============================================================================\n\nexport class Checkout extends EventEmitter {\n private config: CheckoutConfig;\n private iframe: IframeManager;\n private isReady = false;\n private isOpen = false;\n private cartState: CartData = { count: 0, total: 0, currency: \"INR\" };\n private boundMessageHandler: (event: MessageEvent) => void;\n private hasQuickBuyParams = false; // Track if we detected quick buy params\n\n constructor(config: CheckoutConfig) {\n super();\n this.config = config;\n this.iframe = new IframeManager();\n this.boundMessageHandler = this.handleMessage.bind(this);\n this.initialize();\n }\n\n // ===========================================================================\n // INITIALIZATION\n // ===========================================================================\n\n private initialize(): void {\n // Build checkout URL - use direct URL if provided, otherwise resolve from credentials\n let baseUrl: string;\n if (this.config.url) {\n // Direct URL provided (local development)\n baseUrl = this.config.url;\n } else if (this.config.storeId && this.config.apiKey) {\n // Build URL from credentials + environment\n const env = this.config.environment || \"production\";\n baseUrl = buildCheckoutUrl(this.config.storeId, this.config.apiKey, env);\n } else {\n throw new Error(\n \"[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided.\"\n );\n }\n\n const url = new URL(baseUrl);\n\n // Always set iframe mode\n url.searchParams.set(\"mode\", \"iframe\");\n\n // Add store credentials if provided and not already in URL\n if (this.config.storeId && !url.searchParams.has(\"store_id\")) {\n url.searchParams.set(\"store_id\", this.config.storeId);\n }\n if (this.config.apiKey && !url.searchParams.has(\"api_key\")) {\n url.searchParams.set(\"api_key\", this.config.apiKey);\n }\n if (this.config.environment && !url.searchParams.has(\"environment\")) {\n url.searchParams.set(\"environment\", this.config.environment);\n }\n\n // Add theme preference\n if (this.config.theme) {\n url.searchParams.set(\"theme\", this.config.theme);\n }\n\n // Add auth config\n if (this.config.authMode) {\n url.searchParams.set(\"auth_mode\", this.config.authMode);\n }\n if (this.config.accessToken) {\n url.searchParams.set(\"token\", this.config.accessToken);\n }\n if (this.config.refreshToken) {\n url.searchParams.set(\"refresh_token\", this.config.refreshToken);\n }\n\n // Auto-detect quick buy params from parent URL (if enabled and no explicit quickBuy)\n let quickBuy = this.config.quickBuy;\n let sessionMode = this.config.sessionMode;\n\n if (this.config.autoDetectQuickBuy && !quickBuy) {\n const parentParams = new URLSearchParams(window.location.search);\n const productId = parentParams.get(\"product_id\");\n const variantId = parentParams.get(\"variant_id\");\n\n if (productId) {\n quickBuy = {\n productId,\n variantId: variantId ?? null,\n quantity: parseInt(parentParams.get(\"qty\") || parentParams.get(\"quantity\") || \"1\", 10),\n };\n\n // Also check for session_mode in parent URL\n const parentSessionMode = parentParams.get(\"session_mode\");\n if (parentSessionMode === \"force-new\" || parentSessionMode === \"continue-existing\") {\n sessionMode = parentSessionMode;\n }\n\n // Clean quick buy params from URL to prevent duplicate adds on refresh\n // Deferred to next tick to handle React StrictMode double-mounting\n setTimeout(() => this.cleanQuickBuyParamsFromUrl(), 0);\n }\n }\n\n // Add quick buy params\n if (quickBuy) {\n this.hasQuickBuyParams = true;\n url.searchParams.set(\"product_id\", quickBuy.productId);\n if (quickBuy.variantId) {\n url.searchParams.set(\"variant_id\", quickBuy.variantId);\n }\n if (quickBuy.quantity && quickBuy.quantity !== 1) {\n url.searchParams.set(\"qty\", String(quickBuy.quantity));\n }\n }\n\n // Add session mode\n if (sessionMode) {\n url.searchParams.set(\"session_mode\", sessionMode);\n }\n\n // Pass parent origin for postMessage security\n // This allows the iframe to validate incoming messages and send responses to the correct origin\n if (typeof window !== \"undefined\") {\n url.searchParams.set(\"parent_origin\", window.location.origin);\n }\n\n // Create iframe (hidden initially, will show when ready + opened)\n const zIndex = this.config.appearance?.zIndex ?? 99999;\n this.iframe.create(url.toString(), zIndex);\n\n // Listen for messages from checkout iframe\n window.addEventListener(\"message\", this.boundMessageHandler);\n }\n\n // ===========================================================================\n // URL CLEANUP\n // ===========================================================================\n\n /**\n * Remove quick buy params from the parent URL to prevent duplicate adds on refresh\n * Uses replaceState to avoid adding to browser history\n */\n private cleanQuickBuyParamsFromUrl(): void {\n if (typeof window === \"undefined\") return;\n\n const url = new URL(window.location.href);\n const paramsToRemove = [\"product_id\", \"variant_id\", \"qty\", \"quantity\", \"session_mode\"];\n\n let hasChanges = false;\n for (const param of paramsToRemove) {\n if (url.searchParams.has(param)) {\n url.searchParams.delete(param);\n hasChanges = true;\n }\n }\n\n if (hasChanges) {\n window.history.replaceState({}, \"\", url.toString());\n }\n }\n\n // ===========================================================================\n // MESSAGE HANDLING\n // ===========================================================================\n\n private handleMessage(event: MessageEvent): void {\n // Validate origin for security - reject if no expected origin or mismatch\n const expectedOrigin = this.iframe.getCheckoutOrigin();\n if (!expectedOrigin || event.origin !== expectedOrigin) {\n return;\n }\n\n const { type, data } = event.data || {};\n if (!type) return;\n\n switch (type as IncomingEventType) {\n case \"checkout:ready\":\n this.handleReady();\n break;\n\n case \"checkout:open\":\n this.handleOpen();\n break;\n\n case \"checkout:close\":\n this.handleClose();\n break;\n\n case \"checkout:complete\":\n this.handleComplete(data as OrderData);\n break;\n\n case \"checkout:error\":\n this.handleError(data as { message: string });\n break;\n\n case \"cart:updated\":\n this.handleCartUpdate(data as CartData);\n break;\n\n case \"auth:login\":\n this.handleLogin(data as AuthLoginData);\n break;\n\n case \"auth:logout\":\n this.handleLogout(data as AuthLogoutData);\n break;\n\n case \"auth:refresh\":\n this.handleTokenRefresh(data as AuthRefreshData);\n break;\n\n case \"auth:session-error\":\n this.handleSessionError();\n break;\n }\n }\n\n private handleReady(): void {\n this.isReady = true;\n this.config.onReady?.();\n this.emit(\"ready\");\n\n // Auto-open cart if we have quick buy params (from config or auto-detected URL)\n if (this.hasQuickBuyParams) {\n this.openCart();\n }\n }\n\n private handleError(data: { message: string }): void {\n // Set ready so user can still interact (they'll see the error drawer)\n this.isReady = true;\n this.config.onError?.(data);\n this.emit(\"error\", data);\n }\n\n private handleOpen(): void {\n this.iframe.show();\n this.isOpen = true;\n this.config.onOpen?.();\n this.emit(\"open\");\n }\n\n private handleClose(): void {\n this.iframe.hide();\n this.isOpen = false;\n this.config.onClose?.();\n this.emit(\"close\");\n }\n\n private handleComplete(data: OrderData): void {\n this.config.onComplete?.(data);\n this.emit(\"complete\", data);\n }\n\n private handleCartUpdate(data: CartData): void {\n this.cartState = data;\n this.config.onCartUpdate?.(data);\n this.emit(\"cart:updated\", data);\n }\n\n private handleLogin(data: AuthLoginData): void {\n this.config.onLogin?.(data);\n this.emit(\"auth:login\", data);\n }\n\n private handleLogout(data: AuthLogoutData): void {\n this.config.onLogout?.(data);\n this.emit(\"auth:logout\", data);\n }\n\n private handleTokenRefresh(data: AuthRefreshData): void {\n this.config.onTokenRefresh?.(data);\n this.emit(\"auth:refresh\", data);\n }\n\n private handleSessionError(): void {\n this.config.onSessionError?.();\n this.emit(\"auth:session-error\");\n }\n\n // ===========================================================================\n // PUBLIC API\n // ===========================================================================\n\n /**\n * Open the cart drawer\n */\n openCart(): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.show();\n this.isOpen = true;\n this.iframe.postMessage(\"checkout:open-cart\");\n }\n\n /**\n * Open the checkout drawer directly (e.g., for Buy Now flow)\n */\n openCheckout(): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.show();\n this.isOpen = true;\n this.iframe.postMessage(\"checkout:open-checkout\");\n }\n\n /**\n * Close the checkout overlay\n */\n close(): void {\n this.iframe.hide();\n this.isOpen = false;\n this.iframe.postMessage(\"checkout:close\");\n }\n\n /**\n * Update authentication tokens\n * Use this when user logs in/out on the parent site\n */\n updateTokens(accessToken: string, refreshToken?: string): void {\n if (!this.isReady) {\n // Store for when ready\n this.config.accessToken = accessToken;\n this.config.refreshToken = refreshToken;\n return;\n }\n\n this.iframe.postMessage(\"checkout:update-tokens\", { accessToken, refreshToken });\n }\n\n /**\n * Add an item to cart and open the cart drawer\n *\n * @param productId - Product ID (required)\n * @param variantId - Variant ID (required, null for non-variant products)\n * @param quantity - Quantity to add (default: 1)\n */\n addToCart(productId: string, variantId: string | null, quantity = 1): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.postMessage(\"checkout:add-item\", {\n productId,\n variantId,\n quantity,\n });\n\n // Auto-open cart to show the added item\n this.openCart();\n }\n\n /**\n * Get current cart state\n */\n getCart(): CartData {\n return { ...this.cartState };\n }\n\n /**\n * Check if checkout is ready\n */\n get ready(): boolean {\n return this.isReady;\n }\n\n /**\n * Check if checkout overlay is currently open\n */\n get open(): boolean {\n return this.isOpen;\n }\n\n /**\n * Subscribe to an event\n * @override EventEmitter.on with typed overloads\n */\n on(event: \"ready\", listener: EventListener<void>): void;\n on(event: \"open\", listener: EventListener<void>): void;\n on(event: \"close\", listener: EventListener<void>): void;\n on(event: \"complete\", listener: EventListener<OrderData>): void;\n on(event: \"cart:updated\", listener: EventListener<CartData>): void;\n on(event: \"auth:login\", listener: EventListener<AuthLoginData>): void;\n on(event: \"auth:logout\", listener: EventListener<AuthLogoutData>): void;\n on(event: \"auth:refresh\", listener: EventListener<AuthRefreshData>): void;\n on(event: \"auth:session-error\", listener: EventListener<void>): void;\n on<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n super.on(event, listener);\n }\n\n /**\n * Destroy the checkout instance\n * Removes iframe and cleans up event listeners\n */\n destroy(): void {\n window.removeEventListener(\"message\", this.boundMessageHandler);\n this.iframe.destroy();\n this.removeAllListeners();\n this.isReady = false;\n this.isOpen = false;\n }\n}\n","/**\n * Commerce Engine Checkout\n *\n * @commercengine/js - Embeddable checkout SDK\n *\n * @example CDN Usage (Vanilla JS):\n * ```html\n * <script src=\"https://cdn.commercengine.com/v1.js\" async></script>\n * <script>\n * window.Commercengine.onLoad = async () => {\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * theme: \"dark\",\n * onComplete: (order) => console.log(\"Order:\", order.orderNumber),\n * });\n *\n * document.getElementById(\"cart-btn\").onclick = () => checkout.openCart();\n * };\n * </script>\n * ```\n *\n * @example ESM Import:\n * ```typescript\n * import { Commercengine } from \"@commercengine/js\";\n *\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * });\n *\n * checkout.on(\"cart:updated\", (cart) => updateBadge(cart.count));\n * checkout.openCart();\n * ```\n */\n\nimport { Checkout } from \"./checkout\";\nimport type { CheckoutConfig } from \"./types\";\n\n// Re-export types for consumers\nexport type {\n AddToCartItem,\n AuthLoginData,\n AuthLogoutData,\n AuthMode,\n AuthRefreshData,\n CartData,\n Channel,\n CheckoutConfig,\n CheckoutEventType,\n Environment,\n ErrorData,\n LoginMethod,\n OrderData,\n QuickBuyConfig,\n SessionMode,\n UserInfo,\n} from \"./types\";\n\nexport { Checkout };\n\n// =============================================================================\n// GLOBAL API\n// =============================================================================\n\n/**\n * Global Commercengine object interface\n */\ninterface CommercengineGlobal {\n /**\n * Initialize checkout with configuration\n * @returns Promise that resolves to Checkout instance\n */\n init: (config: CheckoutConfig) => Promise<Checkout>;\n\n /**\n * Callback invoked when script has loaded\n * Set this BEFORE including the script to be notified when ready\n */\n onLoad?: () => void;\n\n /**\n * Current checkout instance (if initialized)\n */\n instance?: Checkout;\n}\n\n/**\n * Global Commercengine namespace\n */\nconst Commercengine: CommercengineGlobal = {\n /**\n * Initialize checkout\n *\n * @param config - Checkout configuration\n * @returns Checkout instance\n *\n * @example\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * theme: \"system\",\n * });\n */\n init: async (config: CheckoutConfig): Promise<Checkout> => {\n // Validate required fields - either url (for dev) or storeId+apiKey (for prod)\n if (!config.url && (!config.storeId || !config.apiKey)) {\n throw new Error(\n \"[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided\"\n );\n }\n\n // Destroy existing instance if any\n if (Commercengine.instance) {\n Commercengine.instance.destroy();\n }\n\n // Create new instance\n const checkout = new Checkout(config);\n Commercengine.instance = checkout;\n\n // Wait for ready or error with timeout\n return new Promise((resolve, reject) => {\n if (checkout.ready) {\n resolve(checkout);\n return;\n }\n\n const timeoutMs = 30000; // 30 second timeout\n const timeout = setTimeout(() => {\n reject(new Error(\"Checkout initialization timed out\"));\n }, timeoutMs);\n\n // Resolve on ready\n checkout.once(\"ready\", () => {\n clearTimeout(timeout);\n resolve(checkout);\n });\n\n // Also resolve on error (checkout is still usable, will show error drawer)\n checkout.once(\"error\", () => {\n clearTimeout(timeout);\n resolve(checkout);\n });\n });\n },\n};\n\n// =============================================================================\n// GLOBAL SETUP\n// =============================================================================\n\n// Expose on window for CDN usage\nif (typeof window !== \"undefined\") {\n // Preserve any existing onLoad callback set before script loaded\n const existingOnLoad = (window as unknown as { Commercengine?: CommercengineGlobal })\n .Commercengine?.onLoad;\n\n // Set global\n (window as unknown as { Commercengine: CommercengineGlobal }).Commercengine = Commercengine;\n\n // Restore and call onLoad if it was set\n if (existingOnLoad) {\n Commercengine.onLoad = existingOnLoad;\n try {\n existingOnLoad();\n } catch (error) {\n // biome-ignore lint/suspicious/noConsole: Intentional error logging\n console.error(\"[Commercengine] Error in onLoad callback:\", error);\n }\n }\n}\n\nexport { Commercengine };\nexport default Commercengine;\n"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -273,19 +273,16 @@ var Checkout = class extends EventEmitter {
|
|
|
273
273
|
this.handleCartUpdate(data);
|
|
274
274
|
break;
|
|
275
275
|
case "auth:login":
|
|
276
|
-
this.
|
|
277
|
-
type: "login",
|
|
278
|
-
...data
|
|
279
|
-
});
|
|
276
|
+
this.handleLogin(data);
|
|
280
277
|
break;
|
|
281
278
|
case "auth:logout":
|
|
282
|
-
this.
|
|
279
|
+
this.handleLogout(data);
|
|
283
280
|
break;
|
|
284
281
|
case "auth:refresh":
|
|
285
|
-
this.
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
282
|
+
this.handleTokenRefresh(data);
|
|
283
|
+
break;
|
|
284
|
+
case "auth:session-error":
|
|
285
|
+
this.handleSessionError();
|
|
289
286
|
break;
|
|
290
287
|
}
|
|
291
288
|
}
|
|
@@ -321,9 +318,21 @@ var Checkout = class extends EventEmitter {
|
|
|
321
318
|
this.config.onCartUpdate?.(data);
|
|
322
319
|
this.emit("cart:updated", data);
|
|
323
320
|
}
|
|
324
|
-
|
|
325
|
-
this.config.
|
|
326
|
-
this.emit("auth:
|
|
321
|
+
handleLogin(data) {
|
|
322
|
+
this.config.onLogin?.(data);
|
|
323
|
+
this.emit("auth:login", data);
|
|
324
|
+
}
|
|
325
|
+
handleLogout(data) {
|
|
326
|
+
this.config.onLogout?.(data);
|
|
327
|
+
this.emit("auth:logout", data);
|
|
328
|
+
}
|
|
329
|
+
handleTokenRefresh(data) {
|
|
330
|
+
this.config.onTokenRefresh?.(data);
|
|
331
|
+
this.emit("auth:refresh", data);
|
|
332
|
+
}
|
|
333
|
+
handleSessionError() {
|
|
334
|
+
this.config.onSessionError?.();
|
|
335
|
+
this.emit("auth:session-error");
|
|
327
336
|
}
|
|
328
337
|
/**
|
|
329
338
|
* Open the cart drawer
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["onceListener: EventListener<T>","CHECKOUT_URLS: Record<Environment, string>","baseUrl: string","Commercengine: CommercengineGlobal"],"sources":["../src/events.ts","../src/iframe-manager.ts","../src/checkout.ts","../src/index.ts"],"sourcesContent":["/**\n * Simple Event Emitter\n *\n * Provides pub/sub functionality for checkout events.\n */\n\nimport type { CheckoutEventType, EventListener } from \"./types\";\n\nexport class EventEmitter {\n private listeners: Map<CheckoutEventType, Set<EventListener>> = new Map();\n\n /**\n * Subscribe to an event\n */\n on<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)?.add(listener as EventListener);\n }\n\n /**\n * Unsubscribe from an event\n */\n off<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n const eventListeners = this.listeners.get(event);\n if (eventListeners) {\n eventListeners.delete(listener as EventListener);\n }\n }\n\n /**\n * Subscribe to an event (once)\n */\n once<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n const onceListener: EventListener<T> = (data) => {\n this.off(event, onceListener);\n listener(data);\n };\n this.on(event, onceListener);\n }\n\n /**\n * Emit an event to all subscribers\n */\n protected emit<T = unknown>(event: CheckoutEventType, data?: T): void {\n const eventListeners = this.listeners.get(event);\n if (eventListeners) {\n for (const listener of eventListeners) {\n try {\n listener(data);\n } catch (error) {\n // biome-ignore lint/suspicious/noConsole: Intentional error logging\n console.error(`[Commercengine] Error in ${event} listener:`, error);\n }\n }\n }\n }\n\n /**\n * Remove all listeners\n */\n protected removeAllListeners(): void {\n this.listeners.clear();\n }\n}\n","/**\n * Iframe Manager\n *\n * Handles iframe creation, lifecycle, and visibility management.\n * The iframe is pre-mounted (hidden) on init and shown on demand.\n */\n\nimport type { OutgoingEventType } from \"./types\";\n\nexport class IframeManager {\n private iframe: HTMLIFrameElement | null = null;\n private container: HTMLDivElement | null = null;\n private checkoutOrigin: string | null = null;\n\n /**\n * Create and mount the iframe (hidden initially)\n */\n create(url: string, zIndex: number): void {\n if (this.container) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Iframe already created\");\n return;\n }\n\n // Store origin for postMessage security\n this.checkoutOrigin = new URL(url).origin;\n\n // Create container (covers viewport, initially non-interactive)\n this.container = document.createElement(\"div\");\n this.container.id = \"commercengine-checkout\";\n this.container.style.cssText = `\n position: fixed;\n inset: 0;\n z-index: ${zIndex};\n pointer-events: none;\n `;\n\n // Create iframe\n this.iframe = document.createElement(\"iframe\");\n this.iframe.id = \"commercengine-checkout-iframe\";\n this.iframe.title = \"Shopping cart and checkout\";\n this.iframe.src = url;\n this.iframe.allow = \"payment\";\n this.iframe.style.cssText = `\n width: 100%;\n height: 100%;\n border: none;\n pointer-events: inherit;\n background-color: transparent;\n `;\n\n this.container.appendChild(this.iframe);\n document.body.appendChild(this.container);\n }\n\n /**\n * Show the checkout overlay (enable pointer events)\n */\n show(): void {\n if (this.container) {\n this.container.style.pointerEvents = \"auto\";\n document.body.style.overflow = \"hidden\";\n }\n }\n\n /**\n * Hide the checkout overlay (disable pointer events)\n */\n hide(): void {\n if (this.container) {\n this.container.style.pointerEvents = \"none\";\n document.body.style.overflow = \"\";\n }\n }\n\n /**\n * Check if iframe is visible\n */\n isVisible(): boolean {\n return this.container?.style.pointerEvents === \"auto\";\n }\n\n /**\n * Send a message to the checkout iframe\n */\n postMessage(type: OutgoingEventType, data?: unknown): void {\n if (!this.iframe?.contentWindow) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Cannot send message - iframe not ready\");\n return;\n }\n\n // Use specific origin for security - never use \"*\"\n if (!this.checkoutOrigin) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning for security\n console.warn(\"[Commercengine] Cannot send message - checkout origin not established\");\n return;\n }\n this.iframe.contentWindow.postMessage({ type, data }, this.checkoutOrigin);\n }\n\n /**\n * Get the checkout origin for message validation\n */\n getCheckoutOrigin(): string | null {\n return this.checkoutOrigin;\n }\n\n /**\n * Destroy the iframe and clean up\n */\n destroy(): void {\n if (this.container) {\n this.container.remove();\n this.container = null;\n }\n this.iframe = null;\n this.checkoutOrigin = null;\n\n // Restore body scroll\n document.body.style.overflow = \"\";\n }\n}\n","/**\n * Checkout Class\n *\n * Main entry point for Commerce Engine Checkout integration.\n * Manages iframe lifecycle, event handling, and provides public API.\n */\n\nimport { EventEmitter } from \"./events\";\nimport { IframeManager } from \"./iframe-manager\";\nimport type {\n AuthChangeData,\n CartData,\n CheckoutConfig,\n CheckoutEventType,\n Environment,\n EventListener,\n IncomingEventType,\n OrderData,\n} from \"./types\";\n\n// =============================================================================\n// URL RESOLUTION\n// =============================================================================\n\n/** Checkout URLs by environment */\nconst CHECKOUT_URLS: Record<Environment, string> = {\n production: \"https://checkout.commercengine.com\",\n staging: \"https://staging.checkout.commercengine.com\",\n};\n\n/**\n * Build checkout iframe URL\n */\nfunction buildCheckoutUrl(storeId: string, apiKey: string, environment: Environment): string {\n const baseUrl = CHECKOUT_URLS[environment];\n return `${baseUrl}?store_id=${storeId}&api_key=${apiKey}&environment=${environment}&mode=iframe`;\n}\n\n// =============================================================================\n// CHECKOUT CLASS\n// =============================================================================\n\nexport class Checkout extends EventEmitter {\n private config: CheckoutConfig;\n private iframe: IframeManager;\n private isReady = false;\n private isOpen = false;\n private cartState: CartData = { count: 0, total: 0, currency: \"INR\" };\n private boundMessageHandler: (event: MessageEvent) => void;\n private hasQuickBuyParams = false; // Track if we detected quick buy params\n\n constructor(config: CheckoutConfig) {\n super();\n this.config = config;\n this.iframe = new IframeManager();\n this.boundMessageHandler = this.handleMessage.bind(this);\n this.initialize();\n }\n\n // ===========================================================================\n // INITIALIZATION\n // ===========================================================================\n\n private initialize(): void {\n // Build checkout URL - use direct URL if provided, otherwise resolve from credentials\n let baseUrl: string;\n if (this.config.url) {\n // Direct URL provided (local development)\n baseUrl = this.config.url;\n } else if (this.config.storeId && this.config.apiKey) {\n // Build URL from credentials + environment\n const env = this.config.environment || \"production\";\n baseUrl = buildCheckoutUrl(this.config.storeId, this.config.apiKey, env);\n } else {\n throw new Error(\n \"[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided.\"\n );\n }\n\n const url = new URL(baseUrl);\n\n // Always set iframe mode\n url.searchParams.set(\"mode\", \"iframe\");\n\n // Add store credentials if provided and not already in URL\n if (this.config.storeId && !url.searchParams.has(\"store_id\")) {\n url.searchParams.set(\"store_id\", this.config.storeId);\n }\n if (this.config.apiKey && !url.searchParams.has(\"api_key\")) {\n url.searchParams.set(\"api_key\", this.config.apiKey);\n }\n if (this.config.environment && !url.searchParams.has(\"environment\")) {\n url.searchParams.set(\"environment\", this.config.environment);\n }\n\n // Add theme preference\n if (this.config.theme) {\n url.searchParams.set(\"theme\", this.config.theme);\n }\n\n // Add auth config\n if (this.config.authMode) {\n url.searchParams.set(\"auth_mode\", this.config.authMode);\n }\n if (this.config.accessToken) {\n url.searchParams.set(\"token\", this.config.accessToken);\n }\n if (this.config.refreshToken) {\n url.searchParams.set(\"refresh_token\", this.config.refreshToken);\n }\n\n // Auto-detect quick buy params from parent URL (if enabled and no explicit quickBuy)\n let quickBuy = this.config.quickBuy;\n let sessionMode = this.config.sessionMode;\n\n if (this.config.autoDetectQuickBuy && !quickBuy) {\n const parentParams = new URLSearchParams(window.location.search);\n const productId = parentParams.get(\"product_id\");\n const variantId = parentParams.get(\"variant_id\");\n\n if (productId) {\n quickBuy = {\n productId,\n variantId: variantId ?? null,\n quantity: parseInt(parentParams.get(\"qty\") || parentParams.get(\"quantity\") || \"1\", 10),\n };\n\n // Also check for session_mode in parent URL\n const parentSessionMode = parentParams.get(\"session_mode\");\n if (parentSessionMode === \"force-new\" || parentSessionMode === \"continue-existing\") {\n sessionMode = parentSessionMode;\n }\n\n // Clean quick buy params from URL to prevent duplicate adds on refresh\n // Deferred to next tick to handle React StrictMode double-mounting\n setTimeout(() => this.cleanQuickBuyParamsFromUrl(), 0);\n }\n }\n\n // Add quick buy params\n if (quickBuy) {\n this.hasQuickBuyParams = true;\n url.searchParams.set(\"product_id\", quickBuy.productId);\n if (quickBuy.variantId) {\n url.searchParams.set(\"variant_id\", quickBuy.variantId);\n }\n if (quickBuy.quantity && quickBuy.quantity !== 1) {\n url.searchParams.set(\"qty\", String(quickBuy.quantity));\n }\n }\n\n // Add session mode\n if (sessionMode) {\n url.searchParams.set(\"session_mode\", sessionMode);\n }\n\n // Pass parent origin for postMessage security\n // This allows the iframe to validate incoming messages and send responses to the correct origin\n if (typeof window !== \"undefined\") {\n url.searchParams.set(\"parent_origin\", window.location.origin);\n }\n\n // Create iframe (hidden initially, will show when ready + opened)\n const zIndex = this.config.appearance?.zIndex ?? 99999;\n this.iframe.create(url.toString(), zIndex);\n\n // Listen for messages from checkout iframe\n window.addEventListener(\"message\", this.boundMessageHandler);\n }\n\n // ===========================================================================\n // URL CLEANUP\n // ===========================================================================\n\n /**\n * Remove quick buy params from the parent URL to prevent duplicate adds on refresh\n * Uses replaceState to avoid adding to browser history\n */\n private cleanQuickBuyParamsFromUrl(): void {\n if (typeof window === \"undefined\") return;\n\n const url = new URL(window.location.href);\n const paramsToRemove = [\"product_id\", \"variant_id\", \"qty\", \"quantity\", \"session_mode\"];\n\n let hasChanges = false;\n for (const param of paramsToRemove) {\n if (url.searchParams.has(param)) {\n url.searchParams.delete(param);\n hasChanges = true;\n }\n }\n\n if (hasChanges) {\n window.history.replaceState({}, \"\", url.toString());\n }\n }\n\n // ===========================================================================\n // MESSAGE HANDLING\n // ===========================================================================\n\n private handleMessage(event: MessageEvent): void {\n // Validate origin for security - reject if no expected origin or mismatch\n const expectedOrigin = this.iframe.getCheckoutOrigin();\n if (!expectedOrigin || event.origin !== expectedOrigin) {\n return;\n }\n\n const { type, data } = event.data || {};\n if (!type) return;\n\n switch (type as IncomingEventType) {\n case \"checkout:ready\":\n this.handleReady();\n break;\n\n case \"checkout:open\":\n this.handleOpen();\n break;\n\n case \"checkout:close\":\n this.handleClose();\n break;\n\n case \"checkout:complete\":\n this.handleComplete(data as OrderData);\n break;\n\n case \"checkout:error\":\n this.handleError(data as { message: string });\n break;\n\n case \"cart:updated\":\n this.handleCartUpdate(data as CartData);\n break;\n\n case \"auth:login\":\n this.handleAuthChange({ type: \"login\", ...data });\n break;\n\n case \"auth:logout\":\n this.handleAuthChange({ type: \"logout\" });\n break;\n\n case \"auth:refresh\":\n this.handleAuthChange({ type: \"refresh\", ...data });\n break;\n }\n }\n\n private handleReady(): void {\n this.isReady = true;\n this.config.onReady?.();\n this.emit(\"ready\");\n\n // Auto-open cart if we have quick buy params (from config or auto-detected URL)\n if (this.hasQuickBuyParams) {\n this.openCart();\n }\n }\n\n private handleError(data: { message: string }): void {\n // Set ready so user can still interact (they'll see the error drawer)\n this.isReady = true;\n this.config.onError?.(data);\n this.emit(\"error\", data);\n }\n\n private handleOpen(): void {\n this.iframe.show();\n this.isOpen = true;\n this.config.onOpen?.();\n this.emit(\"open\");\n }\n\n private handleClose(): void {\n this.iframe.hide();\n this.isOpen = false;\n this.config.onClose?.();\n this.emit(\"close\");\n }\n\n private handleComplete(data: OrderData): void {\n this.config.onComplete?.(data);\n this.emit(\"complete\", data);\n }\n\n private handleCartUpdate(data: CartData): void {\n this.cartState = data;\n this.config.onCartUpdate?.(data);\n this.emit(\"cart:updated\", data);\n }\n\n private handleAuthChange(data: AuthChangeData): void {\n this.config.onAuthChange?.(data);\n this.emit(\"auth:change\", data);\n }\n\n // ===========================================================================\n // PUBLIC API\n // ===========================================================================\n\n /**\n * Open the cart drawer\n */\n openCart(): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.show();\n this.isOpen = true;\n this.iframe.postMessage(\"checkout:open-cart\");\n }\n\n /**\n * Open the checkout drawer directly (e.g., for Buy Now flow)\n */\n openCheckout(): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.show();\n this.isOpen = true;\n this.iframe.postMessage(\"checkout:open-checkout\");\n }\n\n /**\n * Close the checkout overlay\n */\n close(): void {\n this.iframe.hide();\n this.isOpen = false;\n this.iframe.postMessage(\"checkout:close\");\n }\n\n /**\n * Update authentication tokens\n * Use this when user logs in/out on the parent site\n */\n updateTokens(accessToken: string, refreshToken?: string): void {\n if (!this.isReady) {\n // Store for when ready\n this.config.accessToken = accessToken;\n this.config.refreshToken = refreshToken;\n return;\n }\n\n this.iframe.postMessage(\"checkout:update-tokens\", { accessToken, refreshToken });\n }\n\n /**\n * Add an item to cart and open the cart drawer\n *\n * @param productId - Product ID (required)\n * @param variantId - Variant ID (required, null for non-variant products)\n * @param quantity - Quantity to add (default: 1)\n */\n addToCart(productId: string, variantId: string | null, quantity = 1): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.postMessage(\"checkout:add-item\", {\n productId,\n variantId,\n quantity,\n });\n\n // Auto-open cart to show the added item\n this.openCart();\n }\n\n /**\n * Get current cart state\n */\n getCart(): CartData {\n return { ...this.cartState };\n }\n\n /**\n * Check if checkout is ready\n */\n get ready(): boolean {\n return this.isReady;\n }\n\n /**\n * Check if checkout overlay is currently open\n */\n get open(): boolean {\n return this.isOpen;\n }\n\n /**\n * Subscribe to an event\n * @override EventEmitter.on with typed overloads\n */\n on(event: \"ready\", listener: EventListener<void>): void;\n on(event: \"open\", listener: EventListener<void>): void;\n on(event: \"close\", listener: EventListener<void>): void;\n on(event: \"complete\", listener: EventListener<OrderData>): void;\n on(event: \"cart:updated\", listener: EventListener<CartData>): void;\n on(event: \"auth:change\", listener: EventListener<AuthChangeData>): void;\n on<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n super.on(event, listener);\n }\n\n /**\n * Destroy the checkout instance\n * Removes iframe and cleans up event listeners\n */\n destroy(): void {\n window.removeEventListener(\"message\", this.boundMessageHandler);\n this.iframe.destroy();\n this.removeAllListeners();\n this.isReady = false;\n this.isOpen = false;\n }\n}\n","/**\n * Commerce Engine Checkout\n *\n * @commercengine/js - Embeddable checkout SDK\n *\n * @example CDN Usage (Vanilla JS):\n * ```html\n * <script src=\"https://cdn.commercengine.com/v1.js\" async></script>\n * <script>\n * window.Commercengine.onLoad = async () => {\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * theme: \"dark\",\n * onComplete: (order) => console.log(\"Order:\", order.orderNumber),\n * });\n *\n * document.getElementById(\"cart-btn\").onclick = () => checkout.openCart();\n * };\n * </script>\n * ```\n *\n * @example ESM Import:\n * ```typescript\n * import { Commercengine } from \"@commercengine/js\";\n *\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * });\n *\n * checkout.on(\"cart:updated\", (cart) => updateBadge(cart.count));\n * checkout.openCart();\n * ```\n */\n\nimport { Checkout } from \"./checkout\";\nimport type { CheckoutConfig } from \"./types\";\n\n// Re-export types for consumers\nexport type {\n AddToCartItem,\n AuthChangeData,\n AuthMode,\n CartData,\n CheckoutConfig,\n CheckoutEventType,\n Environment,\n ErrorData,\n OrderData,\n QuickBuyConfig,\n SessionMode,\n} from \"./types\";\n\nexport { Checkout };\n\n// =============================================================================\n// GLOBAL API\n// =============================================================================\n\n/**\n * Global Commercengine object interface\n */\ninterface CommercengineGlobal {\n /**\n * Initialize checkout with configuration\n * @returns Promise that resolves to Checkout instance\n */\n init: (config: CheckoutConfig) => Promise<Checkout>;\n\n /**\n * Callback invoked when script has loaded\n * Set this BEFORE including the script to be notified when ready\n */\n onLoad?: () => void;\n\n /**\n * Current checkout instance (if initialized)\n */\n instance?: Checkout;\n}\n\n/**\n * Global Commercengine namespace\n */\nconst Commercengine: CommercengineGlobal = {\n /**\n * Initialize checkout\n *\n * @param config - Checkout configuration\n * @returns Checkout instance\n *\n * @example\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * theme: \"system\",\n * });\n */\n init: async (config: CheckoutConfig): Promise<Checkout> => {\n // Validate required fields - either url (for dev) or storeId+apiKey (for prod)\n if (!config.url && (!config.storeId || !config.apiKey)) {\n throw new Error(\n \"[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided\"\n );\n }\n\n // Destroy existing instance if any\n if (Commercengine.instance) {\n Commercengine.instance.destroy();\n }\n\n // Create new instance\n const checkout = new Checkout(config);\n Commercengine.instance = checkout;\n\n // Wait for ready or error with timeout\n return new Promise((resolve, reject) => {\n if (checkout.ready) {\n resolve(checkout);\n return;\n }\n\n const timeoutMs = 30000; // 30 second timeout\n const timeout = setTimeout(() => {\n reject(new Error(\"Checkout initialization timed out\"));\n }, timeoutMs);\n\n // Resolve on ready\n checkout.once(\"ready\", () => {\n clearTimeout(timeout);\n resolve(checkout);\n });\n\n // Also resolve on error (checkout is still usable, will show error drawer)\n checkout.once(\"error\", () => {\n clearTimeout(timeout);\n resolve(checkout);\n });\n });\n },\n};\n\n// =============================================================================\n// GLOBAL SETUP\n// =============================================================================\n\n// Expose on window for CDN usage\nif (typeof window !== \"undefined\") {\n // Preserve any existing onLoad callback set before script loaded\n const existingOnLoad = (window as unknown as { Commercengine?: CommercengineGlobal })\n .Commercengine?.onLoad;\n\n // Set global\n (window as unknown as { Commercengine: CommercengineGlobal }).Commercengine = Commercengine;\n\n // Restore and call onLoad if it was set\n if (existingOnLoad) {\n Commercengine.onLoad = existingOnLoad;\n try {\n existingOnLoad();\n } catch (error) {\n // biome-ignore lint/suspicious/noConsole: Intentional error logging\n console.error(\"[Commercengine] Error in onLoad callback:\", error);\n }\n }\n}\n\nexport { Commercengine };\nexport default Commercengine;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["onceListener: EventListener<T>","CHECKOUT_URLS: Record<Environment, string>","baseUrl: string","Commercengine: CommercengineGlobal"],"sources":["../src/events.ts","../src/iframe-manager.ts","../src/checkout.ts","../src/index.ts"],"sourcesContent":["/**\n * Simple Event Emitter\n *\n * Provides pub/sub functionality for checkout events.\n */\n\nimport type { CheckoutEventType, EventListener } from \"./types\";\n\nexport class EventEmitter {\n private listeners: Map<CheckoutEventType, Set<EventListener>> = new Map();\n\n /**\n * Subscribe to an event\n */\n on<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)?.add(listener as EventListener);\n }\n\n /**\n * Unsubscribe from an event\n */\n off<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n const eventListeners = this.listeners.get(event);\n if (eventListeners) {\n eventListeners.delete(listener as EventListener);\n }\n }\n\n /**\n * Subscribe to an event (once)\n */\n once<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n const onceListener: EventListener<T> = (data) => {\n this.off(event, onceListener);\n listener(data);\n };\n this.on(event, onceListener);\n }\n\n /**\n * Emit an event to all subscribers\n */\n protected emit<T = unknown>(event: CheckoutEventType, data?: T): void {\n const eventListeners = this.listeners.get(event);\n if (eventListeners) {\n for (const listener of eventListeners) {\n try {\n listener(data);\n } catch (error) {\n // biome-ignore lint/suspicious/noConsole: Intentional error logging\n console.error(`[Commercengine] Error in ${event} listener:`, error);\n }\n }\n }\n }\n\n /**\n * Remove all listeners\n */\n protected removeAllListeners(): void {\n this.listeners.clear();\n }\n}\n","/**\n * Iframe Manager\n *\n * Handles iframe creation, lifecycle, and visibility management.\n * The iframe is pre-mounted (hidden) on init and shown on demand.\n */\n\nimport type { OutgoingEventType } from \"./types\";\n\nexport class IframeManager {\n private iframe: HTMLIFrameElement | null = null;\n private container: HTMLDivElement | null = null;\n private checkoutOrigin: string | null = null;\n\n /**\n * Create and mount the iframe (hidden initially)\n */\n create(url: string, zIndex: number): void {\n if (this.container) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Iframe already created\");\n return;\n }\n\n // Store origin for postMessage security\n this.checkoutOrigin = new URL(url).origin;\n\n // Create container (covers viewport, initially non-interactive)\n this.container = document.createElement(\"div\");\n this.container.id = \"commercengine-checkout\";\n this.container.style.cssText = `\n position: fixed;\n inset: 0;\n z-index: ${zIndex};\n pointer-events: none;\n `;\n\n // Create iframe\n this.iframe = document.createElement(\"iframe\");\n this.iframe.id = \"commercengine-checkout-iframe\";\n this.iframe.title = \"Shopping cart and checkout\";\n this.iframe.src = url;\n this.iframe.allow = \"payment\";\n this.iframe.style.cssText = `\n width: 100%;\n height: 100%;\n border: none;\n pointer-events: inherit;\n background-color: transparent;\n `;\n\n this.container.appendChild(this.iframe);\n document.body.appendChild(this.container);\n }\n\n /**\n * Show the checkout overlay (enable pointer events)\n */\n show(): void {\n if (this.container) {\n this.container.style.pointerEvents = \"auto\";\n document.body.style.overflow = \"hidden\";\n }\n }\n\n /**\n * Hide the checkout overlay (disable pointer events)\n */\n hide(): void {\n if (this.container) {\n this.container.style.pointerEvents = \"none\";\n document.body.style.overflow = \"\";\n }\n }\n\n /**\n * Check if iframe is visible\n */\n isVisible(): boolean {\n return this.container?.style.pointerEvents === \"auto\";\n }\n\n /**\n * Send a message to the checkout iframe\n */\n postMessage(type: OutgoingEventType, data?: unknown): void {\n if (!this.iframe?.contentWindow) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Cannot send message - iframe not ready\");\n return;\n }\n\n // Use specific origin for security - never use \"*\"\n if (!this.checkoutOrigin) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning for security\n console.warn(\"[Commercengine] Cannot send message - checkout origin not established\");\n return;\n }\n this.iframe.contentWindow.postMessage({ type, data }, this.checkoutOrigin);\n }\n\n /**\n * Get the checkout origin for message validation\n */\n getCheckoutOrigin(): string | null {\n return this.checkoutOrigin;\n }\n\n /**\n * Destroy the iframe and clean up\n */\n destroy(): void {\n if (this.container) {\n this.container.remove();\n this.container = null;\n }\n this.iframe = null;\n this.checkoutOrigin = null;\n\n // Restore body scroll\n document.body.style.overflow = \"\";\n }\n}\n","/**\n * Checkout Class\n *\n * Main entry point for Commerce Engine Checkout integration.\n * Manages iframe lifecycle, event handling, and provides public API.\n */\n\nimport { EventEmitter } from \"./events\";\nimport { IframeManager } from \"./iframe-manager\";\nimport type {\n AuthLoginData,\n AuthLogoutData,\n AuthRefreshData,\n CartData,\n CheckoutConfig,\n CheckoutEventType,\n Environment,\n EventListener,\n IncomingEventType,\n OrderData,\n} from \"./types\";\n\n// =============================================================================\n// URL RESOLUTION\n// =============================================================================\n\n/** Checkout URLs by environment */\nconst CHECKOUT_URLS: Record<Environment, string> = {\n production: \"https://checkout.commercengine.com\",\n staging: \"https://staging.checkout.commercengine.com\",\n};\n\n/**\n * Build checkout iframe URL\n */\nfunction buildCheckoutUrl(storeId: string, apiKey: string, environment: Environment): string {\n const baseUrl = CHECKOUT_URLS[environment];\n return `${baseUrl}?store_id=${storeId}&api_key=${apiKey}&environment=${environment}&mode=iframe`;\n}\n\n// =============================================================================\n// CHECKOUT CLASS\n// =============================================================================\n\nexport class Checkout extends EventEmitter {\n private config: CheckoutConfig;\n private iframe: IframeManager;\n private isReady = false;\n private isOpen = false;\n private cartState: CartData = { count: 0, total: 0, currency: \"INR\" };\n private boundMessageHandler: (event: MessageEvent) => void;\n private hasQuickBuyParams = false; // Track if we detected quick buy params\n\n constructor(config: CheckoutConfig) {\n super();\n this.config = config;\n this.iframe = new IframeManager();\n this.boundMessageHandler = this.handleMessage.bind(this);\n this.initialize();\n }\n\n // ===========================================================================\n // INITIALIZATION\n // ===========================================================================\n\n private initialize(): void {\n // Build checkout URL - use direct URL if provided, otherwise resolve from credentials\n let baseUrl: string;\n if (this.config.url) {\n // Direct URL provided (local development)\n baseUrl = this.config.url;\n } else if (this.config.storeId && this.config.apiKey) {\n // Build URL from credentials + environment\n const env = this.config.environment || \"production\";\n baseUrl = buildCheckoutUrl(this.config.storeId, this.config.apiKey, env);\n } else {\n throw new Error(\n \"[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided.\"\n );\n }\n\n const url = new URL(baseUrl);\n\n // Always set iframe mode\n url.searchParams.set(\"mode\", \"iframe\");\n\n // Add store credentials if provided and not already in URL\n if (this.config.storeId && !url.searchParams.has(\"store_id\")) {\n url.searchParams.set(\"store_id\", this.config.storeId);\n }\n if (this.config.apiKey && !url.searchParams.has(\"api_key\")) {\n url.searchParams.set(\"api_key\", this.config.apiKey);\n }\n if (this.config.environment && !url.searchParams.has(\"environment\")) {\n url.searchParams.set(\"environment\", this.config.environment);\n }\n\n // Add theme preference\n if (this.config.theme) {\n url.searchParams.set(\"theme\", this.config.theme);\n }\n\n // Add auth config\n if (this.config.authMode) {\n url.searchParams.set(\"auth_mode\", this.config.authMode);\n }\n if (this.config.accessToken) {\n url.searchParams.set(\"token\", this.config.accessToken);\n }\n if (this.config.refreshToken) {\n url.searchParams.set(\"refresh_token\", this.config.refreshToken);\n }\n\n // Auto-detect quick buy params from parent URL (if enabled and no explicit quickBuy)\n let quickBuy = this.config.quickBuy;\n let sessionMode = this.config.sessionMode;\n\n if (this.config.autoDetectQuickBuy && !quickBuy) {\n const parentParams = new URLSearchParams(window.location.search);\n const productId = parentParams.get(\"product_id\");\n const variantId = parentParams.get(\"variant_id\");\n\n if (productId) {\n quickBuy = {\n productId,\n variantId: variantId ?? null,\n quantity: parseInt(parentParams.get(\"qty\") || parentParams.get(\"quantity\") || \"1\", 10),\n };\n\n // Also check for session_mode in parent URL\n const parentSessionMode = parentParams.get(\"session_mode\");\n if (parentSessionMode === \"force-new\" || parentSessionMode === \"continue-existing\") {\n sessionMode = parentSessionMode;\n }\n\n // Clean quick buy params from URL to prevent duplicate adds on refresh\n // Deferred to next tick to handle React StrictMode double-mounting\n setTimeout(() => this.cleanQuickBuyParamsFromUrl(), 0);\n }\n }\n\n // Add quick buy params\n if (quickBuy) {\n this.hasQuickBuyParams = true;\n url.searchParams.set(\"product_id\", quickBuy.productId);\n if (quickBuy.variantId) {\n url.searchParams.set(\"variant_id\", quickBuy.variantId);\n }\n if (quickBuy.quantity && quickBuy.quantity !== 1) {\n url.searchParams.set(\"qty\", String(quickBuy.quantity));\n }\n }\n\n // Add session mode\n if (sessionMode) {\n url.searchParams.set(\"session_mode\", sessionMode);\n }\n\n // Pass parent origin for postMessage security\n // This allows the iframe to validate incoming messages and send responses to the correct origin\n if (typeof window !== \"undefined\") {\n url.searchParams.set(\"parent_origin\", window.location.origin);\n }\n\n // Create iframe (hidden initially, will show when ready + opened)\n const zIndex = this.config.appearance?.zIndex ?? 99999;\n this.iframe.create(url.toString(), zIndex);\n\n // Listen for messages from checkout iframe\n window.addEventListener(\"message\", this.boundMessageHandler);\n }\n\n // ===========================================================================\n // URL CLEANUP\n // ===========================================================================\n\n /**\n * Remove quick buy params from the parent URL to prevent duplicate adds on refresh\n * Uses replaceState to avoid adding to browser history\n */\n private cleanQuickBuyParamsFromUrl(): void {\n if (typeof window === \"undefined\") return;\n\n const url = new URL(window.location.href);\n const paramsToRemove = [\"product_id\", \"variant_id\", \"qty\", \"quantity\", \"session_mode\"];\n\n let hasChanges = false;\n for (const param of paramsToRemove) {\n if (url.searchParams.has(param)) {\n url.searchParams.delete(param);\n hasChanges = true;\n }\n }\n\n if (hasChanges) {\n window.history.replaceState({}, \"\", url.toString());\n }\n }\n\n // ===========================================================================\n // MESSAGE HANDLING\n // ===========================================================================\n\n private handleMessage(event: MessageEvent): void {\n // Validate origin for security - reject if no expected origin or mismatch\n const expectedOrigin = this.iframe.getCheckoutOrigin();\n if (!expectedOrigin || event.origin !== expectedOrigin) {\n return;\n }\n\n const { type, data } = event.data || {};\n if (!type) return;\n\n switch (type as IncomingEventType) {\n case \"checkout:ready\":\n this.handleReady();\n break;\n\n case \"checkout:open\":\n this.handleOpen();\n break;\n\n case \"checkout:close\":\n this.handleClose();\n break;\n\n case \"checkout:complete\":\n this.handleComplete(data as OrderData);\n break;\n\n case \"checkout:error\":\n this.handleError(data as { message: string });\n break;\n\n case \"cart:updated\":\n this.handleCartUpdate(data as CartData);\n break;\n\n case \"auth:login\":\n this.handleLogin(data as AuthLoginData);\n break;\n\n case \"auth:logout\":\n this.handleLogout(data as AuthLogoutData);\n break;\n\n case \"auth:refresh\":\n this.handleTokenRefresh(data as AuthRefreshData);\n break;\n\n case \"auth:session-error\":\n this.handleSessionError();\n break;\n }\n }\n\n private handleReady(): void {\n this.isReady = true;\n this.config.onReady?.();\n this.emit(\"ready\");\n\n // Auto-open cart if we have quick buy params (from config or auto-detected URL)\n if (this.hasQuickBuyParams) {\n this.openCart();\n }\n }\n\n private handleError(data: { message: string }): void {\n // Set ready so user can still interact (they'll see the error drawer)\n this.isReady = true;\n this.config.onError?.(data);\n this.emit(\"error\", data);\n }\n\n private handleOpen(): void {\n this.iframe.show();\n this.isOpen = true;\n this.config.onOpen?.();\n this.emit(\"open\");\n }\n\n private handleClose(): void {\n this.iframe.hide();\n this.isOpen = false;\n this.config.onClose?.();\n this.emit(\"close\");\n }\n\n private handleComplete(data: OrderData): void {\n this.config.onComplete?.(data);\n this.emit(\"complete\", data);\n }\n\n private handleCartUpdate(data: CartData): void {\n this.cartState = data;\n this.config.onCartUpdate?.(data);\n this.emit(\"cart:updated\", data);\n }\n\n private handleLogin(data: AuthLoginData): void {\n this.config.onLogin?.(data);\n this.emit(\"auth:login\", data);\n }\n\n private handleLogout(data: AuthLogoutData): void {\n this.config.onLogout?.(data);\n this.emit(\"auth:logout\", data);\n }\n\n private handleTokenRefresh(data: AuthRefreshData): void {\n this.config.onTokenRefresh?.(data);\n this.emit(\"auth:refresh\", data);\n }\n\n private handleSessionError(): void {\n this.config.onSessionError?.();\n this.emit(\"auth:session-error\");\n }\n\n // ===========================================================================\n // PUBLIC API\n // ===========================================================================\n\n /**\n * Open the cart drawer\n */\n openCart(): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.show();\n this.isOpen = true;\n this.iframe.postMessage(\"checkout:open-cart\");\n }\n\n /**\n * Open the checkout drawer directly (e.g., for Buy Now flow)\n */\n openCheckout(): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.show();\n this.isOpen = true;\n this.iframe.postMessage(\"checkout:open-checkout\");\n }\n\n /**\n * Close the checkout overlay\n */\n close(): void {\n this.iframe.hide();\n this.isOpen = false;\n this.iframe.postMessage(\"checkout:close\");\n }\n\n /**\n * Update authentication tokens\n * Use this when user logs in/out on the parent site\n */\n updateTokens(accessToken: string, refreshToken?: string): void {\n if (!this.isReady) {\n // Store for when ready\n this.config.accessToken = accessToken;\n this.config.refreshToken = refreshToken;\n return;\n }\n\n this.iframe.postMessage(\"checkout:update-tokens\", { accessToken, refreshToken });\n }\n\n /**\n * Add an item to cart and open the cart drawer\n *\n * @param productId - Product ID (required)\n * @param variantId - Variant ID (required, null for non-variant products)\n * @param quantity - Quantity to add (default: 1)\n */\n addToCart(productId: string, variantId: string | null, quantity = 1): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.postMessage(\"checkout:add-item\", {\n productId,\n variantId,\n quantity,\n });\n\n // Auto-open cart to show the added item\n this.openCart();\n }\n\n /**\n * Get current cart state\n */\n getCart(): CartData {\n return { ...this.cartState };\n }\n\n /**\n * Check if checkout is ready\n */\n get ready(): boolean {\n return this.isReady;\n }\n\n /**\n * Check if checkout overlay is currently open\n */\n get open(): boolean {\n return this.isOpen;\n }\n\n /**\n * Subscribe to an event\n * @override EventEmitter.on with typed overloads\n */\n on(event: \"ready\", listener: EventListener<void>): void;\n on(event: \"open\", listener: EventListener<void>): void;\n on(event: \"close\", listener: EventListener<void>): void;\n on(event: \"complete\", listener: EventListener<OrderData>): void;\n on(event: \"cart:updated\", listener: EventListener<CartData>): void;\n on(event: \"auth:login\", listener: EventListener<AuthLoginData>): void;\n on(event: \"auth:logout\", listener: EventListener<AuthLogoutData>): void;\n on(event: \"auth:refresh\", listener: EventListener<AuthRefreshData>): void;\n on(event: \"auth:session-error\", listener: EventListener<void>): void;\n on<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n super.on(event, listener);\n }\n\n /**\n * Destroy the checkout instance\n * Removes iframe and cleans up event listeners\n */\n destroy(): void {\n window.removeEventListener(\"message\", this.boundMessageHandler);\n this.iframe.destroy();\n this.removeAllListeners();\n this.isReady = false;\n this.isOpen = false;\n }\n}\n","/**\n * Commerce Engine Checkout\n *\n * @commercengine/js - Embeddable checkout SDK\n *\n * @example CDN Usage (Vanilla JS):\n * ```html\n * <script src=\"https://cdn.commercengine.com/v1.js\" async></script>\n * <script>\n * window.Commercengine.onLoad = async () => {\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * theme: \"dark\",\n * onComplete: (order) => console.log(\"Order:\", order.orderNumber),\n * });\n *\n * document.getElementById(\"cart-btn\").onclick = () => checkout.openCart();\n * };\n * </script>\n * ```\n *\n * @example ESM Import:\n * ```typescript\n * import { Commercengine } from \"@commercengine/js\";\n *\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * });\n *\n * checkout.on(\"cart:updated\", (cart) => updateBadge(cart.count));\n * checkout.openCart();\n * ```\n */\n\nimport { Checkout } from \"./checkout\";\nimport type { CheckoutConfig } from \"./types\";\n\n// Re-export types for consumers\nexport type {\n AddToCartItem,\n AuthLoginData,\n AuthLogoutData,\n AuthMode,\n AuthRefreshData,\n CartData,\n Channel,\n CheckoutConfig,\n CheckoutEventType,\n Environment,\n ErrorData,\n LoginMethod,\n OrderData,\n QuickBuyConfig,\n SessionMode,\n UserInfo,\n} from \"./types\";\n\nexport { Checkout };\n\n// =============================================================================\n// GLOBAL API\n// =============================================================================\n\n/**\n * Global Commercengine object interface\n */\ninterface CommercengineGlobal {\n /**\n * Initialize checkout with configuration\n * @returns Promise that resolves to Checkout instance\n */\n init: (config: CheckoutConfig) => Promise<Checkout>;\n\n /**\n * Callback invoked when script has loaded\n * Set this BEFORE including the script to be notified when ready\n */\n onLoad?: () => void;\n\n /**\n * Current checkout instance (if initialized)\n */\n instance?: Checkout;\n}\n\n/**\n * Global Commercengine namespace\n */\nconst Commercengine: CommercengineGlobal = {\n /**\n * Initialize checkout\n *\n * @param config - Checkout configuration\n * @returns Checkout instance\n *\n * @example\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * theme: \"system\",\n * });\n */\n init: async (config: CheckoutConfig): Promise<Checkout> => {\n // Validate required fields - either url (for dev) or storeId+apiKey (for prod)\n if (!config.url && (!config.storeId || !config.apiKey)) {\n throw new Error(\n \"[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided\"\n );\n }\n\n // Destroy existing instance if any\n if (Commercengine.instance) {\n Commercengine.instance.destroy();\n }\n\n // Create new instance\n const checkout = new Checkout(config);\n Commercengine.instance = checkout;\n\n // Wait for ready or error with timeout\n return new Promise((resolve, reject) => {\n if (checkout.ready) {\n resolve(checkout);\n return;\n }\n\n const timeoutMs = 30000; // 30 second timeout\n const timeout = setTimeout(() => {\n reject(new Error(\"Checkout initialization timed out\"));\n }, timeoutMs);\n\n // Resolve on ready\n checkout.once(\"ready\", () => {\n clearTimeout(timeout);\n resolve(checkout);\n });\n\n // Also resolve on error (checkout is still usable, will show error drawer)\n checkout.once(\"error\", () => {\n clearTimeout(timeout);\n resolve(checkout);\n });\n });\n },\n};\n\n// =============================================================================\n// GLOBAL SETUP\n// =============================================================================\n\n// Expose on window for CDN usage\nif (typeof window !== \"undefined\") {\n // Preserve any existing onLoad callback set before script loaded\n const existingOnLoad = (window as unknown as { Commercengine?: CommercengineGlobal })\n .Commercengine?.onLoad;\n\n // Set global\n (window as unknown as { Commercengine: CommercengineGlobal }).Commercengine = Commercengine;\n\n // Restore and call onLoad if it was set\n if (existingOnLoad) {\n Commercengine.onLoad = existingOnLoad;\n try {\n existingOnLoad();\n } catch (error) {\n // biome-ignore lint/suspicious/noConsole: Intentional error logging\n console.error(\"[Commercengine] Error in onLoad callback:\", error);\n }\n }\n}\n\nexport { Commercengine };\nexport default Commercengine;\n"],"mappings":"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"}
|