@commercengine/js 0.3.5 → 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 +51 -56
- package/dist/index.d.mts +72 -34
- package/dist/index.d.mts.map +1 -1
- package/dist/index.iife.js +40 -26
- package/dist/index.iife.js.map +1 -1
- package/dist/index.mjs +39 -25
- package/dist/index.mjs.map +1 -1
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -49,7 +49,8 @@ interface CheckoutConfig {
|
|
|
49
49
|
// Overrides environment-based URL
|
|
50
50
|
|
|
51
51
|
// === Theme ===
|
|
52
|
-
theme?: "light" | "dark" | "system"; // Default: "system"
|
|
52
|
+
theme?: "light" | "dark" | "system"; // Per-session override. Default: "system"
|
|
53
|
+
// Primary config via Checkout Studio (Config API)
|
|
53
54
|
|
|
54
55
|
// === Appearance ===
|
|
55
56
|
appearance?: {
|
|
@@ -70,14 +71,8 @@ interface CheckoutConfig {
|
|
|
70
71
|
sessionMode?: "continue-existing" | "force-new"; // Default: "continue-existing"
|
|
71
72
|
autoDetectQuickBuy?: boolean; // Auto-detect from parent URL. Default: false
|
|
72
73
|
|
|
73
|
-
//
|
|
74
|
-
|
|
75
|
-
loyalty?: boolean; // Loyalty points redemption. Default: true
|
|
76
|
-
coupons?: boolean; // Coupon/promo codes. Default: true
|
|
77
|
-
collectInStore?: boolean; // Collect-in-store option. Default: false
|
|
78
|
-
freeShippingProgress?: boolean; // Free shipping progress tracker. Default: true
|
|
79
|
-
productRecommendations?: boolean; // Product recommendations in cart. Default: true
|
|
80
|
-
};
|
|
74
|
+
// Drawer direction, features, and login methods are configured via
|
|
75
|
+
// Checkout Studio (Config API) and are not SDK options.
|
|
81
76
|
|
|
82
77
|
// === Callbacks ===
|
|
83
78
|
onReady?: () => void;
|
|
@@ -85,9 +80,9 @@ interface CheckoutConfig {
|
|
|
85
80
|
onClose?: () => void;
|
|
86
81
|
onComplete?: (order: OrderData) => void;
|
|
87
82
|
onCartUpdate?: (cart: CartData) => void;
|
|
83
|
+
onTokensUpdated?: (data: AuthTokensUpdatedData) => void;
|
|
88
84
|
onLogin?: (data: AuthLoginData) => void;
|
|
89
85
|
onLogout?: (data: AuthLogoutData) => void;
|
|
90
|
-
onTokenRefresh?: (data: AuthRefreshData) => void;
|
|
91
86
|
onSessionError?: () => void;
|
|
92
87
|
onError?: (error: ErrorData) => void;
|
|
93
88
|
}
|
|
@@ -115,14 +110,16 @@ const checkout = await Commercengine.init({
|
|
|
115
110
|
onCartUpdate: (cart) => {
|
|
116
111
|
updateCartBadge(cart.count);
|
|
117
112
|
},
|
|
118
|
-
|
|
119
|
-
//
|
|
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
|
|
120
119
|
console.log("User logged in via", loginMethod, ":", user?.email || user?.phone);
|
|
121
|
-
saveUserSession(accessToken);
|
|
122
120
|
},
|
|
123
121
|
onLogout: () => {
|
|
124
|
-
// User logged out -
|
|
125
|
-
clearUserSession();
|
|
122
|
+
// User logged out - update UI for anonymous state
|
|
126
123
|
},
|
|
127
124
|
onSessionError: () => {
|
|
128
125
|
// Session corrupted - SDK cleared all tokens, user needs to re-authenticate
|
|
@@ -204,6 +201,18 @@ const cart = checkout.getCart();
|
|
|
204
201
|
console.log(cart.count, cart.total, cart.currency);
|
|
205
202
|
```
|
|
206
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
|
+
|
|
207
216
|
### `destroy()`
|
|
208
217
|
|
|
209
218
|
Remove checkout iframe and cleanup event listeners.
|
|
@@ -232,9 +241,9 @@ Subscribe via callbacks (in config) or the event emitter.
|
|
|
232
241
|
| `close` | `onClose` | - | All drawers closed |
|
|
233
242
|
| `complete` | `onComplete` | `OrderData` | Order placed successfully |
|
|
234
243
|
| `cart:updated` | `onCartUpdate` | `CartData` | Cart state changed |
|
|
235
|
-
| `auth:
|
|
236
|
-
| `auth:
|
|
237
|
-
| `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) |
|
|
238
247
|
| `auth:session-error` | `onSessionError` | - | Session corrupted, tokens cleared |
|
|
239
248
|
| `error` | `onError` | `ErrorData` | Configuration error |
|
|
240
249
|
|
|
@@ -252,24 +261,20 @@ interface CartData {
|
|
|
252
261
|
currency: string; // Currency code (e.g., "USD", "INR")
|
|
253
262
|
}
|
|
254
263
|
|
|
255
|
-
interface
|
|
264
|
+
interface AuthTokensUpdatedData {
|
|
256
265
|
accessToken: string;
|
|
257
266
|
refreshToken?: string;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
interface AuthLoginData {
|
|
258
270
|
user?: UserInfo;
|
|
259
271
|
loginMethod?: LoginMethod;
|
|
260
272
|
}
|
|
261
273
|
|
|
262
274
|
interface AuthLogoutData {
|
|
263
|
-
accessToken: string; // New anonymous token
|
|
264
|
-
refreshToken?: string;
|
|
265
275
|
user?: UserInfo; // Anonymous user info
|
|
266
276
|
}
|
|
267
277
|
|
|
268
|
-
interface AuthRefreshData {
|
|
269
|
-
accessToken: string;
|
|
270
|
-
refreshToken?: string;
|
|
271
|
-
}
|
|
272
|
-
|
|
273
278
|
interface ErrorData {
|
|
274
279
|
message: string;
|
|
275
280
|
}
|
|
@@ -330,10 +335,14 @@ User will be prompted to login within checkout (WhatsApp, Phone, or Email OTP).
|
|
|
330
335
|
const checkout = await Commercengine.init({
|
|
331
336
|
storeId: "store_xxx",
|
|
332
337
|
apiKey: "ak_xxx",
|
|
333
|
-
|
|
334
|
-
//
|
|
338
|
+
onTokensUpdated: ({ accessToken, refreshToken }) => {
|
|
339
|
+
// Sync tokens to your auth system (fires on login, logout, refresh)
|
|
335
340
|
saveSession({ accessToken, refreshToken });
|
|
336
341
|
},
|
|
342
|
+
onLogin: ({ user, loginMethod }) => {
|
|
343
|
+
// Update UI with user info
|
|
344
|
+
console.log("Logged in via", loginMethod, ":", user?.email);
|
|
345
|
+
},
|
|
337
346
|
});
|
|
338
347
|
```
|
|
339
348
|
|
|
@@ -368,21 +377,7 @@ myAuth.onLogout(() => {
|
|
|
368
377
|
|
|
369
378
|
## Features
|
|
370
379
|
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
```typescript
|
|
374
|
-
const checkout = await Commercengine.init({
|
|
375
|
-
storeId: "store_xxx",
|
|
376
|
-
apiKey: "ak_xxx",
|
|
377
|
-
features: {
|
|
378
|
-
loyalty: false, // Disable loyalty points redemption
|
|
379
|
-
coupons: false, // Disable coupon/promo codes
|
|
380
|
-
collectInStore: false, // Disable collect-in-store option
|
|
381
|
-
freeShippingProgress: false, // Disable free shipping progress tracker
|
|
382
|
-
productRecommendations: false, // Disable product recommendations
|
|
383
|
-
},
|
|
384
|
-
});
|
|
385
|
-
```
|
|
380
|
+
Features (loyalty, coupons, collect-in-store, free shipping progress, product recommendations) are configured via **Checkout Studio** (Config API). They are not SDK options.
|
|
386
381
|
|
|
387
382
|
| Feature | Default | Description |
|
|
388
383
|
|---------|---------|-------------|
|
|
@@ -403,12 +398,14 @@ import {
|
|
|
403
398
|
type CheckoutConfig,
|
|
404
399
|
type CheckoutEventType,
|
|
405
400
|
type CheckoutFeatures,
|
|
401
|
+
type DrawerDirection,
|
|
402
|
+
type DrawerDirectionConfig,
|
|
406
403
|
type Environment,
|
|
407
404
|
type CartData,
|
|
408
405
|
type OrderData,
|
|
406
|
+
type AuthTokensUpdatedData,
|
|
409
407
|
type AuthLoginData,
|
|
410
408
|
type AuthLogoutData,
|
|
411
|
-
type AuthRefreshData,
|
|
412
409
|
type ErrorData,
|
|
413
410
|
type UserInfo,
|
|
414
411
|
type Channel,
|
|
@@ -418,16 +415,17 @@ import {
|
|
|
418
415
|
|
|
419
416
|
## URL Parameters (Advanced)
|
|
420
417
|
|
|
421
|
-
For direct iframe integration without the SDK, checkout
|
|
418
|
+
For direct iframe integration without the SDK, checkout uses a **subdomain-based** URL scheme. The store ID and environment are encoded in the hostname — they are **not** URL parameters.
|
|
419
|
+
|
|
420
|
+
**URL format:** `https://{storeId}.checkout.commercengine.com?api_key=ak_xxx`
|
|
421
|
+
**Staging:** `https://{storeId}.staging.checkout.commercengine.com?api_key=ak_xxx`
|
|
422
422
|
|
|
423
423
|
| Parameter | Description | Example |
|
|
424
424
|
|-----------|-------------|---------|
|
|
425
|
-
| `
|
|
426
|
-
| `api_key` | API Key | `ak_xxx` |
|
|
427
|
-
| `environment` | SDK environment | `production`, `staging` |
|
|
425
|
+
| `api_key` | API Key (required) | `ak_xxx` |
|
|
428
426
|
| `mode` | Deployment mode | `iframe` |
|
|
429
427
|
| `parent_origin` | Parent origin for postMessage | `https://brand.com` |
|
|
430
|
-
| `theme` | Theme preference | `light`, `dark` |
|
|
428
|
+
| `theme` | Theme preference (per-session override) | `light`, `dark` |
|
|
431
429
|
| `token` | Access token | JWT string |
|
|
432
430
|
| `refresh_token` | Refresh token | JWT string |
|
|
433
431
|
| `auth_mode` | Auth management | `provided`, `managed` |
|
|
@@ -435,16 +433,13 @@ For direct iframe integration without the SDK, checkout accepts these URL parame
|
|
|
435
433
|
| `variant_id` | Quick buy variant | Variant ID |
|
|
436
434
|
| `qty` | Quick buy quantity | `1` |
|
|
437
435
|
| `session_mode` | Session behavior | `continue-existing`, `force-new` |
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
| `collect_in_store` | Collect-in-store feature | `false` to disable |
|
|
441
|
-
| `free_shipping_progress` | Free shipping progress | `false` to disable |
|
|
442
|
-
| `product_recommendations` | Product recommendations | `false` to disable |
|
|
436
|
+
|
|
437
|
+
Features (loyalty, coupons, etc.) and drawer direction are configured via Checkout Studio (Config API) and are not accepted as URL parameters.
|
|
443
438
|
|
|
444
439
|
### Auth Modes
|
|
445
440
|
|
|
446
|
-
- **`managed`** (default): Checkout manages token lifecycle with auto-refresh
|
|
447
|
-
- **`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`
|
|
448
443
|
|
|
449
444
|
### Session Modes
|
|
450
445
|
|
package/dist/index.d.mts
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
* @commercengine/js
|
|
6
6
|
*
|
|
7
7
|
* This is the single source of truth for checkout configuration.
|
|
8
|
-
* The
|
|
8
|
+
* The SDK translates these into subdomain-based iframe URLs and URL parameters.
|
|
9
9
|
*/
|
|
10
10
|
/**
|
|
11
11
|
* Authentication mode
|
|
@@ -87,10 +87,41 @@ interface CheckoutFeatures {
|
|
|
87
87
|
*/
|
|
88
88
|
productRecommendations?: boolean;
|
|
89
89
|
}
|
|
90
|
+
/**
|
|
91
|
+
* Theme mode preference
|
|
92
|
+
* - 'light': Force light mode
|
|
93
|
+
* - 'dark': Force dark mode
|
|
94
|
+
* - 'system': Follow OS preference
|
|
95
|
+
*/
|
|
96
|
+
type ThemeMode = "light" | "dark" | "system";
|
|
97
|
+
/**
|
|
98
|
+
* Drawer slide direction
|
|
99
|
+
* - 'bottom': Slides up from bottom (default on mobile)
|
|
100
|
+
* - 'right': Slides in from right (default on desktop)
|
|
101
|
+
* - 'left': Slides in from left
|
|
102
|
+
* - 'top': Slides down from top
|
|
103
|
+
* - 'modal': Centered modal overlay
|
|
104
|
+
*/
|
|
105
|
+
type DrawerDirection = "bottom" | "top" | "left" | "right" | "modal";
|
|
106
|
+
/**
|
|
107
|
+
* Drawer direction per viewport
|
|
108
|
+
*/
|
|
109
|
+
interface DrawerDirectionConfig {
|
|
110
|
+
/**
|
|
111
|
+
* Direction on mobile viewports (≤767px)
|
|
112
|
+
* @default "bottom"
|
|
113
|
+
*/
|
|
114
|
+
mobile?: DrawerDirection;
|
|
115
|
+
/**
|
|
116
|
+
* Direction on desktop viewports (>767px)
|
|
117
|
+
* @default "right"
|
|
118
|
+
*/
|
|
119
|
+
desktop?: DrawerDirection;
|
|
120
|
+
}
|
|
90
121
|
/**
|
|
91
122
|
* Environment for checkout
|
|
92
|
-
* - 'production': Uses checkout.commercengine.com
|
|
93
|
-
* - 'staging': Uses
|
|
123
|
+
* - 'production': Uses {storeId}.checkout.commercengine.com
|
|
124
|
+
* - 'staging': Uses {storeId}.staging.checkout.commercengine.com
|
|
94
125
|
*/
|
|
95
126
|
type Environment = "production" | "staging";
|
|
96
127
|
/**
|
|
@@ -98,12 +129,16 @@ type Environment = "production" | "staging";
|
|
|
98
129
|
*
|
|
99
130
|
* Either provide `url` directly (for local development) or
|
|
100
131
|
* provide `storeId` + `apiKey` (for production).
|
|
132
|
+
*
|
|
133
|
+
* Appearance settings (drawer direction, features, login methods) are
|
|
134
|
+
* configured via Checkout Studio (Config API) and injected by the edge
|
|
135
|
+
* function. The SDK only passes credentials, auth, quick buy, and theme.
|
|
101
136
|
*/
|
|
102
137
|
interface CheckoutConfig {
|
|
103
138
|
/**
|
|
104
139
|
* Environment to use for checkout URL resolution
|
|
105
|
-
* - 'production': checkout.commercengine.com (default)
|
|
106
|
-
* - 'staging': staging.checkout.commercengine.com
|
|
140
|
+
* - 'production': {storeId}.checkout.commercengine.com (default)
|
|
141
|
+
* - 'staging': {storeId}.staging.checkout.commercengine.com
|
|
107
142
|
*
|
|
108
143
|
* For local development, use the `url` option instead.
|
|
109
144
|
*/
|
|
@@ -127,10 +162,13 @@ interface CheckoutConfig {
|
|
|
127
162
|
*/
|
|
128
163
|
apiKey?: string;
|
|
129
164
|
/**
|
|
130
|
-
* Theme preference
|
|
165
|
+
* Theme preference (per-session override).
|
|
166
|
+
* Primary configuration is via Checkout Studio (Config API).
|
|
167
|
+
* Use this to match the parent page's current theme at runtime.
|
|
168
|
+
* Also works in hosted mode via URL: ?theme=dark
|
|
131
169
|
* @default "system"
|
|
132
170
|
*/
|
|
133
|
-
theme?:
|
|
171
|
+
theme?: ThemeMode;
|
|
134
172
|
/**
|
|
135
173
|
* Appearance customization
|
|
136
174
|
*/
|
|
@@ -174,11 +212,6 @@ interface CheckoutConfig {
|
|
|
174
212
|
* @default false
|
|
175
213
|
*/
|
|
176
214
|
autoDetectQuickBuy?: boolean;
|
|
177
|
-
/**
|
|
178
|
-
* Feature flags to enable/disable checkout features
|
|
179
|
-
* All features are enabled by default
|
|
180
|
-
*/
|
|
181
|
-
features?: CheckoutFeatures;
|
|
182
215
|
/**
|
|
183
216
|
* Called when checkout iframe is loaded and ready
|
|
184
217
|
*/
|
|
@@ -201,17 +234,18 @@ interface CheckoutConfig {
|
|
|
201
234
|
*/
|
|
202
235
|
onCartUpdate?: (cart: CartData) => void;
|
|
203
236
|
/**
|
|
204
|
-
* Called
|
|
237
|
+
* Called whenever tokens change (anonymous auth, login, logout, refresh)
|
|
238
|
+
* Single source of truth for token sync between checkout and parent
|
|
205
239
|
*/
|
|
206
|
-
|
|
240
|
+
onTokensUpdated?: (data: AuthTokensUpdatedData) => void;
|
|
207
241
|
/**
|
|
208
|
-
* Called when user logs
|
|
242
|
+
* Called when user logs in via checkout (semantic — user info only, no tokens)
|
|
209
243
|
*/
|
|
210
|
-
|
|
244
|
+
onLogin?: (data: AuthLoginData) => void;
|
|
211
245
|
/**
|
|
212
|
-
* Called when
|
|
246
|
+
* Called when user logs out (semantic — user info only, no tokens)
|
|
213
247
|
*/
|
|
214
|
-
|
|
248
|
+
onLogout?: (data: AuthLogoutData) => void;
|
|
215
249
|
/**
|
|
216
250
|
* Called when session is corrupted and SDK clears all tokens.
|
|
217
251
|
* User needs to re-authenticate.
|
|
@@ -279,28 +313,25 @@ interface OrderData {
|
|
|
279
313
|
orderNumber: string;
|
|
280
314
|
}
|
|
281
315
|
/**
|
|
282
|
-
* 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)
|
|
283
318
|
*/
|
|
284
|
-
interface
|
|
319
|
+
interface AuthTokensUpdatedData {
|
|
285
320
|
accessToken: string;
|
|
286
321
|
refreshToken?: string;
|
|
287
|
-
user?: UserInfo;
|
|
288
|
-
loginMethod?: LoginMethod;
|
|
289
322
|
}
|
|
290
323
|
/**
|
|
291
|
-
* Data emitted on
|
|
324
|
+
* Data emitted on login event (semantic only — no tokens)
|
|
292
325
|
*/
|
|
293
|
-
interface
|
|
294
|
-
accessToken: string;
|
|
295
|
-
refreshToken?: string;
|
|
326
|
+
interface AuthLoginData {
|
|
296
327
|
user?: UserInfo;
|
|
328
|
+
loginMethod?: LoginMethod;
|
|
297
329
|
}
|
|
298
330
|
/**
|
|
299
|
-
* Data emitted on
|
|
331
|
+
* Data emitted on logout event (semantic only — no tokens)
|
|
300
332
|
*/
|
|
301
|
-
interface
|
|
302
|
-
|
|
303
|
-
refreshToken?: string;
|
|
333
|
+
interface AuthLogoutData {
|
|
334
|
+
user?: UserInfo;
|
|
304
335
|
}
|
|
305
336
|
/**
|
|
306
337
|
* Error data emitted on checkout:error event
|
|
@@ -312,7 +343,7 @@ interface ErrorData {
|
|
|
312
343
|
/**
|
|
313
344
|
* Events emitted by checkout
|
|
314
345
|
*/
|
|
315
|
-
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";
|
|
316
347
|
/**
|
|
317
348
|
* Event listener function type
|
|
318
349
|
*/
|
|
@@ -350,6 +381,7 @@ declare class Checkout extends EventEmitter {
|
|
|
350
381
|
private isReady;
|
|
351
382
|
private isOpen;
|
|
352
383
|
private cartState;
|
|
384
|
+
private tokenState;
|
|
353
385
|
private boundMessageHandler;
|
|
354
386
|
private hasQuickBuyParams;
|
|
355
387
|
constructor(config: CheckoutConfig);
|
|
@@ -366,9 +398,9 @@ declare class Checkout extends EventEmitter {
|
|
|
366
398
|
private handleClose;
|
|
367
399
|
private handleComplete;
|
|
368
400
|
private handleCartUpdate;
|
|
401
|
+
private handleTokensUpdated;
|
|
369
402
|
private handleLogin;
|
|
370
403
|
private handleLogout;
|
|
371
|
-
private handleTokenRefresh;
|
|
372
404
|
private handleSessionError;
|
|
373
405
|
/**
|
|
374
406
|
* Open the cart drawer
|
|
@@ -399,6 +431,12 @@ declare class Checkout extends EventEmitter {
|
|
|
399
431
|
* Get current cart state
|
|
400
432
|
*/
|
|
401
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;
|
|
402
440
|
/**
|
|
403
441
|
* Check if checkout is ready
|
|
404
442
|
*/
|
|
@@ -416,9 +454,9 @@ declare class Checkout extends EventEmitter {
|
|
|
416
454
|
on(event: "close", listener: EventListener<void>): void;
|
|
417
455
|
on(event: "complete", listener: EventListener<OrderData>): void;
|
|
418
456
|
on(event: "cart:updated", listener: EventListener<CartData>): void;
|
|
457
|
+
on(event: "auth:tokens-updated", listener: EventListener<AuthTokensUpdatedData>): void;
|
|
419
458
|
on(event: "auth:login", listener: EventListener<AuthLoginData>): void;
|
|
420
459
|
on(event: "auth:logout", listener: EventListener<AuthLogoutData>): void;
|
|
421
|
-
on(event: "auth:refresh", listener: EventListener<AuthRefreshData>): void;
|
|
422
460
|
on(event: "auth:session-error", listener: EventListener<void>): void;
|
|
423
461
|
/**
|
|
424
462
|
* Destroy the checkout instance
|
|
@@ -452,5 +490,5 @@ interface CommercengineGlobal {
|
|
|
452
490
|
*/
|
|
453
491
|
declare const Commercengine: CommercengineGlobal;
|
|
454
492
|
//#endregion
|
|
455
|
-
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 };
|
|
456
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;
|
|
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
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
var CommercengineExports = (function(exports) {
|
|
2
2
|
|
|
3
|
-
Object.
|
|
3
|
+
Object.defineProperties(exports, { __esModule: { value: true }, [Symbol.toStringTag]: { value: 'Module' } });
|
|
4
4
|
|
|
5
5
|
//#region src/events.ts
|
|
6
6
|
var EventEmitter = class {
|
|
@@ -167,16 +167,20 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
167
167
|
* Main entry point for Commerce Engine Checkout integration.
|
|
168
168
|
* Manages iframe lifecycle, event handling, and provides public API.
|
|
169
169
|
*/
|
|
170
|
-
/** Checkout URLs by environment */
|
|
171
|
-
const CHECKOUT_URLS = {
|
|
172
|
-
production: "https://checkout.commercengine.com",
|
|
173
|
-
staging: "https://staging.checkout.commercengine.com"
|
|
174
|
-
};
|
|
175
170
|
/**
|
|
176
|
-
* Build checkout
|
|
171
|
+
* Build subdomain-based checkout base URL.
|
|
172
|
+
* Each store gets its own origin for localStorage isolation.
|
|
173
|
+
*/
|
|
174
|
+
function getCheckoutBaseUrl(storeId, environment) {
|
|
175
|
+
if (environment === "staging") return `https://${storeId}.staging.checkout.commercengine.com`;
|
|
176
|
+
return `https://${storeId}.checkout.commercengine.com`;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Build checkout iframe URL with store_id as subdomain.
|
|
180
|
+
* Environment is encoded in the hostname — no need for a query param.
|
|
177
181
|
*/
|
|
178
182
|
function buildCheckoutUrl(storeId, apiKey, environment) {
|
|
179
|
-
return `${
|
|
183
|
+
return `${getCheckoutBaseUrl(storeId, environment)}?api_key=${apiKey}&mode=iframe`;
|
|
180
184
|
}
|
|
181
185
|
var Checkout = class extends EventEmitter {
|
|
182
186
|
constructor(config) {
|
|
@@ -188,6 +192,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
188
192
|
total: 0,
|
|
189
193
|
currency: "INR"
|
|
190
194
|
};
|
|
195
|
+
this.tokenState = null;
|
|
191
196
|
this.hasQuickBuyParams = false;
|
|
192
197
|
this.config = config;
|
|
193
198
|
this.iframe = new IframeManager();
|
|
@@ -196,16 +201,20 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
196
201
|
}
|
|
197
202
|
initialize() {
|
|
198
203
|
let baseUrl;
|
|
199
|
-
if (this.config.url)
|
|
200
|
-
|
|
204
|
+
if (this.config.url) {
|
|
205
|
+
const parsedUrl = new URL(this.config.url);
|
|
206
|
+
if (this.config.storeId && parsedUrl.hostname === "localhost") {
|
|
207
|
+
const envPrefix = (this.config.environment || "staging") === "production" ? "prod" : "staging";
|
|
208
|
+
parsedUrl.hostname = `${this.config.storeId}.${envPrefix}.localhost`;
|
|
209
|
+
}
|
|
210
|
+
baseUrl = parsedUrl.toString();
|
|
211
|
+
} else if (this.config.storeId && this.config.apiKey) {
|
|
201
212
|
const env = this.config.environment || "production";
|
|
202
213
|
baseUrl = buildCheckoutUrl(this.config.storeId, this.config.apiKey, env);
|
|
203
214
|
} else throw new Error("[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided.");
|
|
204
215
|
const url = new URL(baseUrl);
|
|
205
216
|
url.searchParams.set("mode", "iframe");
|
|
206
|
-
if (this.config.storeId && !url.searchParams.has("store_id")) url.searchParams.set("store_id", this.config.storeId);
|
|
207
217
|
if (this.config.apiKey && !url.searchParams.has("api_key")) url.searchParams.set("api_key", this.config.apiKey);
|
|
208
|
-
if (this.config.environment && !url.searchParams.has("environment")) url.searchParams.set("environment", this.config.environment);
|
|
209
218
|
if (this.config.theme) url.searchParams.set("theme", this.config.theme);
|
|
210
219
|
if (this.config.authMode) url.searchParams.set("auth_mode", this.config.authMode);
|
|
211
220
|
if (this.config.accessToken) url.searchParams.set("token", this.config.accessToken);
|
|
@@ -234,11 +243,6 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
234
243
|
if (quickBuy.quantity && quickBuy.quantity !== 1) url.searchParams.set("qty", String(quickBuy.quantity));
|
|
235
244
|
}
|
|
236
245
|
if (sessionMode) url.searchParams.set("session_mode", sessionMode);
|
|
237
|
-
if (this.config.features?.loyalty === false) url.searchParams.set("loyalty", "false");
|
|
238
|
-
if (this.config.features?.coupons === false) url.searchParams.set("coupons", "false");
|
|
239
|
-
if (this.config.features?.freeShippingProgress === false) url.searchParams.set("free_shipping_progress", "false");
|
|
240
|
-
if (this.config.features?.productRecommendations === false) url.searchParams.set("product_recommendations", "false");
|
|
241
|
-
if (this.config.features?.collectInStore === true) url.searchParams.set("collect_in_store", "true");
|
|
242
246
|
if (typeof window !== "undefined") url.searchParams.set("parent_origin", window.location.origin);
|
|
243
247
|
const zIndex = this.config.appearance?.zIndex ?? 99999;
|
|
244
248
|
this.iframe.create(url.toString(), zIndex);
|
|
@@ -289,15 +293,15 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
289
293
|
case "cart:updated":
|
|
290
294
|
this.handleCartUpdate(data);
|
|
291
295
|
break;
|
|
296
|
+
case "auth:tokens-updated":
|
|
297
|
+
this.handleTokensUpdated(data);
|
|
298
|
+
break;
|
|
292
299
|
case "auth:login":
|
|
293
300
|
this.handleLogin(data);
|
|
294
301
|
break;
|
|
295
302
|
case "auth:logout":
|
|
296
303
|
this.handleLogout(data);
|
|
297
304
|
break;
|
|
298
|
-
case "auth:refresh":
|
|
299
|
-
this.handleTokenRefresh(data);
|
|
300
|
-
break;
|
|
301
305
|
case "auth:session-error":
|
|
302
306
|
this.handleSessionError();
|
|
303
307
|
break;
|
|
@@ -335,6 +339,11 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
335
339
|
this.config.onCartUpdate?.(data);
|
|
336
340
|
this.emit("cart:updated", data);
|
|
337
341
|
}
|
|
342
|
+
handleTokensUpdated(data) {
|
|
343
|
+
this.tokenState = data;
|
|
344
|
+
this.config.onTokensUpdated?.(data);
|
|
345
|
+
this.emit("auth:tokens-updated", data);
|
|
346
|
+
}
|
|
338
347
|
handleLogin(data) {
|
|
339
348
|
this.config.onLogin?.(data);
|
|
340
349
|
this.emit("auth:login", data);
|
|
@@ -343,11 +352,8 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
343
352
|
this.config.onLogout?.(data);
|
|
344
353
|
this.emit("auth:logout", data);
|
|
345
354
|
}
|
|
346
|
-
handleTokenRefresh(data) {
|
|
347
|
-
this.config.onTokenRefresh?.(data);
|
|
348
|
-
this.emit("auth:refresh", data);
|
|
349
|
-
}
|
|
350
355
|
handleSessionError() {
|
|
356
|
+
this.tokenState = null;
|
|
351
357
|
this.config.onSessionError?.();
|
|
352
358
|
this.emit("auth:session-error");
|
|
353
359
|
}
|
|
@@ -423,6 +429,14 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
423
429
|
return { ...this.cartState };
|
|
424
430
|
}
|
|
425
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
|
+
/**
|
|
426
440
|
* Check if checkout is ready
|
|
427
441
|
*/
|
|
428
442
|
get ready() {
|
|
@@ -447,6 +461,7 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
447
461
|
this.removeAllListeners();
|
|
448
462
|
this.isReady = false;
|
|
449
463
|
this.isOpen = false;
|
|
464
|
+
this.tokenState = null;
|
|
450
465
|
}
|
|
451
466
|
};
|
|
452
467
|
|
|
@@ -525,12 +540,11 @@ Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
525
540
|
}
|
|
526
541
|
}
|
|
527
542
|
}
|
|
528
|
-
var src_default = Commercengine;
|
|
529
543
|
|
|
530
544
|
//#endregion
|
|
531
545
|
exports.Checkout = Checkout;
|
|
532
546
|
exports.Commercengine = Commercengine;
|
|
533
|
-
exports.default =
|
|
547
|
+
exports.default = Commercengine;
|
|
534
548
|
return exports;
|
|
535
549
|
})({});
|
|
536
550
|
//# sourceMappingURL=index.iife.js.map
|
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/** Checkout URLs by environment */\nconst CHECKOUT_URLS: Record<Environment, string> = {\n production: \"https://checkout.commercengine.com\",\n staging: \"https://staging.checkout.commercengine.com\",\n};\n\n/**\n * Build checkout iframe URL\n */\nfunction buildCheckoutUrl(storeId: string, apiKey: string, environment: Environment): string {\n const baseUrl = CHECKOUT_URLS[environment];\n return `${baseUrl}?store_id=${storeId}&api_key=${apiKey}&environment=${environment}&mode=iframe`;\n}\n\n// =============================================================================\n// CHECKOUT CLASS\n// =============================================================================\n\nexport class Checkout extends EventEmitter {\n private config: CheckoutConfig;\n private iframe: IframeManager;\n private isReady = false;\n private isOpen = false;\n private cartState: CartData = { count: 0, total: 0, currency: \"INR\" };\n private boundMessageHandler: (event: MessageEvent) => void;\n private hasQuickBuyParams = false; // Track if we detected quick buy params\n\n constructor(config: CheckoutConfig) {\n super();\n this.config = config;\n this.iframe = new IframeManager();\n this.boundMessageHandler = this.handleMessage.bind(this);\n this.initialize();\n }\n\n // ===========================================================================\n // INITIALIZATION\n // ===========================================================================\n\n private initialize(): void {\n // Build checkout URL - use direct URL if provided, otherwise resolve from credentials\n let baseUrl: string;\n if (this.config.url) {\n // Direct URL provided (local development)\n baseUrl = this.config.url;\n } else if (this.config.storeId && this.config.apiKey) {\n // Build URL from credentials + environment\n const env = this.config.environment || \"production\";\n baseUrl = buildCheckoutUrl(this.config.storeId, this.config.apiKey, env);\n } else {\n throw new Error(\n \"[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided.\"\n );\n }\n\n const url = new URL(baseUrl);\n\n // Always set iframe mode\n url.searchParams.set(\"mode\", \"iframe\");\n\n // Add store credentials if provided and not already in URL\n if (this.config.storeId && !url.searchParams.has(\"store_id\")) {\n url.searchParams.set(\"store_id\", this.config.storeId);\n }\n if (this.config.apiKey && !url.searchParams.has(\"api_key\")) {\n url.searchParams.set(\"api_key\", this.config.apiKey);\n }\n if (this.config.environment && !url.searchParams.has(\"environment\")) {\n url.searchParams.set(\"environment\", this.config.environment);\n }\n\n // Add theme preference\n if (this.config.theme) {\n url.searchParams.set(\"theme\", this.config.theme);\n }\n\n // Add auth config\n if (this.config.authMode) {\n url.searchParams.set(\"auth_mode\", this.config.authMode);\n }\n if (this.config.accessToken) {\n url.searchParams.set(\"token\", this.config.accessToken);\n }\n if (this.config.refreshToken) {\n url.searchParams.set(\"refresh_token\", this.config.refreshToken);\n }\n\n // Auto-detect quick buy params from parent URL (if enabled and no explicit quickBuy)\n let quickBuy = this.config.quickBuy;\n let sessionMode = this.config.sessionMode;\n\n if (this.config.autoDetectQuickBuy && !quickBuy) {\n const parentParams = new URLSearchParams(window.location.search);\n const productId = parentParams.get(\"product_id\");\n const variantId = parentParams.get(\"variant_id\");\n\n if (productId) {\n quickBuy = {\n productId,\n variantId: variantId ?? null,\n quantity: parseInt(parentParams.get(\"qty\") || parentParams.get(\"quantity\") || \"1\", 10),\n };\n\n // Also check for session_mode in parent URL\n const parentSessionMode = parentParams.get(\"session_mode\");\n if (parentSessionMode === \"force-new\" || parentSessionMode === \"continue-existing\") {\n sessionMode = parentSessionMode;\n }\n\n // Clean quick buy params from URL to prevent duplicate adds on refresh\n // Deferred to next tick to handle React StrictMode double-mounting\n setTimeout(() => this.cleanQuickBuyParamsFromUrl(), 0);\n }\n }\n\n // Add quick buy params\n if (quickBuy) {\n this.hasQuickBuyParams = true;\n url.searchParams.set(\"product_id\", quickBuy.productId);\n if (quickBuy.variantId) {\n url.searchParams.set(\"variant_id\", quickBuy.variantId);\n }\n if (quickBuy.quantity && quickBuy.quantity !== 1) {\n url.searchParams.set(\"qty\", String(quickBuy.quantity));\n }\n }\n\n // Add session mode\n if (sessionMode) {\n url.searchParams.set(\"session_mode\", sessionMode);\n }\n\n // Add feature flags (only pass when overriding defaults)\n // Features default to true: pass when explicitly false\n if (this.config.features?.loyalty === false) {\n url.searchParams.set(\"loyalty\", \"false\");\n }\n if (this.config.features?.coupons === false) {\n url.searchParams.set(\"coupons\", \"false\");\n }\n if (this.config.features?.freeShippingProgress === false) {\n url.searchParams.set(\"free_shipping_progress\", \"false\");\n }\n if (this.config.features?.productRecommendations === false) {\n url.searchParams.set(\"product_recommendations\", \"false\");\n }\n // Features default to false: pass when explicitly true\n if (this.config.features?.collectInStore === true) {\n url.searchParams.set(\"collect_in_store\", \"true\");\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 Environment,\n ErrorData,\n LoginMethod,\n OrderData,\n QuickBuyConfig,\n SessionMode,\n UserInfo,\n} from \"./types\";\n\nexport { Checkout };\n\n// =============================================================================\n// GLOBAL API\n// =============================================================================\n\n/**\n * Global Commercengine object interface\n */\ninterface CommercengineGlobal {\n /**\n * Initialize checkout with configuration\n * @returns Promise that resolves to Checkout instance\n */\n init: (config: CheckoutConfig) => Promise<Checkout>;\n\n /**\n * Callback invoked when script has loaded\n * Set this BEFORE including the script to be notified when ready\n */\n onLoad?: () => void;\n\n /**\n * Current checkout instance (if initialized)\n */\n instance?: Checkout;\n}\n\n/**\n * Global Commercengine namespace\n */\nconst Commercengine: CommercengineGlobal = {\n /**\n * Initialize checkout\n *\n * @param config - Checkout configuration\n * @returns Checkout instance\n *\n * @example\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * theme: \"system\",\n * });\n */\n init: async (config: CheckoutConfig): Promise<Checkout> => {\n // Validate required fields - either url (for dev) or storeId+apiKey (for prod)\n if (!config.url && (!config.storeId || !config.apiKey)) {\n throw new Error(\n \"[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided\"\n );\n }\n\n // Destroy existing instance if any\n if (Commercengine.instance) {\n Commercengine.instance.destroy();\n }\n\n // Create new instance\n const checkout = new Checkout(config);\n Commercengine.instance = checkout;\n\n // Wait for ready or error with timeout\n return new Promise((resolve, reject) => {\n if (checkout.ready) {\n resolve(checkout);\n return;\n }\n\n const timeoutMs = 30000; // 30 second timeout\n const timeout = setTimeout(() => {\n reject(new Error(\"Checkout initialization timed out\"));\n }, timeoutMs);\n\n // Resolve on ready\n checkout.once(\"ready\", () => {\n clearTimeout(timeout);\n resolve(checkout);\n });\n\n // Also resolve on error (checkout is still usable, will show error drawer)\n checkout.once(\"error\", () => {\n clearTimeout(timeout);\n resolve(checkout);\n });\n });\n },\n};\n\n// =============================================================================\n// GLOBAL SETUP\n// =============================================================================\n\n// Expose on window for CDN usage\nif (typeof window !== \"undefined\") {\n // Preserve any existing onLoad callback set before script loaded\n const existingOnLoad = (window as unknown as { Commercengine?: CommercengineGlobal })\n .Commercengine?.onLoad;\n\n // Set global\n (window as unknown as { Commercengine: CommercengineGlobal }).Commercengine = Commercengine;\n\n // Restore and call onLoad if it was set\n if (existingOnLoad) {\n Commercengine.onLoad = existingOnLoad;\n try {\n existingOnLoad();\n } catch (error) {\n // biome-ignore lint/suspicious/noConsole: Intentional error logging\n console.error(\"[Commercengine] Error in onLoad callback:\", error);\n }\n }\n}\n\nexport { Commercengine };\nexport default Commercengine;\n"],"mappings":"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"}
|
|
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
|
@@ -163,16 +163,20 @@ var IframeManager = class {
|
|
|
163
163
|
* Main entry point for Commerce Engine Checkout integration.
|
|
164
164
|
* Manages iframe lifecycle, event handling, and provides public API.
|
|
165
165
|
*/
|
|
166
|
-
/** Checkout URLs by environment */
|
|
167
|
-
const CHECKOUT_URLS = {
|
|
168
|
-
production: "https://checkout.commercengine.com",
|
|
169
|
-
staging: "https://staging.checkout.commercengine.com"
|
|
170
|
-
};
|
|
171
166
|
/**
|
|
172
|
-
* Build checkout
|
|
167
|
+
* Build subdomain-based checkout base URL.
|
|
168
|
+
* Each store gets its own origin for localStorage isolation.
|
|
169
|
+
*/
|
|
170
|
+
function getCheckoutBaseUrl(storeId, environment) {
|
|
171
|
+
if (environment === "staging") return `https://${storeId}.staging.checkout.commercengine.com`;
|
|
172
|
+
return `https://${storeId}.checkout.commercengine.com`;
|
|
173
|
+
}
|
|
174
|
+
/**
|
|
175
|
+
* Build checkout iframe URL with store_id as subdomain.
|
|
176
|
+
* Environment is encoded in the hostname — no need for a query param.
|
|
173
177
|
*/
|
|
174
178
|
function buildCheckoutUrl(storeId, apiKey, environment) {
|
|
175
|
-
return `${
|
|
179
|
+
return `${getCheckoutBaseUrl(storeId, environment)}?api_key=${apiKey}&mode=iframe`;
|
|
176
180
|
}
|
|
177
181
|
var Checkout = class extends EventEmitter {
|
|
178
182
|
constructor(config) {
|
|
@@ -184,6 +188,7 @@ var Checkout = class extends EventEmitter {
|
|
|
184
188
|
total: 0,
|
|
185
189
|
currency: "INR"
|
|
186
190
|
};
|
|
191
|
+
this.tokenState = null;
|
|
187
192
|
this.hasQuickBuyParams = false;
|
|
188
193
|
this.config = config;
|
|
189
194
|
this.iframe = new IframeManager();
|
|
@@ -192,16 +197,20 @@ var Checkout = class extends EventEmitter {
|
|
|
192
197
|
}
|
|
193
198
|
initialize() {
|
|
194
199
|
let baseUrl;
|
|
195
|
-
if (this.config.url)
|
|
196
|
-
|
|
200
|
+
if (this.config.url) {
|
|
201
|
+
const parsedUrl = new URL(this.config.url);
|
|
202
|
+
if (this.config.storeId && parsedUrl.hostname === "localhost") {
|
|
203
|
+
const envPrefix = (this.config.environment || "staging") === "production" ? "prod" : "staging";
|
|
204
|
+
parsedUrl.hostname = `${this.config.storeId}.${envPrefix}.localhost`;
|
|
205
|
+
}
|
|
206
|
+
baseUrl = parsedUrl.toString();
|
|
207
|
+
} else if (this.config.storeId && this.config.apiKey) {
|
|
197
208
|
const env = this.config.environment || "production";
|
|
198
209
|
baseUrl = buildCheckoutUrl(this.config.storeId, this.config.apiKey, env);
|
|
199
210
|
} else throw new Error("[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided.");
|
|
200
211
|
const url = new URL(baseUrl);
|
|
201
212
|
url.searchParams.set("mode", "iframe");
|
|
202
|
-
if (this.config.storeId && !url.searchParams.has("store_id")) url.searchParams.set("store_id", this.config.storeId);
|
|
203
213
|
if (this.config.apiKey && !url.searchParams.has("api_key")) url.searchParams.set("api_key", this.config.apiKey);
|
|
204
|
-
if (this.config.environment && !url.searchParams.has("environment")) url.searchParams.set("environment", this.config.environment);
|
|
205
214
|
if (this.config.theme) url.searchParams.set("theme", this.config.theme);
|
|
206
215
|
if (this.config.authMode) url.searchParams.set("auth_mode", this.config.authMode);
|
|
207
216
|
if (this.config.accessToken) url.searchParams.set("token", this.config.accessToken);
|
|
@@ -230,11 +239,6 @@ var Checkout = class extends EventEmitter {
|
|
|
230
239
|
if (quickBuy.quantity && quickBuy.quantity !== 1) url.searchParams.set("qty", String(quickBuy.quantity));
|
|
231
240
|
}
|
|
232
241
|
if (sessionMode) url.searchParams.set("session_mode", sessionMode);
|
|
233
|
-
if (this.config.features?.loyalty === false) url.searchParams.set("loyalty", "false");
|
|
234
|
-
if (this.config.features?.coupons === false) url.searchParams.set("coupons", "false");
|
|
235
|
-
if (this.config.features?.freeShippingProgress === false) url.searchParams.set("free_shipping_progress", "false");
|
|
236
|
-
if (this.config.features?.productRecommendations === false) url.searchParams.set("product_recommendations", "false");
|
|
237
|
-
if (this.config.features?.collectInStore === true) url.searchParams.set("collect_in_store", "true");
|
|
238
242
|
if (typeof window !== "undefined") url.searchParams.set("parent_origin", window.location.origin);
|
|
239
243
|
const zIndex = this.config.appearance?.zIndex ?? 99999;
|
|
240
244
|
this.iframe.create(url.toString(), zIndex);
|
|
@@ -285,15 +289,15 @@ var Checkout = class extends EventEmitter {
|
|
|
285
289
|
case "cart:updated":
|
|
286
290
|
this.handleCartUpdate(data);
|
|
287
291
|
break;
|
|
292
|
+
case "auth:tokens-updated":
|
|
293
|
+
this.handleTokensUpdated(data);
|
|
294
|
+
break;
|
|
288
295
|
case "auth:login":
|
|
289
296
|
this.handleLogin(data);
|
|
290
297
|
break;
|
|
291
298
|
case "auth:logout":
|
|
292
299
|
this.handleLogout(data);
|
|
293
300
|
break;
|
|
294
|
-
case "auth:refresh":
|
|
295
|
-
this.handleTokenRefresh(data);
|
|
296
|
-
break;
|
|
297
301
|
case "auth:session-error":
|
|
298
302
|
this.handleSessionError();
|
|
299
303
|
break;
|
|
@@ -331,6 +335,11 @@ var Checkout = class extends EventEmitter {
|
|
|
331
335
|
this.config.onCartUpdate?.(data);
|
|
332
336
|
this.emit("cart:updated", data);
|
|
333
337
|
}
|
|
338
|
+
handleTokensUpdated(data) {
|
|
339
|
+
this.tokenState = data;
|
|
340
|
+
this.config.onTokensUpdated?.(data);
|
|
341
|
+
this.emit("auth:tokens-updated", data);
|
|
342
|
+
}
|
|
334
343
|
handleLogin(data) {
|
|
335
344
|
this.config.onLogin?.(data);
|
|
336
345
|
this.emit("auth:login", data);
|
|
@@ -339,11 +348,8 @@ var Checkout = class extends EventEmitter {
|
|
|
339
348
|
this.config.onLogout?.(data);
|
|
340
349
|
this.emit("auth:logout", data);
|
|
341
350
|
}
|
|
342
|
-
handleTokenRefresh(data) {
|
|
343
|
-
this.config.onTokenRefresh?.(data);
|
|
344
|
-
this.emit("auth:refresh", data);
|
|
345
|
-
}
|
|
346
351
|
handleSessionError() {
|
|
352
|
+
this.tokenState = null;
|
|
347
353
|
this.config.onSessionError?.();
|
|
348
354
|
this.emit("auth:session-error");
|
|
349
355
|
}
|
|
@@ -419,6 +425,14 @@ var Checkout = class extends EventEmitter {
|
|
|
419
425
|
return { ...this.cartState };
|
|
420
426
|
}
|
|
421
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
|
+
/**
|
|
422
436
|
* Check if checkout is ready
|
|
423
437
|
*/
|
|
424
438
|
get ready() {
|
|
@@ -443,6 +457,7 @@ var Checkout = class extends EventEmitter {
|
|
|
443
457
|
this.removeAllListeners();
|
|
444
458
|
this.isReady = false;
|
|
445
459
|
this.isOpen = false;
|
|
460
|
+
this.tokenState = null;
|
|
446
461
|
}
|
|
447
462
|
};
|
|
448
463
|
|
|
@@ -521,8 +536,7 @@ if (typeof window !== "undefined") {
|
|
|
521
536
|
}
|
|
522
537
|
}
|
|
523
538
|
}
|
|
524
|
-
var src_default = Commercengine;
|
|
525
539
|
|
|
526
540
|
//#endregion
|
|
527
|
-
export { Checkout, Commercengine,
|
|
541
|
+
export { Checkout, Commercengine, Commercengine as default };
|
|
528
542
|
//# sourceMappingURL=index.mjs.map
|
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/** Checkout URLs by environment */\nconst CHECKOUT_URLS: Record<Environment, string> = {\n production: \"https://checkout.commercengine.com\",\n staging: \"https://staging.checkout.commercengine.com\",\n};\n\n/**\n * Build checkout iframe URL\n */\nfunction buildCheckoutUrl(storeId: string, apiKey: string, environment: Environment): string {\n const baseUrl = CHECKOUT_URLS[environment];\n return `${baseUrl}?store_id=${storeId}&api_key=${apiKey}&environment=${environment}&mode=iframe`;\n}\n\n// =============================================================================\n// CHECKOUT CLASS\n// =============================================================================\n\nexport class Checkout extends EventEmitter {\n private config: CheckoutConfig;\n private iframe: IframeManager;\n private isReady = false;\n private isOpen = false;\n private cartState: CartData = { count: 0, total: 0, currency: \"INR\" };\n private boundMessageHandler: (event: MessageEvent) => void;\n private hasQuickBuyParams = false; // Track if we detected quick buy params\n\n constructor(config: CheckoutConfig) {\n super();\n this.config = config;\n this.iframe = new IframeManager();\n this.boundMessageHandler = this.handleMessage.bind(this);\n this.initialize();\n }\n\n // ===========================================================================\n // INITIALIZATION\n // ===========================================================================\n\n private initialize(): void {\n // Build checkout URL - use direct URL if provided, otherwise resolve from credentials\n let baseUrl: string;\n if (this.config.url) {\n // Direct URL provided (local development)\n baseUrl = this.config.url;\n } else if (this.config.storeId && this.config.apiKey) {\n // Build URL from credentials + environment\n const env = this.config.environment || \"production\";\n baseUrl = buildCheckoutUrl(this.config.storeId, this.config.apiKey, env);\n } else {\n throw new Error(\n \"[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided.\"\n );\n }\n\n const url = new URL(baseUrl);\n\n // Always set iframe mode\n url.searchParams.set(\"mode\", \"iframe\");\n\n // Add store credentials if provided and not already in URL\n if (this.config.storeId && !url.searchParams.has(\"store_id\")) {\n url.searchParams.set(\"store_id\", this.config.storeId);\n }\n if (this.config.apiKey && !url.searchParams.has(\"api_key\")) {\n url.searchParams.set(\"api_key\", this.config.apiKey);\n }\n if (this.config.environment && !url.searchParams.has(\"environment\")) {\n url.searchParams.set(\"environment\", this.config.environment);\n }\n\n // Add theme preference\n if (this.config.theme) {\n url.searchParams.set(\"theme\", this.config.theme);\n }\n\n // Add auth config\n if (this.config.authMode) {\n url.searchParams.set(\"auth_mode\", this.config.authMode);\n }\n if (this.config.accessToken) {\n url.searchParams.set(\"token\", this.config.accessToken);\n }\n if (this.config.refreshToken) {\n url.searchParams.set(\"refresh_token\", this.config.refreshToken);\n }\n\n // Auto-detect quick buy params from parent URL (if enabled and no explicit quickBuy)\n let quickBuy = this.config.quickBuy;\n let sessionMode = this.config.sessionMode;\n\n if (this.config.autoDetectQuickBuy && !quickBuy) {\n const parentParams = new URLSearchParams(window.location.search);\n const productId = parentParams.get(\"product_id\");\n const variantId = parentParams.get(\"variant_id\");\n\n if (productId) {\n quickBuy = {\n productId,\n variantId: variantId ?? null,\n quantity: parseInt(parentParams.get(\"qty\") || parentParams.get(\"quantity\") || \"1\", 10),\n };\n\n // Also check for session_mode in parent URL\n const parentSessionMode = parentParams.get(\"session_mode\");\n if (parentSessionMode === \"force-new\" || parentSessionMode === \"continue-existing\") {\n sessionMode = parentSessionMode;\n }\n\n // Clean quick buy params from URL to prevent duplicate adds on refresh\n // Deferred to next tick to handle React StrictMode double-mounting\n setTimeout(() => this.cleanQuickBuyParamsFromUrl(), 0);\n }\n }\n\n // Add quick buy params\n if (quickBuy) {\n this.hasQuickBuyParams = true;\n url.searchParams.set(\"product_id\", quickBuy.productId);\n if (quickBuy.variantId) {\n url.searchParams.set(\"variant_id\", quickBuy.variantId);\n }\n if (quickBuy.quantity && quickBuy.quantity !== 1) {\n url.searchParams.set(\"qty\", String(quickBuy.quantity));\n }\n }\n\n // Add session mode\n if (sessionMode) {\n url.searchParams.set(\"session_mode\", sessionMode);\n }\n\n // Add feature flags (only pass when overriding defaults)\n // Features default to true: pass when explicitly false\n if (this.config.features?.loyalty === false) {\n url.searchParams.set(\"loyalty\", \"false\");\n }\n if (this.config.features?.coupons === false) {\n url.searchParams.set(\"coupons\", \"false\");\n }\n if (this.config.features?.freeShippingProgress === false) {\n url.searchParams.set(\"free_shipping_progress\", \"false\");\n }\n if (this.config.features?.productRecommendations === false) {\n url.searchParams.set(\"product_recommendations\", \"false\");\n }\n // Features default to false: pass when explicitly true\n if (this.config.features?.collectInStore === true) {\n url.searchParams.set(\"collect_in_store\", \"true\");\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 Environment,\n ErrorData,\n LoginMethod,\n OrderData,\n QuickBuyConfig,\n SessionMode,\n UserInfo,\n} from \"./types\";\n\nexport { Checkout };\n\n// =============================================================================\n// GLOBAL API\n// =============================================================================\n\n/**\n * Global Commercengine object interface\n */\ninterface CommercengineGlobal {\n /**\n * Initialize checkout with configuration\n * @returns Promise that resolves to Checkout instance\n */\n init: (config: CheckoutConfig) => Promise<Checkout>;\n\n /**\n * Callback invoked when script has loaded\n * Set this BEFORE including the script to be notified when ready\n */\n onLoad?: () => void;\n\n /**\n * Current checkout instance (if initialized)\n */\n instance?: Checkout;\n}\n\n/**\n * Global Commercengine namespace\n */\nconst Commercengine: CommercengineGlobal = {\n /**\n * Initialize checkout\n *\n * @param config - Checkout configuration\n * @returns Checkout instance\n *\n * @example\n * const checkout = await Commercengine.init({\n * storeId: \"store_xxx\",\n * apiKey: \"ak_xxx\",\n * theme: \"system\",\n * });\n */\n init: async (config: CheckoutConfig): Promise<Checkout> => {\n // Validate required fields - either url (for dev) or storeId+apiKey (for prod)\n if (!config.url && (!config.storeId || !config.apiKey)) {\n throw new Error(\n \"[Commercengine] Either 'url' or both 'storeId' and 'apiKey' must be provided\"\n );\n }\n\n // Destroy existing instance if any\n if (Commercengine.instance) {\n Commercengine.instance.destroy();\n }\n\n // Create new instance\n const checkout = new Checkout(config);\n Commercengine.instance = checkout;\n\n // Wait for ready or error with timeout\n return new Promise((resolve, reject) => {\n if (checkout.ready) {\n resolve(checkout);\n return;\n }\n\n const timeoutMs = 30000; // 30 second timeout\n const timeout = setTimeout(() => {\n reject(new Error(\"Checkout initialization timed out\"));\n }, timeoutMs);\n\n // Resolve on ready\n checkout.once(\"ready\", () => {\n clearTimeout(timeout);\n resolve(checkout);\n });\n\n // Also resolve on error (checkout is still usable, will show error drawer)\n checkout.once(\"error\", () => {\n clearTimeout(timeout);\n resolve(checkout);\n });\n });\n },\n};\n\n// =============================================================================\n// GLOBAL SETUP\n// =============================================================================\n\n// Expose on window for CDN usage\nif (typeof window !== \"undefined\") {\n // Preserve any existing onLoad callback set before script loaded\n const existingOnLoad = (window as unknown as { Commercengine?: CommercengineGlobal })\n .Commercengine?.onLoad;\n\n // Set global\n (window as unknown as { Commercengine: CommercengineGlobal }).Commercengine = Commercengine;\n\n // Restore and call onLoad if it was set\n if (existingOnLoad) {\n Commercengine.onLoad = existingOnLoad;\n try {\n existingOnLoad();\n } catch (error) {\n // biome-ignore lint/suspicious/noConsole: Intentional error logging\n console.error(\"[Commercengine] Error in onLoad callback:\", error);\n }\n }\n}\n\nexport { Commercengine };\nexport default Commercengine;\n"],"mappings":"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"}
|
|
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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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@commercengine/js",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Commerce Engine Checkout - Embeddable checkout SDK",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "dist/index.mjs",
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
"dist"
|
|
18
18
|
],
|
|
19
19
|
"devDependencies": {
|
|
20
|
-
"tsdown": "^0.20.
|
|
20
|
+
"tsdown": "^0.20.3",
|
|
21
21
|
"typescript": "^5.9.3"
|
|
22
22
|
},
|
|
23
23
|
"keywords": [
|