@commercengine/js 0.4.0 → 0.5.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 +36 -22
- package/dist/index.d.mts +27 -22
- package/dist/index.d.mts.map +1 -1
- package/dist/index.iife.js +19 -7
- package/dist/index.iife.js.map +1 -1
- package/dist/index.mjs +19 -7
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -80,9 +80,9 @@ interface CheckoutConfig {
|
|
|
80
80
|
onClose?: () => void;
|
|
81
81
|
onComplete?: (order: OrderData) => void;
|
|
82
82
|
onCartUpdate?: (cart: CartData) => void;
|
|
83
|
+
onTokensUpdated?: (data: AuthTokensUpdatedData) => void;
|
|
83
84
|
onLogin?: (data: AuthLoginData) => void;
|
|
84
85
|
onLogout?: (data: AuthLogoutData) => void;
|
|
85
|
-
onTokenRefresh?: (data: AuthRefreshData) => void;
|
|
86
86
|
onSessionError?: () => void;
|
|
87
87
|
onError?: (error: ErrorData) => void;
|
|
88
88
|
}
|
|
@@ -110,14 +110,16 @@ const checkout = await Commercengine.init({
|
|
|
110
110
|
onCartUpdate: (cart) => {
|
|
111
111
|
updateCartBadge(cart.count);
|
|
112
112
|
},
|
|
113
|
-
|
|
114
|
-
//
|
|
113
|
+
onTokensUpdated: ({ accessToken, refreshToken }) => {
|
|
114
|
+
// Tokens changed - sync to your auth system (fires on every token change)
|
|
115
|
+
saveUserSession(accessToken, refreshToken);
|
|
116
|
+
},
|
|
117
|
+
onLogin: ({ user, loginMethod }) => {
|
|
118
|
+
// User logged in via checkout - update UI
|
|
115
119
|
console.log("User logged in via", loginMethod, ":", user?.email || user?.phone);
|
|
116
|
-
saveUserSession(accessToken);
|
|
117
120
|
},
|
|
118
121
|
onLogout: () => {
|
|
119
|
-
// User logged out -
|
|
120
|
-
clearUserSession();
|
|
122
|
+
// User logged out - update UI for anonymous state
|
|
121
123
|
},
|
|
122
124
|
onSessionError: () => {
|
|
123
125
|
// Session corrupted - SDK cleared all tokens, user needs to re-authenticate
|
|
@@ -199,6 +201,18 @@ const cart = checkout.getCart();
|
|
|
199
201
|
console.log(cart.count, cart.total, cart.currency);
|
|
200
202
|
```
|
|
201
203
|
|
|
204
|
+
### `getTokens()`
|
|
205
|
+
|
|
206
|
+
Get the latest token payload seen by the SDK from `auth:tokens-updated`.
|
|
207
|
+
This avoids race conditions when `.on("auth:tokens-updated")` is attached after `init()`.
|
|
208
|
+
Returns `null` if no token update event has been received yet.
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
const checkout = await Commercengine.init({ ... });
|
|
212
|
+
const tokens = checkout.getTokens();
|
|
213
|
+
// tokens.accessToken, tokens.refreshToken
|
|
214
|
+
```
|
|
215
|
+
|
|
202
216
|
### `destroy()`
|
|
203
217
|
|
|
204
218
|
Remove checkout iframe and cleanup event listeners.
|
|
@@ -227,9 +241,9 @@ Subscribe via callbacks (in config) or the event emitter.
|
|
|
227
241
|
| `close` | `onClose` | - | All drawers closed |
|
|
228
242
|
| `complete` | `onComplete` | `OrderData` | Order placed successfully |
|
|
229
243
|
| `cart:updated` | `onCartUpdate` | `CartData` | Cart state changed |
|
|
230
|
-
| `auth:
|
|
231
|
-
| `auth:
|
|
232
|
-
| `auth:
|
|
244
|
+
| `auth:tokens-updated` | `onTokensUpdated` | `AuthTokensUpdatedData` | Tokens changed (any reason) |
|
|
245
|
+
| `auth:login` | `onLogin` | `AuthLoginData` | User logged in (semantic) |
|
|
246
|
+
| `auth:logout` | `onLogout` | `AuthLogoutData` | User logged out (semantic) |
|
|
233
247
|
| `auth:session-error` | `onSessionError` | - | Session corrupted, tokens cleared |
|
|
234
248
|
| `error` | `onError` | `ErrorData` | Configuration error |
|
|
235
249
|
|
|
@@ -247,24 +261,20 @@ interface CartData {
|
|
|
247
261
|
currency: string; // Currency code (e.g., "USD", "INR")
|
|
248
262
|
}
|
|
249
263
|
|
|
250
|
-
interface
|
|
264
|
+
interface AuthTokensUpdatedData {
|
|
251
265
|
accessToken: string;
|
|
252
266
|
refreshToken?: string;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
interface AuthLoginData {
|
|
253
270
|
user?: UserInfo;
|
|
254
271
|
loginMethod?: LoginMethod;
|
|
255
272
|
}
|
|
256
273
|
|
|
257
274
|
interface AuthLogoutData {
|
|
258
|
-
accessToken: string; // New anonymous token
|
|
259
|
-
refreshToken?: string;
|
|
260
275
|
user?: UserInfo; // Anonymous user info
|
|
261
276
|
}
|
|
262
277
|
|
|
263
|
-
interface AuthRefreshData {
|
|
264
|
-
accessToken: string;
|
|
265
|
-
refreshToken?: string;
|
|
266
|
-
}
|
|
267
|
-
|
|
268
278
|
interface ErrorData {
|
|
269
279
|
message: string;
|
|
270
280
|
}
|
|
@@ -325,10 +335,14 @@ User will be prompted to login within checkout (WhatsApp, Phone, or Email OTP).
|
|
|
325
335
|
const checkout = await Commercengine.init({
|
|
326
336
|
storeId: "store_xxx",
|
|
327
337
|
apiKey: "ak_xxx",
|
|
328
|
-
|
|
329
|
-
//
|
|
338
|
+
onTokensUpdated: ({ accessToken, refreshToken }) => {
|
|
339
|
+
// Sync tokens to your auth system (fires on login, logout, refresh)
|
|
330
340
|
saveSession({ accessToken, refreshToken });
|
|
331
341
|
},
|
|
342
|
+
onLogin: ({ user, loginMethod }) => {
|
|
343
|
+
// Update UI with user info
|
|
344
|
+
console.log("Logged in via", loginMethod, ":", user?.email);
|
|
345
|
+
},
|
|
332
346
|
});
|
|
333
347
|
```
|
|
334
348
|
|
|
@@ -389,9 +403,9 @@ import {
|
|
|
389
403
|
type Environment,
|
|
390
404
|
type CartData,
|
|
391
405
|
type OrderData,
|
|
406
|
+
type AuthTokensUpdatedData,
|
|
392
407
|
type AuthLoginData,
|
|
393
408
|
type AuthLogoutData,
|
|
394
|
-
type AuthRefreshData,
|
|
395
409
|
type ErrorData,
|
|
396
410
|
type UserInfo,
|
|
397
411
|
type Channel,
|
|
@@ -424,8 +438,8 @@ Features (loyalty, coupons, etc.) and drawer direction are configured via Checko
|
|
|
424
438
|
|
|
425
439
|
### Auth Modes
|
|
426
440
|
|
|
427
|
-
- **`managed`** (default): Checkout manages token lifecycle with auto-refresh
|
|
428
|
-
- **`provided
|
|
441
|
+
- **`managed`** (default): Checkout manages token lifecycle with auto-refresh. Best for lightweight integrations (marketing pages, buy-now buttons)
|
|
442
|
+
- **`provided`** (recommended for storefront apps): Parent manages tokens via storefront SDK, checkout uses in-memory only. Tokens are synced via `updateTokens()` and `onTokensUpdated`
|
|
429
443
|
|
|
430
444
|
### Session Modes
|
|
431
445
|
|
package/dist/index.d.mts
CHANGED
|
@@ -234,17 +234,18 @@ interface CheckoutConfig {
|
|
|
234
234
|
*/
|
|
235
235
|
onCartUpdate?: (cart: CartData) => void;
|
|
236
236
|
/**
|
|
237
|
-
* Called
|
|
237
|
+
* Called whenever tokens change (anonymous auth, login, logout, refresh)
|
|
238
|
+
* Single source of truth for token sync between checkout and parent
|
|
238
239
|
*/
|
|
239
|
-
|
|
240
|
+
onTokensUpdated?: (data: AuthTokensUpdatedData) => void;
|
|
240
241
|
/**
|
|
241
|
-
* Called when user logs
|
|
242
|
+
* Called when user logs in via checkout (semantic — user info only, no tokens)
|
|
242
243
|
*/
|
|
243
|
-
|
|
244
|
+
onLogin?: (data: AuthLoginData) => void;
|
|
244
245
|
/**
|
|
245
|
-
* Called when
|
|
246
|
+
* Called when user logs out (semantic — user info only, no tokens)
|
|
246
247
|
*/
|
|
247
|
-
|
|
248
|
+
onLogout?: (data: AuthLogoutData) => void;
|
|
248
249
|
/**
|
|
249
250
|
* Called when session is corrupted and SDK clears all tokens.
|
|
250
251
|
* User needs to re-authenticate.
|
|
@@ -312,28 +313,25 @@ interface OrderData {
|
|
|
312
313
|
orderNumber: string;
|
|
313
314
|
}
|
|
314
315
|
/**
|
|
315
|
-
* Data emitted on
|
|
316
|
+
* Data emitted on auth:tokens-updated event
|
|
317
|
+
* Universal token event — fires on every token change (anonymous auth, login, logout, refresh)
|
|
316
318
|
*/
|
|
317
|
-
interface
|
|
319
|
+
interface AuthTokensUpdatedData {
|
|
318
320
|
accessToken: string;
|
|
319
321
|
refreshToken?: string;
|
|
320
|
-
user?: UserInfo;
|
|
321
|
-
loginMethod?: LoginMethod;
|
|
322
322
|
}
|
|
323
323
|
/**
|
|
324
|
-
* Data emitted on
|
|
324
|
+
* Data emitted on login event (semantic only — no tokens)
|
|
325
325
|
*/
|
|
326
|
-
interface
|
|
327
|
-
accessToken: string;
|
|
328
|
-
refreshToken?: string;
|
|
326
|
+
interface AuthLoginData {
|
|
329
327
|
user?: UserInfo;
|
|
328
|
+
loginMethod?: LoginMethod;
|
|
330
329
|
}
|
|
331
330
|
/**
|
|
332
|
-
* Data emitted on
|
|
331
|
+
* Data emitted on logout event (semantic only — no tokens)
|
|
333
332
|
*/
|
|
334
|
-
interface
|
|
335
|
-
|
|
336
|
-
refreshToken?: string;
|
|
333
|
+
interface AuthLogoutData {
|
|
334
|
+
user?: UserInfo;
|
|
337
335
|
}
|
|
338
336
|
/**
|
|
339
337
|
* Error data emitted on checkout:error event
|
|
@@ -345,7 +343,7 @@ interface ErrorData {
|
|
|
345
343
|
/**
|
|
346
344
|
* Events emitted by checkout
|
|
347
345
|
*/
|
|
348
|
-
type CheckoutEventType = "ready" | "open" | "close" | "complete" | "error" | "cart:updated" | "auth:
|
|
346
|
+
type CheckoutEventType = "ready" | "open" | "close" | "complete" | "error" | "cart:updated" | "auth:tokens-updated" | "auth:login" | "auth:logout" | "auth:session-error";
|
|
349
347
|
/**
|
|
350
348
|
* Event listener function type
|
|
351
349
|
*/
|
|
@@ -383,6 +381,7 @@ declare class Checkout extends EventEmitter {
|
|
|
383
381
|
private isReady;
|
|
384
382
|
private isOpen;
|
|
385
383
|
private cartState;
|
|
384
|
+
private tokenState;
|
|
386
385
|
private boundMessageHandler;
|
|
387
386
|
private hasQuickBuyParams;
|
|
388
387
|
constructor(config: CheckoutConfig);
|
|
@@ -399,9 +398,9 @@ declare class Checkout extends EventEmitter {
|
|
|
399
398
|
private handleClose;
|
|
400
399
|
private handleComplete;
|
|
401
400
|
private handleCartUpdate;
|
|
401
|
+
private handleTokensUpdated;
|
|
402
402
|
private handleLogin;
|
|
403
403
|
private handleLogout;
|
|
404
|
-
private handleTokenRefresh;
|
|
405
404
|
private handleSessionError;
|
|
406
405
|
/**
|
|
407
406
|
* Open the cart drawer
|
|
@@ -432,6 +431,12 @@ declare class Checkout extends EventEmitter {
|
|
|
432
431
|
* Get current cart state
|
|
433
432
|
*/
|
|
434
433
|
getCart(): CartData;
|
|
434
|
+
/**
|
|
435
|
+
* Get current tokens (null if no tokens received yet)
|
|
436
|
+
* Use this after init() resolves to read tokens without race conditions.
|
|
437
|
+
* For ongoing changes, subscribe to "auth:tokens-updated" event.
|
|
438
|
+
*/
|
|
439
|
+
getTokens(): AuthTokensUpdatedData | null;
|
|
435
440
|
/**
|
|
436
441
|
* Check if checkout is ready
|
|
437
442
|
*/
|
|
@@ -449,9 +454,9 @@ declare class Checkout extends EventEmitter {
|
|
|
449
454
|
on(event: "close", listener: EventListener<void>): void;
|
|
450
455
|
on(event: "complete", listener: EventListener<OrderData>): void;
|
|
451
456
|
on(event: "cart:updated", listener: EventListener<CartData>): void;
|
|
457
|
+
on(event: "auth:tokens-updated", listener: EventListener<AuthTokensUpdatedData>): void;
|
|
452
458
|
on(event: "auth:login", listener: EventListener<AuthLoginData>): void;
|
|
453
459
|
on(event: "auth:logout", listener: EventListener<AuthLogoutData>): void;
|
|
454
|
-
on(event: "auth:refresh", listener: EventListener<AuthRefreshData>): void;
|
|
455
460
|
on(event: "auth:session-error", listener: EventListener<void>): void;
|
|
456
461
|
/**
|
|
457
462
|
* Destroy the checkout instance
|
|
@@ -485,5 +490,5 @@ interface CommercengineGlobal {
|
|
|
485
490
|
*/
|
|
486
491
|
declare const Commercengine: CommercengineGlobal;
|
|
487
492
|
//#endregion
|
|
488
|
-
export { type AddToCartItem, type AuthLoginData, type AuthLogoutData, type AuthMode, type
|
|
493
|
+
export { type AddToCartItem, type AuthLoginData, type AuthLogoutData, type AuthMode, type AuthTokensUpdatedData, type CartData, type Channel, Checkout, type CheckoutConfig, type CheckoutEventType, type CheckoutFeatures, Commercengine, Commercengine as default, type DrawerDirection, type DrawerDirectionConfig, type Environment, type ErrorData, type LoginMethod, type OrderData, type QuickBuyConfig, type SessionMode, type ThemeMode, type UserInfo };
|
|
489
494
|
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.d.mts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/events.ts","../src/checkout.ts","../src/index.ts"],"mappings":";;AAkBA;;;;;AASA;;;;;;;KATY,QAAA;;AA+BZ;;UAtBiB,aAAA;EAsBc;;;EAlB7B,SAAA;EAiCQ;;AAYV;EAxCE,SAAA;;;;AAkDF;EA5CE,QAAA;AAAA;;;;;UAOe,cAAA;EAkEf;;;EA9DA,SAAA;EA2EmB;;;EAtEnB,SAAA;EAgFU;;;;EA1EV,QAAA;AAAA;;;;;;KAYU,WAAA;;;;AA0FZ;UAhFiB,gBAAA;;;;AA4FjB;EAvFE,OAAA;;;;;EAMA,OAAA;EAgLc;;;;EA1Kd,cAAA;
|
|
1
|
+
{"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/events.ts","../src/checkout.ts","../src/index.ts"],"mappings":"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"}
|
package/dist/index.iife.js
CHANGED
|
@@ -192,6 +192,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
|
|
|
192
192
|
total: 0,
|
|
193
193
|
currency: "INR"
|
|
194
194
|
};
|
|
195
|
+
this.tokenState = null;
|
|
195
196
|
this.hasQuickBuyParams = false;
|
|
196
197
|
this.config = config;
|
|
197
198
|
this.iframe = new IframeManager();
|
|
@@ -292,15 +293,15 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
|
|
|
292
293
|
case "cart:updated":
|
|
293
294
|
this.handleCartUpdate(data);
|
|
294
295
|
break;
|
|
296
|
+
case "auth:tokens-updated":
|
|
297
|
+
this.handleTokensUpdated(data);
|
|
298
|
+
break;
|
|
295
299
|
case "auth:login":
|
|
296
300
|
this.handleLogin(data);
|
|
297
301
|
break;
|
|
298
302
|
case "auth:logout":
|
|
299
303
|
this.handleLogout(data);
|
|
300
304
|
break;
|
|
301
|
-
case "auth:refresh":
|
|
302
|
-
this.handleTokenRefresh(data);
|
|
303
|
-
break;
|
|
304
305
|
case "auth:session-error":
|
|
305
306
|
this.handleSessionError();
|
|
306
307
|
break;
|
|
@@ -338,6 +339,11 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
|
|
|
338
339
|
this.config.onCartUpdate?.(data);
|
|
339
340
|
this.emit("cart:updated", data);
|
|
340
341
|
}
|
|
342
|
+
handleTokensUpdated(data) {
|
|
343
|
+
this.tokenState = data;
|
|
344
|
+
this.config.onTokensUpdated?.(data);
|
|
345
|
+
this.emit("auth:tokens-updated", data);
|
|
346
|
+
}
|
|
341
347
|
handleLogin(data) {
|
|
342
348
|
this.config.onLogin?.(data);
|
|
343
349
|
this.emit("auth:login", data);
|
|
@@ -346,11 +352,8 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
|
|
|
346
352
|
this.config.onLogout?.(data);
|
|
347
353
|
this.emit("auth:logout", data);
|
|
348
354
|
}
|
|
349
|
-
handleTokenRefresh(data) {
|
|
350
|
-
this.config.onTokenRefresh?.(data);
|
|
351
|
-
this.emit("auth:refresh", data);
|
|
352
|
-
}
|
|
353
355
|
handleSessionError() {
|
|
356
|
+
this.tokenState = null;
|
|
354
357
|
this.config.onSessionError?.();
|
|
355
358
|
this.emit("auth:session-error");
|
|
356
359
|
}
|
|
@@ -426,6 +429,14 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
|
|
|
426
429
|
return { ...this.cartState };
|
|
427
430
|
}
|
|
428
431
|
/**
|
|
432
|
+
* Get current tokens (null if no tokens received yet)
|
|
433
|
+
* Use this after init() resolves to read tokens without race conditions.
|
|
434
|
+
* For ongoing changes, subscribe to "auth:tokens-updated" event.
|
|
435
|
+
*/
|
|
436
|
+
getTokens() {
|
|
437
|
+
return this.tokenState ? { ...this.tokenState } : null;
|
|
438
|
+
}
|
|
439
|
+
/**
|
|
429
440
|
* Check if checkout is ready
|
|
430
441
|
*/
|
|
431
442
|
get ready() {
|
|
@@ -450,6 +461,7 @@ Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toString
|
|
|
450
461
|
this.removeAllListeners();
|
|
451
462
|
this.isReady = false;
|
|
452
463
|
this.isOpen = false;
|
|
464
|
+
this.tokenState = null;
|
|
453
465
|
}
|
|
454
466
|
};
|
|
455
467
|
|
package/dist/index.iife.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.iife.js","names":[],"sources":["../src/events.ts","../src/iframe-manager.ts","../src/checkout.ts","../src/index.ts"],"sourcesContent":["/**\n * Simple Event Emitter\n *\n * Provides pub/sub functionality for checkout events.\n */\n\nimport type { CheckoutEventType, EventListener } from \"./types\";\n\nexport class EventEmitter {\n private listeners: Map<CheckoutEventType, Set<EventListener>> = new Map();\n\n /**\n * Subscribe to an event\n */\n on<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)?.add(listener as EventListener);\n }\n\n /**\n * Unsubscribe from an event\n */\n off<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n const eventListeners = this.listeners.get(event);\n if (eventListeners) {\n eventListeners.delete(listener as EventListener);\n }\n }\n\n /**\n * Subscribe to an event (once)\n */\n once<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n const onceListener: EventListener<T> = (data) => {\n this.off(event, onceListener);\n listener(data);\n };\n this.on(event, onceListener);\n }\n\n /**\n * Emit an event to all subscribers\n */\n protected emit<T = unknown>(event: CheckoutEventType, data?: T): void {\n const eventListeners = this.listeners.get(event);\n if (eventListeners) {\n for (const listener of eventListeners) {\n try {\n listener(data);\n } catch (error) {\n // biome-ignore lint/suspicious/noConsole: Intentional error logging\n console.error(`[Commercengine] Error in ${event} listener:`, error);\n }\n }\n }\n }\n\n /**\n * Remove all listeners\n */\n protected removeAllListeners(): void {\n this.listeners.clear();\n }\n}\n","/**\n * Iframe Manager\n *\n * Handles iframe creation, lifecycle, and visibility management.\n * The iframe is pre-mounted (hidden) on init and shown on demand.\n */\n\nimport type { OutgoingEventType } from \"./types\";\n\nexport class IframeManager {\n private iframe: HTMLIFrameElement | null = null;\n private container: HTMLDivElement | null = null;\n private checkoutOrigin: string | null = null;\n\n /**\n * Create and mount the iframe (hidden initially)\n */\n create(url: string, zIndex: number): void {\n if (this.container) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Iframe already created\");\n return;\n }\n\n // Store origin for postMessage security\n this.checkoutOrigin = new URL(url).origin;\n\n // Create container (covers viewport, initially non-interactive)\n this.container = document.createElement(\"div\");\n this.container.id = \"commercengine-checkout\";\n this.container.style.cssText = `\n position: fixed;\n inset: 0;\n z-index: ${zIndex};\n pointer-events: none;\n `;\n\n // Create iframe\n this.iframe = document.createElement(\"iframe\");\n this.iframe.id = \"commercengine-checkout-iframe\";\n this.iframe.title = \"Shopping cart and checkout\";\n this.iframe.src = url;\n this.iframe.allow = \"payment\";\n this.iframe.style.cssText = `\n width: 100%;\n height: 100%;\n border: none;\n pointer-events: inherit;\n background-color: transparent;\n `;\n\n this.container.appendChild(this.iframe);\n document.body.appendChild(this.container);\n }\n\n /**\n * Show the checkout overlay (enable pointer events + backdrop)\n * Backdrop provides consistent dark overlay with blur, matching css-drawer's backdrop.\n * This ensures the brand site is obscured even during payment gateway redirects.\n */\n show(): void {\n if (this.container) {\n this.container.style.pointerEvents = \"auto\";\n this.container.style.background = \"hsl(0 0% 0% / 0.4)\";\n this.container.style.backdropFilter = \"blur(4px)\";\n // @ts-expect-error - webkitBackdropFilter for Safari support\n this.container.style.webkitBackdropFilter = \"blur(4px)\";\n document.body.style.overflow = \"hidden\";\n }\n }\n\n /**\n * Hide the checkout overlay (disable pointer events + remove backdrop)\n */\n hide(): void {\n if (this.container) {\n this.container.style.pointerEvents = \"none\";\n this.container.style.background = \"\";\n this.container.style.backdropFilter = \"\";\n // @ts-expect-error - webkitBackdropFilter for Safari support\n this.container.style.webkitBackdropFilter = \"\";\n document.body.style.overflow = \"\";\n }\n }\n\n /**\n * Check if iframe is visible\n */\n isVisible(): boolean {\n return this.container?.style.pointerEvents === \"auto\";\n }\n\n /**\n * Send a message to the checkout iframe\n */\n postMessage(type: OutgoingEventType, data?: unknown): void {\n if (!this.iframe?.contentWindow) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Cannot send message - iframe not ready\");\n return;\n }\n\n // Use specific origin for security - never use \"*\"\n if (!this.checkoutOrigin) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning for security\n console.warn(\"[Commercengine] Cannot send message - checkout origin not established\");\n return;\n }\n this.iframe.contentWindow.postMessage({ type, data }, this.checkoutOrigin);\n }\n\n /**\n * Get the checkout origin for message validation\n */\n getCheckoutOrigin(): string | null {\n return this.checkoutOrigin;\n }\n\n /**\n * Destroy the iframe and clean up\n */\n destroy(): void {\n if (this.container) {\n this.container.remove();\n this.container = null;\n }\n this.iframe = null;\n this.checkoutOrigin = null;\n\n // Restore body scroll\n document.body.style.overflow = \"\";\n }\n}\n","/**\n * Checkout Class\n *\n * Main entry point for Commerce Engine Checkout integration.\n * Manages iframe lifecycle, event handling, and provides public API.\n */\n\nimport { EventEmitter } from \"./events\";\nimport { IframeManager } from \"./iframe-manager\";\nimport type {\n AuthLoginData,\n AuthLogoutData,\n AuthRefreshData,\n CartData,\n CheckoutConfig,\n CheckoutEventType,\n Environment,\n EventListener,\n IncomingEventType,\n OrderData,\n} from \"./types\";\n\n// =============================================================================\n// URL RESOLUTION\n// =============================================================================\n\n/**\n * Build subdomain-based checkout base URL.\n * Each store gets its own origin for localStorage isolation.\n */\nfunction getCheckoutBaseUrl(storeId: string, environment: Environment): string {\n if (environment === \"staging\") {\n return `https://${storeId}.staging.checkout.commercengine.com`;\n }\n return `https://${storeId}.checkout.commercengine.com`;\n}\n\n/**\n * Build checkout iframe URL with store_id as subdomain.\n * Environment is encoded in the hostname — no need for a query param.\n */\nfunction buildCheckoutUrl(storeId: string, apiKey: string, environment: Environment): string {\n const baseUrl = getCheckoutBaseUrl(storeId, environment);\n return `${baseUrl}?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 // Inject storeId + environment as subdomain: store.staging.localhost / store.prod.localhost\n const parsedUrl = new URL(this.config.url);\n if (this.config.storeId && parsedUrl.hostname === \"localhost\") {\n const env = this.config.environment || \"staging\";\n const envPrefix = env === \"production\" ? \"prod\" : \"staging\";\n parsedUrl.hostname = `${this.config.storeId}.${envPrefix}.localhost`;\n }\n baseUrl = parsedUrl.toString();\n } else if (this.config.storeId && this.config.apiKey) {\n // Build URL from credentials + environment (store_id as subdomain)\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 api_key as URL param (store_id and environment are in the subdomain)\n if (this.config.apiKey && !url.searchParams.has(\"api_key\")) {\n url.searchParams.set(\"api_key\", this.config.apiKey);\n }\n\n // Add theme (only per-session override still passed as URL param;\n // drawerDirection and features are configured via Config API)\n if (this.config.theme) {\n url.searchParams.set(\"theme\", this.config.theme);\n }\n\n // Add auth config\n if (this.config.authMode) {\n url.searchParams.set(\"auth_mode\", this.config.authMode);\n }\n if (this.config.accessToken) {\n url.searchParams.set(\"token\", this.config.accessToken);\n }\n if (this.config.refreshToken) {\n url.searchParams.set(\"refresh_token\", this.config.refreshToken);\n }\n\n // Auto-detect quick buy params from parent URL (if enabled and no explicit quickBuy)\n let quickBuy = this.config.quickBuy;\n let sessionMode = this.config.sessionMode;\n\n if (this.config.autoDetectQuickBuy && !quickBuy) {\n const parentParams = new URLSearchParams(window.location.search);\n const productId = parentParams.get(\"product_id\");\n const variantId = parentParams.get(\"variant_id\");\n\n if (productId) {\n quickBuy = {\n productId,\n variantId: variantId ?? null,\n quantity: parseInt(parentParams.get(\"qty\") || parentParams.get(\"quantity\") || \"1\", 10),\n };\n\n // Also check for session_mode in parent URL\n const parentSessionMode = parentParams.get(\"session_mode\");\n if (parentSessionMode === \"force-new\" || parentSessionMode === \"continue-existing\") {\n sessionMode = parentSessionMode;\n }\n\n // Clean quick buy params from URL to prevent duplicate adds on refresh\n // Deferred to next tick to handle React StrictMode double-mounting\n setTimeout(() => this.cleanQuickBuyParamsFromUrl(), 0);\n }\n }\n\n // Add quick buy params\n if (quickBuy) {\n this.hasQuickBuyParams = true;\n url.searchParams.set(\"product_id\", quickBuy.productId);\n if (quickBuy.variantId) {\n url.searchParams.set(\"variant_id\", quickBuy.variantId);\n }\n if (quickBuy.quantity && quickBuy.quantity !== 1) {\n url.searchParams.set(\"qty\", String(quickBuy.quantity));\n }\n }\n\n // Add session mode\n if (sessionMode) {\n url.searchParams.set(\"session_mode\", sessionMode);\n }\n\n // Pass parent origin for postMessage security\n // This allows the iframe to validate incoming messages and send responses to the correct origin\n if (typeof window !== \"undefined\") {\n url.searchParams.set(\"parent_origin\", window.location.origin);\n }\n\n // Create iframe (hidden initially, will show when ready + opened)\n const zIndex = this.config.appearance?.zIndex ?? 99999;\n this.iframe.create(url.toString(), zIndex);\n\n // Listen for messages from checkout iframe\n window.addEventListener(\"message\", this.boundMessageHandler);\n }\n\n // ===========================================================================\n // URL CLEANUP\n // ===========================================================================\n\n /**\n * Remove quick buy params from the parent URL to prevent duplicate adds on refresh\n * Uses replaceState to avoid adding to browser history\n */\n private cleanQuickBuyParamsFromUrl(): void {\n if (typeof window === \"undefined\") return;\n\n const url = new URL(window.location.href);\n const paramsToRemove = [\"product_id\", \"variant_id\", \"qty\", \"quantity\", \"session_mode\"];\n\n let hasChanges = false;\n for (const param of paramsToRemove) {\n if (url.searchParams.has(param)) {\n url.searchParams.delete(param);\n hasChanges = true;\n }\n }\n\n if (hasChanges) {\n window.history.replaceState({}, \"\", url.toString());\n }\n }\n\n // ===========================================================================\n // MESSAGE HANDLING\n // ===========================================================================\n\n private handleMessage(event: MessageEvent): void {\n // Validate origin for security - reject if no expected origin or mismatch\n const expectedOrigin = this.iframe.getCheckoutOrigin();\n if (!expectedOrigin || event.origin !== expectedOrigin) {\n return;\n }\n\n const { type, data } = event.data || {};\n if (!type) return;\n\n switch (type as IncomingEventType) {\n case \"checkout:ready\":\n this.handleReady();\n break;\n\n case \"checkout:open\":\n this.handleOpen();\n break;\n\n case \"checkout:close\":\n this.handleClose();\n break;\n\n case \"checkout:complete\":\n this.handleComplete(data as OrderData);\n break;\n\n case \"checkout:error\":\n this.handleError(data as { message: string });\n break;\n\n case \"cart:updated\":\n this.handleCartUpdate(data as CartData);\n break;\n\n case \"auth:login\":\n this.handleLogin(data as AuthLoginData);\n break;\n\n case \"auth:logout\":\n this.handleLogout(data as AuthLogoutData);\n break;\n\n case \"auth:refresh\":\n this.handleTokenRefresh(data as AuthRefreshData);\n break;\n\n case \"auth:session-error\":\n this.handleSessionError();\n break;\n }\n }\n\n private handleReady(): void {\n this.isReady = true;\n this.config.onReady?.();\n this.emit(\"ready\");\n\n // Auto-open cart if we have quick buy params (from config or auto-detected URL)\n if (this.hasQuickBuyParams) {\n this.openCart();\n }\n }\n\n private handleError(data: { message: string }): void {\n // Set ready so user can still interact (they'll see the error drawer)\n this.isReady = true;\n this.config.onError?.(data);\n this.emit(\"error\", data);\n }\n\n private handleOpen(): void {\n this.iframe.show();\n this.isOpen = true;\n this.config.onOpen?.();\n this.emit(\"open\");\n }\n\n private handleClose(): void {\n this.iframe.hide();\n this.isOpen = false;\n this.config.onClose?.();\n this.emit(\"close\");\n }\n\n private handleComplete(data: OrderData): void {\n this.config.onComplete?.(data);\n this.emit(\"complete\", data);\n }\n\n private handleCartUpdate(data: CartData): void {\n this.cartState = data;\n this.config.onCartUpdate?.(data);\n this.emit(\"cart:updated\", data);\n }\n\n private handleLogin(data: AuthLoginData): void {\n this.config.onLogin?.(data);\n this.emit(\"auth:login\", data);\n }\n\n private handleLogout(data: AuthLogoutData): void {\n this.config.onLogout?.(data);\n this.emit(\"auth:logout\", data);\n }\n\n private handleTokenRefresh(data: AuthRefreshData): void {\n this.config.onTokenRefresh?.(data);\n this.emit(\"auth:refresh\", data);\n }\n\n private handleSessionError(): void {\n this.config.onSessionError?.();\n this.emit(\"auth:session-error\");\n }\n\n // ===========================================================================\n // PUBLIC API\n // ===========================================================================\n\n /**\n * Open the cart drawer\n */\n openCart(): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.show();\n this.isOpen = true;\n this.iframe.postMessage(\"checkout:open-cart\");\n }\n\n /**\n * Open the checkout drawer directly (e.g., for Buy Now flow)\n */\n openCheckout(): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.show();\n this.isOpen = true;\n this.iframe.postMessage(\"checkout:open-checkout\");\n }\n\n /**\n * Close the checkout overlay\n */\n close(): void {\n this.iframe.hide();\n this.isOpen = false;\n this.iframe.postMessage(\"checkout:close\");\n }\n\n /**\n * Update authentication tokens\n * Use this when user logs in/out on the parent site\n */\n updateTokens(accessToken: string, refreshToken?: string): void {\n if (!this.isReady) {\n // Store for when ready\n this.config.accessToken = accessToken;\n this.config.refreshToken = refreshToken;\n return;\n }\n\n this.iframe.postMessage(\"checkout:update-tokens\", { accessToken, refreshToken });\n }\n\n /**\n * Add an item to cart and open the cart drawer\n *\n * @param productId - Product ID (required)\n * @param variantId - Variant ID (required, null for non-variant products)\n * @param quantity - Quantity to add (default: 1)\n */\n addToCart(productId: string, variantId: string | null, quantity = 1): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.postMessage(\"checkout:add-item\", {\n productId,\n variantId,\n quantity,\n });\n }\n\n /**\n * Get current cart state\n */\n getCart(): CartData {\n return { ...this.cartState };\n }\n\n /**\n * Check if checkout is ready\n */\n get ready(): boolean {\n return this.isReady;\n }\n\n /**\n * Check if checkout overlay is currently open\n */\n get open(): boolean {\n return this.isOpen;\n }\n\n /**\n * Subscribe to an event\n * @override EventEmitter.on with typed overloads\n */\n on(event: \"ready\", listener: EventListener<void>): void;\n on(event: \"open\", listener: EventListener<void>): void;\n on(event: \"close\", listener: EventListener<void>): void;\n on(event: \"complete\", listener: EventListener<OrderData>): void;\n on(event: \"cart:updated\", listener: EventListener<CartData>): void;\n on(event: \"auth:login\", listener: EventListener<AuthLoginData>): void;\n on(event: \"auth:logout\", listener: EventListener<AuthLogoutData>): void;\n on(event: \"auth:refresh\", listener: EventListener<AuthRefreshData>): void;\n on(event: \"auth:session-error\", listener: EventListener<void>): void;\n on<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n super.on(event, listener);\n }\n\n /**\n * Destroy the checkout instance\n * Removes iframe and cleans up event listeners\n */\n destroy(): void {\n window.removeEventListener(\"message\", this.boundMessageHandler);\n this.iframe.destroy();\n this.removeAllListeners();\n this.isReady = false;\n this.isOpen = false;\n }\n}\n","/**\n * Commerce Engine Checkout\n *\n * @commercengine/js - Embeddable checkout SDK\n *\n * @example CDN Usage (Vanilla JS):\n * ```html\n * <script src=\"https://cdn.commercengine.com/v1.js\" async></script>\n * <script>\n * window.Commercengine.onLoad = async () => {\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * theme: \"dark\",\n * onComplete: (order) => console.log(\"Order:\", order.orderNumber),\n * });\n *\n * document.getElementById(\"cart-btn\").onclick = () => checkout.openCart();\n * };\n * </script>\n * ```\n *\n * @example ESM Import:\n * ```typescript\n * import { Commercengine } from \"@commercengine/js\";\n *\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * });\n *\n * checkout.on(\"cart:updated\", (cart) => updateBadge(cart.count));\n * checkout.openCart();\n * ```\n */\n\nimport { Checkout } from \"./checkout\";\nimport type { CheckoutConfig } from \"./types\";\n\n// Re-export types for consumers\nexport type {\n AddToCartItem,\n AuthLoginData,\n AuthLogoutData,\n AuthMode,\n AuthRefreshData,\n CartData,\n Channel,\n CheckoutConfig,\n CheckoutEventType,\n CheckoutFeatures,\n DrawerDirection,\n DrawerDirectionConfig,\n Environment,\n ErrorData,\n LoginMethod,\n OrderData,\n QuickBuyConfig,\n SessionMode,\n ThemeMode,\n UserInfo,\n} from \"./types\";\n\nexport { Checkout };\n\n// =============================================================================\n// GLOBAL API\n// =============================================================================\n\n/**\n * Global Commercengine object interface\n */\ninterface CommercengineGlobal {\n /**\n * Initialize checkout with configuration\n * @returns Promise that resolves to Checkout instance\n */\n init: (config: CheckoutConfig) => Promise<Checkout>;\n\n /**\n * Callback invoked when script has loaded\n * Set this BEFORE including the script to be notified when ready\n */\n onLoad?: () => void;\n\n /**\n * Current checkout instance (if initialized)\n */\n instance?: Checkout;\n}\n\n/**\n * Global Commercengine namespace\n */\nconst Commercengine: CommercengineGlobal = {\n /**\n * Initialize checkout\n *\n * @param config - Checkout configuration\n * @returns Checkout instance\n *\n * @example\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * theme: \"system\",\n * });\n */\n init: async (config: CheckoutConfig): Promise<Checkout> => {\n // Validate required fields - either url (for dev) or storeId+apiKey (for prod)\n if (!config.url && (!config.storeId || !config.apiKey)) {\n throw new Error(\n \"[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided\"\n );\n }\n\n // Destroy existing instance if any\n if (Commercengine.instance) {\n Commercengine.instance.destroy();\n }\n\n // Create new instance\n const checkout = new Checkout(config);\n Commercengine.instance = checkout;\n\n // Wait for ready or error with timeout\n return new Promise((resolve, reject) => {\n if (checkout.ready) {\n resolve(checkout);\n return;\n }\n\n const timeoutMs = 30000; // 30 second timeout\n const timeout = setTimeout(() => {\n reject(new Error(\"Checkout initialization timed out\"));\n }, timeoutMs);\n\n // Resolve on ready\n checkout.once(\"ready\", () => {\n clearTimeout(timeout);\n resolve(checkout);\n });\n\n // Also resolve on error (checkout is still usable, will show error drawer)\n checkout.once(\"error\", () => {\n clearTimeout(timeout);\n resolve(checkout);\n });\n });\n },\n};\n\n// =============================================================================\n// GLOBAL SETUP\n// =============================================================================\n\n// Expose on window for CDN usage\nif (typeof window !== \"undefined\") {\n // Preserve any existing onLoad callback set before script loaded\n const existingOnLoad = (window as unknown as { Commercengine?: CommercengineGlobal })\n .Commercengine?.onLoad;\n\n // Set global\n (window as unknown as { Commercengine: CommercengineGlobal }).Commercengine = Commercengine;\n\n // Restore and call onLoad if it was set\n if (existingOnLoad) {\n Commercengine.onLoad = existingOnLoad;\n try {\n existingOnLoad();\n } catch (error) {\n // biome-ignore lint/suspicious/noConsole: Intentional error logging\n console.error(\"[Commercengine] Error in onLoad callback:\", error);\n }\n }\n}\n\nexport { Commercengine };\nexport default Commercengine;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.iife.js","names":[],"sources":["../src/events.ts","../src/iframe-manager.ts","../src/checkout.ts","../src/index.ts"],"sourcesContent":["/**\n * Simple Event Emitter\n *\n * Provides pub/sub functionality for checkout events.\n */\n\nimport type { CheckoutEventType, EventListener } from \"./types\";\n\nexport class EventEmitter {\n private listeners: Map<CheckoutEventType, Set<EventListener>> = new Map();\n\n /**\n * Subscribe to an event\n */\n on<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)?.add(listener as EventListener);\n }\n\n /**\n * Unsubscribe from an event\n */\n off<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n const eventListeners = this.listeners.get(event);\n if (eventListeners) {\n eventListeners.delete(listener as EventListener);\n }\n }\n\n /**\n * Subscribe to an event (once)\n */\n once<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n const onceListener: EventListener<T> = (data) => {\n this.off(event, onceListener);\n listener(data);\n };\n this.on(event, onceListener);\n }\n\n /**\n * Emit an event to all subscribers\n */\n protected emit<T = unknown>(event: CheckoutEventType, data?: T): void {\n const eventListeners = this.listeners.get(event);\n if (eventListeners) {\n for (const listener of eventListeners) {\n try {\n listener(data);\n } catch (error) {\n // biome-ignore lint/suspicious/noConsole: Intentional error logging\n console.error(`[Commercengine] Error in ${event} listener:`, error);\n }\n }\n }\n }\n\n /**\n * Remove all listeners\n */\n protected removeAllListeners(): void {\n this.listeners.clear();\n }\n}\n","/**\n * Iframe Manager\n *\n * Handles iframe creation, lifecycle, and visibility management.\n * The iframe is pre-mounted (hidden) on init and shown on demand.\n */\n\nimport type { OutgoingEventType } from \"./types\";\n\nexport class IframeManager {\n private iframe: HTMLIFrameElement | null = null;\n private container: HTMLDivElement | null = null;\n private checkoutOrigin: string | null = null;\n\n /**\n * Create and mount the iframe (hidden initially)\n */\n create(url: string, zIndex: number): void {\n if (this.container) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Iframe already created\");\n return;\n }\n\n // Store origin for postMessage security\n this.checkoutOrigin = new URL(url).origin;\n\n // Create container (covers viewport, initially non-interactive)\n this.container = document.createElement(\"div\");\n this.container.id = \"commercengine-checkout\";\n this.container.style.cssText = `\n position: fixed;\n inset: 0;\n z-index: ${zIndex};\n pointer-events: none;\n `;\n\n // Create iframe\n this.iframe = document.createElement(\"iframe\");\n this.iframe.id = \"commercengine-checkout-iframe\";\n this.iframe.title = \"Shopping cart and checkout\";\n this.iframe.src = url;\n this.iframe.allow = \"payment\";\n this.iframe.style.cssText = `\n width: 100%;\n height: 100%;\n border: none;\n pointer-events: inherit;\n background-color: transparent;\n `;\n\n this.container.appendChild(this.iframe);\n document.body.appendChild(this.container);\n }\n\n /**\n * Show the checkout overlay (enable pointer events + backdrop)\n * Backdrop provides consistent dark overlay with blur, matching css-drawer's backdrop.\n * This ensures the brand site is obscured even during payment gateway redirects.\n */\n show(): void {\n if (this.container) {\n this.container.style.pointerEvents = \"auto\";\n this.container.style.background = \"hsl(0 0% 0% / 0.4)\";\n this.container.style.backdropFilter = \"blur(4px)\";\n // @ts-expect-error - webkitBackdropFilter for Safari support\n this.container.style.webkitBackdropFilter = \"blur(4px)\";\n document.body.style.overflow = \"hidden\";\n }\n }\n\n /**\n * Hide the checkout overlay (disable pointer events + remove backdrop)\n */\n hide(): void {\n if (this.container) {\n this.container.style.pointerEvents = \"none\";\n this.container.style.background = \"\";\n this.container.style.backdropFilter = \"\";\n // @ts-expect-error - webkitBackdropFilter for Safari support\n this.container.style.webkitBackdropFilter = \"\";\n document.body.style.overflow = \"\";\n }\n }\n\n /**\n * Check if iframe is visible\n */\n isVisible(): boolean {\n return this.container?.style.pointerEvents === \"auto\";\n }\n\n /**\n * Send a message to the checkout iframe\n */\n postMessage(type: OutgoingEventType, data?: unknown): void {\n if (!this.iframe?.contentWindow) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Cannot send message - iframe not ready\");\n return;\n }\n\n // Use specific origin for security - never use \"*\"\n if (!this.checkoutOrigin) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning for security\n console.warn(\"[Commercengine] Cannot send message - checkout origin not established\");\n return;\n }\n this.iframe.contentWindow.postMessage({ type, data }, this.checkoutOrigin);\n }\n\n /**\n * Get the checkout origin for message validation\n */\n getCheckoutOrigin(): string | null {\n return this.checkoutOrigin;\n }\n\n /**\n * Destroy the iframe and clean up\n */\n destroy(): void {\n if (this.container) {\n this.container.remove();\n this.container = null;\n }\n this.iframe = null;\n this.checkoutOrigin = null;\n\n // Restore body scroll\n document.body.style.overflow = \"\";\n }\n}\n","/**\n * Checkout Class\n *\n * Main entry point for Commerce Engine Checkout integration.\n * Manages iframe lifecycle, event handling, and provides public API.\n */\n\nimport { EventEmitter } from \"./events\";\nimport { IframeManager } from \"./iframe-manager\";\nimport type {\n AuthLoginData,\n AuthLogoutData,\n AuthTokensUpdatedData,\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/**\n * Build subdomain-based checkout base URL.\n * Each store gets its own origin for localStorage isolation.\n */\nfunction getCheckoutBaseUrl(storeId: string, environment: Environment): string {\n if (environment === \"staging\") {\n return `https://${storeId}.staging.checkout.commercengine.com`;\n }\n return `https://${storeId}.checkout.commercengine.com`;\n}\n\n/**\n * Build checkout iframe URL with store_id as subdomain.\n * Environment is encoded in the hostname — no need for a query param.\n */\nfunction buildCheckoutUrl(storeId: string, apiKey: string, environment: Environment): string {\n const baseUrl = getCheckoutBaseUrl(storeId, environment);\n return `${baseUrl}?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 tokenState: AuthTokensUpdatedData | null = null;\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 // Inject storeId + environment as subdomain: store.staging.localhost / store.prod.localhost\n const parsedUrl = new URL(this.config.url);\n if (this.config.storeId && parsedUrl.hostname === \"localhost\") {\n const env = this.config.environment || \"staging\";\n const envPrefix = env === \"production\" ? \"prod\" : \"staging\";\n parsedUrl.hostname = `${this.config.storeId}.${envPrefix}.localhost`;\n }\n baseUrl = parsedUrl.toString();\n } else if (this.config.storeId && this.config.apiKey) {\n // Build URL from credentials + environment (store_id as subdomain)\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 api_key as URL param (store_id and environment are in the subdomain)\n if (this.config.apiKey && !url.searchParams.has(\"api_key\")) {\n url.searchParams.set(\"api_key\", this.config.apiKey);\n }\n\n // Add theme (only per-session override still passed as URL param;\n // drawerDirection and features are configured via Config API)\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:tokens-updated\":\n this.handleTokensUpdated(data as AuthTokensUpdatedData);\n break;\n\n case \"auth:login\":\n this.handleLogin(data as AuthLoginData);\n break;\n\n case \"auth:logout\":\n this.handleLogout(data as AuthLogoutData);\n break;\n\n case \"auth:session-error\":\n this.handleSessionError();\n break;\n }\n }\n\n private handleReady(): void {\n this.isReady = true;\n this.config.onReady?.();\n this.emit(\"ready\");\n\n // Auto-open cart if we have quick buy params (from config or auto-detected URL)\n if (this.hasQuickBuyParams) {\n this.openCart();\n }\n }\n\n private handleError(data: { message: string }): void {\n // Set ready so user can still interact (they'll see the error drawer)\n this.isReady = true;\n this.config.onError?.(data);\n this.emit(\"error\", data);\n }\n\n private handleOpen(): void {\n this.iframe.show();\n this.isOpen = true;\n this.config.onOpen?.();\n this.emit(\"open\");\n }\n\n private handleClose(): void {\n this.iframe.hide();\n this.isOpen = false;\n this.config.onClose?.();\n this.emit(\"close\");\n }\n\n private handleComplete(data: OrderData): void {\n this.config.onComplete?.(data);\n this.emit(\"complete\", data);\n }\n\n private handleCartUpdate(data: CartData): void {\n this.cartState = data;\n this.config.onCartUpdate?.(data);\n this.emit(\"cart:updated\", data);\n }\n\n private handleTokensUpdated(data: AuthTokensUpdatedData): void {\n this.tokenState = data;\n this.config.onTokensUpdated?.(data);\n this.emit(\"auth:tokens-updated\", data);\n }\n\n private handleLogin(data: AuthLoginData): void {\n this.config.onLogin?.(data);\n this.emit(\"auth:login\", data);\n }\n\n private handleLogout(data: AuthLogoutData): void {\n this.config.onLogout?.(data);\n this.emit(\"auth:logout\", data);\n }\n\n private handleSessionError(): void {\n this.tokenState = null;\n this.config.onSessionError?.();\n this.emit(\"auth:session-error\");\n }\n\n // ===========================================================================\n // PUBLIC API\n // ===========================================================================\n\n /**\n * Open the cart drawer\n */\n openCart(): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.show();\n this.isOpen = true;\n this.iframe.postMessage(\"checkout:open-cart\");\n }\n\n /**\n * Open the checkout drawer directly (e.g., for Buy Now flow)\n */\n openCheckout(): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.show();\n this.isOpen = true;\n this.iframe.postMessage(\"checkout:open-checkout\");\n }\n\n /**\n * Close the checkout overlay\n */\n close(): void {\n this.iframe.hide();\n this.isOpen = false;\n this.iframe.postMessage(\"checkout:close\");\n }\n\n /**\n * Update authentication tokens\n * Use this when user logs in/out on the parent site\n */\n updateTokens(accessToken: string, refreshToken?: string): void {\n if (!this.isReady) {\n // Store for when ready\n this.config.accessToken = accessToken;\n this.config.refreshToken = refreshToken;\n return;\n }\n\n this.iframe.postMessage(\"checkout:update-tokens\", { accessToken, refreshToken });\n }\n\n /**\n * Add an item to cart and open the cart drawer\n *\n * @param productId - Product ID (required)\n * @param variantId - Variant ID (required, null for non-variant products)\n * @param quantity - Quantity to add (default: 1)\n */\n addToCart(productId: string, variantId: string | null, quantity = 1): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.postMessage(\"checkout:add-item\", {\n productId,\n variantId,\n quantity,\n });\n }\n\n /**\n * Get current cart state\n */\n getCart(): CartData {\n return { ...this.cartState };\n }\n\n /**\n * Get current tokens (null if no tokens received yet)\n * Use this after init() resolves to read tokens without race conditions.\n * For ongoing changes, subscribe to \"auth:tokens-updated\" event.\n */\n getTokens(): AuthTokensUpdatedData | null {\n return this.tokenState ? { ...this.tokenState } : null;\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:tokens-updated\", listener: EventListener<AuthTokensUpdatedData>): void;\n on(event: \"auth:login\", listener: EventListener<AuthLoginData>): void;\n on(event: \"auth:logout\", listener: EventListener<AuthLogoutData>): void;\n on(event: \"auth:session-error\", listener: EventListener<void>): void;\n on<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n super.on(event, listener);\n }\n\n /**\n * Destroy the checkout instance\n * Removes iframe and cleans up event listeners\n */\n destroy(): void {\n window.removeEventListener(\"message\", this.boundMessageHandler);\n this.iframe.destroy();\n this.removeAllListeners();\n this.isReady = false;\n this.isOpen = false;\n this.tokenState = null;\n }\n}\n","/**\n * Commerce Engine Checkout\n *\n * @commercengine/js - Embeddable checkout SDK\n *\n * @example CDN Usage (Vanilla JS):\n * ```html\n * <script src=\"https://cdn.commercengine.com/v1.js\" async></script>\n * <script>\n * window.Commercengine.onLoad = async () => {\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * theme: \"dark\",\n * onComplete: (order) => console.log(\"Order:\", order.orderNumber),\n * });\n *\n * document.getElementById(\"cart-btn\").onclick = () => checkout.openCart();\n * };\n * </script>\n * ```\n *\n * @example ESM Import:\n * ```typescript\n * import { Commercengine } from \"@commercengine/js\";\n *\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * });\n *\n * checkout.on(\"cart:updated\", (cart) => updateBadge(cart.count));\n * checkout.openCart();\n * ```\n */\n\nimport { Checkout } from \"./checkout\";\nimport type { CheckoutConfig } from \"./types\";\n\n// Re-export types for consumers\nexport type {\n AddToCartItem,\n AuthLoginData,\n AuthLogoutData,\n AuthMode,\n AuthTokensUpdatedData,\n CartData,\n Channel,\n CheckoutConfig,\n CheckoutEventType,\n CheckoutFeatures,\n DrawerDirection,\n DrawerDirectionConfig,\n Environment,\n ErrorData,\n LoginMethod,\n OrderData,\n QuickBuyConfig,\n SessionMode,\n ThemeMode,\n UserInfo,\n} from \"./types\";\n\nexport { Checkout };\n\n// =============================================================================\n// GLOBAL API\n// =============================================================================\n\n/**\n * Global Commercengine object interface\n */\ninterface CommercengineGlobal {\n /**\n * Initialize checkout with configuration\n * @returns Promise that resolves to Checkout instance\n */\n init: (config: CheckoutConfig) => Promise<Checkout>;\n\n /**\n * Callback invoked when script has loaded\n * Set this BEFORE including the script to be notified when ready\n */\n onLoad?: () => void;\n\n /**\n * Current checkout instance (if initialized)\n */\n instance?: Checkout;\n}\n\n/**\n * Global Commercengine namespace\n */\nconst Commercengine: CommercengineGlobal = {\n /**\n * Initialize checkout\n *\n * @param config - Checkout configuration\n * @returns Checkout instance\n *\n * @example\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * theme: \"system\",\n * });\n */\n init: async (config: CheckoutConfig): Promise<Checkout> => {\n // Validate required fields - either url (for dev) or storeId+apiKey (for prod)\n if (!config.url && (!config.storeId || !config.apiKey)) {\n throw new Error(\n \"[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided\"\n );\n }\n\n // Destroy existing instance if any\n if (Commercengine.instance) {\n Commercengine.instance.destroy();\n }\n\n // Create new instance\n const checkout = new Checkout(config);\n Commercengine.instance = checkout;\n\n // Wait for ready or error with timeout\n return new Promise((resolve, reject) => {\n if (checkout.ready) {\n resolve(checkout);\n return;\n }\n\n const timeoutMs = 30000; // 30 second timeout\n const timeout = setTimeout(() => {\n reject(new Error(\"Checkout initialization timed out\"));\n }, timeoutMs);\n\n // Resolve on ready\n checkout.once(\"ready\", () => {\n clearTimeout(timeout);\n resolve(checkout);\n });\n\n // Also resolve on error (checkout is still usable, will show error drawer)\n checkout.once(\"error\", () => {\n clearTimeout(timeout);\n resolve(checkout);\n });\n });\n },\n};\n\n// =============================================================================\n// GLOBAL SETUP\n// =============================================================================\n\n// Expose on window for CDN usage\nif (typeof window !== \"undefined\") {\n // Preserve any existing onLoad callback set before script loaded\n const existingOnLoad = (window as unknown as { Commercengine?: CommercengineGlobal })\n .Commercengine?.onLoad;\n\n // Set global\n (window as unknown as { Commercengine: CommercengineGlobal }).Commercengine = Commercengine;\n\n // Restore and call onLoad if it was set\n if (existingOnLoad) {\n Commercengine.onLoad = existingOnLoad;\n try {\n existingOnLoad();\n } catch (error) {\n // biome-ignore lint/suspicious/noConsole: Intentional error logging\n console.error(\"[Commercengine] Error in onLoad callback:\", error);\n }\n }\n}\n\nexport { Commercengine };\nexport default Commercengine;\n"],"mappings":"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"}
|
package/dist/index.mjs
CHANGED
|
@@ -188,6 +188,7 @@ var Checkout = class extends EventEmitter {
|
|
|
188
188
|
total: 0,
|
|
189
189
|
currency: "INR"
|
|
190
190
|
};
|
|
191
|
+
this.tokenState = null;
|
|
191
192
|
this.hasQuickBuyParams = false;
|
|
192
193
|
this.config = config;
|
|
193
194
|
this.iframe = new IframeManager();
|
|
@@ -288,15 +289,15 @@ var Checkout = class extends EventEmitter {
|
|
|
288
289
|
case "cart:updated":
|
|
289
290
|
this.handleCartUpdate(data);
|
|
290
291
|
break;
|
|
292
|
+
case "auth:tokens-updated":
|
|
293
|
+
this.handleTokensUpdated(data);
|
|
294
|
+
break;
|
|
291
295
|
case "auth:login":
|
|
292
296
|
this.handleLogin(data);
|
|
293
297
|
break;
|
|
294
298
|
case "auth:logout":
|
|
295
299
|
this.handleLogout(data);
|
|
296
300
|
break;
|
|
297
|
-
case "auth:refresh":
|
|
298
|
-
this.handleTokenRefresh(data);
|
|
299
|
-
break;
|
|
300
301
|
case "auth:session-error":
|
|
301
302
|
this.handleSessionError();
|
|
302
303
|
break;
|
|
@@ -334,6 +335,11 @@ var Checkout = class extends EventEmitter {
|
|
|
334
335
|
this.config.onCartUpdate?.(data);
|
|
335
336
|
this.emit("cart:updated", data);
|
|
336
337
|
}
|
|
338
|
+
handleTokensUpdated(data) {
|
|
339
|
+
this.tokenState = data;
|
|
340
|
+
this.config.onTokensUpdated?.(data);
|
|
341
|
+
this.emit("auth:tokens-updated", data);
|
|
342
|
+
}
|
|
337
343
|
handleLogin(data) {
|
|
338
344
|
this.config.onLogin?.(data);
|
|
339
345
|
this.emit("auth:login", data);
|
|
@@ -342,11 +348,8 @@ var Checkout = class extends EventEmitter {
|
|
|
342
348
|
this.config.onLogout?.(data);
|
|
343
349
|
this.emit("auth:logout", data);
|
|
344
350
|
}
|
|
345
|
-
handleTokenRefresh(data) {
|
|
346
|
-
this.config.onTokenRefresh?.(data);
|
|
347
|
-
this.emit("auth:refresh", data);
|
|
348
|
-
}
|
|
349
351
|
handleSessionError() {
|
|
352
|
+
this.tokenState = null;
|
|
350
353
|
this.config.onSessionError?.();
|
|
351
354
|
this.emit("auth:session-error");
|
|
352
355
|
}
|
|
@@ -422,6 +425,14 @@ var Checkout = class extends EventEmitter {
|
|
|
422
425
|
return { ...this.cartState };
|
|
423
426
|
}
|
|
424
427
|
/**
|
|
428
|
+
* Get current tokens (null if no tokens received yet)
|
|
429
|
+
* Use this after init() resolves to read tokens without race conditions.
|
|
430
|
+
* For ongoing changes, subscribe to "auth:tokens-updated" event.
|
|
431
|
+
*/
|
|
432
|
+
getTokens() {
|
|
433
|
+
return this.tokenState ? { ...this.tokenState } : null;
|
|
434
|
+
}
|
|
435
|
+
/**
|
|
425
436
|
* Check if checkout is ready
|
|
426
437
|
*/
|
|
427
438
|
get ready() {
|
|
@@ -446,6 +457,7 @@ var Checkout = class extends EventEmitter {
|
|
|
446
457
|
this.removeAllListeners();
|
|
447
458
|
this.isReady = false;
|
|
448
459
|
this.isOpen = false;
|
|
460
|
+
this.tokenState = null;
|
|
449
461
|
}
|
|
450
462
|
};
|
|
451
463
|
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/events.ts","../src/iframe-manager.ts","../src/checkout.ts","../src/index.ts"],"sourcesContent":["/**\n * Simple Event Emitter\n *\n * Provides pub/sub functionality for checkout events.\n */\n\nimport type { CheckoutEventType, EventListener } from \"./types\";\n\nexport class EventEmitter {\n private listeners: Map<CheckoutEventType, Set<EventListener>> = new Map();\n\n /**\n * Subscribe to an event\n */\n on<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)?.add(listener as EventListener);\n }\n\n /**\n * Unsubscribe from an event\n */\n off<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n const eventListeners = this.listeners.get(event);\n if (eventListeners) {\n eventListeners.delete(listener as EventListener);\n }\n }\n\n /**\n * Subscribe to an event (once)\n */\n once<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n const onceListener: EventListener<T> = (data) => {\n this.off(event, onceListener);\n listener(data);\n };\n this.on(event, onceListener);\n }\n\n /**\n * Emit an event to all subscribers\n */\n protected emit<T = unknown>(event: CheckoutEventType, data?: T): void {\n const eventListeners = this.listeners.get(event);\n if (eventListeners) {\n for (const listener of eventListeners) {\n try {\n listener(data);\n } catch (error) {\n // biome-ignore lint/suspicious/noConsole: Intentional error logging\n console.error(`[Commercengine] Error in ${event} listener:`, error);\n }\n }\n }\n }\n\n /**\n * Remove all listeners\n */\n protected removeAllListeners(): void {\n this.listeners.clear();\n }\n}\n","/**\n * Iframe Manager\n *\n * Handles iframe creation, lifecycle, and visibility management.\n * The iframe is pre-mounted (hidden) on init and shown on demand.\n */\n\nimport type { OutgoingEventType } from \"./types\";\n\nexport class IframeManager {\n private iframe: HTMLIFrameElement | null = null;\n private container: HTMLDivElement | null = null;\n private checkoutOrigin: string | null = null;\n\n /**\n * Create and mount the iframe (hidden initially)\n */\n create(url: string, zIndex: number): void {\n if (this.container) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Iframe already created\");\n return;\n }\n\n // Store origin for postMessage security\n this.checkoutOrigin = new URL(url).origin;\n\n // Create container (covers viewport, initially non-interactive)\n this.container = document.createElement(\"div\");\n this.container.id = \"commercengine-checkout\";\n this.container.style.cssText = `\n position: fixed;\n inset: 0;\n z-index: ${zIndex};\n pointer-events: none;\n `;\n\n // Create iframe\n this.iframe = document.createElement(\"iframe\");\n this.iframe.id = \"commercengine-checkout-iframe\";\n this.iframe.title = \"Shopping cart and checkout\";\n this.iframe.src = url;\n this.iframe.allow = \"payment\";\n this.iframe.style.cssText = `\n width: 100%;\n height: 100%;\n border: none;\n pointer-events: inherit;\n background-color: transparent;\n `;\n\n this.container.appendChild(this.iframe);\n document.body.appendChild(this.container);\n }\n\n /**\n * Show the checkout overlay (enable pointer events + backdrop)\n * Backdrop provides consistent dark overlay with blur, matching css-drawer's backdrop.\n * This ensures the brand site is obscured even during payment gateway redirects.\n */\n show(): void {\n if (this.container) {\n this.container.style.pointerEvents = \"auto\";\n this.container.style.background = \"hsl(0 0% 0% / 0.4)\";\n this.container.style.backdropFilter = \"blur(4px)\";\n // @ts-expect-error - webkitBackdropFilter for Safari support\n this.container.style.webkitBackdropFilter = \"blur(4px)\";\n document.body.style.overflow = \"hidden\";\n }\n }\n\n /**\n * Hide the checkout overlay (disable pointer events + remove backdrop)\n */\n hide(): void {\n if (this.container) {\n this.container.style.pointerEvents = \"none\";\n this.container.style.background = \"\";\n this.container.style.backdropFilter = \"\";\n // @ts-expect-error - webkitBackdropFilter for Safari support\n this.container.style.webkitBackdropFilter = \"\";\n document.body.style.overflow = \"\";\n }\n }\n\n /**\n * Check if iframe is visible\n */\n isVisible(): boolean {\n return this.container?.style.pointerEvents === \"auto\";\n }\n\n /**\n * Send a message to the checkout iframe\n */\n postMessage(type: OutgoingEventType, data?: unknown): void {\n if (!this.iframe?.contentWindow) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Cannot send message - iframe not ready\");\n return;\n }\n\n // Use specific origin for security - never use \"*\"\n if (!this.checkoutOrigin) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning for security\n console.warn(\"[Commercengine] Cannot send message - checkout origin not established\");\n return;\n }\n this.iframe.contentWindow.postMessage({ type, data }, this.checkoutOrigin);\n }\n\n /**\n * Get the checkout origin for message validation\n */\n getCheckoutOrigin(): string | null {\n return this.checkoutOrigin;\n }\n\n /**\n * Destroy the iframe and clean up\n */\n destroy(): void {\n if (this.container) {\n this.container.remove();\n this.container = null;\n }\n this.iframe = null;\n this.checkoutOrigin = null;\n\n // Restore body scroll\n document.body.style.overflow = \"\";\n }\n}\n","/**\n * Checkout Class\n *\n * Main entry point for Commerce Engine Checkout integration.\n * Manages iframe lifecycle, event handling, and provides public API.\n */\n\nimport { EventEmitter } from \"./events\";\nimport { IframeManager } from \"./iframe-manager\";\nimport type {\n AuthLoginData,\n AuthLogoutData,\n AuthRefreshData,\n CartData,\n CheckoutConfig,\n CheckoutEventType,\n Environment,\n EventListener,\n IncomingEventType,\n OrderData,\n} from \"./types\";\n\n// =============================================================================\n// URL RESOLUTION\n// =============================================================================\n\n/**\n * Build subdomain-based checkout base URL.\n * Each store gets its own origin for localStorage isolation.\n */\nfunction getCheckoutBaseUrl(storeId: string, environment: Environment): string {\n if (environment === \"staging\") {\n return `https://${storeId}.staging.checkout.commercengine.com`;\n }\n return `https://${storeId}.checkout.commercengine.com`;\n}\n\n/**\n * Build checkout iframe URL with store_id as subdomain.\n * Environment is encoded in the hostname — no need for a query param.\n */\nfunction buildCheckoutUrl(storeId: string, apiKey: string, environment: Environment): string {\n const baseUrl = getCheckoutBaseUrl(storeId, environment);\n return `${baseUrl}?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 // Inject storeId + environment as subdomain: store.staging.localhost / store.prod.localhost\n const parsedUrl = new URL(this.config.url);\n if (this.config.storeId && parsedUrl.hostname === \"localhost\") {\n const env = this.config.environment || \"staging\";\n const envPrefix = env === \"production\" ? \"prod\" : \"staging\";\n parsedUrl.hostname = `${this.config.storeId}.${envPrefix}.localhost`;\n }\n baseUrl = parsedUrl.toString();\n } else if (this.config.storeId && this.config.apiKey) {\n // Build URL from credentials + environment (store_id as subdomain)\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 api_key as URL param (store_id and environment are in the subdomain)\n if (this.config.apiKey && !url.searchParams.has(\"api_key\")) {\n url.searchParams.set(\"api_key\", this.config.apiKey);\n }\n\n // Add theme (only per-session override still passed as URL param;\n // drawerDirection and features are configured via Config API)\n if (this.config.theme) {\n url.searchParams.set(\"theme\", this.config.theme);\n }\n\n // Add auth config\n if (this.config.authMode) {\n url.searchParams.set(\"auth_mode\", this.config.authMode);\n }\n if (this.config.accessToken) {\n url.searchParams.set(\"token\", this.config.accessToken);\n }\n if (this.config.refreshToken) {\n url.searchParams.set(\"refresh_token\", this.config.refreshToken);\n }\n\n // Auto-detect quick buy params from parent URL (if enabled and no explicit quickBuy)\n let quickBuy = this.config.quickBuy;\n let sessionMode = this.config.sessionMode;\n\n if (this.config.autoDetectQuickBuy && !quickBuy) {\n const parentParams = new URLSearchParams(window.location.search);\n const productId = parentParams.get(\"product_id\");\n const variantId = parentParams.get(\"variant_id\");\n\n if (productId) {\n quickBuy = {\n productId,\n variantId: variantId ?? null,\n quantity: parseInt(parentParams.get(\"qty\") || parentParams.get(\"quantity\") || \"1\", 10),\n };\n\n // Also check for session_mode in parent URL\n const parentSessionMode = parentParams.get(\"session_mode\");\n if (parentSessionMode === \"force-new\" || parentSessionMode === \"continue-existing\") {\n sessionMode = parentSessionMode;\n }\n\n // Clean quick buy params from URL to prevent duplicate adds on refresh\n // Deferred to next tick to handle React StrictMode double-mounting\n setTimeout(() => this.cleanQuickBuyParamsFromUrl(), 0);\n }\n }\n\n // Add quick buy params\n if (quickBuy) {\n this.hasQuickBuyParams = true;\n url.searchParams.set(\"product_id\", quickBuy.productId);\n if (quickBuy.variantId) {\n url.searchParams.set(\"variant_id\", quickBuy.variantId);\n }\n if (quickBuy.quantity && quickBuy.quantity !== 1) {\n url.searchParams.set(\"qty\", String(quickBuy.quantity));\n }\n }\n\n // Add session mode\n if (sessionMode) {\n url.searchParams.set(\"session_mode\", sessionMode);\n }\n\n // Pass parent origin for postMessage security\n // This allows the iframe to validate incoming messages and send responses to the correct origin\n if (typeof window !== \"undefined\") {\n url.searchParams.set(\"parent_origin\", window.location.origin);\n }\n\n // Create iframe (hidden initially, will show when ready + opened)\n const zIndex = this.config.appearance?.zIndex ?? 99999;\n this.iframe.create(url.toString(), zIndex);\n\n // Listen for messages from checkout iframe\n window.addEventListener(\"message\", this.boundMessageHandler);\n }\n\n // ===========================================================================\n // URL CLEANUP\n // ===========================================================================\n\n /**\n * Remove quick buy params from the parent URL to prevent duplicate adds on refresh\n * Uses replaceState to avoid adding to browser history\n */\n private cleanQuickBuyParamsFromUrl(): void {\n if (typeof window === \"undefined\") return;\n\n const url = new URL(window.location.href);\n const paramsToRemove = [\"product_id\", \"variant_id\", \"qty\", \"quantity\", \"session_mode\"];\n\n let hasChanges = false;\n for (const param of paramsToRemove) {\n if (url.searchParams.has(param)) {\n url.searchParams.delete(param);\n hasChanges = true;\n }\n }\n\n if (hasChanges) {\n window.history.replaceState({}, \"\", url.toString());\n }\n }\n\n // ===========================================================================\n // MESSAGE HANDLING\n // ===========================================================================\n\n private handleMessage(event: MessageEvent): void {\n // Validate origin for security - reject if no expected origin or mismatch\n const expectedOrigin = this.iframe.getCheckoutOrigin();\n if (!expectedOrigin || event.origin !== expectedOrigin) {\n return;\n }\n\n const { type, data } = event.data || {};\n if (!type) return;\n\n switch (type as IncomingEventType) {\n case \"checkout:ready\":\n this.handleReady();\n break;\n\n case \"checkout:open\":\n this.handleOpen();\n break;\n\n case \"checkout:close\":\n this.handleClose();\n break;\n\n case \"checkout:complete\":\n this.handleComplete(data as OrderData);\n break;\n\n case \"checkout:error\":\n this.handleError(data as { message: string });\n break;\n\n case \"cart:updated\":\n this.handleCartUpdate(data as CartData);\n break;\n\n case \"auth:login\":\n this.handleLogin(data as AuthLoginData);\n break;\n\n case \"auth:logout\":\n this.handleLogout(data as AuthLogoutData);\n break;\n\n case \"auth:refresh\":\n this.handleTokenRefresh(data as AuthRefreshData);\n break;\n\n case \"auth:session-error\":\n this.handleSessionError();\n break;\n }\n }\n\n private handleReady(): void {\n this.isReady = true;\n this.config.onReady?.();\n this.emit(\"ready\");\n\n // Auto-open cart if we have quick buy params (from config or auto-detected URL)\n if (this.hasQuickBuyParams) {\n this.openCart();\n }\n }\n\n private handleError(data: { message: string }): void {\n // Set ready so user can still interact (they'll see the error drawer)\n this.isReady = true;\n this.config.onError?.(data);\n this.emit(\"error\", data);\n }\n\n private handleOpen(): void {\n this.iframe.show();\n this.isOpen = true;\n this.config.onOpen?.();\n this.emit(\"open\");\n }\n\n private handleClose(): void {\n this.iframe.hide();\n this.isOpen = false;\n this.config.onClose?.();\n this.emit(\"close\");\n }\n\n private handleComplete(data: OrderData): void {\n this.config.onComplete?.(data);\n this.emit(\"complete\", data);\n }\n\n private handleCartUpdate(data: CartData): void {\n this.cartState = data;\n this.config.onCartUpdate?.(data);\n this.emit(\"cart:updated\", data);\n }\n\n private handleLogin(data: AuthLoginData): void {\n this.config.onLogin?.(data);\n this.emit(\"auth:login\", data);\n }\n\n private handleLogout(data: AuthLogoutData): void {\n this.config.onLogout?.(data);\n this.emit(\"auth:logout\", data);\n }\n\n private handleTokenRefresh(data: AuthRefreshData): void {\n this.config.onTokenRefresh?.(data);\n this.emit(\"auth:refresh\", data);\n }\n\n private handleSessionError(): void {\n this.config.onSessionError?.();\n this.emit(\"auth:session-error\");\n }\n\n // ===========================================================================\n // PUBLIC API\n // ===========================================================================\n\n /**\n * Open the cart drawer\n */\n openCart(): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.show();\n this.isOpen = true;\n this.iframe.postMessage(\"checkout:open-cart\");\n }\n\n /**\n * Open the checkout drawer directly (e.g., for Buy Now flow)\n */\n openCheckout(): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.show();\n this.isOpen = true;\n this.iframe.postMessage(\"checkout:open-checkout\");\n }\n\n /**\n * Close the checkout overlay\n */\n close(): void {\n this.iframe.hide();\n this.isOpen = false;\n this.iframe.postMessage(\"checkout:close\");\n }\n\n /**\n * Update authentication tokens\n * Use this when user logs in/out on the parent site\n */\n updateTokens(accessToken: string, refreshToken?: string): void {\n if (!this.isReady) {\n // Store for when ready\n this.config.accessToken = accessToken;\n this.config.refreshToken = refreshToken;\n return;\n }\n\n this.iframe.postMessage(\"checkout:update-tokens\", { accessToken, refreshToken });\n }\n\n /**\n * Add an item to cart and open the cart drawer\n *\n * @param productId - Product ID (required)\n * @param variantId - Variant ID (required, null for non-variant products)\n * @param quantity - Quantity to add (default: 1)\n */\n addToCart(productId: string, variantId: string | null, quantity = 1): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.postMessage(\"checkout:add-item\", {\n productId,\n variantId,\n quantity,\n });\n }\n\n /**\n * Get current cart state\n */\n getCart(): CartData {\n return { ...this.cartState };\n }\n\n /**\n * Check if checkout is ready\n */\n get ready(): boolean {\n return this.isReady;\n }\n\n /**\n * Check if checkout overlay is currently open\n */\n get open(): boolean {\n return this.isOpen;\n }\n\n /**\n * Subscribe to an event\n * @override EventEmitter.on with typed overloads\n */\n on(event: \"ready\", listener: EventListener<void>): void;\n on(event: \"open\", listener: EventListener<void>): void;\n on(event: \"close\", listener: EventListener<void>): void;\n on(event: \"complete\", listener: EventListener<OrderData>): void;\n on(event: \"cart:updated\", listener: EventListener<CartData>): void;\n on(event: \"auth:login\", listener: EventListener<AuthLoginData>): void;\n on(event: \"auth:logout\", listener: EventListener<AuthLogoutData>): void;\n on(event: \"auth:refresh\", listener: EventListener<AuthRefreshData>): void;\n on(event: \"auth:session-error\", listener: EventListener<void>): void;\n on<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n super.on(event, listener);\n }\n\n /**\n * Destroy the checkout instance\n * Removes iframe and cleans up event listeners\n */\n destroy(): void {\n window.removeEventListener(\"message\", this.boundMessageHandler);\n this.iframe.destroy();\n this.removeAllListeners();\n this.isReady = false;\n this.isOpen = false;\n }\n}\n","/**\n * Commerce Engine Checkout\n *\n * @commercengine/js - Embeddable checkout SDK\n *\n * @example CDN Usage (Vanilla JS):\n * ```html\n * <script src=\"https://cdn.commercengine.com/v1.js\" async></script>\n * <script>\n * window.Commercengine.onLoad = async () => {\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * theme: \"dark\",\n * onComplete: (order) => console.log(\"Order:\", order.orderNumber),\n * });\n *\n * document.getElementById(\"cart-btn\").onclick = () => checkout.openCart();\n * };\n * </script>\n * ```\n *\n * @example ESM Import:\n * ```typescript\n * import { Commercengine } from \"@commercengine/js\";\n *\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * });\n *\n * checkout.on(\"cart:updated\", (cart) => updateBadge(cart.count));\n * checkout.openCart();\n * ```\n */\n\nimport { Checkout } from \"./checkout\";\nimport type { CheckoutConfig } from \"./types\";\n\n// Re-export types for consumers\nexport type {\n AddToCartItem,\n AuthLoginData,\n AuthLogoutData,\n AuthMode,\n AuthRefreshData,\n CartData,\n Channel,\n CheckoutConfig,\n CheckoutEventType,\n CheckoutFeatures,\n DrawerDirection,\n DrawerDirectionConfig,\n Environment,\n ErrorData,\n LoginMethod,\n OrderData,\n QuickBuyConfig,\n SessionMode,\n ThemeMode,\n UserInfo,\n} from \"./types\";\n\nexport { Checkout };\n\n// =============================================================================\n// GLOBAL API\n// =============================================================================\n\n/**\n * Global Commercengine object interface\n */\ninterface CommercengineGlobal {\n /**\n * Initialize checkout with configuration\n * @returns Promise that resolves to Checkout instance\n */\n init: (config: CheckoutConfig) => Promise<Checkout>;\n\n /**\n * Callback invoked when script has loaded\n * Set this BEFORE including the script to be notified when ready\n */\n onLoad?: () => void;\n\n /**\n * Current checkout instance (if initialized)\n */\n instance?: Checkout;\n}\n\n/**\n * Global Commercengine namespace\n */\nconst Commercengine: CommercengineGlobal = {\n /**\n * Initialize checkout\n *\n * @param config - Checkout configuration\n * @returns Checkout instance\n *\n * @example\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * theme: \"system\",\n * });\n */\n init: async (config: CheckoutConfig): Promise<Checkout> => {\n // Validate required fields - either url (for dev) or storeId+apiKey (for prod)\n if (!config.url && (!config.storeId || !config.apiKey)) {\n throw new Error(\n \"[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided\"\n );\n }\n\n // Destroy existing instance if any\n if (Commercengine.instance) {\n Commercengine.instance.destroy();\n }\n\n // Create new instance\n const checkout = new Checkout(config);\n Commercengine.instance = checkout;\n\n // Wait for ready or error with timeout\n return new Promise((resolve, reject) => {\n if (checkout.ready) {\n resolve(checkout);\n return;\n }\n\n const timeoutMs = 30000; // 30 second timeout\n const timeout = setTimeout(() => {\n reject(new Error(\"Checkout initialization timed out\"));\n }, timeoutMs);\n\n // Resolve on ready\n checkout.once(\"ready\", () => {\n clearTimeout(timeout);\n resolve(checkout);\n });\n\n // Also resolve on error (checkout is still usable, will show error drawer)\n checkout.once(\"error\", () => {\n clearTimeout(timeout);\n resolve(checkout);\n });\n });\n },\n};\n\n// =============================================================================\n// GLOBAL SETUP\n// =============================================================================\n\n// Expose on window for CDN usage\nif (typeof window !== \"undefined\") {\n // Preserve any existing onLoad callback set before script loaded\n const existingOnLoad = (window as unknown as { Commercengine?: CommercengineGlobal })\n .Commercengine?.onLoad;\n\n // Set global\n (window as unknown as { Commercengine: CommercengineGlobal }).Commercengine = Commercengine;\n\n // Restore and call onLoad if it was set\n if (existingOnLoad) {\n Commercengine.onLoad = existingOnLoad;\n try {\n existingOnLoad();\n } catch (error) {\n // biome-ignore lint/suspicious/noConsole: Intentional error logging\n console.error(\"[Commercengine] Error in onLoad callback:\", error);\n }\n }\n}\n\nexport { Commercengine };\nexport default Commercengine;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/events.ts","../src/iframe-manager.ts","../src/checkout.ts","../src/index.ts"],"sourcesContent":["/**\n * Simple Event Emitter\n *\n * Provides pub/sub functionality for checkout events.\n */\n\nimport type { CheckoutEventType, EventListener } from \"./types\";\n\nexport class EventEmitter {\n private listeners: Map<CheckoutEventType, Set<EventListener>> = new Map();\n\n /**\n * Subscribe to an event\n */\n on<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n if (!this.listeners.has(event)) {\n this.listeners.set(event, new Set());\n }\n this.listeners.get(event)?.add(listener as EventListener);\n }\n\n /**\n * Unsubscribe from an event\n */\n off<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n const eventListeners = this.listeners.get(event);\n if (eventListeners) {\n eventListeners.delete(listener as EventListener);\n }\n }\n\n /**\n * Subscribe to an event (once)\n */\n once<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n const onceListener: EventListener<T> = (data) => {\n this.off(event, onceListener);\n listener(data);\n };\n this.on(event, onceListener);\n }\n\n /**\n * Emit an event to all subscribers\n */\n protected emit<T = unknown>(event: CheckoutEventType, data?: T): void {\n const eventListeners = this.listeners.get(event);\n if (eventListeners) {\n for (const listener of eventListeners) {\n try {\n listener(data);\n } catch (error) {\n // biome-ignore lint/suspicious/noConsole: Intentional error logging\n console.error(`[Commercengine] Error in ${event} listener:`, error);\n }\n }\n }\n }\n\n /**\n * Remove all listeners\n */\n protected removeAllListeners(): void {\n this.listeners.clear();\n }\n}\n","/**\n * Iframe Manager\n *\n * Handles iframe creation, lifecycle, and visibility management.\n * The iframe is pre-mounted (hidden) on init and shown on demand.\n */\n\nimport type { OutgoingEventType } from \"./types\";\n\nexport class IframeManager {\n private iframe: HTMLIFrameElement | null = null;\n private container: HTMLDivElement | null = null;\n private checkoutOrigin: string | null = null;\n\n /**\n * Create and mount the iframe (hidden initially)\n */\n create(url: string, zIndex: number): void {\n if (this.container) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Iframe already created\");\n return;\n }\n\n // Store origin for postMessage security\n this.checkoutOrigin = new URL(url).origin;\n\n // Create container (covers viewport, initially non-interactive)\n this.container = document.createElement(\"div\");\n this.container.id = \"commercengine-checkout\";\n this.container.style.cssText = `\n position: fixed;\n inset: 0;\n z-index: ${zIndex};\n pointer-events: none;\n `;\n\n // Create iframe\n this.iframe = document.createElement(\"iframe\");\n this.iframe.id = \"commercengine-checkout-iframe\";\n this.iframe.title = \"Shopping cart and checkout\";\n this.iframe.src = url;\n this.iframe.allow = \"payment\";\n this.iframe.style.cssText = `\n width: 100%;\n height: 100%;\n border: none;\n pointer-events: inherit;\n background-color: transparent;\n `;\n\n this.container.appendChild(this.iframe);\n document.body.appendChild(this.container);\n }\n\n /**\n * Show the checkout overlay (enable pointer events + backdrop)\n * Backdrop provides consistent dark overlay with blur, matching css-drawer's backdrop.\n * This ensures the brand site is obscured even during payment gateway redirects.\n */\n show(): void {\n if (this.container) {\n this.container.style.pointerEvents = \"auto\";\n this.container.style.background = \"hsl(0 0% 0% / 0.4)\";\n this.container.style.backdropFilter = \"blur(4px)\";\n // @ts-expect-error - webkitBackdropFilter for Safari support\n this.container.style.webkitBackdropFilter = \"blur(4px)\";\n document.body.style.overflow = \"hidden\";\n }\n }\n\n /**\n * Hide the checkout overlay (disable pointer events + remove backdrop)\n */\n hide(): void {\n if (this.container) {\n this.container.style.pointerEvents = \"none\";\n this.container.style.background = \"\";\n this.container.style.backdropFilter = \"\";\n // @ts-expect-error - webkitBackdropFilter for Safari support\n this.container.style.webkitBackdropFilter = \"\";\n document.body.style.overflow = \"\";\n }\n }\n\n /**\n * Check if iframe is visible\n */\n isVisible(): boolean {\n return this.container?.style.pointerEvents === \"auto\";\n }\n\n /**\n * Send a message to the checkout iframe\n */\n postMessage(type: OutgoingEventType, data?: unknown): void {\n if (!this.iframe?.contentWindow) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Cannot send message - iframe not ready\");\n return;\n }\n\n // Use specific origin for security - never use \"*\"\n if (!this.checkoutOrigin) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning for security\n console.warn(\"[Commercengine] Cannot send message - checkout origin not established\");\n return;\n }\n this.iframe.contentWindow.postMessage({ type, data }, this.checkoutOrigin);\n }\n\n /**\n * Get the checkout origin for message validation\n */\n getCheckoutOrigin(): string | null {\n return this.checkoutOrigin;\n }\n\n /**\n * Destroy the iframe and clean up\n */\n destroy(): void {\n if (this.container) {\n this.container.remove();\n this.container = null;\n }\n this.iframe = null;\n this.checkoutOrigin = null;\n\n // Restore body scroll\n document.body.style.overflow = \"\";\n }\n}\n","/**\n * Checkout Class\n *\n * Main entry point for Commerce Engine Checkout integration.\n * Manages iframe lifecycle, event handling, and provides public API.\n */\n\nimport { EventEmitter } from \"./events\";\nimport { IframeManager } from \"./iframe-manager\";\nimport type {\n AuthLoginData,\n AuthLogoutData,\n AuthTokensUpdatedData,\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/**\n * Build subdomain-based checkout base URL.\n * Each store gets its own origin for localStorage isolation.\n */\nfunction getCheckoutBaseUrl(storeId: string, environment: Environment): string {\n if (environment === \"staging\") {\n return `https://${storeId}.staging.checkout.commercengine.com`;\n }\n return `https://${storeId}.checkout.commercengine.com`;\n}\n\n/**\n * Build checkout iframe URL with store_id as subdomain.\n * Environment is encoded in the hostname — no need for a query param.\n */\nfunction buildCheckoutUrl(storeId: string, apiKey: string, environment: Environment): string {\n const baseUrl = getCheckoutBaseUrl(storeId, environment);\n return `${baseUrl}?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 tokenState: AuthTokensUpdatedData | null = null;\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 // Inject storeId + environment as subdomain: store.staging.localhost / store.prod.localhost\n const parsedUrl = new URL(this.config.url);\n if (this.config.storeId && parsedUrl.hostname === \"localhost\") {\n const env = this.config.environment || \"staging\";\n const envPrefix = env === \"production\" ? \"prod\" : \"staging\";\n parsedUrl.hostname = `${this.config.storeId}.${envPrefix}.localhost`;\n }\n baseUrl = parsedUrl.toString();\n } else if (this.config.storeId && this.config.apiKey) {\n // Build URL from credentials + environment (store_id as subdomain)\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 api_key as URL param (store_id and environment are in the subdomain)\n if (this.config.apiKey && !url.searchParams.has(\"api_key\")) {\n url.searchParams.set(\"api_key\", this.config.apiKey);\n }\n\n // Add theme (only per-session override still passed as URL param;\n // drawerDirection and features are configured via Config API)\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:tokens-updated\":\n this.handleTokensUpdated(data as AuthTokensUpdatedData);\n break;\n\n case \"auth:login\":\n this.handleLogin(data as AuthLoginData);\n break;\n\n case \"auth:logout\":\n this.handleLogout(data as AuthLogoutData);\n break;\n\n case \"auth:session-error\":\n this.handleSessionError();\n break;\n }\n }\n\n private handleReady(): void {\n this.isReady = true;\n this.config.onReady?.();\n this.emit(\"ready\");\n\n // Auto-open cart if we have quick buy params (from config or auto-detected URL)\n if (this.hasQuickBuyParams) {\n this.openCart();\n }\n }\n\n private handleError(data: { message: string }): void {\n // Set ready so user can still interact (they'll see the error drawer)\n this.isReady = true;\n this.config.onError?.(data);\n this.emit(\"error\", data);\n }\n\n private handleOpen(): void {\n this.iframe.show();\n this.isOpen = true;\n this.config.onOpen?.();\n this.emit(\"open\");\n }\n\n private handleClose(): void {\n this.iframe.hide();\n this.isOpen = false;\n this.config.onClose?.();\n this.emit(\"close\");\n }\n\n private handleComplete(data: OrderData): void {\n this.config.onComplete?.(data);\n this.emit(\"complete\", data);\n }\n\n private handleCartUpdate(data: CartData): void {\n this.cartState = data;\n this.config.onCartUpdate?.(data);\n this.emit(\"cart:updated\", data);\n }\n\n private handleTokensUpdated(data: AuthTokensUpdatedData): void {\n this.tokenState = data;\n this.config.onTokensUpdated?.(data);\n this.emit(\"auth:tokens-updated\", data);\n }\n\n private handleLogin(data: AuthLoginData): void {\n this.config.onLogin?.(data);\n this.emit(\"auth:login\", data);\n }\n\n private handleLogout(data: AuthLogoutData): void {\n this.config.onLogout?.(data);\n this.emit(\"auth:logout\", data);\n }\n\n private handleSessionError(): void {\n this.tokenState = null;\n this.config.onSessionError?.();\n this.emit(\"auth:session-error\");\n }\n\n // ===========================================================================\n // PUBLIC API\n // ===========================================================================\n\n /**\n * Open the cart drawer\n */\n openCart(): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.show();\n this.isOpen = true;\n this.iframe.postMessage(\"checkout:open-cart\");\n }\n\n /**\n * Open the checkout drawer directly (e.g., for Buy Now flow)\n */\n openCheckout(): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.show();\n this.isOpen = true;\n this.iframe.postMessage(\"checkout:open-checkout\");\n }\n\n /**\n * Close the checkout overlay\n */\n close(): void {\n this.iframe.hide();\n this.isOpen = false;\n this.iframe.postMessage(\"checkout:close\");\n }\n\n /**\n * Update authentication tokens\n * Use this when user logs in/out on the parent site\n */\n updateTokens(accessToken: string, refreshToken?: string): void {\n if (!this.isReady) {\n // Store for when ready\n this.config.accessToken = accessToken;\n this.config.refreshToken = refreshToken;\n return;\n }\n\n this.iframe.postMessage(\"checkout:update-tokens\", { accessToken, refreshToken });\n }\n\n /**\n * Add an item to cart and open the cart drawer\n *\n * @param productId - Product ID (required)\n * @param variantId - Variant ID (required, null for non-variant products)\n * @param quantity - Quantity to add (default: 1)\n */\n addToCart(productId: string, variantId: string | null, quantity = 1): void {\n if (!this.isReady) {\n // biome-ignore lint/suspicious/noConsole: Intentional warning\n console.warn(\"[Commercengine] Not ready. Wait for onReady callback or 'ready' event.\");\n return;\n }\n\n this.iframe.postMessage(\"checkout:add-item\", {\n productId,\n variantId,\n quantity,\n });\n }\n\n /**\n * Get current cart state\n */\n getCart(): CartData {\n return { ...this.cartState };\n }\n\n /**\n * Get current tokens (null if no tokens received yet)\n * Use this after init() resolves to read tokens without race conditions.\n * For ongoing changes, subscribe to \"auth:tokens-updated\" event.\n */\n getTokens(): AuthTokensUpdatedData | null {\n return this.tokenState ? { ...this.tokenState } : null;\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:tokens-updated\", listener: EventListener<AuthTokensUpdatedData>): void;\n on(event: \"auth:login\", listener: EventListener<AuthLoginData>): void;\n on(event: \"auth:logout\", listener: EventListener<AuthLogoutData>): void;\n on(event: \"auth:session-error\", listener: EventListener<void>): void;\n on<T = unknown>(event: CheckoutEventType, listener: EventListener<T>): void {\n super.on(event, listener);\n }\n\n /**\n * Destroy the checkout instance\n * Removes iframe and cleans up event listeners\n */\n destroy(): void {\n window.removeEventListener(\"message\", this.boundMessageHandler);\n this.iframe.destroy();\n this.removeAllListeners();\n this.isReady = false;\n this.isOpen = false;\n this.tokenState = null;\n }\n}\n","/**\n * Commerce Engine Checkout\n *\n * @commercengine/js - Embeddable checkout SDK\n *\n * @example CDN Usage (Vanilla JS):\n * ```html\n * <script src=\"https://cdn.commercengine.com/v1.js\" async></script>\n * <script>\n * window.Commercengine.onLoad = async () => {\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * theme: \"dark\",\n * onComplete: (order) => console.log(\"Order:\", order.orderNumber),\n * });\n *\n * document.getElementById(\"cart-btn\").onclick = () => checkout.openCart();\n * };\n * </script>\n * ```\n *\n * @example ESM Import:\n * ```typescript\n * import { Commercengine } from \"@commercengine/js\";\n *\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * });\n *\n * checkout.on(\"cart:updated\", (cart) => updateBadge(cart.count));\n * checkout.openCart();\n * ```\n */\n\nimport { Checkout } from \"./checkout\";\nimport type { CheckoutConfig } from \"./types\";\n\n// Re-export types for consumers\nexport type {\n AddToCartItem,\n AuthLoginData,\n AuthLogoutData,\n AuthMode,\n AuthTokensUpdatedData,\n CartData,\n Channel,\n CheckoutConfig,\n CheckoutEventType,\n CheckoutFeatures,\n DrawerDirection,\n DrawerDirectionConfig,\n Environment,\n ErrorData,\n LoginMethod,\n OrderData,\n QuickBuyConfig,\n SessionMode,\n ThemeMode,\n UserInfo,\n} from \"./types\";\n\nexport { Checkout };\n\n// =============================================================================\n// GLOBAL API\n// =============================================================================\n\n/**\n * Global Commercengine object interface\n */\ninterface CommercengineGlobal {\n /**\n * Initialize checkout with configuration\n * @returns Promise that resolves to Checkout instance\n */\n init: (config: CheckoutConfig) => Promise<Checkout>;\n\n /**\n * Callback invoked when script has loaded\n * Set this BEFORE including the script to be notified when ready\n */\n onLoad?: () => void;\n\n /**\n * Current checkout instance (if initialized)\n */\n instance?: Checkout;\n}\n\n/**\n * Global Commercengine namespace\n */\nconst Commercengine: CommercengineGlobal = {\n /**\n * Initialize checkout\n *\n * @param config - Checkout configuration\n * @returns Checkout instance\n *\n * @example\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * theme: \"system\",\n * });\n */\n init: async (config: CheckoutConfig): Promise<Checkout> => {\n // Validate required fields - either url (for dev) or storeId+apiKey (for prod)\n if (!config.url && (!config.storeId || !config.apiKey)) {\n throw new Error(\n \"[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided\"\n );\n }\n\n // Destroy existing instance if any\n if (Commercengine.instance) {\n Commercengine.instance.destroy();\n }\n\n // Create new instance\n const checkout = new Checkout(config);\n Commercengine.instance = checkout;\n\n // Wait for ready or error with timeout\n return new Promise((resolve, reject) => {\n if (checkout.ready) {\n resolve(checkout);\n return;\n }\n\n const timeoutMs = 30000; // 30 second timeout\n const timeout = setTimeout(() => {\n reject(new Error(\"Checkout initialization timed out\"));\n }, timeoutMs);\n\n // Resolve on ready\n checkout.once(\"ready\", () => {\n clearTimeout(timeout);\n resolve(checkout);\n });\n\n // Also resolve on error (checkout is still usable, will show error drawer)\n checkout.once(\"error\", () => {\n clearTimeout(timeout);\n resolve(checkout);\n });\n });\n },\n};\n\n// =============================================================================\n// GLOBAL SETUP\n// =============================================================================\n\n// Expose on window for CDN usage\nif (typeof window !== \"undefined\") {\n // Preserve any existing onLoad callback set before script loaded\n const existingOnLoad = (window as unknown as { Commercengine?: CommercengineGlobal })\n .Commercengine?.onLoad;\n\n // Set global\n (window as unknown as { Commercengine: CommercengineGlobal }).Commercengine = Commercengine;\n\n // Restore and call onLoad if it was set\n if (existingOnLoad) {\n Commercengine.onLoad = existingOnLoad;\n try {\n existingOnLoad();\n } catch (error) {\n // biome-ignore lint/suspicious/noConsole: Intentional error logging\n console.error(\"[Commercengine] Error in onLoad callback:\", error);\n }\n }\n}\n\nexport { Commercengine };\nexport default Commercengine;\n"],"mappings":"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"}
|