@commercengine/js 0.1.0 → 0.2.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 CHANGED
@@ -7,7 +7,7 @@ Embed Commerce Engine checkout in any website.
7
7
  ### CDN (Recommended)
8
8
 
9
9
  ```html
10
- <script src="https://js.commercengine.com/v1.js" async></script>
10
+ <script src="https://cdn.commercengine.com/v1.js" async></script>
11
11
  ```
12
12
 
13
13
  ### npm
@@ -19,12 +19,13 @@ npm install @commercengine/js
19
19
  ## Quick Start
20
20
 
21
21
  ```html
22
- <script src="https://js.commercengine.com/v1.js" async></script>
22
+ <script src="https://cdn.commercengine.com/v1.js" async></script>
23
23
  <script>
24
24
  window.Commercengine.onLoad = async () => {
25
25
  const checkout = await Commercengine.init({
26
26
  storeId: "store_xxx",
27
27
  apiKey: "ak_xxx",
28
+ environment: "production", // or "staging"
28
29
  });
29
30
 
30
31
  document.getElementById("cart-btn").onclick = () => checkout.openCart();
@@ -41,10 +42,11 @@ interface CheckoutConfig {
41
42
  // === Credentials (required) ===
42
43
  storeId: string; // Your Commerce Engine Store ID
43
44
  apiKey: string; // Your Commerce Engine API Key
45
+ environment?: "production" | "staging"; // Default: "production"
44
46
 
45
47
  // === Development ===
46
- url?: string; // Direct checkout URL (local dev only)
47
- // If provided, storeId/apiKey are optional
48
+ url?: string; // Direct checkout URL (for local dev)
49
+ // Overrides environment-based URL
48
50
 
49
51
  // === Theme ===
50
52
  theme?: "light" | "dark" | "system"; // Default: "system"
@@ -84,6 +86,7 @@ interface CheckoutConfig {
84
86
  const checkout = await Commercengine.init({
85
87
  storeId: "store_xxx",
86
88
  apiKey: "ak_xxx",
89
+ environment: "production",
87
90
  theme: "dark",
88
91
  appearance: {
89
92
  zIndex: 100000,
@@ -311,6 +314,7 @@ import {
311
314
  Checkout,
312
315
  type CheckoutConfig,
313
316
  type CheckoutEventType,
317
+ type Environment,
314
318
  type CartData,
315
319
  type OrderData,
316
320
  type AuthChangeData,
@@ -325,6 +329,7 @@ For direct iframe integration without the SDK, checkout accepts these URL parame
325
329
  |-----------|-------------|---------|
326
330
  | `store_id` | Store ID | `store_xxx` |
327
331
  | `api_key` | API Key | `ak_xxx` |
332
+ | `environment` | SDK environment | `production`, `staging` |
328
333
  | `mode` | Deployment mode | `iframe` |
329
334
  | `parent_origin` | Parent origin for postMessage | `https://brand.com` |
330
335
  | `theme` | Theme preference | `light`, `dark` |
package/dist/index.cjs CHANGED
@@ -156,23 +156,16 @@ var IframeManager = class {
156
156
  * Main entry point for Commerce Engine Checkout integration.
157
157
  * Manages iframe lifecycle, event handling, and provides public API.
158
158
  */
159
+ /** Checkout URLs by environment */
160
+ const CHECKOUT_URLS = {
161
+ production: "https://checkout.commercengine.com",
162
+ staging: "https://staging.checkout.commercengine.com"
163
+ };
159
164
  /**
160
- * Resolve checkout URL based on current environment
161
- *
162
- * The environment is determined by where the script was loaded from:
163
- * - js.commercengine.com → production checkout
164
- * - js.staging.commercengine.com → staging checkout
165
- * - localhost → local development
165
+ * Build checkout iframe URL
166
166
  */
167
- function resolveCheckoutUrl(storeId, apiKey) {
168
- const scripts = document.querySelectorAll("script[src*=\"commercengine\"]");
169
- let isStaging = false;
170
- for (const script of scripts) if ((script.getAttribute("src") || "").includes("staging.commercengine.com")) {
171
- isStaging = true;
172
- break;
173
- }
174
- if (typeof window !== "undefined" && window.location.hostname === "localhost") return `http://localhost:8080?store_id=${storeId}&api_key=${apiKey}&mode=iframe`;
175
- return `${isStaging ? "https://checkout.staging.commercengine.com" : "https://checkout.commercengine.com"}?store_id=${storeId}&api_key=${apiKey}&mode=iframe`;
167
+ function buildCheckoutUrl(storeId, apiKey, environment) {
168
+ return `${CHECKOUT_URLS[environment]}?store_id=${storeId}&api_key=${apiKey}&environment=${environment}&mode=iframe`;
176
169
  }
177
170
  var Checkout = class extends EventEmitter {
178
171
  constructor(config) {
@@ -193,12 +186,15 @@ var Checkout = class extends EventEmitter {
193
186
  initialize() {
194
187
  let baseUrl;
195
188
  if (this.config.url) baseUrl = this.config.url;
196
- else if (this.config.storeId && this.config.apiKey) baseUrl = resolveCheckoutUrl(this.config.storeId, this.config.apiKey);
197
- else throw new Error("[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided.");
189
+ else if (this.config.storeId && this.config.apiKey) {
190
+ const env = this.config.environment || "production";
191
+ baseUrl = buildCheckoutUrl(this.config.storeId, this.config.apiKey, env);
192
+ } else throw new Error("[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided.");
198
193
  const url = new URL(baseUrl);
199
194
  url.searchParams.set("mode", "iframe");
200
195
  if (this.config.storeId && !url.searchParams.has("store_id")) url.searchParams.set("store_id", this.config.storeId);
201
196
  if (this.config.apiKey && !url.searchParams.has("api_key")) url.searchParams.set("api_key", this.config.apiKey);
197
+ if (this.config.environment && !url.searchParams.has("environment")) url.searchParams.set("environment", this.config.environment);
202
198
  if (this.config.theme) url.searchParams.set("theme", this.config.theme);
203
199
  if (this.config.authMode) url.searchParams.set("auth_mode", this.config.authMode);
204
200
  if (this.config.accessToken) url.searchParams.set("token", this.config.accessToken);
@@ -439,7 +435,7 @@ var Checkout = class extends EventEmitter {
439
435
  *
440
436
  * @example CDN Usage (Vanilla JS):
441
437
  * ```html
442
- * <script src="https://js.commercengine.com/v1.js" async><\/script>
438
+ * <script src="https://cdn.commercengine.com/v1.js" async><\/script>
443
439
  * <script>
444
440
  * window.Commercengine.onLoad = async () => {
445
441
  * const checkout = await Commercengine.init({
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["onceListener: EventListener<T>","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.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 EventListener,\n IncomingEventType,\n OrderData,\n} from \"./types\";\n\n// =============================================================================\n// URL RESOLUTION\n// =============================================================================\n\n/**\n * Resolve checkout URL based on current environment\n *\n * The environment is determined by where the script was loaded from:\n * - js.commercengine.com → production checkout\n * - js.staging.commercengine.com → staging checkout\n * - localhost → local development\n */\nfunction resolveCheckoutUrl(storeId: string, apiKey: string): string {\n // Detect environment from script source\n const scripts = document.querySelectorAll('script[src*=\"commercengine\"]');\n let isStaging = false;\n\n for (const script of scripts) {\n const src = script.getAttribute(\"src\") || \"\";\n if (src.includes(\"staging.commercengine.com\")) {\n isStaging = true;\n break;\n }\n }\n\n // For local development, use localhost\n if (typeof window !== \"undefined\" && window.location.hostname === \"localhost\") {\n return `http://localhost:8080?store_id=${storeId}&api_key=${apiKey}&mode=iframe`;\n }\n\n // Determine checkout URL based on environment\n const baseUrl = isStaging\n ? \"https://checkout.staging.commercengine.com\"\n : \"https://checkout.commercengine.com\";\n\n return `${baseUrl}?store_id=${storeId}&api_key=${apiKey}&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 // Resolve from credentials\n baseUrl = resolveCheckoutUrl(this.config.storeId, this.config.apiKey);\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\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://js.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 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.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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"}
package/dist/index.d.cts CHANGED
@@ -56,6 +56,12 @@ interface QuickBuyConfig {
56
56
  * - 'force-new': Clear cart and start fresh
57
57
  */
58
58
  type SessionMode = "continue-existing" | "force-new";
59
+ /**
60
+ * Environment for checkout
61
+ * - 'production': Uses checkout.commercengine.com
62
+ * - 'staging': Uses checkout.staging.commercengine.com
63
+ */
64
+ type Environment = "production" | "staging";
59
65
  /**
60
66
  * Configuration for initializing checkout
61
67
  *
@@ -64,7 +70,15 @@ type SessionMode = "continue-existing" | "force-new";
64
70
  */
65
71
  interface CheckoutConfig {
66
72
  /**
67
- * Direct checkout URL (for local development)
73
+ * Environment to use for checkout URL resolution
74
+ * - 'production': checkout.commercengine.com (default)
75
+ * - 'staging': staging.checkout.commercengine.com
76
+ *
77
+ * For local development, use the `url` option instead.
78
+ */
79
+ environment?: Environment;
80
+ /**
81
+ * Direct checkout URL (overrides environment detection)
68
82
  * If provided, storeId and apiKey are optional.
69
83
  * @example "http://localhost:8080"
70
84
  */
@@ -337,5 +351,5 @@ interface CommercengineGlobal {
337
351
  */
338
352
  declare const Commercengine: CommercengineGlobal;
339
353
  //#endregion
340
- export { type AddToCartItem, type AuthChangeData, type AuthMode, type CartData, Checkout, type CheckoutConfig, type CheckoutEventType, Commercengine, Commercengine as default, type ErrorData, type OrderData, type QuickBuyConfig, type SessionMode };
354
+ export { type AddToCartItem, type AuthChangeData, type AuthMode, type CartData, Checkout, type CheckoutConfig, type CheckoutEventType, Commercengine, Commercengine as default, type Environment, type ErrorData, type OrderData, type QuickBuyConfig, type SessionMode };
341
355
  //# sourceMappingURL=index.d.cts.map
@@ -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":"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"}
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;;;;;;;AAwIwB,KArNZ,QAAA,GAqNY,SAAA,GAAA,UAAA;;;AAkBxB;AAYiB,UA1OA,aAAA,CA0OS;EAUT;AAYjB;AAYA;EAYY,SAAA,EAAA,MAAa;;;;EC3SZ,SAAA,EAAA,MAAY,GAAA,IAAA;EAMA;;;;EAU4C,QAAA,CAAA,EAAA,MAAA;;;;;;AAqBN,UDI9C,cAAA,CCJ8C;EAAC;;;;ECHnD;;;EA2WkB,SAAA,EAAA,MAAA,GAAA,IAAA;EACD;;;;EAGsB,QAAA,CAAA,EAAA,MAAA;;;;;;;KF7UxC,WAAA;;AGtBQ;;;;AAyBP,KHQD,WAAA,GGRC,YAAA,GAAA,SAAA;;AAAQ;;;;;UHgBJ,cAAA;;;;;;;;gBAYD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAsDH;;;;;;;;;;;;;aAoBA;;;;;gBAMG;;;;;;;;;;;;;;;;;;;;;;;;uBAiCO;;;;;wBAMC;;;;wBAKA;;;;;;;oBAQJ;;;;;UAUH,QAAA;;;;;;;;;;;UAYA,SAAA;;;;;;;;;UAUA,cAAA;;;;;;;;;;;UAYA,SAAA;;;;;;;KAYL,iBAAA;;;;KAYA,oCAAoC;;;AApN/B,cCvFJ,YAAA,CDuFkB;EAYf,QAAA,SAAA;EAsDH;;;EA2DU,EAAA,CAAA,IAAA,OAAA,CAAA,CAAA,KAAA,EC9ME,iBD8MF,EAAA,QAAA,EC9M+B,aD8M/B,CC9M6C,CD8M7C,CAAA,CAAA,EAAA,IAAA;EAMC;;;EAaK,GAAA,CAAA,IAAA,OAAA,CAAA,CAAA,KAAA,ECvNH,iBDuNG,EAAA,QAAA,ECvN0B,aDuN1B,CCvNwC,CDuNxC,CAAA,CAAA,EAAA,IAAA;EAUZ;AAYjB;AAUA;EAYiB,IAAA,CAAA,IAAA,OAAS,CAAA,CAAA,KAAA,ECzPC,iBDyPD,EAAA,QAAA,ECzP8B,aDyP9B,CCzP4C,CDyP5C,CAAA,CAAA,EAAA,IAAA;EAYd;AAYZ;;qCCtQqC,0BAA0B;;AArC/D;;EAMoE,UAAA,kBAAA,CAAA,CAAA,EAAA,IAAA;;;;ADmJvD,cEvHA,QAAA,SAAiB,YAAA,CFuHjB;EAoBA,QAAA,MAAA;EAMG,QAAA,MAAA;EAiCO,QAAA,OAAA;EAMC,QAAA,MAAA;EAKA,QAAA,SAAA;EAQJ,QAAA,mBAAA;EAAS,QAAA,iBAAA;EAUZ,WAAQ,CAAA,MAAA,EEtMH,cFsMG;EAYR,QAAA,UAAS;EAUT;AAYjB;AAYA;AAYA;;;;EC3Sa,QAAA,WAAY;EAMA,QAAA,UAAA;EAA2C,QAAA,WAAA;EAAd,QAAA,cAAA;EAU5B,QAAA,gBAAA;EAA2C,QAAA,gBAAA;EAAd;;;EAUC,QAAA,CAAA,CAAA,EAAA,IAAA;EAWnB;;;;;;ACHrC;EASsB,KAAA,CAAA,CAAA,EAAA,IAAA;EA4UT;;;;EAyBmC,YAAA,CAAA,WAAA,EAAA,MAAA,EAAA,YAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;EAAd;;;;;;;;;;AClWd;EAcH,OAAA,CAAA,CAAA,ED2TJ,QC3TI;EAA2B;;;EAWvB,IAAA,KAAA,CAAA,CAAA,EAAA,OAAA;EAMf;;;;;;;;+BDgUyB;8BACD;+BACC;kCACG,cAAc;sCACV,cAAc;qCACf,cAAc;;;;;;;;;;;AAhXnD;UCqBU,mBAAA,CDZY;EA4UT;;;;EAyBmC,IAAA,EAAA,CAAA,MAAA,ECpV/B,cDoV+B,EAAA,GCpVZ,ODoVY,CCpVJ,QDoVI,CAAA;EAAd;;;;EAEG,MAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAhXP;;;aCqCjB;;AAzBO;;;cA+Bd,aAjB8B,EAiBf,mBAjBe"}
package/dist/index.d.mts CHANGED
@@ -56,6 +56,12 @@ interface QuickBuyConfig {
56
56
  * - 'force-new': Clear cart and start fresh
57
57
  */
58
58
  type SessionMode = "continue-existing" | "force-new";
59
+ /**
60
+ * Environment for checkout
61
+ * - 'production': Uses checkout.commercengine.com
62
+ * - 'staging': Uses checkout.staging.commercengine.com
63
+ */
64
+ type Environment = "production" | "staging";
59
65
  /**
60
66
  * Configuration for initializing checkout
61
67
  *
@@ -64,7 +70,15 @@ type SessionMode = "continue-existing" | "force-new";
64
70
  */
65
71
  interface CheckoutConfig {
66
72
  /**
67
- * Direct checkout URL (for local development)
73
+ * Environment to use for checkout URL resolution
74
+ * - 'production': checkout.commercengine.com (default)
75
+ * - 'staging': staging.checkout.commercengine.com
76
+ *
77
+ * For local development, use the `url` option instead.
78
+ */
79
+ environment?: Environment;
80
+ /**
81
+ * Direct checkout URL (overrides environment detection)
68
82
  * If provided, storeId and apiKey are optional.
69
83
  * @example "http://localhost:8080"
70
84
  */
@@ -337,5 +351,5 @@ interface CommercengineGlobal {
337
351
  */
338
352
  declare const Commercengine: CommercengineGlobal;
339
353
  //#endregion
340
- export { type AddToCartItem, type AuthChangeData, type AuthMode, type CartData, Checkout, type CheckoutConfig, type CheckoutEventType, Commercengine, Commercengine as default, type ErrorData, type OrderData, type QuickBuyConfig, type SessionMode };
354
+ export { type AddToCartItem, type AuthChangeData, type AuthMode, type CartData, Checkout, type CheckoutConfig, type CheckoutEventType, Commercengine, Commercengine as default, type Environment, type ErrorData, type OrderData, type QuickBuyConfig, type SessionMode };
341
355
  //# sourceMappingURL=index.d.mts.map
@@ -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":"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"}
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;;;;;;;AAwIwB,KArNZ,QAAA,GAqNY,SAAA,GAAA,UAAA;;;AAkBxB;AAYiB,UA1OA,aAAA,CA0OS;EAUT;AAYjB;AAYA;EAYY,SAAA,EAAA,MAAa;;;;EC3SZ,SAAA,EAAA,MAAY,GAAA,IAAA;EAMA;;;;EAU4C,QAAA,CAAA,EAAA,MAAA;;;;;;AAqBN,UDI9C,cAAA,CCJ8C;EAAC;;;;ECHnD;;;EA2WkB,SAAA,EAAA,MAAA,GAAA,IAAA;EACD;;;;EAGsB,QAAA,CAAA,EAAA,MAAA;;;;;;;KF7UxC,WAAA;;AGtBQ;;;;AAyBP,KHQD,WAAA,GGRC,YAAA,GAAA,SAAA;;AAAQ;;;;;UHgBJ,cAAA;;;;;;;;gBAYD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAsDH;;;;;;;;;;;;;aAoBA;;;;;gBAMG;;;;;;;;;;;;;;;;;;;;;;;;uBAiCO;;;;;wBAMC;;;;wBAKA;;;;;;;oBAQJ;;;;;UAUH,QAAA;;;;;;;;;;;UAYA,SAAA;;;;;;;;;UAUA,cAAA;;;;;;;;;;;UAYA,SAAA;;;;;;;KAYL,iBAAA;;;;KAYA,oCAAoC;;;AApN/B,cCvFJ,YAAA,CDuFkB;EAYf,QAAA,SAAA;EAsDH;;;EA2DU,EAAA,CAAA,IAAA,OAAA,CAAA,CAAA,KAAA,EC9ME,iBD8MF,EAAA,QAAA,EC9M+B,aD8M/B,CC9M6C,CD8M7C,CAAA,CAAA,EAAA,IAAA;EAMC;;;EAaK,GAAA,CAAA,IAAA,OAAA,CAAA,CAAA,KAAA,ECvNH,iBDuNG,EAAA,QAAA,ECvN0B,aDuN1B,CCvNwC,CDuNxC,CAAA,CAAA,EAAA,IAAA;EAUZ;AAYjB;AAUA;EAYiB,IAAA,CAAA,IAAA,OAAS,CAAA,CAAA,KAAA,ECzPC,iBDyPD,EAAA,QAAA,ECzP8B,aDyP9B,CCzP4C,CDyP5C,CAAA,CAAA,EAAA,IAAA;EAYd;AAYZ;;qCCtQqC,0BAA0B;;AArC/D;;EAMoE,UAAA,kBAAA,CAAA,CAAA,EAAA,IAAA;;;;ADmJvD,cEvHA,QAAA,SAAiB,YAAA,CFuHjB;EAoBA,QAAA,MAAA;EAMG,QAAA,MAAA;EAiCO,QAAA,OAAA;EAMC,QAAA,MAAA;EAKA,QAAA,SAAA;EAQJ,QAAA,mBAAA;EAAS,QAAA,iBAAA;EAUZ,WAAQ,CAAA,MAAA,EEtMH,cFsMG;EAYR,QAAA,UAAS;EAUT;AAYjB;AAYA;AAYA;;;;EC3Sa,QAAA,WAAY;EAMA,QAAA,UAAA;EAA2C,QAAA,WAAA;EAAd,QAAA,cAAA;EAU5B,QAAA,gBAAA;EAA2C,QAAA,gBAAA;EAAd;;;EAUC,QAAA,CAAA,CAAA,EAAA,IAAA;EAWnB;;;;;;ACHrC;EASsB,KAAA,CAAA,CAAA,EAAA,IAAA;EA4UT;;;;EAyBmC,YAAA,CAAA,WAAA,EAAA,MAAA,EAAA,YAAA,CAAA,EAAA,MAAA,CAAA,EAAA,IAAA;EAAd;;;;;;;;;;AClWd;EAcH,OAAA,CAAA,CAAA,ED2TJ,QC3TI;EAA2B;;;EAWvB,IAAA,KAAA,CAAA,CAAA,EAAA,OAAA;EAMf;;;;;;;;+BDgUyB;8BACD;+BACC;kCACG,cAAc;sCACV,cAAc;qCACf,cAAc;;;;;;;;;;;AAhXnD;UCqBU,mBAAA,CDZY;EA4UT;;;;EAyBmC,IAAA,EAAA,CAAA,MAAA,ECpV/B,cDoV+B,EAAA,GCpVZ,ODoVY,CCpVJ,QDoVI,CAAA;EAAd;;;;EAEG,MAAA,CAAA,EAAA,GAAA,GAAA,IAAA;EAhXP;;;aCqCjB;;AAzBO;;;cA+Bd,aAjB8B,EAiBf,mBAjBe"}
@@ -158,23 +158,16 @@ Object.defineProperty(exports, '__esModule', { value: true });
158
158
  * Main entry point for Commerce Engine Checkout integration.
159
159
  * Manages iframe lifecycle, event handling, and provides public API.
160
160
  */
161
+ /** Checkout URLs by environment */
162
+ const CHECKOUT_URLS = {
163
+ production: "https://checkout.commercengine.com",
164
+ staging: "https://staging.checkout.commercengine.com"
165
+ };
161
166
  /**
162
- * Resolve checkout URL based on current environment
163
- *
164
- * The environment is determined by where the script was loaded from:
165
- * - js.commercengine.com → production checkout
166
- * - js.staging.commercengine.com → staging checkout
167
- * - localhost → local development
167
+ * Build checkout iframe URL
168
168
  */
169
- function resolveCheckoutUrl(storeId, apiKey) {
170
- const scripts = document.querySelectorAll("script[src*=\"commercengine\"]");
171
- let isStaging = false;
172
- for (const script of scripts) if ((script.getAttribute("src") || "").includes("staging.commercengine.com")) {
173
- isStaging = true;
174
- break;
175
- }
176
- if (typeof window !== "undefined" && window.location.hostname === "localhost") return `http://localhost:8080?store_id=${storeId}&api_key=${apiKey}&mode=iframe`;
177
- return `${isStaging ? "https://checkout.staging.commercengine.com" : "https://checkout.commercengine.com"}?store_id=${storeId}&api_key=${apiKey}&mode=iframe`;
169
+ function buildCheckoutUrl(storeId, apiKey, environment) {
170
+ return `${CHECKOUT_URLS[environment]}?store_id=${storeId}&api_key=${apiKey}&environment=${environment}&mode=iframe`;
178
171
  }
179
172
  var Checkout = class extends EventEmitter {
180
173
  constructor(config) {
@@ -195,12 +188,15 @@ Object.defineProperty(exports, '__esModule', { value: true });
195
188
  initialize() {
196
189
  let baseUrl;
197
190
  if (this.config.url) baseUrl = this.config.url;
198
- else if (this.config.storeId && this.config.apiKey) baseUrl = resolveCheckoutUrl(this.config.storeId, this.config.apiKey);
199
- else throw new Error("[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided.");
191
+ else if (this.config.storeId && this.config.apiKey) {
192
+ const env = this.config.environment || "production";
193
+ baseUrl = buildCheckoutUrl(this.config.storeId, this.config.apiKey, env);
194
+ } else throw new Error("[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided.");
200
195
  const url = new URL(baseUrl);
201
196
  url.searchParams.set("mode", "iframe");
202
197
  if (this.config.storeId && !url.searchParams.has("store_id")) url.searchParams.set("store_id", this.config.storeId);
203
198
  if (this.config.apiKey && !url.searchParams.has("api_key")) url.searchParams.set("api_key", this.config.apiKey);
199
+ if (this.config.environment && !url.searchParams.has("environment")) url.searchParams.set("environment", this.config.environment);
204
200
  if (this.config.theme) url.searchParams.set("theme", this.config.theme);
205
201
  if (this.config.authMode) url.searchParams.set("auth_mode", this.config.authMode);
206
202
  if (this.config.accessToken) url.searchParams.set("token", this.config.accessToken);
@@ -441,7 +437,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
441
437
  *
442
438
  * @example CDN Usage (Vanilla JS):
443
439
  * ```html
444
- * <script src="https://js.commercengine.com/v1.js" async><\/script>
440
+ * <script src="https://cdn.commercengine.com/v1.js" async><\/script>
445
441
  * <script>
446
442
  * window.Commercengine.onLoad = async () => {
447
443
  * const checkout = await Commercengine.init({
@@ -1 +1 @@
1
- {"version":3,"file":"index.iife.js","names":["onceListener: EventListener<T>","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.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 EventListener,\n IncomingEventType,\n OrderData,\n} from \"./types\";\n\n// =============================================================================\n// URL RESOLUTION\n// =============================================================================\n\n/**\n * Resolve checkout URL based on current environment\n *\n * The environment is determined by where the script was loaded from:\n * - js.commercengine.com → production checkout\n * - js.staging.commercengine.com → staging checkout\n * - localhost → local development\n */\nfunction resolveCheckoutUrl(storeId: string, apiKey: string): string {\n // Detect environment from script source\n const scripts = document.querySelectorAll('script[src*=\"commercengine\"]');\n let isStaging = false;\n\n for (const script of scripts) {\n const src = script.getAttribute(\"src\") || \"\";\n if (src.includes(\"staging.commercengine.com\")) {\n isStaging = true;\n break;\n }\n }\n\n // For local development, use localhost\n if (typeof window !== \"undefined\" && window.location.hostname === \"localhost\") {\n return `http://localhost:8080?store_id=${storeId}&api_key=${apiKey}&mode=iframe`;\n }\n\n // Determine checkout URL based on environment\n const baseUrl = isStaging\n ? \"https://checkout.staging.commercengine.com\"\n : \"https://checkout.commercengine.com\";\n\n return `${baseUrl}?store_id=${storeId}&api_key=${apiKey}&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 // Resolve from credentials\n baseUrl = resolveCheckoutUrl(this.config.storeId, this.config.apiKey);\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\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://js.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 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.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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"}
package/dist/index.mjs CHANGED
@@ -154,23 +154,16 @@ var IframeManager = class {
154
154
  * Main entry point for Commerce Engine Checkout integration.
155
155
  * Manages iframe lifecycle, event handling, and provides public API.
156
156
  */
157
+ /** Checkout URLs by environment */
158
+ const CHECKOUT_URLS = {
159
+ production: "https://checkout.commercengine.com",
160
+ staging: "https://staging.checkout.commercengine.com"
161
+ };
157
162
  /**
158
- * Resolve checkout URL based on current environment
159
- *
160
- * The environment is determined by where the script was loaded from:
161
- * - js.commercengine.com → production checkout
162
- * - js.staging.commercengine.com → staging checkout
163
- * - localhost → local development
163
+ * Build checkout iframe URL
164
164
  */
165
- function resolveCheckoutUrl(storeId, apiKey) {
166
- const scripts = document.querySelectorAll("script[src*=\"commercengine\"]");
167
- let isStaging = false;
168
- for (const script of scripts) if ((script.getAttribute("src") || "").includes("staging.commercengine.com")) {
169
- isStaging = true;
170
- break;
171
- }
172
- if (typeof window !== "undefined" && window.location.hostname === "localhost") return `http://localhost:8080?store_id=${storeId}&api_key=${apiKey}&mode=iframe`;
173
- return `${isStaging ? "https://checkout.staging.commercengine.com" : "https://checkout.commercengine.com"}?store_id=${storeId}&api_key=${apiKey}&mode=iframe`;
165
+ function buildCheckoutUrl(storeId, apiKey, environment) {
166
+ return `${CHECKOUT_URLS[environment]}?store_id=${storeId}&api_key=${apiKey}&environment=${environment}&mode=iframe`;
174
167
  }
175
168
  var Checkout = class extends EventEmitter {
176
169
  constructor(config) {
@@ -191,12 +184,15 @@ var Checkout = class extends EventEmitter {
191
184
  initialize() {
192
185
  let baseUrl;
193
186
  if (this.config.url) baseUrl = this.config.url;
194
- else if (this.config.storeId && this.config.apiKey) baseUrl = resolveCheckoutUrl(this.config.storeId, this.config.apiKey);
195
- else throw new Error("[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided.");
187
+ else if (this.config.storeId && this.config.apiKey) {
188
+ const env = this.config.environment || "production";
189
+ baseUrl = buildCheckoutUrl(this.config.storeId, this.config.apiKey, env);
190
+ } else throw new Error("[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided.");
196
191
  const url = new URL(baseUrl);
197
192
  url.searchParams.set("mode", "iframe");
198
193
  if (this.config.storeId && !url.searchParams.has("store_id")) url.searchParams.set("store_id", this.config.storeId);
199
194
  if (this.config.apiKey && !url.searchParams.has("api_key")) url.searchParams.set("api_key", this.config.apiKey);
195
+ if (this.config.environment && !url.searchParams.has("environment")) url.searchParams.set("environment", this.config.environment);
200
196
  if (this.config.theme) url.searchParams.set("theme", this.config.theme);
201
197
  if (this.config.authMode) url.searchParams.set("auth_mode", this.config.authMode);
202
198
  if (this.config.accessToken) url.searchParams.set("token", this.config.accessToken);
@@ -437,7 +433,7 @@ var Checkout = class extends EventEmitter {
437
433
  *
438
434
  * @example CDN Usage (Vanilla JS):
439
435
  * ```html
440
- * <script src="https://js.commercengine.com/v1.js" async><\/script>
436
+ * <script src="https://cdn.commercengine.com/v1.js" async><\/script>
441
437
  * <script>
442
438
  * window.Commercengine.onLoad = async () => {
443
439
  * const checkout = await Commercengine.init({
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["onceListener: EventListener<T>","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.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 EventListener,\n IncomingEventType,\n OrderData,\n} from \"./types\";\n\n// =============================================================================\n// URL RESOLUTION\n// =============================================================================\n\n/**\n * Resolve checkout URL based on current environment\n *\n * The environment is determined by where the script was loaded from:\n * - js.commercengine.com → production checkout\n * - js.staging.commercengine.com → staging checkout\n * - localhost → local development\n */\nfunction resolveCheckoutUrl(storeId: string, apiKey: string): string {\n // Detect environment from script source\n const scripts = document.querySelectorAll('script[src*=\"commercengine\"]');\n let isStaging = false;\n\n for (const script of scripts) {\n const src = script.getAttribute(\"src\") || \"\";\n if (src.includes(\"staging.commercengine.com\")) {\n isStaging = true;\n break;\n }\n }\n\n // For local development, use localhost\n if (typeof window !== \"undefined\" && window.location.hostname === \"localhost\") {\n return `http://localhost:8080?store_id=${storeId}&api_key=${apiKey}&mode=iframe`;\n }\n\n // Determine checkout URL based on environment\n const baseUrl = isStaging\n ? \"https://checkout.staging.commercengine.com\"\n : \"https://checkout.commercengine.com\";\n\n return `${baseUrl}?store_id=${storeId}&api_key=${apiKey}&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 // Resolve from credentials\n baseUrl = resolveCheckoutUrl(this.config.storeId, this.config.apiKey);\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\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://js.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 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.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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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@commercengine/js",
3
- "version": "0.1.0",
3
+ "version": "0.2.0",
4
4
  "description": "Commerce Engine Checkout - Embeddable checkout SDK",
5
5
  "type": "module",
6
6
  "main": "dist/index.cjs",
@@ -42,6 +42,7 @@
42
42
  "homepage": "https://docs.commercengine.io",
43
43
  "scripts": {
44
44
  "dev": "tsdown --watch",
45
- "build": "tsdown"
45
+ "build": "tsdown && pnpm run build:cdn",
46
+ "build:cdn": "mkdir -p cdn && cp dist/index.iife.js cdn/v1.js"
46
47
  }
47
48
  }