@behindthescenes/cart 0.0.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +413 -0
- package/dist/browser/browser.js +1 -0
- package/dist/cjs/index.js +1 -0
- package/dist/cjs/package.json +3 -0
- package/dist/esm/index.js +1 -0
- package/dist/manifest.json +11 -0
- package/dist/types/browser.d.ts +3 -0
- package/dist/types/constants.d.ts +4 -0
- package/dist/types/index.d.ts +41 -0
- package/dist/types/types/browser.types.d.ts +14 -0
- package/dist/types/types/index.types.d.ts +75 -0
- package/package.json +65 -0
package/README.md
ADDED
|
@@ -0,0 +1,413 @@
|
|
|
1
|
+
# `@behindthescenes/cart`
|
|
2
|
+
|
|
3
|
+
Browser SDK for BTS external-site cart state and checkout-link handoff. Use it to persist selected products, validate availability with the BTS API, and send shoppers to static or dynamic BTS checkout links.
|
|
4
|
+
|
|
5
|
+
## Overview
|
|
6
|
+
|
|
7
|
+
The BTS Cart SDK provides:
|
|
8
|
+
|
|
9
|
+
- **Persistent cart state** - Selected products and quantities are stored in browser `localStorage` by default.
|
|
10
|
+
- **Dynamic checkout links** - Generates BTS `/c/:checkoutLinkId?cart=...` URLs from `{ lineItemId, quantity, discount }` cart items.
|
|
11
|
+
- **Static checkout links** - Supports one-click static checkout links through the same SDK API.
|
|
12
|
+
- **Multiple checkout links** - Configure named checkout links and choose which one to use at checkout.
|
|
13
|
+
- **Site-key protection** - Cart URL generation is validated by the BTS website cart API using the same public site key and verified-domain model as `@behindthescenes/analytics`.
|
|
14
|
+
- **Availability validation** - The API validates checkout link status, products/access tiers, requested quantity, and discounts before returning a checkout URL.
|
|
15
|
+
|
|
16
|
+
## Installation
|
|
17
|
+
|
|
18
|
+
### npm/yarn/pnpm/bun
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
npm install @behindthescenes/cart
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
```bash
|
|
25
|
+
pnpm add @behindthescenes/cart
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
```bash
|
|
29
|
+
yarn add @behindthescenes/cart
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
bun add @behindthescenes/cart
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
### Browser Bundle (Hosted)
|
|
37
|
+
|
|
38
|
+
```html
|
|
39
|
+
<script type="module">
|
|
40
|
+
import { createBTSCart } from "https://behindthescenes.com/sdk/@behindthescenes/cart/latest/browser/browser.js";
|
|
41
|
+
|
|
42
|
+
window.btsCart = createBTSCart({
|
|
43
|
+
siteKey: "your-public-site-key",
|
|
44
|
+
checkoutLinks: {
|
|
45
|
+
tickets: {
|
|
46
|
+
checkoutLinkId: "your-checkout-link-id",
|
|
47
|
+
mode: "dynamic"
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
defaultCheckoutLinkKey: "tickets"
|
|
51
|
+
});
|
|
52
|
+
</script>
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Quick Start
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
import { createBTSCart } from "@behindthescenes/cart";
|
|
59
|
+
|
|
60
|
+
const cart = createBTSCart({
|
|
61
|
+
siteKey: "your-public-site-key",
|
|
62
|
+
checkoutLinks: {
|
|
63
|
+
tickets: {
|
|
64
|
+
checkoutLinkId: "https://dev.behindthescenes.com/c/1a9d4f91-c1dd-455d-aa4c-851b296aff75",
|
|
65
|
+
mode: "dynamic",
|
|
66
|
+
},
|
|
67
|
+
},
|
|
68
|
+
defaultCheckoutLinkKey: "tickets",
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
cart.addItem({
|
|
72
|
+
id: "garnet",
|
|
73
|
+
lineItemId: "2dd3b989-616e-4e2f-9c47-21e1288d3823",
|
|
74
|
+
checkoutLinkKey: "tickets",
|
|
75
|
+
name: "Garnet",
|
|
76
|
+
unitPrice: 395,
|
|
77
|
+
fullPrice: 550,
|
|
78
|
+
quantity: 1,
|
|
79
|
+
discount: "GARNET-EARLYBIRD",
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
await cart.checkout("tickets");
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
## Setup in BTS
|
|
86
|
+
|
|
87
|
+
Before using the SDK, configure the destination site in BTS:
|
|
88
|
+
|
|
89
|
+
1. In BTS, go to the relevant space.
|
|
90
|
+
2. Configure the external website integration and copy the public site key.
|
|
91
|
+
3. Verify the external site domain for production use.
|
|
92
|
+
4. Create a checkout link.
|
|
93
|
+
5. For dynamic checkout links, use either the checkout link ID or the full checkout link URL plus the relevant product/access-tier line item IDs.
|
|
94
|
+
|
|
95
|
+
Local development can use localhost origins without verified-domain matching when the API runs in a local/development/test environment.
|
|
96
|
+
|
|
97
|
+
## Dynamic Checkout Links
|
|
98
|
+
|
|
99
|
+
Dynamic checkout links use the BTS checkout cart query parameter:
|
|
100
|
+
|
|
101
|
+
```ts
|
|
102
|
+
encodeURIComponent(
|
|
103
|
+
JSON.stringify([
|
|
104
|
+
{
|
|
105
|
+
lineItemId: "2dd3b989-616e-4e2f-9c47-21e1288d3823",
|
|
106
|
+
quantity: 1,
|
|
107
|
+
discount: "c16cfeea-c604-4009-851c-05e6dcdfbcea",
|
|
108
|
+
},
|
|
109
|
+
]),
|
|
110
|
+
);
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
The SDK sends that cart payload to the BTS API for validation and receives a final checkout URL. The returned URL is equivalent to:
|
|
114
|
+
|
|
115
|
+
```text
|
|
116
|
+
https://behindthescenes.com/c/<checkoutLinkId>?cart=<encoded-json>
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
For dynamic checkout links with no saved checkout-link line items, BTS resolves valid line items from the checkout link space’s public products and access tiers. In that case, `lineItemId` is the product ID or access tier ID.
|
|
120
|
+
|
|
121
|
+
`discount` can be either the internal promo-code ID or the customer-facing promo code. The cart API resolves customer-facing codes to the internal IDs required by the BTS checkout page before returning the final URL.
|
|
122
|
+
|
|
123
|
+
Checkout link config also accepts full BTS checkout URLs:
|
|
124
|
+
|
|
125
|
+
```ts
|
|
126
|
+
const cart = createBTSCart({
|
|
127
|
+
siteKey: "your-public-site-key",
|
|
128
|
+
checkoutLinks: {
|
|
129
|
+
tickets: {
|
|
130
|
+
checkoutLinkId: "https://behindthescenes.com/c/1a9d4f91-c1dd-455d-aa4c-851b296aff75?discountCode=EARLYBIRD",
|
|
131
|
+
mode: "dynamic",
|
|
132
|
+
},
|
|
133
|
+
},
|
|
134
|
+
});
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The SDK extracts the checkout link ID and default `discountCode` from the URL.
|
|
138
|
+
|
|
139
|
+
## Static Checkout Links
|
|
140
|
+
|
|
141
|
+
```ts
|
|
142
|
+
const cart = createBTSCart({
|
|
143
|
+
siteKey: "your-public-site-key",
|
|
144
|
+
checkoutLinks: {
|
|
145
|
+
vip: {
|
|
146
|
+
checkoutLinkId: "static-checkout-link-id",
|
|
147
|
+
mode: "static",
|
|
148
|
+
discountCode: "VIP-EARLYBIRD",
|
|
149
|
+
},
|
|
150
|
+
},
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
await cart.checkout("vip");
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
Static links ignore cart items and validate the configured checkout link before redirecting.
|
|
157
|
+
|
|
158
|
+
## Multiple Checkout Links
|
|
159
|
+
|
|
160
|
+
```ts
|
|
161
|
+
const cart = createBTSCart({
|
|
162
|
+
siteKey: "your-public-site-key",
|
|
163
|
+
checkoutLinks: {
|
|
164
|
+
tickets: {
|
|
165
|
+
checkoutLinkId: "dynamic-ticket-link-id",
|
|
166
|
+
mode: "dynamic",
|
|
167
|
+
},
|
|
168
|
+
merch: {
|
|
169
|
+
checkoutLinkId: "dynamic-merch-link-id",
|
|
170
|
+
mode: "dynamic",
|
|
171
|
+
},
|
|
172
|
+
},
|
|
173
|
+
defaultCheckoutLinkKey: "tickets",
|
|
174
|
+
});
|
|
175
|
+
|
|
176
|
+
cart.addItem({
|
|
177
|
+
id: "shirt",
|
|
178
|
+
lineItemId: "product-line-item-id",
|
|
179
|
+
checkoutLinkKey: "merch",
|
|
180
|
+
name: "Event Shirt",
|
|
181
|
+
unitPrice: 49,
|
|
182
|
+
});
|
|
183
|
+
|
|
184
|
+
await cart.checkout("merch");
|
|
185
|
+
```
|
|
186
|
+
|
|
187
|
+
## Persistence
|
|
188
|
+
|
|
189
|
+
The SDK persists cart state to `localStorage` by default.
|
|
190
|
+
|
|
191
|
+
```ts
|
|
192
|
+
const cart = createBTSCart({
|
|
193
|
+
siteKey: "your-public-site-key",
|
|
194
|
+
storageKey: "my-site-cart",
|
|
195
|
+
persist: true,
|
|
196
|
+
});
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
Disable persistence:
|
|
200
|
+
|
|
201
|
+
```ts
|
|
202
|
+
const cart = createBTSCart({
|
|
203
|
+
siteKey: "your-public-site-key",
|
|
204
|
+
persist: false,
|
|
205
|
+
});
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
Provide a custom storage adapter:
|
|
209
|
+
|
|
210
|
+
```ts
|
|
211
|
+
const cart = createBTSCart({
|
|
212
|
+
siteKey: "your-public-site-key",
|
|
213
|
+
storage: {
|
|
214
|
+
getItem: (key) => sessionStorage.getItem(key),
|
|
215
|
+
setItem: (key, value) => sessionStorage.setItem(key, value),
|
|
216
|
+
removeItem: (key) => sessionStorage.removeItem(key),
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
```
|
|
220
|
+
|
|
221
|
+
## State API
|
|
222
|
+
|
|
223
|
+
```ts
|
|
224
|
+
cart.addItem(item);
|
|
225
|
+
cart.removeItem("garnet");
|
|
226
|
+
cart.setQuantity("garnet", 2);
|
|
227
|
+
cart.increment("garnet");
|
|
228
|
+
cart.decrement("garnet");
|
|
229
|
+
cart.clear();
|
|
230
|
+
|
|
231
|
+
const items = cart.getItems();
|
|
232
|
+
const itemCount = cart.getItemCount();
|
|
233
|
+
const subtotal = cart.getSubtotal();
|
|
234
|
+
const total = cart.getTotal();
|
|
235
|
+
```
|
|
236
|
+
|
|
237
|
+
Subscribe to changes:
|
|
238
|
+
|
|
239
|
+
```ts
|
|
240
|
+
const unsubscribe = cart.subscribe((state) => {
|
|
241
|
+
console.log(state.items);
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
unsubscribe();
|
|
245
|
+
```
|
|
246
|
+
|
|
247
|
+
## Checkout API
|
|
248
|
+
|
|
249
|
+
Return a checkout URL without redirecting:
|
|
250
|
+
|
|
251
|
+
```ts
|
|
252
|
+
const url = await cart.resolveCheckoutUrl("tickets");
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
Validate and redirect:
|
|
256
|
+
|
|
257
|
+
```ts
|
|
258
|
+
await cart.checkout("tickets");
|
|
259
|
+
```
|
|
260
|
+
|
|
261
|
+
## React Integration
|
|
262
|
+
|
|
263
|
+
```tsx
|
|
264
|
+
import { createBTSCart, type BTSCart, type BTSCartItem } from "@behindthescenes/cart";
|
|
265
|
+
import { createContext, useContext, useEffect, useMemo, useState } from "react";
|
|
266
|
+
|
|
267
|
+
const CartContext = createContext<BTSCart | null>(null);
|
|
268
|
+
|
|
269
|
+
export function CartProvider({ children }: { children: React.ReactNode }) {
|
|
270
|
+
const cart = useMemo(
|
|
271
|
+
() =>
|
|
272
|
+
createBTSCart({
|
|
273
|
+
siteKey: "your-public-site-key",
|
|
274
|
+
checkoutLinks: {
|
|
275
|
+
tickets: {
|
|
276
|
+
checkoutLinkId: "dynamic-ticket-link-id",
|
|
277
|
+
mode: "dynamic",
|
|
278
|
+
},
|
|
279
|
+
},
|
|
280
|
+
defaultCheckoutLinkKey: "tickets",
|
|
281
|
+
}),
|
|
282
|
+
[],
|
|
283
|
+
);
|
|
284
|
+
|
|
285
|
+
return <CartContext.Provider value={cart}>{children}</CartContext.Provider>;
|
|
286
|
+
}
|
|
287
|
+
|
|
288
|
+
export function useCartItems(): BTSCartItem[] {
|
|
289
|
+
const cart = useContext(CartContext);
|
|
290
|
+
if (!cart) throw new Error("useCartItems must be used inside CartProvider");
|
|
291
|
+
|
|
292
|
+
const [items, setItems] = useState(() => cart.getItems());
|
|
293
|
+
|
|
294
|
+
useEffect(() => cart.subscribe((state) => setItems(state.items)), [cart]);
|
|
295
|
+
|
|
296
|
+
return items;
|
|
297
|
+
}
|
|
298
|
+
```
|
|
299
|
+
|
|
300
|
+
## Configuration
|
|
301
|
+
|
|
302
|
+
```ts
|
|
303
|
+
type BTSCartInit = {
|
|
304
|
+
siteKey: string;
|
|
305
|
+
endpoint?: string;
|
|
306
|
+
checkoutLinks?: Record<string, BTSCartCheckoutLinkConfig>;
|
|
307
|
+
defaultCheckoutLinkKey?: string;
|
|
308
|
+
debug?: boolean;
|
|
309
|
+
persist?: boolean;
|
|
310
|
+
storageKey?: string;
|
|
311
|
+
storage?: BTSCartStorage;
|
|
312
|
+
requestHeaders?: HeadersInit | ((request: BTSCartRequestContext) => HeadersInit | Promise<HeadersInit>);
|
|
313
|
+
};
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
Defaults:
|
|
317
|
+
|
|
318
|
+
- `endpoint`: `https://api.bts.it.com/v2/website/cart`
|
|
319
|
+
- `persist`: `true`
|
|
320
|
+
- `storageKey`: `bts-cart`
|
|
321
|
+
- `defaultCheckoutLinkKey`: first configured checkout link key, or `default`
|
|
322
|
+
|
|
323
|
+
## Browser Global API
|
|
324
|
+
|
|
325
|
+
The hosted browser bundle installs:
|
|
326
|
+
|
|
327
|
+
- `window.BTSCart`
|
|
328
|
+
- `window.createBTSCart`
|
|
329
|
+
- `window.btsCart`
|
|
330
|
+
- `window.btsCartDataLayer`
|
|
331
|
+
- `window.btsCartCommand`
|
|
332
|
+
|
|
333
|
+
Queued command example:
|
|
334
|
+
|
|
335
|
+
```html
|
|
336
|
+
<script>
|
|
337
|
+
window.btsCartDataLayer = window.btsCartDataLayer || [];
|
|
338
|
+
function btsCartCommand(){window.btsCartDataLayer.push(arguments);}
|
|
339
|
+
|
|
340
|
+
btsCartCommand("config", {
|
|
341
|
+
siteKey: "your-public-site-key",
|
|
342
|
+
checkoutLinks: {
|
|
343
|
+
tickets: {
|
|
344
|
+
checkoutLinkId: "dynamic-ticket-link-id",
|
|
345
|
+
mode: "dynamic"
|
|
346
|
+
}
|
|
347
|
+
}
|
|
348
|
+
});
|
|
349
|
+
</script>
|
|
350
|
+
```
|
|
351
|
+
|
|
352
|
+
## Development
|
|
353
|
+
|
|
354
|
+
```bash
|
|
355
|
+
bun install
|
|
356
|
+
bun run type-check
|
|
357
|
+
bun test
|
|
358
|
+
bun run build
|
|
359
|
+
```
|
|
360
|
+
|
|
361
|
+
Build artifacts:
|
|
362
|
+
|
|
363
|
+
- `dist/esm/index.js`
|
|
364
|
+
- `dist/cjs/index.js`
|
|
365
|
+
- `dist/browser/browser.js`
|
|
366
|
+
- `dist/types/index.d.ts`
|
|
367
|
+
- `dist/manifest.json`
|
|
368
|
+
|
|
369
|
+
## Hosted Bundle
|
|
370
|
+
|
|
371
|
+
Sync the hosted bundle into `apps/bts-web/public/sdk/@behindthescenes/cart`:
|
|
372
|
+
|
|
373
|
+
```bash
|
|
374
|
+
bun run build:hosted
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
Force a rebuild before syncing:
|
|
378
|
+
|
|
379
|
+
```bash
|
|
380
|
+
bun run build:hosted:force
|
|
381
|
+
```
|
|
382
|
+
|
|
383
|
+
Hosted layout matches `@behindthescenes/analytics`:
|
|
384
|
+
|
|
385
|
+
- `latest`
|
|
386
|
+
- one major-version directory, for example `0` or `1`
|
|
387
|
+
|
|
388
|
+
## Release
|
|
389
|
+
|
|
390
|
+
The cart SDK uses the same release pipeline as `@behindthescenes/analytics`.
|
|
391
|
+
|
|
392
|
+
Manual release from GitHub Actions:
|
|
393
|
+
|
|
394
|
+
1. Open **Cart SDK Release**.
|
|
395
|
+
2. Choose `patch`, `minor`, or `major`.
|
|
396
|
+
3. Run the workflow from `main`.
|
|
397
|
+
|
|
398
|
+
The workflow:
|
|
399
|
+
|
|
400
|
+
- installs dependencies with Bun
|
|
401
|
+
- bumps the package version
|
|
402
|
+
- runs `bun run release:check`
|
|
403
|
+
- publishes with `npm publish --provenance --access public`
|
|
404
|
+
- commits the version bump back to `main`
|
|
405
|
+
|
|
406
|
+
Tag release:
|
|
407
|
+
|
|
408
|
+
```bash
|
|
409
|
+
git tag cart-sdk-v0.0.2
|
|
410
|
+
git push origin cart-sdk-v0.0.2
|
|
411
|
+
```
|
|
412
|
+
|
|
413
|
+
The tag version is applied to `packages/cart/package.json` before publishing.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var R="https://api.bts.it.com/v2/website/cart",v="bts-cart",F="default",G=1;class S extends Error{code;status;constructor(C,w,f){super(w);this.name="BTSCartError",this.code=C,this.status=f}}function M(){return typeof window>"u"?null:window}function Y(){try{return M()?.localStorage}catch{return}}function Z(C){return(C??R).replace(/\/$/,"")}function _(C){if(!Number.isFinite(C??1))return 1;return Math.max(1,Math.floor(C??1))}function J(C){return{items:C.map((w)=>({...w}))}}function $(C,w){let f=new Headers(C);if(!w)return f;return new Headers(w).forEach((r,B)=>f.set(B,r)),f}function z(C){let w=C.checkoutLinkId.trim();try{let f=new URL(w.includes("://")?w:w.startsWith("/")?`https://behindthescenes.com${w}`:w),r=f.pathname.split("/").filter(Boolean),B=r.indexOf("c"),b=B>=0?r[B+1]:r.at(-1);if(b)return{checkoutLinkId:b,discountCode:C.discountCode??f.searchParams.get("discountCode")??void 0}}catch{}return{checkoutLinkId:w,discountCode:C.discountCode}}class T{siteKey;endpoint;debug;persist;storageKey;storage;checkoutLinks;defaultCheckoutLinkKey;requestHeaders;items=[];listeners=new Set;constructor(C){this.siteKey=C.siteKey,this.endpoint=Z(C.endpoint),this.debug=C.debug??!1,this.persist=C.persist??!0,this.storageKey=C.storageKey??v,this.storage=C.storage??Y(),this.checkoutLinks=C.checkoutLinks??{},this.defaultCheckoutLinkKey=C.defaultCheckoutLinkKey??Object.keys(this.checkoutLinks)[0]??F,this.requestHeaders=C.requestHeaders,this.rehydrate()}static init(C){return new T(C)}getItems(){return J(this.items).items}getState(){return J(this.items)}getItemCount(){return this.items.reduce((C,w)=>C+w.quantity,0)}getSubtotal(){return this.items.reduce((C,w)=>C+(w.fullPrice??w.unitPrice)*w.quantity,0)}getTotal(){return this.items.reduce((C,w)=>C+w.unitPrice*w.quantity,0)}subscribe(C){return this.listeners.add(C),C(this.getState()),()=>{this.listeners.delete(C)}}destroy(){this.listeners.clear()}rehydrate(){if(!this.persist||!this.storage)return;try{let C=this.storage.getItem(this.storageKey);if(!C)return;let w=JSON.parse(C),f=Array.isArray(w)?w:w.items;if(!Array.isArray(f))return;this.items=f.map((r)=>this.normalizeItem(r)).filter((r)=>Boolean(r)),this.emit()}catch(C){this.log("Failed to hydrate cart",C)}}addItem(C){let w=this.normalizeItem(C);if(!w)return;let f=this.items.find((r)=>r.id===w.id);if(f){this.setQuantity(f.id,f.quantity+w.quantity);return}this.items=[...this.items,w],this.commit()}removeItem(C){this.items=this.items.filter((w)=>w.id!==C),this.commit()}setQuantity(C,w){let f=Math.floor(w);this.items=f<=0?this.items.filter((r)=>r.id!==C):this.items.map((r)=>r.id===C?{...r,quantity:f}:r),this.commit()}increment(C,w=1){let f=this.items.find((r)=>r.id===C);if(f)this.setQuantity(C,f.quantity+Math.max(1,Math.floor(w)))}decrement(C,w=1){let f=this.items.find((r)=>r.id===C);if(f)this.setQuantity(C,f.quantity-Math.max(1,Math.floor(w)))}clear(){if(this.items=[],this.persist&&this.storage)try{this.storage.removeItem(this.storageKey)}catch(C){this.log("Failed to clear cart storage",C)}this.emit()}async resolveCheckoutUrl(C=this.defaultCheckoutLinkKey){let w=this.checkoutLinks[C];if(!w)throw new S("checkout_link_not_found",`Unknown checkout link key: ${C}`);let f=z(w),r=this.itemsForCheckout(C,w);return(await this.post("/checkout-url",{siteKey:this.siteKey,checkoutLinkId:f.checkoutLinkId,mode:w.mode,discountCode:f.discountCode,items:r})).checkoutUrl}async checkout(C=this.defaultCheckoutLinkKey){let w=await this.resolveCheckoutUrl(C),f=M();if(f)f.location.assign(w);return w}itemsForCheckout(C,w){if(w.mode==="static")return[];let f=this.items.filter((r)=>r.checkoutLinkKey===C).map((r)=>({lineItemId:r.lineItemId,quantity:r.quantity,...r.discount?{discount:r.discount}:{}}));if(f.length===0)throw new S("cart_empty","Your cart is empty.");return f}normalizeItem(C){if(!C.id||!C.lineItemId||!C.name||typeof C.unitPrice!=="number")return null;return{...C,id:C.id,lineItemId:C.lineItemId,checkoutLinkKey:C.checkoutLinkKey??this.defaultCheckoutLinkKey,name:C.name,unitPrice:C.unitPrice,quantity:_(C.quantity)}}async post(C,w){let f=`${this.endpoint}${C}`,r=JSON.stringify(w),B={"Content-Type":"application/json"},b={body:w,bodyText:r,endpoint:this.endpoint,headers:B,path:C,siteKey:this.siteKey,url:f},P=typeof this.requestHeaders==="function"?await this.requestHeaders(b):this.requestHeaders,U=$(B,P),D=await fetch(f,{method:"POST",headers:U,body:r}),A=await D.json().catch(()=>null);if(!D.ok){let V=typeof A==="object"&&A&&"code"in A?A.code:"request_failed",X=typeof A==="object"&&A&&"message"in A?A.message:"Cart request failed";throw new S(V??"request_failed",X??"Cart request failed",D.status)}return A}commit(){if(this.persist&&this.storage)try{let C={version:G,items:this.items};this.storage.setItem(this.storageKey,JSON.stringify(C))}catch(C){this.log("Failed to persist cart",C)}this.emit()}emit(){let C=this.getState();for(let w of this.listeners)w(C)}log(...C){if(this.debug)console.log("[@behindthescenes/cart]",...C)}}function I(C){return T.init(C)}function N(C){return typeof C==="object"&&C!==null&&!Array.isArray(C)}function O(C){let w=typeof window>"u"?null:window;if(!w)return;let[f,r,B]=C;if(typeof f!=="string")return;if(f==="config"){if(!N(r))return;w.btsCart?.destroy?.(),w.btsCart=I(r);return}let b=w.btsCart;if(!b)return;if(f==="add"&&N(r)){b.addItem(r);return}if(f==="remove"&&typeof r==="string"){b.removeItem(r);return}if(f==="quantity"&&typeof r==="string"&&typeof B==="number"){b.setQuantity(r,B);return}if(f==="clear"){b.clear();return}if(f==="checkout")b.checkout(typeof r==="string"?r:void 0)}if(typeof window<"u"){let C=Array.isArray(window.btsCartDataLayer)?[...window.btsCartDataLayer]:[];window.btsCartDataLayer=Array.isArray(window.btsCartDataLayer)?window.btsCartDataLayer:[],window.BTSCart={BTSCart:T,createBTSCart:I},window.createBTSCart=I;for(let w of C)O(Array.from(w));window.btsCartCommand=(...w)=>{window.btsCartDataLayer?.push(w),O(w)}}export{I as createBTSCart,T as BTSCart};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var{defineProperty:_,getOwnPropertyNames:H,getOwnPropertyDescriptor:b}=Object,q=Object.prototype.hasOwnProperty;function B(F){return this[F]}var I=(F)=>{var G=($??=new WeakMap).get(F),J;if(G)return G;if(G=_({},"__esModule",{value:!0}),F&&typeof F==="object"||typeof F==="function"){for(var M of H(F))if(!q.call(G,M))_(G,M,{get:B.bind(F,M),enumerable:!(J=b(F,M))||J.enumerable})}return $.set(F,G),G},$;var j=(F)=>F;function K(F,G){this[F]=j.bind(null,G)}var T=(F,G)=>{for(var J in G)_(F,J,{get:G[J],enumerable:!0,configurable:!0,set:K.bind(G,J)})};var g={};T(g,{createBTSCart:()=>f,BTSCartError:()=>V,BTSCart:()=>Y});module.exports=I(g);var A="https://api.bts.it.com/v2/website/cart",P="bts-cart",R="default",v=1;class V extends Error{code;status;constructor(F,G,J){super(G);this.name="BTSCartError",this.code=F,this.status=J}}function D(){return typeof window>"u"?null:window}function C(){try{return D()?.localStorage}catch{return}}function S(F){return(F??A).replace(/\/$/,"")}function w(F){if(!Number.isFinite(F??1))return 1;return Math.max(1,Math.floor(F??1))}function z(F){return{items:F.map((G)=>({...G}))}}function x(F,G){let J=new Headers(F);if(!G)return J;return new Headers(G).forEach((M,O)=>J.set(O,M)),J}function L(F){let G=F.checkoutLinkId.trim();try{let J=new URL(G.includes("://")?G:G.startsWith("/")?`https://behindthescenes.com${G}`:G),M=J.pathname.split("/").filter(Boolean),O=M.indexOf("c"),X=O>=0?M[O+1]:M.at(-1);if(X)return{checkoutLinkId:X,discountCode:F.discountCode??J.searchParams.get("discountCode")??void 0}}catch{}return{checkoutLinkId:G,discountCode:F.discountCode}}class Y{siteKey;endpoint;debug;persist;storageKey;storage;checkoutLinks;defaultCheckoutLinkKey;requestHeaders;items=[];listeners=new Set;constructor(F){this.siteKey=F.siteKey,this.endpoint=S(F.endpoint),this.debug=F.debug??!1,this.persist=F.persist??!0,this.storageKey=F.storageKey??P,this.storage=F.storage??C(),this.checkoutLinks=F.checkoutLinks??{},this.defaultCheckoutLinkKey=F.defaultCheckoutLinkKey??Object.keys(this.checkoutLinks)[0]??R,this.requestHeaders=F.requestHeaders,this.rehydrate()}static init(F){return new Y(F)}getItems(){return z(this.items).items}getState(){return z(this.items)}getItemCount(){return this.items.reduce((F,G)=>F+G.quantity,0)}getSubtotal(){return this.items.reduce((F,G)=>F+(G.fullPrice??G.unitPrice)*G.quantity,0)}getTotal(){return this.items.reduce((F,G)=>F+G.unitPrice*G.quantity,0)}subscribe(F){return this.listeners.add(F),F(this.getState()),()=>{this.listeners.delete(F)}}destroy(){this.listeners.clear()}rehydrate(){if(!this.persist||!this.storage)return;try{let F=this.storage.getItem(this.storageKey);if(!F)return;let G=JSON.parse(F),J=Array.isArray(G)?G:G.items;if(!Array.isArray(J))return;this.items=J.map((M)=>this.normalizeItem(M)).filter((M)=>Boolean(M)),this.emit()}catch(F){this.log("Failed to hydrate cart",F)}}addItem(F){let G=this.normalizeItem(F);if(!G)return;let J=this.items.find((M)=>M.id===G.id);if(J){this.setQuantity(J.id,J.quantity+G.quantity);return}this.items=[...this.items,G],this.commit()}removeItem(F){this.items=this.items.filter((G)=>G.id!==F),this.commit()}setQuantity(F,G){let J=Math.floor(G);this.items=J<=0?this.items.filter((M)=>M.id!==F):this.items.map((M)=>M.id===F?{...M,quantity:J}:M),this.commit()}increment(F,G=1){let J=this.items.find((M)=>M.id===F);if(J)this.setQuantity(F,J.quantity+Math.max(1,Math.floor(G)))}decrement(F,G=1){let J=this.items.find((M)=>M.id===F);if(J)this.setQuantity(F,J.quantity-Math.max(1,Math.floor(G)))}clear(){if(this.items=[],this.persist&&this.storage)try{this.storage.removeItem(this.storageKey)}catch(F){this.log("Failed to clear cart storage",F)}this.emit()}async resolveCheckoutUrl(F=this.defaultCheckoutLinkKey){let G=this.checkoutLinks[F];if(!G)throw new V("checkout_link_not_found",`Unknown checkout link key: ${F}`);let J=L(G),M=this.itemsForCheckout(F,G);return(await this.post("/checkout-url",{siteKey:this.siteKey,checkoutLinkId:J.checkoutLinkId,mode:G.mode,discountCode:J.discountCode,items:M})).checkoutUrl}async checkout(F=this.defaultCheckoutLinkKey){let G=await this.resolveCheckoutUrl(F),J=D();if(J)J.location.assign(G);return G}itemsForCheckout(F,G){if(G.mode==="static")return[];let J=this.items.filter((M)=>M.checkoutLinkKey===F).map((M)=>({lineItemId:M.lineItemId,quantity:M.quantity,...M.discount?{discount:M.discount}:{}}));if(J.length===0)throw new V("cart_empty","Your cart is empty.");return J}normalizeItem(F){if(!F.id||!F.lineItemId||!F.name||typeof F.unitPrice!=="number")return null;return{...F,id:F.id,lineItemId:F.lineItemId,checkoutLinkKey:F.checkoutLinkKey??this.defaultCheckoutLinkKey,name:F.name,unitPrice:F.unitPrice,quantity:w(F.quantity)}}async post(F,G){let J=`${this.endpoint}${F}`,M=JSON.stringify(G),O={"Content-Type":"application/json"},X={body:G,bodyText:M,endpoint:this.endpoint,headers:O,path:F,siteKey:this.siteKey,url:J},U=typeof this.requestHeaders==="function"?await this.requestHeaders(X):this.requestHeaders,W=x(O,U),Z=await fetch(J,{method:"POST",headers:W,body:M}),N=await Z.json().catch(()=>null);if(!Z.ok){let Q=typeof N==="object"&&N&&"code"in N?N.code:"request_failed",E=typeof N==="object"&&N&&"message"in N?N.message:"Cart request failed";throw new V(Q??"request_failed",E??"Cart request failed",Z.status)}return N}commit(){if(this.persist&&this.storage)try{let F={version:v,items:this.items};this.storage.setItem(this.storageKey,JSON.stringify(F))}catch(F){this.log("Failed to persist cart",F)}this.emit()}emit(){let F=this.getState();for(let G of this.listeners)G(F)}log(...F){if(this.debug)console.log("[@behindthescenes/cart]",...F)}}function f(F){return Y.init(F)}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
var _="https://api.bts.it.com/v2/website/cart",$="bts-cart",A="default",P=1;class V extends Error{code;status;constructor(F,G,J){super(G);this.name="BTSCartError",this.code=F,this.status=J}}function v(){return typeof window>"u"?null:window}function Q(){try{return v()?.localStorage}catch{return}}function E(F){return(F??_).replace(/\/$/,"")}function H(F){if(!Number.isFinite(F??1))return 1;return Math.max(1,Math.floor(F??1))}function R(F){return{items:F.map((G)=>({...G}))}}function b(F,G){let J=new Headers(F);if(!G)return J;return new Headers(G).forEach((M,O)=>J.set(O,M)),J}function q(F){let G=F.checkoutLinkId.trim();try{let J=new URL(G.includes("://")?G:G.startsWith("/")?`https://behindthescenes.com${G}`:G),M=J.pathname.split("/").filter(Boolean),O=M.indexOf("c"),X=O>=0?M[O+1]:M.at(-1);if(X)return{checkoutLinkId:X,discountCode:F.discountCode??J.searchParams.get("discountCode")??void 0}}catch{}return{checkoutLinkId:G,discountCode:F.discountCode}}class Z{siteKey;endpoint;debug;persist;storageKey;storage;checkoutLinks;defaultCheckoutLinkKey;requestHeaders;items=[];listeners=new Set;constructor(F){this.siteKey=F.siteKey,this.endpoint=E(F.endpoint),this.debug=F.debug??!1,this.persist=F.persist??!0,this.storageKey=F.storageKey??$,this.storage=F.storage??Q(),this.checkoutLinks=F.checkoutLinks??{},this.defaultCheckoutLinkKey=F.defaultCheckoutLinkKey??Object.keys(this.checkoutLinks)[0]??A,this.requestHeaders=F.requestHeaders,this.rehydrate()}static init(F){return new Z(F)}getItems(){return R(this.items).items}getState(){return R(this.items)}getItemCount(){return this.items.reduce((F,G)=>F+G.quantity,0)}getSubtotal(){return this.items.reduce((F,G)=>F+(G.fullPrice??G.unitPrice)*G.quantity,0)}getTotal(){return this.items.reduce((F,G)=>F+G.unitPrice*G.quantity,0)}subscribe(F){return this.listeners.add(F),F(this.getState()),()=>{this.listeners.delete(F)}}destroy(){this.listeners.clear()}rehydrate(){if(!this.persist||!this.storage)return;try{let F=this.storage.getItem(this.storageKey);if(!F)return;let G=JSON.parse(F),J=Array.isArray(G)?G:G.items;if(!Array.isArray(J))return;this.items=J.map((M)=>this.normalizeItem(M)).filter((M)=>Boolean(M)),this.emit()}catch(F){this.log("Failed to hydrate cart",F)}}addItem(F){let G=this.normalizeItem(F);if(!G)return;let J=this.items.find((M)=>M.id===G.id);if(J){this.setQuantity(J.id,J.quantity+G.quantity);return}this.items=[...this.items,G],this.commit()}removeItem(F){this.items=this.items.filter((G)=>G.id!==F),this.commit()}setQuantity(F,G){let J=Math.floor(G);this.items=J<=0?this.items.filter((M)=>M.id!==F):this.items.map((M)=>M.id===F?{...M,quantity:J}:M),this.commit()}increment(F,G=1){let J=this.items.find((M)=>M.id===F);if(J)this.setQuantity(F,J.quantity+Math.max(1,Math.floor(G)))}decrement(F,G=1){let J=this.items.find((M)=>M.id===F);if(J)this.setQuantity(F,J.quantity-Math.max(1,Math.floor(G)))}clear(){if(this.items=[],this.persist&&this.storage)try{this.storage.removeItem(this.storageKey)}catch(F){this.log("Failed to clear cart storage",F)}this.emit()}async resolveCheckoutUrl(F=this.defaultCheckoutLinkKey){let G=this.checkoutLinks[F];if(!G)throw new V("checkout_link_not_found",`Unknown checkout link key: ${F}`);let J=q(G),M=this.itemsForCheckout(F,G);return(await this.post("/checkout-url",{siteKey:this.siteKey,checkoutLinkId:J.checkoutLinkId,mode:G.mode,discountCode:J.discountCode,items:M})).checkoutUrl}async checkout(F=this.defaultCheckoutLinkKey){let G=await this.resolveCheckoutUrl(F),J=v();if(J)J.location.assign(G);return G}itemsForCheckout(F,G){if(G.mode==="static")return[];let J=this.items.filter((M)=>M.checkoutLinkKey===F).map((M)=>({lineItemId:M.lineItemId,quantity:M.quantity,...M.discount?{discount:M.discount}:{}}));if(J.length===0)throw new V("cart_empty","Your cart is empty.");return J}normalizeItem(F){if(!F.id||!F.lineItemId||!F.name||typeof F.unitPrice!=="number")return null;return{...F,id:F.id,lineItemId:F.lineItemId,checkoutLinkKey:F.checkoutLinkKey??this.defaultCheckoutLinkKey,name:F.name,unitPrice:F.unitPrice,quantity:H(F.quantity)}}async post(F,G){let J=`${this.endpoint}${F}`,M=JSON.stringify(G),O={"Content-Type":"application/json"},X={body:G,bodyText:M,endpoint:this.endpoint,headers:O,path:F,siteKey:this.siteKey,url:J},z=typeof this.requestHeaders==="function"?await this.requestHeaders(X):this.requestHeaders,D=b(O,z),Y=await fetch(J,{method:"POST",headers:D,body:M}),N=await Y.json().catch(()=>null);if(!Y.ok){let U=typeof N==="object"&&N&&"code"in N?N.code:"request_failed",W=typeof N==="object"&&N&&"message"in N?N.message:"Cart request failed";throw new V(U??"request_failed",W??"Cart request failed",Y.status)}return N}commit(){if(this.persist&&this.storage)try{let F={version:P,items:this.items};this.storage.setItem(this.storageKey,JSON.stringify(F))}catch(F){this.log("Failed to persist cart",F)}this.emit()}emit(){let F=this.getState();for(let G of this.listeners)G(F)}log(...F){if(this.debug)console.log("[@behindthescenes/cart]",...F)}}function T(F){return Z.init(F)}export{T as createBTSCart,V as BTSCartError,Z as BTSCart};
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
import { BTSCart, createBTSCart } from "./index";
|
|
2
|
+
export { BTSCart, createBTSCart };
|
|
3
|
+
export type { BTSCartCheckoutItemPayload, BTSCartCheckoutLinkConfig, BTSCartCheckoutUrlRequest, BTSCartCheckoutUrlResult, BTSCartInit, BTSCartItem, BTSCartItemInput, BTSCartState, } from "./types/index.types";
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import type { BTSCartInit, BTSCartItem, BTSCartItemInput, BTSCartListener, BTSCartState } from "./types/index.types";
|
|
2
|
+
export declare class BTSCart {
|
|
3
|
+
private siteKey;
|
|
4
|
+
private endpoint;
|
|
5
|
+
private debug;
|
|
6
|
+
private persist;
|
|
7
|
+
private storageKey;
|
|
8
|
+
private storage?;
|
|
9
|
+
private checkoutLinks;
|
|
10
|
+
private defaultCheckoutLinkKey;
|
|
11
|
+
private requestHeaders?;
|
|
12
|
+
private items;
|
|
13
|
+
private listeners;
|
|
14
|
+
constructor(init: BTSCartInit);
|
|
15
|
+
static init(opts: BTSCartInit): BTSCart;
|
|
16
|
+
getItems(): BTSCartItem[];
|
|
17
|
+
getState(): BTSCartState;
|
|
18
|
+
getItemCount(): number;
|
|
19
|
+
getSubtotal(): number;
|
|
20
|
+
getTotal(): number;
|
|
21
|
+
subscribe(listener: BTSCartListener): () => void;
|
|
22
|
+
destroy(): void;
|
|
23
|
+
rehydrate(): void;
|
|
24
|
+
addItem(input: BTSCartItemInput): void;
|
|
25
|
+
removeItem(id: string): void;
|
|
26
|
+
setQuantity(id: string, quantity: number): void;
|
|
27
|
+
increment(id: string, amount?: number): void;
|
|
28
|
+
decrement(id: string, amount?: number): void;
|
|
29
|
+
clear(): void;
|
|
30
|
+
resolveCheckoutUrl(checkoutLinkKey?: string): Promise<string>;
|
|
31
|
+
checkout(checkoutLinkKey?: string): Promise<string>;
|
|
32
|
+
private itemsForCheckout;
|
|
33
|
+
private normalizeItem;
|
|
34
|
+
private post;
|
|
35
|
+
private commit;
|
|
36
|
+
private emit;
|
|
37
|
+
private log;
|
|
38
|
+
}
|
|
39
|
+
export declare function createBTSCart(init: BTSCartInit): BTSCart;
|
|
40
|
+
export type { BTSCartCheckoutItemPayload, BTSCartCheckoutLinkConfig, BTSCartCheckoutUrlRequest, BTSCartCheckoutUrlResult, BTSCartErrorCode, BTSCartInit, BTSCartItem, BTSCartItemInput, BTSCartListener, BTSCartMode, BTSCartRequestContext, BTSCartRequestHeaders, BTSCartState, BTSCartStorage, } from "./types/index.types";
|
|
41
|
+
export { BTSCartError } from "./types/index.types";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { BTSCart, createBTSCart } from "../index";
|
|
2
|
+
declare global {
|
|
3
|
+
interface Window {
|
|
4
|
+
BTSCart?: {
|
|
5
|
+
BTSCart: typeof BTSCart;
|
|
6
|
+
createBTSCart: typeof createBTSCart;
|
|
7
|
+
};
|
|
8
|
+
createBTSCart?: typeof createBTSCart;
|
|
9
|
+
btsCart?: BTSCart;
|
|
10
|
+
btsCartDataLayer?: Array<ArrayLike<unknown>>;
|
|
11
|
+
btsCartCommand?: (...args: unknown[]) => void;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
export type BTSCartMode = "static" | "dynamic";
|
|
2
|
+
export type BTSCartStorage = Pick<Storage, "getItem" | "setItem" | "removeItem">;
|
|
3
|
+
export type BTSCartRequestContext = {
|
|
4
|
+
body: unknown;
|
|
5
|
+
bodyText: string;
|
|
6
|
+
endpoint: string;
|
|
7
|
+
headers: Record<string, string>;
|
|
8
|
+
path: string;
|
|
9
|
+
siteKey: string;
|
|
10
|
+
url: string;
|
|
11
|
+
};
|
|
12
|
+
export type BTSCartRequestHeaders = HeadersInit | ((request: BTSCartRequestContext) => HeadersInit | Promise<HeadersInit>);
|
|
13
|
+
export type BTSCartCheckoutLinkConfig = {
|
|
14
|
+
/** Raw checkout link ID, or a full BTS checkout URL like `https://behindthescenes.com/c/<id>?discountCode=CODE`. */
|
|
15
|
+
checkoutLinkId: string;
|
|
16
|
+
mode: BTSCartMode;
|
|
17
|
+
discountCode?: string;
|
|
18
|
+
};
|
|
19
|
+
export type BTSCartItemInput = {
|
|
20
|
+
id: string;
|
|
21
|
+
lineItemId: string;
|
|
22
|
+
checkoutLinkKey?: string;
|
|
23
|
+
productId?: string;
|
|
24
|
+
accessTierId?: string;
|
|
25
|
+
name: string;
|
|
26
|
+
unitPrice: number;
|
|
27
|
+
fullPrice?: number;
|
|
28
|
+
image?: string;
|
|
29
|
+
quantity?: number;
|
|
30
|
+
/** Internal promo-code ID or customer-facing promo code. Customer codes are resolved by the BTS cart API. */
|
|
31
|
+
discount?: string;
|
|
32
|
+
};
|
|
33
|
+
export type BTSCartItem = Required<Pick<BTSCartItemInput, "id" | "lineItemId" | "name" | "unitPrice" | "quantity">> & Omit<BTSCartItemInput, "quantity"> & {
|
|
34
|
+
checkoutLinkKey: string;
|
|
35
|
+
};
|
|
36
|
+
export type BTSCartState = {
|
|
37
|
+
items: BTSCartItem[];
|
|
38
|
+
};
|
|
39
|
+
export type BTSCartListener = (state: BTSCartState) => void;
|
|
40
|
+
export type BTSCartInit = {
|
|
41
|
+
siteKey: string;
|
|
42
|
+
endpoint?: string;
|
|
43
|
+
checkoutLinks?: Record<string, BTSCartCheckoutLinkConfig>;
|
|
44
|
+
defaultCheckoutLinkKey?: string;
|
|
45
|
+
debug?: boolean;
|
|
46
|
+
persist?: boolean;
|
|
47
|
+
storageKey?: string;
|
|
48
|
+
storage?: BTSCartStorage;
|
|
49
|
+
requestHeaders?: BTSCartRequestHeaders;
|
|
50
|
+
};
|
|
51
|
+
export type BTSCartCheckoutItemPayload = {
|
|
52
|
+
lineItemId: string;
|
|
53
|
+
quantity: number;
|
|
54
|
+
discount?: string;
|
|
55
|
+
};
|
|
56
|
+
export type BTSCartCheckoutUrlRequest = {
|
|
57
|
+
siteKey: string;
|
|
58
|
+
checkoutLinkId: string;
|
|
59
|
+
mode: BTSCartMode;
|
|
60
|
+
items?: BTSCartCheckoutItemPayload[];
|
|
61
|
+
discountCode?: string;
|
|
62
|
+
};
|
|
63
|
+
export type BTSCartCheckoutUrlResult = {
|
|
64
|
+
ok: true;
|
|
65
|
+
checkoutUrl: string;
|
|
66
|
+
checkoutLinkId: string;
|
|
67
|
+
mode: BTSCartMode;
|
|
68
|
+
items: BTSCartCheckoutItemPayload[];
|
|
69
|
+
};
|
|
70
|
+
export type BTSCartErrorCode = "unknown_site" | "domain_not_verified" | "analytics_disabled" | "checkout_link_not_found" | "checkout_link_inactive" | "checkout_link_expired" | "checkout_link_sold_out" | "checkout_link_mode_mismatch" | "cart_empty" | "line_item_not_found" | "invalid_quantity" | "line_item_sold_out" | "product_unavailable" | "discount_invalid" | "request_failed";
|
|
71
|
+
export declare class BTSCartError extends Error {
|
|
72
|
+
code: BTSCartErrorCode;
|
|
73
|
+
status?: number;
|
|
74
|
+
constructor(code: BTSCartErrorCode, message: string, status?: number);
|
|
75
|
+
}
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@behindthescenes/cart",
|
|
3
|
+
"version": "0.0.3",
|
|
4
|
+
"description": "Browser cart SDK for BTS external-site checkout links.",
|
|
5
|
+
"license": "UNLICENSED",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"main": "./dist/cjs/index.js",
|
|
8
|
+
"module": "./dist/esm/index.js",
|
|
9
|
+
"types": "./dist/types/index.d.ts",
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "git+https://github.com/gwop-company/bts-backend.git",
|
|
13
|
+
"directory": "packages/cart"
|
|
14
|
+
},
|
|
15
|
+
"keywords": [
|
|
16
|
+
"cart",
|
|
17
|
+
"checkout",
|
|
18
|
+
"browser",
|
|
19
|
+
"sdk"
|
|
20
|
+
],
|
|
21
|
+
"exports": {
|
|
22
|
+
".": {
|
|
23
|
+
"types": "./dist/types/index.d.ts",
|
|
24
|
+
"import": "./dist/esm/index.js",
|
|
25
|
+
"require": "./dist/cjs/index.js",
|
|
26
|
+
"default": "./dist/esm/index.js"
|
|
27
|
+
},
|
|
28
|
+
"./browser": {
|
|
29
|
+
"types": "./dist/types/browser.d.ts",
|
|
30
|
+
"import": "./dist/browser/browser.js",
|
|
31
|
+
"default": "./dist/browser/browser.js"
|
|
32
|
+
},
|
|
33
|
+
"./package.json": "./package.json"
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist",
|
|
37
|
+
"docs",
|
|
38
|
+
"README.md"
|
|
39
|
+
],
|
|
40
|
+
"publishConfig": {
|
|
41
|
+
"access": "public"
|
|
42
|
+
},
|
|
43
|
+
"scripts": {
|
|
44
|
+
"build": "bun ./scripts/build.ts",
|
|
45
|
+
"build:hosted": "bun ./scripts/sync-hosted-bundle.ts",
|
|
46
|
+
"build:hosted:force": "bun ./scripts/sync-hosted-bundle.ts --build",
|
|
47
|
+
"release:version": "bun ./scripts/bump-release-version.ts",
|
|
48
|
+
"type-check": "tsc --noEmit",
|
|
49
|
+
"test": "bun test",
|
|
50
|
+
"lint": "eslint . --fix",
|
|
51
|
+
"lint:check": "eslint .",
|
|
52
|
+
"release:check": "bun run lint:check && bun run type-check && bun run test && bun run build && npm pack --dry-run",
|
|
53
|
+
"release:publish": "bun run release:version && bun run release:check && npm publish --access public",
|
|
54
|
+
"prepublishOnly": "bun run release:check && bun ./scripts/sync-hosted-bundle.ts"
|
|
55
|
+
},
|
|
56
|
+
"devDependencies": {
|
|
57
|
+
"@bts/eslint-config": "workspace:*",
|
|
58
|
+
"@types/bun": "^1.3.11",
|
|
59
|
+
"eslint": "^9.17.0",
|
|
60
|
+
"typescript": "^5.8.3"
|
|
61
|
+
},
|
|
62
|
+
"peerDependencies": {
|
|
63
|
+
"typescript": "^5"
|
|
64
|
+
}
|
|
65
|
+
}
|