@managesales/storefront 1.0.2 → 1.1.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/CHANGELOG.md +32 -0
- package/README.md +80 -4
- package/dist/index.cjs +179 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +88 -1
- package/dist/index.d.ts +88 -1
- package/dist/index.js +179 -4
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,38 @@
|
|
|
3
3
|
All notable changes to `@managesales/storefront`. This project adheres to
|
|
4
4
|
[Semantic Versioning](https://semver.org).
|
|
5
5
|
|
|
6
|
+
## 1.1.0 — 2026-07-11
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
- **Stateful cart** — `client.cart.create()` / `client.cart.current()` return a `StorefrontCart` with
|
|
10
|
+
`addLineItem` (auto-enriches price/name from `products.get`, merges duplicate product+variant
|
|
11
|
+
lines), `updateLineItem`, `removeLineItem`, `clear`, and `checkout()` to materialize the cart
|
|
12
|
+
straight into a checkout session. Persisted to `localStorage` by default (`persistCart: false` to
|
|
13
|
+
opt out); safe to import under SSR/Node with no DOM. `client.cart.recover(token)` is unchanged.
|
|
14
|
+
- **`orders` and `customers` alias namespaces** — `client.orders.list()` and
|
|
15
|
+
`client.customers.{login,register,loginWithGoogle,logout,isAuthenticated,setTokens,me,orders,addresses}`
|
|
16
|
+
are convenience references onto the existing `auth`/`customer` methods (no duplicated logic, no new
|
|
17
|
+
backend calls).
|
|
18
|
+
- **Configurable request retries** — network errors and 5xx/408/425/429 responses on idempotent
|
|
19
|
+
(`GET`/`HEAD`) requests are now retried automatically with exponential backoff, honoring a
|
|
20
|
+
`Retry-After` response header as a delay floor. Configure via
|
|
21
|
+
`createStorefrontClient({ retry: { retries, baseDelayMs, maxDelayMs, retryOnStatus } })`, or pass
|
|
22
|
+
`retry: false` to disable. Non-idempotent requests (`POST`/`PUT`/`PATCH`/`DELETE`) are never
|
|
23
|
+
retried once a response is received from the server; a network failure that never reached the
|
|
24
|
+
server (e.g. `fetch` throwing) is retried regardless of method, since no side effect could have
|
|
25
|
+
occurred yet.
|
|
26
|
+
|
|
27
|
+
### Fixed
|
|
28
|
+
- `checkout.create` now sends line items under the `lines` key (matching the `/checkout` endpoint's
|
|
29
|
+
actual request shape) instead of `items`. Note this only corrects the request envelope — direct
|
|
30
|
+
callers must still supply every field the backend requires (see next item).
|
|
31
|
+
- `CheckoutLineItem` now includes the backend-required `productName` (string) and `unitPrice`
|
|
32
|
+
(number) fields, plus optional `imageUrl`. The previous `{ productId, variantId?, quantity }`
|
|
33
|
+
shape type-checked but was always rejected by the real `/checkout` endpoint's validation for
|
|
34
|
+
direct `client.checkout.create()` callers; `cart.checkout()` was unaffected since `CartLine`
|
|
35
|
+
already carried both fields. Callers who don't want to supply `productName`/`unitPrice` by hand
|
|
36
|
+
should use the stateful cart (`cart.addLineItem`), which auto-enriches them from `products.get`.
|
|
37
|
+
|
|
6
38
|
## 1.0.2 — 2026-07-11
|
|
7
39
|
|
|
8
40
|
### Docs
|
package/README.md
CHANGED
|
@@ -30,8 +30,10 @@ like Shopify's Storefront client, for ManageSales. Everything you need to ship a
|
|
|
30
30
|
- [Products & search](#products--search)
|
|
31
31
|
- [Collections](#collections)
|
|
32
32
|
- [Cart & checkout](#cart--checkout)
|
|
33
|
+
- [Stateful cart](#stateful-cart)
|
|
33
34
|
- [Authentication](#authentication)
|
|
34
35
|
- [Customer account](#customer-account)
|
|
36
|
+
- [Retries](#retries)
|
|
35
37
|
- [Error handling](#error-handling)
|
|
36
38
|
- [TypeScript support](#typescript-support)
|
|
37
39
|
- [Framework examples](#framework-examples)
|
|
@@ -83,7 +85,9 @@ client.checkout.create(lineItems) // start a checkout from line items
|
|
|
83
85
|
client.checkout.get(checkoutId)
|
|
84
86
|
client.checkout.applyCoupon(checkoutId, code)
|
|
85
87
|
client.checkout.complete(checkoutId, payment?)
|
|
88
|
+
client.cart.create() / client.cart.current() // stateful cart — see Stateful cart
|
|
86
89
|
client.cart.recover(token) // rebuild a checkout from a recovery link
|
|
90
|
+
client.orders.list(params?) // alias for client.customer.orders(params?)
|
|
87
91
|
|
|
88
92
|
// Auth & customer (customer session)
|
|
89
93
|
client.auth.register({ email, password, name })
|
|
@@ -95,6 +99,8 @@ client.auth.setTokens(access, refresh) // restore a session (e.g. from cookies)
|
|
|
95
99
|
client.customer.me()
|
|
96
100
|
client.customer.orders(params?) // order history
|
|
97
101
|
client.customer.addresses()
|
|
102
|
+
// client.customers.* — alias namespace bundling auth + customer under one name
|
|
103
|
+
// (client.customers.login === client.auth.login, client.customers.me === client.customer.me, ...)
|
|
98
104
|
|
|
99
105
|
// Content (public)
|
|
100
106
|
client.blog.list(params?) / client.blog.get(slug)
|
|
@@ -163,8 +169,8 @@ it:
|
|
|
163
169
|
```ts
|
|
164
170
|
// 1. Create a checkout from line items
|
|
165
171
|
const session = await client.checkout.create([
|
|
166
|
-
{ productId: "prod_123", variantId: "var_456", quantity: 2 },
|
|
167
|
-
{ productId: "prod_789", quantity: 1 },
|
|
172
|
+
{ productId: "prod_123", variantId: "var_456", productName: "Blue T-Shirt", quantity: 2, unitPrice: 24.99 },
|
|
173
|
+
{ productId: "prod_789", productName: "Canvas Tote", quantity: 1, unitPrice: 18 },
|
|
168
174
|
]);
|
|
169
175
|
|
|
170
176
|
// 2. Re-fetch a session (e.g. after a reload)
|
|
@@ -182,6 +188,12 @@ const order = await client.checkout.complete(session.id, {
|
|
|
182
188
|
console.log("Order placed:", order.orderNumber);
|
|
183
189
|
```
|
|
184
190
|
|
|
191
|
+
> `productName` and `unitPrice` are required on every line item passed directly to
|
|
192
|
+
> `client.checkout.create()` — the backend validates them. If you'd rather not look these up
|
|
193
|
+
> yourself, use the [stateful cart](#stateful-cart) instead: `cart.addLineItem` auto-enriches
|
|
194
|
+
> `productName`/`unitPrice` (and `imageUrl`) via `products.get`, so cart callers only ever supply
|
|
195
|
+
> `productId`/`variantId`/`quantity`.
|
|
196
|
+
|
|
185
197
|
**Abandoned-cart recovery** — turn a recovery link back into a checkout session:
|
|
186
198
|
|
|
187
199
|
```ts
|
|
@@ -189,6 +201,39 @@ const token = new URLSearchParams(location.search).get("token")!;
|
|
|
189
201
|
const recovered = await client.cart.recover(token);
|
|
190
202
|
```
|
|
191
203
|
|
|
204
|
+
## Stateful cart
|
|
205
|
+
|
|
206
|
+
For a persistent, imperative cart — add/update/remove lines, then check out in one call — use
|
|
207
|
+
`client.cart` instead of assembling line items by hand:
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
// Create a fresh cart, or resume one already persisted for this workspace
|
|
211
|
+
const cart = client.cart.create(); // client.cart.current() resumes a persisted cart instead
|
|
212
|
+
|
|
213
|
+
// Add items — price, name, and image are looked up automatically via products.get()
|
|
214
|
+
await cart.addLineItem({ productId: "prod_123", variantId: "var_456", quantity: 2 });
|
|
215
|
+
await cart.addLineItem({ productId: "prod_123", variantId: "var_456", quantity: 1 }); // merges into the existing line
|
|
216
|
+
|
|
217
|
+
console.log(cart.itemCount); // 3
|
|
218
|
+
console.log(cart.subtotal); // sum of unitPrice * quantity across all lines
|
|
219
|
+
|
|
220
|
+
cart.updateLineItem(cart.lines[0].id, 5); // set a line's quantity (quantity <= 0 removes it)
|
|
221
|
+
cart.removeLineItem(cart.lines[0].id);
|
|
222
|
+
cart.clear();
|
|
223
|
+
|
|
224
|
+
// Turn the cart directly into a checkout session
|
|
225
|
+
const session = await cart.checkout({ clearOnSuccess: true });
|
|
226
|
+
console.log(session.id, session.total);
|
|
227
|
+
```
|
|
228
|
+
|
|
229
|
+
By default the cart is persisted to `localStorage` under a per-workspace key, so `client.cart.current()`
|
|
230
|
+
resumes it after a page reload. Pass `persistCart: false` to `createStorefrontClient` to keep it
|
|
231
|
+
in-memory only (tests, SSR, multi-cart UIs). All storage access is guarded, so the SDK still imports
|
|
232
|
+
and runs fine under Node/SSR where `localStorage` doesn't exist — the cart simply won't persist.
|
|
233
|
+
|
|
234
|
+
> `cart.checkout()` forwards the cart's line items to `client.checkout.create()` under the hood, so
|
|
235
|
+
> coupons/payment on the returned session work exactly as in [Cart & checkout](#cart--checkout).
|
|
236
|
+
|
|
192
237
|
## Authentication
|
|
193
238
|
|
|
194
239
|
Most pages need **no auth**. When a customer logs in, the SDK stores the access + refresh tokens and
|
|
@@ -235,6 +280,37 @@ const { data: orders } = await client.customer.orders({ page: 1 }); // order his
|
|
|
235
280
|
const addresses = await client.customer.addresses(); // saved addresses
|
|
236
281
|
```
|
|
237
282
|
|
|
283
|
+
`client.customers` mirrors `auth` + `customer` under one namespace, and `client.orders.list()` is a
|
|
284
|
+
shorthand for `client.customer.orders()` — same functions, same requests, just organized differently:
|
|
285
|
+
|
|
286
|
+
```ts
|
|
287
|
+
await client.customers.login("jane@example.com", "SecurePass123!"); // === client.auth.login
|
|
288
|
+
const { data: orders } = await client.orders.list({ page: 1 }); // === client.customer.orders({ page: 1 })
|
|
289
|
+
```
|
|
290
|
+
|
|
291
|
+
## Retries
|
|
292
|
+
|
|
293
|
+
Network failures and `408` / `425` / `429` / `5xx` responses to idempotent (`GET`/`HEAD`) requests are
|
|
294
|
+
retried automatically with exponential backoff, honoring a `Retry-After` response header as a delay
|
|
295
|
+
floor when the server sends one. Non-idempotent requests (`POST`/`PUT`/`PATCH`/`DELETE`) are retried
|
|
296
|
+
**only** when the request never reached the server (e.g. `fetch` itself throws) — never after a server
|
|
297
|
+
response, since the side effect may already have happened.
|
|
298
|
+
|
|
299
|
+
```ts
|
|
300
|
+
const client = createStorefrontClient({
|
|
301
|
+
workspaceId: "ws_123",
|
|
302
|
+
retry: {
|
|
303
|
+
retries: 2, // default: 2 additional attempts after the first
|
|
304
|
+
baseDelayMs: 300, // default: 300 — base for exponential backoff
|
|
305
|
+
maxDelayMs: 4000, // default: 4000 — cap on any single backoff delay
|
|
306
|
+
retryOnStatus: [408, 425, 429, 500, 502, 503, 504], // default shown
|
|
307
|
+
},
|
|
308
|
+
});
|
|
309
|
+
|
|
310
|
+
// Disable retries entirely
|
|
311
|
+
const noRetryClient = createStorefrontClient({ workspaceId: "ws_123", retry: false });
|
|
312
|
+
```
|
|
313
|
+
|
|
238
314
|
## Error handling
|
|
239
315
|
|
|
240
316
|
Every non-2xx response throws a `StorefrontError` with structured fields:
|
|
@@ -257,8 +333,8 @@ try {
|
|
|
257
333
|
```
|
|
258
334
|
|
|
259
335
|
> **Resilience:** the client automatically retries a request **once** with a refreshed token when a
|
|
260
|
-
> `401` indicates an expired access token
|
|
261
|
-
>
|
|
336
|
+
> `401` indicates an expired access token, and separately retries transient network/5xx failures per
|
|
337
|
+
> the [Retries](#retries) config.
|
|
262
338
|
|
|
263
339
|
## TypeScript support
|
|
264
340
|
|
package/dist/index.cjs
CHANGED
|
@@ -15,6 +15,18 @@ function createStorefrontClient(config) {
|
|
|
15
15
|
const fetchFn = config.customFetch ?? globalThis.fetch.bind(globalThis);
|
|
16
16
|
let accessToken = null;
|
|
17
17
|
let refreshToken = null;
|
|
18
|
+
const RETRY_DEFAULTS = {
|
|
19
|
+
retries: 2,
|
|
20
|
+
baseDelayMs: 300,
|
|
21
|
+
maxDelayMs: 4e3,
|
|
22
|
+
retryOnStatus: [408, 425, 429, 500, 502, 503, 504]
|
|
23
|
+
};
|
|
24
|
+
const rc = config.retry === false ? { ...RETRY_DEFAULTS, retries: 0 } : { ...RETRY_DEFAULTS, ...config.retry || {} };
|
|
25
|
+
const IDEMPOTENT = /* @__PURE__ */ new Set(["GET", "HEAD"]);
|
|
26
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
27
|
+
function backoff(attempt) {
|
|
28
|
+
return Math.min(rc.maxDelayMs, Math.round(Math.random() * rc.baseDelayMs * Math.pow(2, attempt)));
|
|
29
|
+
}
|
|
18
30
|
async function request(method, path, options) {
|
|
19
31
|
const url = new URL(`${apiUrl}${path}`);
|
|
20
32
|
if (options?.params) {
|
|
@@ -31,11 +43,32 @@ function createStorefrontClient(config) {
|
|
|
31
43
|
if (options?.body) {
|
|
32
44
|
headers["Content-Type"] = "application/json";
|
|
33
45
|
}
|
|
34
|
-
const
|
|
46
|
+
const fetchOptions = {
|
|
35
47
|
method,
|
|
36
48
|
headers,
|
|
37
49
|
body: options?.body ? JSON.stringify(options.body) : void 0
|
|
38
|
-
}
|
|
50
|
+
};
|
|
51
|
+
let attempt = 0;
|
|
52
|
+
let res;
|
|
53
|
+
while (true) {
|
|
54
|
+
try {
|
|
55
|
+
res = await fetchFn(url.toString(), fetchOptions);
|
|
56
|
+
} catch (err) {
|
|
57
|
+
if (attempt < rc.retries) {
|
|
58
|
+
await sleep(backoff(attempt));
|
|
59
|
+
attempt++;
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
throw err;
|
|
63
|
+
}
|
|
64
|
+
if (attempt < rc.retries && rc.retryOnStatus.includes(res.status) && IDEMPOTENT.has(method)) {
|
|
65
|
+
const ra = Number(res.headers.get("retry-after"));
|
|
66
|
+
await sleep(Number.isFinite(ra) && ra > 0 ? ra * 1e3 : backoff(attempt));
|
|
67
|
+
attempt++;
|
|
68
|
+
continue;
|
|
69
|
+
}
|
|
70
|
+
break;
|
|
71
|
+
}
|
|
39
72
|
if (res.status === 401 && options?.auth && refreshToken) {
|
|
40
73
|
const refreshed = await refreshSession();
|
|
41
74
|
if (refreshed) {
|
|
@@ -122,7 +155,7 @@ function createStorefrontClient(config) {
|
|
|
122
155
|
const checkout = {
|
|
123
156
|
/** Create a checkout session from cart items */
|
|
124
157
|
async create(items) {
|
|
125
|
-
return request("POST", "/checkout", { body: { items } });
|
|
158
|
+
return request("POST", "/checkout", { body: { lines: items } });
|
|
126
159
|
},
|
|
127
160
|
/** Retrieve an existing checkout session */
|
|
128
161
|
async get(checkoutId) {
|
|
@@ -254,12 +287,152 @@ function createStorefrontClient(config) {
|
|
|
254
287
|
return res.json();
|
|
255
288
|
}
|
|
256
289
|
};
|
|
290
|
+
const CART_KEY = "managesales_cart_" + config.workspaceId;
|
|
291
|
+
const hasLS = () => {
|
|
292
|
+
try {
|
|
293
|
+
return config.persistCart !== false && typeof localStorage !== "undefined";
|
|
294
|
+
} catch {
|
|
295
|
+
return false;
|
|
296
|
+
}
|
|
297
|
+
};
|
|
298
|
+
const loadCart = () => {
|
|
299
|
+
if (!hasLS()) return null;
|
|
300
|
+
try {
|
|
301
|
+
const s = localStorage.getItem(CART_KEY);
|
|
302
|
+
return s ? JSON.parse(s) : null;
|
|
303
|
+
} catch {
|
|
304
|
+
return null;
|
|
305
|
+
}
|
|
306
|
+
};
|
|
307
|
+
const saveCart = (st) => {
|
|
308
|
+
if (!hasLS()) return;
|
|
309
|
+
try {
|
|
310
|
+
localStorage.setItem(CART_KEY, JSON.stringify(st));
|
|
311
|
+
} catch {
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
let __lineSeq = 0;
|
|
315
|
+
const genId = (p) => typeof crypto !== "undefined" && crypto.randomUUID ? p + crypto.randomUUID() : p + Date.now() + "_" + ++__lineSeq;
|
|
316
|
+
const nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
317
|
+
const freshCart = () => ({ id: genId("cart_"), lines: [], createdAt: nowIso(), updatedAt: nowIso() });
|
|
318
|
+
function makeCart(state) {
|
|
319
|
+
const persist = () => {
|
|
320
|
+
state.updatedAt = nowIso();
|
|
321
|
+
saveCart(state);
|
|
322
|
+
};
|
|
323
|
+
const cartObj = {
|
|
324
|
+
get id() {
|
|
325
|
+
return state.id;
|
|
326
|
+
},
|
|
327
|
+
get lines() {
|
|
328
|
+
return state.lines;
|
|
329
|
+
},
|
|
330
|
+
get itemCount() {
|
|
331
|
+
return state.lines.reduce((n, l) => n + l.quantity, 0);
|
|
332
|
+
},
|
|
333
|
+
get subtotal() {
|
|
334
|
+
return state.lines.reduce((s, l) => s + l.unitPrice * l.quantity, 0);
|
|
335
|
+
},
|
|
336
|
+
// Supplying unitPrice alone does not skip the lookup — productName must also be
|
|
337
|
+
// provided, otherwise this falls through to the products.get() network lookup below.
|
|
338
|
+
async addLineItem(input) {
|
|
339
|
+
let line;
|
|
340
|
+
if (input.unitPrice != null && input.productName) {
|
|
341
|
+
line = {
|
|
342
|
+
id: genId("line_"),
|
|
343
|
+
productId: input.productId,
|
|
344
|
+
variantId: input.variantId,
|
|
345
|
+
quantity: input.quantity,
|
|
346
|
+
unitPrice: input.unitPrice,
|
|
347
|
+
productName: input.productName,
|
|
348
|
+
imageUrl: input.imageUrl
|
|
349
|
+
};
|
|
350
|
+
} else {
|
|
351
|
+
const p = await products.get(input.productId);
|
|
352
|
+
const v = input.variantId && Array.isArray(p.variants) ? p.variants.find((x) => x.id === input.variantId) : void 0;
|
|
353
|
+
line = {
|
|
354
|
+
id: genId("line_"),
|
|
355
|
+
productId: input.productId,
|
|
356
|
+
variantId: input.variantId,
|
|
357
|
+
quantity: input.quantity,
|
|
358
|
+
unitPrice: v && v.price != null ? v.price : p.price,
|
|
359
|
+
productName: p.name,
|
|
360
|
+
imageUrl: Array.isArray(p.images) && p.images.length > 0 ? p.images[0].url : void 0
|
|
361
|
+
};
|
|
362
|
+
}
|
|
363
|
+
const existing = state.lines.find((l) => l.productId === line.productId && l.variantId === line.variantId);
|
|
364
|
+
if (existing) existing.quantity += line.quantity;
|
|
365
|
+
else state.lines.push(line);
|
|
366
|
+
persist();
|
|
367
|
+
return cartObj;
|
|
368
|
+
},
|
|
369
|
+
updateLineItem(lineId, quantity) {
|
|
370
|
+
const l = state.lines.find((x) => x.id === lineId);
|
|
371
|
+
if (l) {
|
|
372
|
+
if (quantity <= 0) state.lines = state.lines.filter((x) => x.id !== lineId);
|
|
373
|
+
else l.quantity = quantity;
|
|
374
|
+
persist();
|
|
375
|
+
}
|
|
376
|
+
return cartObj;
|
|
377
|
+
},
|
|
378
|
+
removeLineItem(lineId) {
|
|
379
|
+
state.lines = state.lines.filter((x) => x.id !== lineId);
|
|
380
|
+
persist();
|
|
381
|
+
return cartObj;
|
|
382
|
+
},
|
|
383
|
+
clear() {
|
|
384
|
+
state.lines = [];
|
|
385
|
+
persist();
|
|
386
|
+
return cartObj;
|
|
387
|
+
},
|
|
388
|
+
async checkout(opts) {
|
|
389
|
+
const session = await checkout.create(
|
|
390
|
+
state.lines.map((l) => ({
|
|
391
|
+
productId: l.productId,
|
|
392
|
+
variantId: l.variantId,
|
|
393
|
+
productName: l.productName,
|
|
394
|
+
quantity: l.quantity,
|
|
395
|
+
unitPrice: l.unitPrice,
|
|
396
|
+
imageUrl: l.imageUrl
|
|
397
|
+
}))
|
|
398
|
+
);
|
|
399
|
+
if (opts && opts.clearOnSuccess) cartObj.clear();
|
|
400
|
+
return session;
|
|
401
|
+
}
|
|
402
|
+
};
|
|
403
|
+
return cartObj;
|
|
404
|
+
}
|
|
257
405
|
const cart = {
|
|
406
|
+
/** Create a new, empty cart (replaces any persisted cart) */
|
|
407
|
+
create() {
|
|
408
|
+
const st = freshCart();
|
|
409
|
+
saveCart(st);
|
|
410
|
+
return makeCart(st);
|
|
411
|
+
},
|
|
412
|
+
/** Get the current cart — loaded from storage if persisted, otherwise a fresh one */
|
|
413
|
+
current() {
|
|
414
|
+
return makeCart(loadCart() || freshCart());
|
|
415
|
+
},
|
|
258
416
|
/** Recover an abandoned cart by recovery token */
|
|
259
417
|
async recover(token) {
|
|
260
418
|
return request("GET", `/abandoned-carts/recover/${token}`);
|
|
261
419
|
}
|
|
262
420
|
};
|
|
421
|
+
const orders = {
|
|
422
|
+
/** Get customer's order history (delegates to `customer.orders`) */
|
|
423
|
+
list: (params) => customer.orders(params)
|
|
424
|
+
};
|
|
425
|
+
const customers = {
|
|
426
|
+
login: auth.login,
|
|
427
|
+
register: auth.register,
|
|
428
|
+
loginWithGoogle: auth.loginWithGoogle,
|
|
429
|
+
logout: auth.logout,
|
|
430
|
+
isAuthenticated: auth.isAuthenticated,
|
|
431
|
+
setTokens: auth.setTokens,
|
|
432
|
+
me: customer.me,
|
|
433
|
+
orders: customer.orders,
|
|
434
|
+
addresses: customer.addresses
|
|
435
|
+
};
|
|
263
436
|
return {
|
|
264
437
|
store,
|
|
265
438
|
products,
|
|
@@ -270,7 +443,9 @@ function createStorefrontClient(config) {
|
|
|
270
443
|
blog,
|
|
271
444
|
bookings,
|
|
272
445
|
forms,
|
|
273
|
-
cart
|
|
446
|
+
cart,
|
|
447
|
+
orders,
|
|
448
|
+
customers
|
|
274
449
|
};
|
|
275
450
|
}
|
|
276
451
|
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * ManageSales Storefront SDK\n * --------------------------\n * Resource-based client for building online storefronts.\n * Works with any framework: Next.js, Remix, Nuxt, Astro, plain HTML.\n *\n * Usage:\n * import { createStorefrontClient } from \"./managesales-storefront\";\n * const client = createStorefrontClient({ workspaceId: \"<workspace_id>\" });\n * const products = await client.products.list();\n */\n\n// ── Types ──────────────────────────────────────────────────────────\n\nexport interface StorefrontConfig {\n /** Your workspace ID (required) */\n workspaceId: string;\n /** API base URL — defaults to production */\n apiUrl?: string;\n /** Custom fetch implementation (for SSR / edge) */\n customFetch?: typeof fetch;\n /** Default locale for content */\n locale?: string;\n}\n\nexport interface ListParams {\n page?: number;\n limit?: number;\n search?: string;\n sort?: string;\n order?: \"asc\" | \"desc\";\n [key: string]: unknown;\n}\n\nexport interface PaginatedResponse<T> {\n data: T[];\n total: number;\n page: number;\n limit: number;\n totalPages: number;\n}\n\n// ── Product types ──\n\nexport interface Product {\n id: string;\n name: string;\n slug: string;\n description: string;\n status: string;\n price: number;\n compareAtPrice?: number;\n currency: string;\n images: ProductImage[];\n variants: ProductVariant[];\n category?: { id: string; name: string };\n brand?: { id: string; name: string };\n tags: string[];\n metadata: Record<string, unknown>;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface ProductImage {\n id: string;\n url: string;\n alt: string;\n position: number;\n}\n\nexport interface ProductVariant {\n id: string;\n name: string;\n sku: string;\n price: number;\n compareAtPrice?: number;\n inventory: number;\n options: Record<string, string>;\n}\n\n// ── Collection types ──\n\nexport interface Collection {\n id: string;\n name: string;\n slug: string;\n description: string;\n image?: string;\n productCount: number;\n}\n\n// ── Cart & Checkout types ──\n\nexport interface CheckoutLineItem {\n productId: string;\n variantId?: string;\n quantity: number;\n}\n\nexport interface CheckoutSession {\n id: string;\n status: \"open\" | \"completed\" | \"expired\";\n items: CheckoutLineItem[];\n subtotal: number;\n tax: number;\n shipping: number;\n discount: number;\n total: number;\n currency: string;\n couponCode?: string;\n}\n\nexport interface Order {\n id: string;\n orderNumber: string;\n status: string;\n total: number;\n currency: string;\n items: OrderItem[];\n createdAt: string;\n}\n\nexport interface OrderItem {\n productId: string;\n name: string;\n quantity: number;\n price: number;\n}\n\n// ── Customer types ──\n\nexport interface Customer {\n id: string;\n email: string;\n name: string;\n phone?: string;\n createdAt: string;\n}\n\nexport interface AuthSession {\n customer: Customer;\n accessToken: string;\n refreshToken: string;\n}\n\nexport interface Address {\n id: string;\n label?: string;\n line1: string;\n line2?: string;\n city: string;\n state: string;\n postalCode: string;\n country: string;\n isDefault: boolean;\n}\n\n// ── Blog types ──\n\nexport interface BlogPost {\n id: string;\n title: string;\n slug: string;\n excerpt: string;\n content: string;\n coverImage?: string;\n tags: string[];\n publishedAt: string;\n}\n\n// ── Booking types ──\n\nexport interface BookableService {\n id: string;\n name: string;\n duration: number;\n price: number;\n description: string;\n}\n\nexport interface TimeSlot {\n start: string;\n end: string;\n available: boolean;\n}\n\nexport interface Booking {\n id: string;\n serviceId: string;\n date: string;\n startTime: string;\n endTime: string;\n status: string;\n customer: { name: string; email: string };\n}\n\n// ── Form types ──\n\nexport interface FormDefinition {\n id: string;\n title: string;\n description: string;\n fields: FormField[];\n}\n\nexport interface FormField {\n id: string;\n type: string;\n label: string;\n required: boolean;\n options?: string[];\n placeholder?: string;\n}\n\n// ── Store config ──\n\nexport interface StoreConfig {\n name: string;\n slug: string;\n logo?: string;\n theme: Record<string, unknown>;\n currency: string;\n locale: string;\n}\n\n// ── SDK Error ──\n\nexport class StorefrontError extends Error {\n status: number;\n code: string;\n details?: unknown;\n\n constructor(status: number, code: string, message: string, details?: unknown) {\n super(message);\n this.name = \"StorefrontError\";\n this.status = status;\n this.code = code;\n this.details = details;\n }\n}\n\n// ── Core client ────────────────────────────────────────────────────\n\nexport function createStorefrontClient(config: StorefrontConfig) {\n const apiUrl = (config.apiUrl ?? \"https://managesales-backend-phase1test.up.railway.app/api/v1\").replace(/\\/$/, \"\");\n const fetchFn = config.customFetch ?? globalThis.fetch.bind(globalThis);\n let accessToken: string | null = null;\n let refreshToken: string | null = null;\n\n // ── Internal helpers ──\n\n async function request<T>(\n method: string,\n path: string,\n options?: {\n body?: unknown;\n params?: Record<string, unknown>;\n auth?: boolean;\n },\n ): Promise<T> {\n const url = new URL(`${apiUrl}${path}`);\n if (options?.params) {\n for (const [k, v] of Object.entries(options.params)) {\n if (v !== undefined && v !== null) url.searchParams.set(k, String(v));\n }\n }\n\n const headers: Record<string, string> = {\n \"X-Workspace-Id\": config.workspaceId,\n };\n\n if (options?.auth && accessToken) {\n headers[\"Authorization\"] = `Bearer ${accessToken}`;\n }\n\n if (options?.body) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n const res = await fetchFn(url.toString(), {\n method,\n headers,\n body: options?.body ? JSON.stringify(options.body) : undefined,\n });\n\n // Auto-refresh on 401\n if (res.status === 401 && options?.auth && refreshToken) {\n const refreshed = await refreshSession();\n if (refreshed) {\n headers[\"Authorization\"] = `Bearer ${accessToken}`;\n const retry = await fetchFn(url.toString(), {\n method,\n headers,\n body: options?.body ? JSON.stringify(options.body) : undefined,\n });\n if (!retry.ok) throw await parseError(retry);\n return retry.status === 204 ? (undefined as T) : retry.json();\n }\n }\n\n if (!res.ok) throw await parseError(res);\n return res.status === 204 ? (undefined as T) : res.json();\n }\n\n async function parseError(res: Response): Promise<StorefrontError> {\n try {\n const body = await res.json();\n return new StorefrontError(\n res.status,\n body.code ?? body.error ?? \"UNKNOWN\",\n body.message ?? res.statusText,\n body,\n );\n } catch {\n return new StorefrontError(res.status, \"UNKNOWN\", res.statusText);\n }\n }\n\n async function refreshSession(): Promise<boolean> {\n if (!refreshToken) return false;\n try {\n const res = await fetchFn(`${apiUrl}/storefront/auth/refresh`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Workspace-Id\": config.workspaceId,\n },\n body: JSON.stringify({ refreshToken }),\n });\n if (!res.ok) {\n accessToken = null;\n refreshToken = null;\n return false;\n }\n const data = await res.json();\n accessToken = data.accessToken;\n refreshToken = data.refreshToken;\n return true;\n } catch {\n return false;\n }\n }\n\n // ── Resource namespaces ──────────────────────────────────────────\n\n const store = {\n /** Resolve store config by workspace (returns theme, branding, currency) */\n async resolve(): Promise<StoreConfig> {\n return request(\"GET\", \"/storefront-api/resolve\");\n },\n };\n\n const products = {\n /** List published products with pagination and filters */\n async list(params?: ListParams): Promise<PaginatedResponse<Product>> {\n return request(\"GET\", \"/storefront-api/products\", { params });\n },\n /** Get a single product by ID or slug */\n async get(idOrSlug: string): Promise<Product> {\n return request(\"GET\", `/storefront-api/products/${idOrSlug}`);\n },\n /** Search products (shorthand for list with search param) */\n async search(query: string, params?: ListParams): Promise<PaginatedResponse<Product>> {\n return request(\"GET\", \"/storefront-api/search\", { params: { ...params, q: query } });\n },\n };\n\n const collections = {\n /** List published collections */\n async list(params?: ListParams): Promise<PaginatedResponse<Collection>> {\n return request(\"GET\", \"/storefront-api/collections\", { params });\n },\n /** Get a collection with its products */\n async get(idOrSlug: string): Promise<Collection & { products: Product[] }> {\n return request(\"GET\", `/storefront-api/collections/${idOrSlug}`);\n },\n };\n\n const checkout = {\n /** Create a checkout session from cart items */\n async create(items: CheckoutLineItem[]): Promise<CheckoutSession> {\n return request(\"POST\", \"/checkout\", { body: { items } });\n },\n /** Retrieve an existing checkout session */\n async get(checkoutId: string): Promise<CheckoutSession> {\n return request(\"GET\", `/checkout/${checkoutId}`);\n },\n /** Apply a coupon or discount code */\n async applyCoupon(checkoutId: string, couponCode: string): Promise<CheckoutSession> {\n return request(\"POST\", `/checkout/${checkoutId}/coupon`, { body: { couponCode } });\n },\n /** Complete checkout — process payment and create order */\n async complete(checkoutId: string, payment?: Record<string, unknown>): Promise<Order> {\n return request(\"POST\", `/checkout/${checkoutId}/complete`, { body: payment });\n },\n };\n\n const auth = {\n /** Register a new customer account */\n async register(data: {\n email: string;\n password: string;\n name: string;\n phone?: string;\n }): Promise<AuthSession> {\n const session = await request<AuthSession>(\"POST\", \"/storefront/auth/register\", { body: data });\n accessToken = session.accessToken;\n refreshToken = session.refreshToken;\n return session;\n },\n /** Log in with email + password */\n async login(email: string, password: string): Promise<AuthSession> {\n const session = await request<AuthSession>(\"POST\", \"/storefront/auth/login\", {\n body: { email, password },\n });\n accessToken = session.accessToken;\n refreshToken = session.refreshToken;\n return session;\n },\n /** Log in with Google ID token */\n async loginWithGoogle(idToken: string): Promise<AuthSession> {\n const session = await request<AuthSession>(\"POST\", \"/storefront/auth/google\", {\n body: { idToken },\n });\n accessToken = session.accessToken;\n refreshToken = session.refreshToken;\n return session;\n },\n /** Log out and clear local session */\n async logout(): Promise<void> {\n if (accessToken) {\n try {\n await request(\"POST\", \"/storefront/auth/logout\", { auth: true });\n } catch { /* ignore logout errors */ }\n }\n accessToken = null;\n refreshToken = null;\n },\n /** Refresh the access token */\n async refresh(): Promise<boolean> {\n return refreshSession();\n },\n /** Check if client has a valid session */\n isAuthenticated(): boolean {\n return accessToken !== null;\n },\n /** Manually set tokens (e.g., from SSR or cookie restoration) */\n setTokens(access: string, refresh: string): void {\n accessToken = access;\n refreshToken = refresh;\n },\n };\n\n const customer = {\n /** Get current customer profile */\n async me(): Promise<Customer> {\n return request(\"GET\", \"/storefront/auth/me\", { auth: true });\n },\n /** Get customer's order history */\n async orders(params?: ListParams): Promise<PaginatedResponse<Order>> {\n const me = await request<Customer>(\"GET\", \"/storefront/auth/me\", { auth: true });\n return request(\"GET\", `/storefront-api/customer/${me.id}/orders`, { params, auth: true });\n },\n /** Get customer's saved addresses */\n async addresses(): Promise<Address[]> {\n const me = await request<Customer>(\"GET\", \"/storefront/auth/me\", { auth: true });\n return request(\"GET\", `/storefront-api/customer/${me.id}/addresses`, { auth: true });\n },\n };\n\n const blog = {\n /** List published blog posts */\n async list(params?: ListParams & { tags?: string }): Promise<PaginatedResponse<BlogPost>> {\n return request(\"GET\", \"/storefront-api/blog/posts\", { params });\n },\n /** Get a blog post by slug */\n async get(slug: string): Promise<BlogPost> {\n return request(\"GET\", `/storefront-api/blog/posts/${slug}`);\n },\n };\n\n const bookings = {\n /** List bookable services */\n async services(): Promise<BookableService[]> {\n return request(\"GET\", \"/bookings-public/services\");\n },\n /** List bookable staff */\n async staff(): Promise<Array<{ id: string; name: string; avatar?: string }>> {\n return request(\"GET\", \"/bookings-public/staff\");\n },\n /** Check available time slots */\n async availability(params: {\n serviceId: string;\n date: string;\n staffId?: string;\n }): Promise<TimeSlot[]> {\n return request(\"GET\", \"/bookings-public/availability\", { params });\n },\n /** Create a booking */\n async book(data: {\n serviceId: string;\n date: string;\n startTime: string;\n staffId?: string;\n customer: { name: string; email: string; phone?: string };\n }): Promise<Booking> {\n return request(\"POST\", \"/bookings-public/book\", { body: data });\n },\n };\n\n const forms = {\n /** Get a published form by ID or slug */\n async get(idOrSlug: string): Promise<FormDefinition> {\n return request(\"GET\", `/public/forms/${idOrSlug}`);\n },\n /** Submit form response */\n async submit(formId: string, data: Record<string, unknown>): Promise<{ id: string }> {\n return request(\"POST\", `/public/forms/${formId}/submit`, { body: data });\n },\n /** Upload file for a form file field */\n async uploadFile(formId: string, file: File): Promise<{ url: string }> {\n const formData = new FormData();\n formData.append(\"file\", file);\n const res = await fetchFn(`${apiUrl}/public/forms/${formId}/upload`, {\n method: \"POST\",\n headers: { \"X-Workspace-Id\": config.workspaceId },\n body: formData,\n });\n if (!res.ok) throw await parseError(res);\n return res.json();\n },\n };\n\n const cart = {\n /** Recover an abandoned cart by recovery token */\n async recover(token: string): Promise<CheckoutSession> {\n return request(\"GET\", `/abandoned-carts/recover/${token}`);\n },\n };\n\n // ── Public API ───────────────────────────────────────────────────\n\n return {\n store,\n products,\n collections,\n checkout,\n auth,\n customer,\n blog,\n bookings,\n forms,\n cart,\n };\n}\n\n// ── Type export for TypeScript users ──\n\nexport type StorefrontClient = ReturnType<typeof createStorefrontClient>;\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.cjs","sourcesContent":["/**\n * ManageSales Storefront SDK\n * --------------------------\n * Resource-based client for building online storefronts.\n * Works with any framework: Next.js, Remix, Nuxt, Astro, plain HTML.\n *\n * Usage:\n * import { createStorefrontClient } from \"./managesales-storefront\";\n * const client = createStorefrontClient({ workspaceId: \"<workspace_id>\" });\n * const products = await client.products.list();\n */\n\n// ── Types ──────────────────────────────────────────────────────────\n\nexport interface RetryConfig {\n /** Max retry attempts for a failed request (default: 2) */\n retries?: number;\n /** Base delay in ms used for exponential backoff (default: 300) */\n baseDelayMs?: number;\n /** Upper bound for any single backoff delay, in ms (default: 4000) */\n maxDelayMs?: number;\n /** HTTP status codes that are retried, GET/HEAD only (default: [408,425,429,500,502,503,504]) */\n retryOnStatus?: number[];\n}\n\nexport interface StorefrontConfig {\n /** Your workspace ID (required) */\n workspaceId: string;\n /** API base URL — defaults to production */\n apiUrl?: string;\n /** Custom fetch implementation (for SSR / edge) */\n customFetch?: typeof fetch;\n /** Default locale for content */\n locale?: string;\n /**\n * Retry behavior for transient failures (network errors, and 5xx/408/425/429\n * responses on idempotent GET/HEAD requests). Pass `false` to disable retries\n * entirely. Defaults to 2 retries with exponential backoff.\n */\n retry?: RetryConfig | false;\n /**\n * Persist the stateful cart (`client.cart`) to `localStorage` so it survives\n * page reloads. Defaults to `true`. Has no effect in environments without\n * `localStorage` (e.g. Node/SSR) — the cart simply stays in-memory.\n */\n persistCart?: boolean;\n}\n\nexport interface ListParams {\n page?: number;\n limit?: number;\n search?: string;\n sort?: string;\n order?: \"asc\" | \"desc\";\n [key: string]: unknown;\n}\n\nexport interface PaginatedResponse<T> {\n data: T[];\n total: number;\n page: number;\n limit: number;\n totalPages: number;\n}\n\n// ── Product types ──\n\nexport interface Product {\n id: string;\n name: string;\n slug: string;\n description: string;\n status: string;\n price: number;\n compareAtPrice?: number;\n currency: string;\n images: ProductImage[];\n variants: ProductVariant[];\n category?: { id: string; name: string };\n brand?: { id: string; name: string };\n tags: string[];\n metadata: Record<string, unknown>;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface ProductImage {\n id: string;\n url: string;\n alt: string;\n position: number;\n}\n\nexport interface ProductVariant {\n id: string;\n name: string;\n sku: string;\n price: number;\n compareAtPrice?: number;\n inventory: number;\n options: Record<string, string>;\n}\n\n// ── Collection types ──\n\nexport interface Collection {\n id: string;\n name: string;\n slug: string;\n description: string;\n image?: string;\n productCount: number;\n}\n\n// ── Cart & Checkout types ──\n\nexport interface CheckoutLineItem {\n productId: string;\n variantId?: string;\n productName: string;\n quantity: number;\n unitPrice: number;\n imageUrl?: string;\n}\n\nexport interface CheckoutSession {\n id: string;\n status: \"open\" | \"completed\" | \"expired\";\n items: CheckoutLineItem[];\n subtotal: number;\n tax: number;\n shipping: number;\n discount: number;\n total: number;\n currency: string;\n couponCode?: string;\n}\n\nexport interface CartLine {\n id: string;\n productId: string;\n variantId?: string;\n quantity: number;\n unitPrice: number;\n productName: string;\n imageUrl?: string;\n}\n\nexport interface CartState {\n id: string;\n lines: CartLine[];\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface AddCartLineItemInput {\n productId: string;\n variantId?: string;\n quantity: number;\n /** Skip the `products.get` price lookup by supplying these directly */\n unitPrice?: number;\n productName?: string;\n imageUrl?: string;\n}\n\nexport interface StorefrontCart {\n readonly id: string;\n readonly lines: CartLine[];\n readonly itemCount: number;\n readonly subtotal: number;\n /** Adds a line item. Supplying `unitPrice` also requires `productName`, otherwise this falls through to a `products.get` network lookup. */\n addLineItem(input: AddCartLineItemInput): Promise<StorefrontCart>;\n updateLineItem(lineId: string, quantity: number): StorefrontCart;\n removeLineItem(lineId: string): StorefrontCart;\n clear(): StorefrontCart;\n checkout(opts?: { clearOnSuccess?: boolean }): Promise<CheckoutSession>;\n}\n\nexport interface Order {\n id: string;\n orderNumber: string;\n status: string;\n total: number;\n currency: string;\n items: OrderItem[];\n createdAt: string;\n}\n\nexport interface OrderItem {\n productId: string;\n name: string;\n quantity: number;\n price: number;\n}\n\n// ── Customer types ──\n\nexport interface Customer {\n id: string;\n email: string;\n name: string;\n phone?: string;\n createdAt: string;\n}\n\nexport interface AuthSession {\n customer: Customer;\n accessToken: string;\n refreshToken: string;\n}\n\nexport interface Address {\n id: string;\n label?: string;\n line1: string;\n line2?: string;\n city: string;\n state: string;\n postalCode: string;\n country: string;\n isDefault: boolean;\n}\n\n// ── Blog types ──\n\nexport interface BlogPost {\n id: string;\n title: string;\n slug: string;\n excerpt: string;\n content: string;\n coverImage?: string;\n tags: string[];\n publishedAt: string;\n}\n\n// ── Booking types ──\n\nexport interface BookableService {\n id: string;\n name: string;\n duration: number;\n price: number;\n description: string;\n}\n\nexport interface TimeSlot {\n start: string;\n end: string;\n available: boolean;\n}\n\nexport interface Booking {\n id: string;\n serviceId: string;\n date: string;\n startTime: string;\n endTime: string;\n status: string;\n customer: { name: string; email: string };\n}\n\n// ── Form types ──\n\nexport interface FormDefinition {\n id: string;\n title: string;\n description: string;\n fields: FormField[];\n}\n\nexport interface FormField {\n id: string;\n type: string;\n label: string;\n required: boolean;\n options?: string[];\n placeholder?: string;\n}\n\n// ── Store config ──\n\nexport interface StoreConfig {\n name: string;\n slug: string;\n logo?: string;\n theme: Record<string, unknown>;\n currency: string;\n locale: string;\n}\n\n// ── SDK Error ──\n\nexport class StorefrontError extends Error {\n status: number;\n code: string;\n details?: unknown;\n\n constructor(status: number, code: string, message: string, details?: unknown) {\n super(message);\n this.name = \"StorefrontError\";\n this.status = status;\n this.code = code;\n this.details = details;\n }\n}\n\n// ── Core client ────────────────────────────────────────────────────\n\nexport function createStorefrontClient(config: StorefrontConfig) {\n const apiUrl = (config.apiUrl ?? \"https://managesales-backend-phase1test.up.railway.app/api/v1\").replace(/\\/$/, \"\");\n const fetchFn = config.customFetch ?? globalThis.fetch.bind(globalThis);\n let accessToken: string | null = null;\n let refreshToken: string | null = null;\n\n // ── Retry configuration ──\n\n const RETRY_DEFAULTS = {\n retries: 2,\n baseDelayMs: 300,\n maxDelayMs: 4000,\n retryOnStatus: [408, 425, 429, 500, 502, 503, 504],\n };\n const rc =\n config.retry === false\n ? { ...RETRY_DEFAULTS, retries: 0 }\n : { ...RETRY_DEFAULTS, ...(config.retry || {}) };\n const IDEMPOTENT = new Set([\"GET\", \"HEAD\"]);\n const sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));\n function backoff(attempt: number): number {\n return Math.min(rc.maxDelayMs, Math.round(Math.random() * rc.baseDelayMs * Math.pow(2, attempt)));\n }\n\n // ── Internal helpers ──\n\n async function request<T>(\n method: string,\n path: string,\n options?: {\n body?: unknown;\n params?: Record<string, unknown>;\n auth?: boolean;\n },\n ): Promise<T> {\n const url = new URL(`${apiUrl}${path}`);\n if (options?.params) {\n for (const [k, v] of Object.entries(options.params)) {\n if (v !== undefined && v !== null) url.searchParams.set(k, String(v));\n }\n }\n\n const headers: Record<string, string> = {\n \"X-Workspace-Id\": config.workspaceId,\n };\n\n if (options?.auth && accessToken) {\n headers[\"Authorization\"] = `Bearer ${accessToken}`;\n }\n\n if (options?.body) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n const fetchOptions: RequestInit = {\n method,\n headers,\n body: options?.body ? JSON.stringify(options.body) : undefined,\n };\n\n let attempt = 0;\n let res: Response;\n while (true) {\n try {\n res = await fetchFn(url.toString(), fetchOptions);\n } catch (err) {\n if (attempt < rc.retries) {\n await sleep(backoff(attempt));\n attempt++;\n continue;\n }\n throw err;\n }\n if (attempt < rc.retries && rc.retryOnStatus.includes(res.status) && IDEMPOTENT.has(method)) {\n const ra = Number(res.headers.get(\"retry-after\"));\n await sleep(Number.isFinite(ra) && ra > 0 ? ra * 1000 : backoff(attempt));\n attempt++;\n continue;\n }\n break;\n }\n\n // Auto-refresh on 401\n if (res.status === 401 && options?.auth && refreshToken) {\n const refreshed = await refreshSession();\n if (refreshed) {\n headers[\"Authorization\"] = `Bearer ${accessToken}`;\n const retry = await fetchFn(url.toString(), {\n method,\n headers,\n body: options?.body ? JSON.stringify(options.body) : undefined,\n });\n if (!retry.ok) throw await parseError(retry);\n return retry.status === 204 ? (undefined as T) : retry.json();\n }\n }\n\n if (!res.ok) throw await parseError(res);\n return res.status === 204 ? (undefined as T) : res.json();\n }\n\n async function parseError(res: Response): Promise<StorefrontError> {\n try {\n const body = await res.json();\n return new StorefrontError(\n res.status,\n body.code ?? body.error ?? \"UNKNOWN\",\n body.message ?? res.statusText,\n body,\n );\n } catch {\n return new StorefrontError(res.status, \"UNKNOWN\", res.statusText);\n }\n }\n\n async function refreshSession(): Promise<boolean> {\n if (!refreshToken) return false;\n try {\n const res = await fetchFn(`${apiUrl}/storefront/auth/refresh`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Workspace-Id\": config.workspaceId,\n },\n body: JSON.stringify({ refreshToken }),\n });\n if (!res.ok) {\n accessToken = null;\n refreshToken = null;\n return false;\n }\n const data = await res.json();\n accessToken = data.accessToken;\n refreshToken = data.refreshToken;\n return true;\n } catch {\n return false;\n }\n }\n\n // ── Resource namespaces ──────────────────────────────────────────\n\n const store = {\n /** Resolve store config by workspace (returns theme, branding, currency) */\n async resolve(): Promise<StoreConfig> {\n return request(\"GET\", \"/storefront-api/resolve\");\n },\n };\n\n const products = {\n /** List published products with pagination and filters */\n async list(params?: ListParams): Promise<PaginatedResponse<Product>> {\n return request(\"GET\", \"/storefront-api/products\", { params });\n },\n /** Get a single product by ID or slug */\n async get(idOrSlug: string): Promise<Product> {\n return request(\"GET\", `/storefront-api/products/${idOrSlug}`);\n },\n /** Search products (shorthand for list with search param) */\n async search(query: string, params?: ListParams): Promise<PaginatedResponse<Product>> {\n return request(\"GET\", \"/storefront-api/search\", { params: { ...params, q: query } });\n },\n };\n\n const collections = {\n /** List published collections */\n async list(params?: ListParams): Promise<PaginatedResponse<Collection>> {\n return request(\"GET\", \"/storefront-api/collections\", { params });\n },\n /** Get a collection with its products */\n async get(idOrSlug: string): Promise<Collection & { products: Product[] }> {\n return request(\"GET\", `/storefront-api/collections/${idOrSlug}`);\n },\n };\n\n const checkout = {\n /** Create a checkout session from cart items */\n async create(items: CheckoutLineItem[]): Promise<CheckoutSession> {\n return request(\"POST\", \"/checkout\", { body: { lines: items } });\n },\n /** Retrieve an existing checkout session */\n async get(checkoutId: string): Promise<CheckoutSession> {\n return request(\"GET\", `/checkout/${checkoutId}`);\n },\n /** Apply a coupon or discount code */\n async applyCoupon(checkoutId: string, couponCode: string): Promise<CheckoutSession> {\n return request(\"POST\", `/checkout/${checkoutId}/coupon`, { body: { couponCode } });\n },\n /** Complete checkout — process payment and create order */\n async complete(checkoutId: string, payment?: Record<string, unknown>): Promise<Order> {\n return request(\"POST\", `/checkout/${checkoutId}/complete`, { body: payment });\n },\n };\n\n const auth = {\n /** Register a new customer account */\n async register(data: {\n email: string;\n password: string;\n name: string;\n phone?: string;\n }): Promise<AuthSession> {\n const session = await request<AuthSession>(\"POST\", \"/storefront/auth/register\", { body: data });\n accessToken = session.accessToken;\n refreshToken = session.refreshToken;\n return session;\n },\n /** Log in with email + password */\n async login(email: string, password: string): Promise<AuthSession> {\n const session = await request<AuthSession>(\"POST\", \"/storefront/auth/login\", {\n body: { email, password },\n });\n accessToken = session.accessToken;\n refreshToken = session.refreshToken;\n return session;\n },\n /** Log in with Google ID token */\n async loginWithGoogle(idToken: string): Promise<AuthSession> {\n const session = await request<AuthSession>(\"POST\", \"/storefront/auth/google\", {\n body: { idToken },\n });\n accessToken = session.accessToken;\n refreshToken = session.refreshToken;\n return session;\n },\n /** Log out and clear local session */\n async logout(): Promise<void> {\n if (accessToken) {\n try {\n await request(\"POST\", \"/storefront/auth/logout\", { auth: true });\n } catch { /* ignore logout errors */ }\n }\n accessToken = null;\n refreshToken = null;\n },\n /** Refresh the access token */\n async refresh(): Promise<boolean> {\n return refreshSession();\n },\n /** Check if client has a valid session */\n isAuthenticated(): boolean {\n return accessToken !== null;\n },\n /** Manually set tokens (e.g., from SSR or cookie restoration) */\n setTokens(access: string, refresh: string): void {\n accessToken = access;\n refreshToken = refresh;\n },\n };\n\n const customer = {\n /** Get current customer profile */\n async me(): Promise<Customer> {\n return request(\"GET\", \"/storefront/auth/me\", { auth: true });\n },\n /** Get customer's order history */\n async orders(params?: ListParams): Promise<PaginatedResponse<Order>> {\n const me = await request<Customer>(\"GET\", \"/storefront/auth/me\", { auth: true });\n return request(\"GET\", `/storefront-api/customer/${me.id}/orders`, { params, auth: true });\n },\n /** Get customer's saved addresses */\n async addresses(): Promise<Address[]> {\n const me = await request<Customer>(\"GET\", \"/storefront/auth/me\", { auth: true });\n return request(\"GET\", `/storefront-api/customer/${me.id}/addresses`, { auth: true });\n },\n };\n\n const blog = {\n /** List published blog posts */\n async list(params?: ListParams & { tags?: string }): Promise<PaginatedResponse<BlogPost>> {\n return request(\"GET\", \"/storefront-api/blog/posts\", { params });\n },\n /** Get a blog post by slug */\n async get(slug: string): Promise<BlogPost> {\n return request(\"GET\", `/storefront-api/blog/posts/${slug}`);\n },\n };\n\n const bookings = {\n /** List bookable services */\n async services(): Promise<BookableService[]> {\n return request(\"GET\", \"/bookings-public/services\");\n },\n /** List bookable staff */\n async staff(): Promise<Array<{ id: string; name: string; avatar?: string }>> {\n return request(\"GET\", \"/bookings-public/staff\");\n },\n /** Check available time slots */\n async availability(params: {\n serviceId: string;\n date: string;\n staffId?: string;\n }): Promise<TimeSlot[]> {\n return request(\"GET\", \"/bookings-public/availability\", { params });\n },\n /** Create a booking */\n async book(data: {\n serviceId: string;\n date: string;\n startTime: string;\n staffId?: string;\n customer: { name: string; email: string; phone?: string };\n }): Promise<Booking> {\n return request(\"POST\", \"/bookings-public/book\", { body: data });\n },\n };\n\n const forms = {\n /** Get a published form by ID or slug */\n async get(idOrSlug: string): Promise<FormDefinition> {\n return request(\"GET\", `/public/forms/${idOrSlug}`);\n },\n /** Submit form response */\n async submit(formId: string, data: Record<string, unknown>): Promise<{ id: string }> {\n return request(\"POST\", `/public/forms/${formId}/submit`, { body: data });\n },\n /** Upload file for a form file field */\n async uploadFile(formId: string, file: File): Promise<{ url: string }> {\n const formData = new FormData();\n formData.append(\"file\", file);\n const res = await fetchFn(`${apiUrl}/public/forms/${formId}/upload`, {\n method: \"POST\",\n headers: { \"X-Workspace-Id\": config.workspaceId },\n body: formData,\n });\n if (!res.ok) throw await parseError(res);\n return res.json();\n },\n };\n\n // ── Stateful cart (client-side, localStorage-persisted) ──\n\n const CART_KEY = \"managesales_cart_\" + config.workspaceId;\n const hasLS = () => {\n try {\n return config.persistCart !== false && typeof localStorage !== \"undefined\";\n } catch {\n return false;\n }\n };\n const loadCart = (): CartState | null => {\n if (!hasLS()) return null;\n try {\n const s = localStorage.getItem(CART_KEY);\n return s ? JSON.parse(s) : null;\n } catch {\n return null;\n }\n };\n const saveCart = (st: CartState): void => {\n if (!hasLS()) return;\n try {\n localStorage.setItem(CART_KEY, JSON.stringify(st));\n } catch {\n /* ignore persistence errors (quota, privacy mode, etc.) */\n }\n };\n let __lineSeq = 0;\n const genId = (p: string): string =>\n typeof crypto !== \"undefined\" && crypto.randomUUID ? p + crypto.randomUUID() : p + Date.now() + \"_\" + ++__lineSeq;\n const nowIso = (): string => new Date().toISOString();\n const freshCart = (): CartState => ({ id: genId(\"cart_\"), lines: [], createdAt: nowIso(), updatedAt: nowIso() });\n\n function makeCart(state: CartState): StorefrontCart {\n const persist = () => {\n state.updatedAt = nowIso();\n saveCart(state);\n };\n const cartObj: StorefrontCart = {\n get id() {\n return state.id;\n },\n get lines() {\n return state.lines;\n },\n get itemCount() {\n return state.lines.reduce((n, l) => n + l.quantity, 0);\n },\n get subtotal() {\n return state.lines.reduce((s, l) => s + l.unitPrice * l.quantity, 0);\n },\n // Supplying unitPrice alone does not skip the lookup — productName must also be\n // provided, otherwise this falls through to the products.get() network lookup below.\n async addLineItem(input: AddCartLineItemInput): Promise<StorefrontCart> {\n let line: CartLine;\n if (input.unitPrice != null && input.productName) {\n line = {\n id: genId(\"line_\"),\n productId: input.productId,\n variantId: input.variantId,\n quantity: input.quantity,\n unitPrice: input.unitPrice,\n productName: input.productName,\n imageUrl: input.imageUrl,\n };\n } else {\n const p = await products.get(input.productId);\n const v = input.variantId && Array.isArray(p.variants) ? p.variants.find((x) => x.id === input.variantId) : undefined;\n line = {\n id: genId(\"line_\"),\n productId: input.productId,\n variantId: input.variantId,\n quantity: input.quantity,\n unitPrice: v && v.price != null ? v.price : p.price,\n productName: p.name,\n imageUrl: Array.isArray(p.images) && p.images.length > 0 ? p.images[0].url : undefined,\n };\n }\n const existing = state.lines.find((l) => l.productId === line.productId && l.variantId === line.variantId);\n if (existing) existing.quantity += line.quantity;\n else state.lines.push(line);\n persist();\n return cartObj;\n },\n updateLineItem(lineId: string, quantity: number): StorefrontCart {\n const l = state.lines.find((x) => x.id === lineId);\n if (l) {\n if (quantity <= 0) state.lines = state.lines.filter((x) => x.id !== lineId);\n else l.quantity = quantity;\n persist();\n }\n return cartObj;\n },\n removeLineItem(lineId: string): StorefrontCart {\n state.lines = state.lines.filter((x) => x.id !== lineId);\n persist();\n return cartObj;\n },\n clear(): StorefrontCart {\n state.lines = [];\n persist();\n return cartObj;\n },\n async checkout(opts?: { clearOnSuccess?: boolean }): Promise<CheckoutSession> {\n const session = await checkout.create(\n state.lines.map((l) => ({\n productId: l.productId,\n variantId: l.variantId,\n productName: l.productName,\n quantity: l.quantity,\n unitPrice: l.unitPrice,\n imageUrl: l.imageUrl,\n })),\n );\n if (opts && opts.clearOnSuccess) cartObj.clear();\n return session;\n },\n };\n return cartObj;\n }\n\n const cart = {\n /** Create a new, empty cart (replaces any persisted cart) */\n create(): StorefrontCart {\n const st = freshCart();\n saveCart(st);\n return makeCart(st);\n },\n /** Get the current cart — loaded from storage if persisted, otherwise a fresh one */\n current(): StorefrontCart {\n return makeCart(loadCart() || freshCart());\n },\n /** Recover an abandoned cart by recovery token */\n async recover(token: string): Promise<CheckoutSession> {\n return request(\"GET\", `/abandoned-carts/recover/${token}`);\n },\n };\n\n // ── Alias namespaces (convenience wrappers over existing methods) ──\n\n /** Alias for `client.customer.orders` — order history for the logged-in customer */\n const orders = {\n /** Get customer's order history (delegates to `customer.orders`) */\n list: (params?: ListParams): Promise<PaginatedResponse<Order>> => customer.orders(params),\n };\n\n /** Convenience namespace bundling `auth` + `customer` under one name */\n const customers = {\n login: auth.login,\n register: auth.register,\n loginWithGoogle: auth.loginWithGoogle,\n logout: auth.logout,\n isAuthenticated: auth.isAuthenticated,\n setTokens: auth.setTokens,\n me: customer.me,\n orders: customer.orders,\n addresses: customer.addresses,\n };\n\n // ── Public API ───────────────────────────────────────────────────\n\n return {\n store,\n products,\n collections,\n checkout,\n auth,\n customer,\n blog,\n bookings,\n forms,\n cart,\n orders,\n customers,\n };\n}\n\n// ── Type export for TypeScript users ──\n\nexport type StorefrontClient = ReturnType<typeof createStorefrontClient>;\n"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -9,6 +9,16 @@
|
|
|
9
9
|
* const client = createStorefrontClient({ workspaceId: "<workspace_id>" });
|
|
10
10
|
* const products = await client.products.list();
|
|
11
11
|
*/
|
|
12
|
+
interface RetryConfig {
|
|
13
|
+
/** Max retry attempts for a failed request (default: 2) */
|
|
14
|
+
retries?: number;
|
|
15
|
+
/** Base delay in ms used for exponential backoff (default: 300) */
|
|
16
|
+
baseDelayMs?: number;
|
|
17
|
+
/** Upper bound for any single backoff delay, in ms (default: 4000) */
|
|
18
|
+
maxDelayMs?: number;
|
|
19
|
+
/** HTTP status codes that are retried, GET/HEAD only (default: [408,425,429,500,502,503,504]) */
|
|
20
|
+
retryOnStatus?: number[];
|
|
21
|
+
}
|
|
12
22
|
interface StorefrontConfig {
|
|
13
23
|
/** Your workspace ID (required) */
|
|
14
24
|
workspaceId: string;
|
|
@@ -18,6 +28,18 @@ interface StorefrontConfig {
|
|
|
18
28
|
customFetch?: typeof fetch;
|
|
19
29
|
/** Default locale for content */
|
|
20
30
|
locale?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Retry behavior for transient failures (network errors, and 5xx/408/425/429
|
|
33
|
+
* responses on idempotent GET/HEAD requests). Pass `false` to disable retries
|
|
34
|
+
* entirely. Defaults to 2 retries with exponential backoff.
|
|
35
|
+
*/
|
|
36
|
+
retry?: RetryConfig | false;
|
|
37
|
+
/**
|
|
38
|
+
* Persist the stateful cart (`client.cart`) to `localStorage` so it survives
|
|
39
|
+
* page reloads. Defaults to `true`. Has no effect in environments without
|
|
40
|
+
* `localStorage` (e.g. Node/SSR) — the cart simply stays in-memory.
|
|
41
|
+
*/
|
|
42
|
+
persistCart?: boolean;
|
|
21
43
|
}
|
|
22
44
|
interface ListParams {
|
|
23
45
|
page?: number;
|
|
@@ -84,7 +106,10 @@ interface Collection {
|
|
|
84
106
|
interface CheckoutLineItem {
|
|
85
107
|
productId: string;
|
|
86
108
|
variantId?: string;
|
|
109
|
+
productName: string;
|
|
87
110
|
quantity: number;
|
|
111
|
+
unitPrice: number;
|
|
112
|
+
imageUrl?: string;
|
|
88
113
|
}
|
|
89
114
|
interface CheckoutSession {
|
|
90
115
|
id: string;
|
|
@@ -98,6 +123,44 @@ interface CheckoutSession {
|
|
|
98
123
|
currency: string;
|
|
99
124
|
couponCode?: string;
|
|
100
125
|
}
|
|
126
|
+
interface CartLine {
|
|
127
|
+
id: string;
|
|
128
|
+
productId: string;
|
|
129
|
+
variantId?: string;
|
|
130
|
+
quantity: number;
|
|
131
|
+
unitPrice: number;
|
|
132
|
+
productName: string;
|
|
133
|
+
imageUrl?: string;
|
|
134
|
+
}
|
|
135
|
+
interface CartState {
|
|
136
|
+
id: string;
|
|
137
|
+
lines: CartLine[];
|
|
138
|
+
createdAt: string;
|
|
139
|
+
updatedAt: string;
|
|
140
|
+
}
|
|
141
|
+
interface AddCartLineItemInput {
|
|
142
|
+
productId: string;
|
|
143
|
+
variantId?: string;
|
|
144
|
+
quantity: number;
|
|
145
|
+
/** Skip the `products.get` price lookup by supplying these directly */
|
|
146
|
+
unitPrice?: number;
|
|
147
|
+
productName?: string;
|
|
148
|
+
imageUrl?: string;
|
|
149
|
+
}
|
|
150
|
+
interface StorefrontCart {
|
|
151
|
+
readonly id: string;
|
|
152
|
+
readonly lines: CartLine[];
|
|
153
|
+
readonly itemCount: number;
|
|
154
|
+
readonly subtotal: number;
|
|
155
|
+
/** Adds a line item. Supplying `unitPrice` also requires `productName`, otherwise this falls through to a `products.get` network lookup. */
|
|
156
|
+
addLineItem(input: AddCartLineItemInput): Promise<StorefrontCart>;
|
|
157
|
+
updateLineItem(lineId: string, quantity: number): StorefrontCart;
|
|
158
|
+
removeLineItem(lineId: string): StorefrontCart;
|
|
159
|
+
clear(): StorefrontCart;
|
|
160
|
+
checkout(opts?: {
|
|
161
|
+
clearOnSuccess?: boolean;
|
|
162
|
+
}): Promise<CheckoutSession>;
|
|
163
|
+
}
|
|
101
164
|
interface Order {
|
|
102
165
|
id: string;
|
|
103
166
|
orderNumber: string;
|
|
@@ -307,10 +370,34 @@ declare function createStorefrontClient(config: StorefrontConfig): {
|
|
|
307
370
|
}>;
|
|
308
371
|
};
|
|
309
372
|
cart: {
|
|
373
|
+
/** Create a new, empty cart (replaces any persisted cart) */
|
|
374
|
+
create(): StorefrontCart;
|
|
375
|
+
/** Get the current cart — loaded from storage if persisted, otherwise a fresh one */
|
|
376
|
+
current(): StorefrontCart;
|
|
310
377
|
/** Recover an abandoned cart by recovery token */
|
|
311
378
|
recover(token: string): Promise<CheckoutSession>;
|
|
312
379
|
};
|
|
380
|
+
orders: {
|
|
381
|
+
/** Get customer's order history (delegates to `customer.orders`) */
|
|
382
|
+
list: (params?: ListParams) => Promise<PaginatedResponse<Order>>;
|
|
383
|
+
};
|
|
384
|
+
customers: {
|
|
385
|
+
login: (email: string, password: string) => Promise<AuthSession>;
|
|
386
|
+
register: (data: {
|
|
387
|
+
email: string;
|
|
388
|
+
password: string;
|
|
389
|
+
name: string;
|
|
390
|
+
phone?: string;
|
|
391
|
+
}) => Promise<AuthSession>;
|
|
392
|
+
loginWithGoogle: (idToken: string) => Promise<AuthSession>;
|
|
393
|
+
logout: () => Promise<void>;
|
|
394
|
+
isAuthenticated: () => boolean;
|
|
395
|
+
setTokens: (access: string, refresh: string) => void;
|
|
396
|
+
me: () => Promise<Customer>;
|
|
397
|
+
orders: (params?: ListParams) => Promise<PaginatedResponse<Order>>;
|
|
398
|
+
addresses: () => Promise<Address[]>;
|
|
399
|
+
};
|
|
313
400
|
};
|
|
314
401
|
type StorefrontClient = ReturnType<typeof createStorefrontClient>;
|
|
315
402
|
|
|
316
|
-
export { type Address, type AuthSession, type BlogPost, type BookableService, type Booking, type CheckoutLineItem, type CheckoutSession, type Collection, type Customer, type FormDefinition, type FormField, type ListParams, type Order, type OrderItem, type PaginatedResponse, type Product, type ProductImage, type ProductVariant, type StoreConfig, type StorefrontClient, type StorefrontConfig, StorefrontError, type TimeSlot, createStorefrontClient };
|
|
403
|
+
export { type AddCartLineItemInput, type Address, type AuthSession, type BlogPost, type BookableService, type Booking, type CartLine, type CartState, type CheckoutLineItem, type CheckoutSession, type Collection, type Customer, type FormDefinition, type FormField, type ListParams, type Order, type OrderItem, type PaginatedResponse, type Product, type ProductImage, type ProductVariant, type RetryConfig, type StoreConfig, type StorefrontCart, type StorefrontClient, type StorefrontConfig, StorefrontError, type TimeSlot, createStorefrontClient };
|
package/dist/index.d.ts
CHANGED
|
@@ -9,6 +9,16 @@
|
|
|
9
9
|
* const client = createStorefrontClient({ workspaceId: "<workspace_id>" });
|
|
10
10
|
* const products = await client.products.list();
|
|
11
11
|
*/
|
|
12
|
+
interface RetryConfig {
|
|
13
|
+
/** Max retry attempts for a failed request (default: 2) */
|
|
14
|
+
retries?: number;
|
|
15
|
+
/** Base delay in ms used for exponential backoff (default: 300) */
|
|
16
|
+
baseDelayMs?: number;
|
|
17
|
+
/** Upper bound for any single backoff delay, in ms (default: 4000) */
|
|
18
|
+
maxDelayMs?: number;
|
|
19
|
+
/** HTTP status codes that are retried, GET/HEAD only (default: [408,425,429,500,502,503,504]) */
|
|
20
|
+
retryOnStatus?: number[];
|
|
21
|
+
}
|
|
12
22
|
interface StorefrontConfig {
|
|
13
23
|
/** Your workspace ID (required) */
|
|
14
24
|
workspaceId: string;
|
|
@@ -18,6 +28,18 @@ interface StorefrontConfig {
|
|
|
18
28
|
customFetch?: typeof fetch;
|
|
19
29
|
/** Default locale for content */
|
|
20
30
|
locale?: string;
|
|
31
|
+
/**
|
|
32
|
+
* Retry behavior for transient failures (network errors, and 5xx/408/425/429
|
|
33
|
+
* responses on idempotent GET/HEAD requests). Pass `false` to disable retries
|
|
34
|
+
* entirely. Defaults to 2 retries with exponential backoff.
|
|
35
|
+
*/
|
|
36
|
+
retry?: RetryConfig | false;
|
|
37
|
+
/**
|
|
38
|
+
* Persist the stateful cart (`client.cart`) to `localStorage` so it survives
|
|
39
|
+
* page reloads. Defaults to `true`. Has no effect in environments without
|
|
40
|
+
* `localStorage` (e.g. Node/SSR) — the cart simply stays in-memory.
|
|
41
|
+
*/
|
|
42
|
+
persistCart?: boolean;
|
|
21
43
|
}
|
|
22
44
|
interface ListParams {
|
|
23
45
|
page?: number;
|
|
@@ -84,7 +106,10 @@ interface Collection {
|
|
|
84
106
|
interface CheckoutLineItem {
|
|
85
107
|
productId: string;
|
|
86
108
|
variantId?: string;
|
|
109
|
+
productName: string;
|
|
87
110
|
quantity: number;
|
|
111
|
+
unitPrice: number;
|
|
112
|
+
imageUrl?: string;
|
|
88
113
|
}
|
|
89
114
|
interface CheckoutSession {
|
|
90
115
|
id: string;
|
|
@@ -98,6 +123,44 @@ interface CheckoutSession {
|
|
|
98
123
|
currency: string;
|
|
99
124
|
couponCode?: string;
|
|
100
125
|
}
|
|
126
|
+
interface CartLine {
|
|
127
|
+
id: string;
|
|
128
|
+
productId: string;
|
|
129
|
+
variantId?: string;
|
|
130
|
+
quantity: number;
|
|
131
|
+
unitPrice: number;
|
|
132
|
+
productName: string;
|
|
133
|
+
imageUrl?: string;
|
|
134
|
+
}
|
|
135
|
+
interface CartState {
|
|
136
|
+
id: string;
|
|
137
|
+
lines: CartLine[];
|
|
138
|
+
createdAt: string;
|
|
139
|
+
updatedAt: string;
|
|
140
|
+
}
|
|
141
|
+
interface AddCartLineItemInput {
|
|
142
|
+
productId: string;
|
|
143
|
+
variantId?: string;
|
|
144
|
+
quantity: number;
|
|
145
|
+
/** Skip the `products.get` price lookup by supplying these directly */
|
|
146
|
+
unitPrice?: number;
|
|
147
|
+
productName?: string;
|
|
148
|
+
imageUrl?: string;
|
|
149
|
+
}
|
|
150
|
+
interface StorefrontCart {
|
|
151
|
+
readonly id: string;
|
|
152
|
+
readonly lines: CartLine[];
|
|
153
|
+
readonly itemCount: number;
|
|
154
|
+
readonly subtotal: number;
|
|
155
|
+
/** Adds a line item. Supplying `unitPrice` also requires `productName`, otherwise this falls through to a `products.get` network lookup. */
|
|
156
|
+
addLineItem(input: AddCartLineItemInput): Promise<StorefrontCart>;
|
|
157
|
+
updateLineItem(lineId: string, quantity: number): StorefrontCart;
|
|
158
|
+
removeLineItem(lineId: string): StorefrontCart;
|
|
159
|
+
clear(): StorefrontCart;
|
|
160
|
+
checkout(opts?: {
|
|
161
|
+
clearOnSuccess?: boolean;
|
|
162
|
+
}): Promise<CheckoutSession>;
|
|
163
|
+
}
|
|
101
164
|
interface Order {
|
|
102
165
|
id: string;
|
|
103
166
|
orderNumber: string;
|
|
@@ -307,10 +370,34 @@ declare function createStorefrontClient(config: StorefrontConfig): {
|
|
|
307
370
|
}>;
|
|
308
371
|
};
|
|
309
372
|
cart: {
|
|
373
|
+
/** Create a new, empty cart (replaces any persisted cart) */
|
|
374
|
+
create(): StorefrontCart;
|
|
375
|
+
/** Get the current cart — loaded from storage if persisted, otherwise a fresh one */
|
|
376
|
+
current(): StorefrontCart;
|
|
310
377
|
/** Recover an abandoned cart by recovery token */
|
|
311
378
|
recover(token: string): Promise<CheckoutSession>;
|
|
312
379
|
};
|
|
380
|
+
orders: {
|
|
381
|
+
/** Get customer's order history (delegates to `customer.orders`) */
|
|
382
|
+
list: (params?: ListParams) => Promise<PaginatedResponse<Order>>;
|
|
383
|
+
};
|
|
384
|
+
customers: {
|
|
385
|
+
login: (email: string, password: string) => Promise<AuthSession>;
|
|
386
|
+
register: (data: {
|
|
387
|
+
email: string;
|
|
388
|
+
password: string;
|
|
389
|
+
name: string;
|
|
390
|
+
phone?: string;
|
|
391
|
+
}) => Promise<AuthSession>;
|
|
392
|
+
loginWithGoogle: (idToken: string) => Promise<AuthSession>;
|
|
393
|
+
logout: () => Promise<void>;
|
|
394
|
+
isAuthenticated: () => boolean;
|
|
395
|
+
setTokens: (access: string, refresh: string) => void;
|
|
396
|
+
me: () => Promise<Customer>;
|
|
397
|
+
orders: (params?: ListParams) => Promise<PaginatedResponse<Order>>;
|
|
398
|
+
addresses: () => Promise<Address[]>;
|
|
399
|
+
};
|
|
313
400
|
};
|
|
314
401
|
type StorefrontClient = ReturnType<typeof createStorefrontClient>;
|
|
315
402
|
|
|
316
|
-
export { type Address, type AuthSession, type BlogPost, type BookableService, type Booking, type CheckoutLineItem, type CheckoutSession, type Collection, type Customer, type FormDefinition, type FormField, type ListParams, type Order, type OrderItem, type PaginatedResponse, type Product, type ProductImage, type ProductVariant, type StoreConfig, type StorefrontClient, type StorefrontConfig, StorefrontError, type TimeSlot, createStorefrontClient };
|
|
403
|
+
export { type AddCartLineItemInput, type Address, type AuthSession, type BlogPost, type BookableService, type Booking, type CartLine, type CartState, type CheckoutLineItem, type CheckoutSession, type Collection, type Customer, type FormDefinition, type FormField, type ListParams, type Order, type OrderItem, type PaginatedResponse, type Product, type ProductImage, type ProductVariant, type RetryConfig, type StoreConfig, type StorefrontCart, type StorefrontClient, type StorefrontConfig, StorefrontError, type TimeSlot, createStorefrontClient };
|
package/dist/index.js
CHANGED
|
@@ -13,6 +13,18 @@ function createStorefrontClient(config) {
|
|
|
13
13
|
const fetchFn = config.customFetch ?? globalThis.fetch.bind(globalThis);
|
|
14
14
|
let accessToken = null;
|
|
15
15
|
let refreshToken = null;
|
|
16
|
+
const RETRY_DEFAULTS = {
|
|
17
|
+
retries: 2,
|
|
18
|
+
baseDelayMs: 300,
|
|
19
|
+
maxDelayMs: 4e3,
|
|
20
|
+
retryOnStatus: [408, 425, 429, 500, 502, 503, 504]
|
|
21
|
+
};
|
|
22
|
+
const rc = config.retry === false ? { ...RETRY_DEFAULTS, retries: 0 } : { ...RETRY_DEFAULTS, ...config.retry || {} };
|
|
23
|
+
const IDEMPOTENT = /* @__PURE__ */ new Set(["GET", "HEAD"]);
|
|
24
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
25
|
+
function backoff(attempt) {
|
|
26
|
+
return Math.min(rc.maxDelayMs, Math.round(Math.random() * rc.baseDelayMs * Math.pow(2, attempt)));
|
|
27
|
+
}
|
|
16
28
|
async function request(method, path, options) {
|
|
17
29
|
const url = new URL(`${apiUrl}${path}`);
|
|
18
30
|
if (options?.params) {
|
|
@@ -29,11 +41,32 @@ function createStorefrontClient(config) {
|
|
|
29
41
|
if (options?.body) {
|
|
30
42
|
headers["Content-Type"] = "application/json";
|
|
31
43
|
}
|
|
32
|
-
const
|
|
44
|
+
const fetchOptions = {
|
|
33
45
|
method,
|
|
34
46
|
headers,
|
|
35
47
|
body: options?.body ? JSON.stringify(options.body) : void 0
|
|
36
|
-
}
|
|
48
|
+
};
|
|
49
|
+
let attempt = 0;
|
|
50
|
+
let res;
|
|
51
|
+
while (true) {
|
|
52
|
+
try {
|
|
53
|
+
res = await fetchFn(url.toString(), fetchOptions);
|
|
54
|
+
} catch (err) {
|
|
55
|
+
if (attempt < rc.retries) {
|
|
56
|
+
await sleep(backoff(attempt));
|
|
57
|
+
attempt++;
|
|
58
|
+
continue;
|
|
59
|
+
}
|
|
60
|
+
throw err;
|
|
61
|
+
}
|
|
62
|
+
if (attempt < rc.retries && rc.retryOnStatus.includes(res.status) && IDEMPOTENT.has(method)) {
|
|
63
|
+
const ra = Number(res.headers.get("retry-after"));
|
|
64
|
+
await sleep(Number.isFinite(ra) && ra > 0 ? ra * 1e3 : backoff(attempt));
|
|
65
|
+
attempt++;
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
37
70
|
if (res.status === 401 && options?.auth && refreshToken) {
|
|
38
71
|
const refreshed = await refreshSession();
|
|
39
72
|
if (refreshed) {
|
|
@@ -120,7 +153,7 @@ function createStorefrontClient(config) {
|
|
|
120
153
|
const checkout = {
|
|
121
154
|
/** Create a checkout session from cart items */
|
|
122
155
|
async create(items) {
|
|
123
|
-
return request("POST", "/checkout", { body: { items } });
|
|
156
|
+
return request("POST", "/checkout", { body: { lines: items } });
|
|
124
157
|
},
|
|
125
158
|
/** Retrieve an existing checkout session */
|
|
126
159
|
async get(checkoutId) {
|
|
@@ -252,12 +285,152 @@ function createStorefrontClient(config) {
|
|
|
252
285
|
return res.json();
|
|
253
286
|
}
|
|
254
287
|
};
|
|
288
|
+
const CART_KEY = "managesales_cart_" + config.workspaceId;
|
|
289
|
+
const hasLS = () => {
|
|
290
|
+
try {
|
|
291
|
+
return config.persistCart !== false && typeof localStorage !== "undefined";
|
|
292
|
+
} catch {
|
|
293
|
+
return false;
|
|
294
|
+
}
|
|
295
|
+
};
|
|
296
|
+
const loadCart = () => {
|
|
297
|
+
if (!hasLS()) return null;
|
|
298
|
+
try {
|
|
299
|
+
const s = localStorage.getItem(CART_KEY);
|
|
300
|
+
return s ? JSON.parse(s) : null;
|
|
301
|
+
} catch {
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
};
|
|
305
|
+
const saveCart = (st) => {
|
|
306
|
+
if (!hasLS()) return;
|
|
307
|
+
try {
|
|
308
|
+
localStorage.setItem(CART_KEY, JSON.stringify(st));
|
|
309
|
+
} catch {
|
|
310
|
+
}
|
|
311
|
+
};
|
|
312
|
+
let __lineSeq = 0;
|
|
313
|
+
const genId = (p) => typeof crypto !== "undefined" && crypto.randomUUID ? p + crypto.randomUUID() : p + Date.now() + "_" + ++__lineSeq;
|
|
314
|
+
const nowIso = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
315
|
+
const freshCart = () => ({ id: genId("cart_"), lines: [], createdAt: nowIso(), updatedAt: nowIso() });
|
|
316
|
+
function makeCart(state) {
|
|
317
|
+
const persist = () => {
|
|
318
|
+
state.updatedAt = nowIso();
|
|
319
|
+
saveCart(state);
|
|
320
|
+
};
|
|
321
|
+
const cartObj = {
|
|
322
|
+
get id() {
|
|
323
|
+
return state.id;
|
|
324
|
+
},
|
|
325
|
+
get lines() {
|
|
326
|
+
return state.lines;
|
|
327
|
+
},
|
|
328
|
+
get itemCount() {
|
|
329
|
+
return state.lines.reduce((n, l) => n + l.quantity, 0);
|
|
330
|
+
},
|
|
331
|
+
get subtotal() {
|
|
332
|
+
return state.lines.reduce((s, l) => s + l.unitPrice * l.quantity, 0);
|
|
333
|
+
},
|
|
334
|
+
// Supplying unitPrice alone does not skip the lookup — productName must also be
|
|
335
|
+
// provided, otherwise this falls through to the products.get() network lookup below.
|
|
336
|
+
async addLineItem(input) {
|
|
337
|
+
let line;
|
|
338
|
+
if (input.unitPrice != null && input.productName) {
|
|
339
|
+
line = {
|
|
340
|
+
id: genId("line_"),
|
|
341
|
+
productId: input.productId,
|
|
342
|
+
variantId: input.variantId,
|
|
343
|
+
quantity: input.quantity,
|
|
344
|
+
unitPrice: input.unitPrice,
|
|
345
|
+
productName: input.productName,
|
|
346
|
+
imageUrl: input.imageUrl
|
|
347
|
+
};
|
|
348
|
+
} else {
|
|
349
|
+
const p = await products.get(input.productId);
|
|
350
|
+
const v = input.variantId && Array.isArray(p.variants) ? p.variants.find((x) => x.id === input.variantId) : void 0;
|
|
351
|
+
line = {
|
|
352
|
+
id: genId("line_"),
|
|
353
|
+
productId: input.productId,
|
|
354
|
+
variantId: input.variantId,
|
|
355
|
+
quantity: input.quantity,
|
|
356
|
+
unitPrice: v && v.price != null ? v.price : p.price,
|
|
357
|
+
productName: p.name,
|
|
358
|
+
imageUrl: Array.isArray(p.images) && p.images.length > 0 ? p.images[0].url : void 0
|
|
359
|
+
};
|
|
360
|
+
}
|
|
361
|
+
const existing = state.lines.find((l) => l.productId === line.productId && l.variantId === line.variantId);
|
|
362
|
+
if (existing) existing.quantity += line.quantity;
|
|
363
|
+
else state.lines.push(line);
|
|
364
|
+
persist();
|
|
365
|
+
return cartObj;
|
|
366
|
+
},
|
|
367
|
+
updateLineItem(lineId, quantity) {
|
|
368
|
+
const l = state.lines.find((x) => x.id === lineId);
|
|
369
|
+
if (l) {
|
|
370
|
+
if (quantity <= 0) state.lines = state.lines.filter((x) => x.id !== lineId);
|
|
371
|
+
else l.quantity = quantity;
|
|
372
|
+
persist();
|
|
373
|
+
}
|
|
374
|
+
return cartObj;
|
|
375
|
+
},
|
|
376
|
+
removeLineItem(lineId) {
|
|
377
|
+
state.lines = state.lines.filter((x) => x.id !== lineId);
|
|
378
|
+
persist();
|
|
379
|
+
return cartObj;
|
|
380
|
+
},
|
|
381
|
+
clear() {
|
|
382
|
+
state.lines = [];
|
|
383
|
+
persist();
|
|
384
|
+
return cartObj;
|
|
385
|
+
},
|
|
386
|
+
async checkout(opts) {
|
|
387
|
+
const session = await checkout.create(
|
|
388
|
+
state.lines.map((l) => ({
|
|
389
|
+
productId: l.productId,
|
|
390
|
+
variantId: l.variantId,
|
|
391
|
+
productName: l.productName,
|
|
392
|
+
quantity: l.quantity,
|
|
393
|
+
unitPrice: l.unitPrice,
|
|
394
|
+
imageUrl: l.imageUrl
|
|
395
|
+
}))
|
|
396
|
+
);
|
|
397
|
+
if (opts && opts.clearOnSuccess) cartObj.clear();
|
|
398
|
+
return session;
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
return cartObj;
|
|
402
|
+
}
|
|
255
403
|
const cart = {
|
|
404
|
+
/** Create a new, empty cart (replaces any persisted cart) */
|
|
405
|
+
create() {
|
|
406
|
+
const st = freshCart();
|
|
407
|
+
saveCart(st);
|
|
408
|
+
return makeCart(st);
|
|
409
|
+
},
|
|
410
|
+
/** Get the current cart — loaded from storage if persisted, otherwise a fresh one */
|
|
411
|
+
current() {
|
|
412
|
+
return makeCart(loadCart() || freshCart());
|
|
413
|
+
},
|
|
256
414
|
/** Recover an abandoned cart by recovery token */
|
|
257
415
|
async recover(token) {
|
|
258
416
|
return request("GET", `/abandoned-carts/recover/${token}`);
|
|
259
417
|
}
|
|
260
418
|
};
|
|
419
|
+
const orders = {
|
|
420
|
+
/** Get customer's order history (delegates to `customer.orders`) */
|
|
421
|
+
list: (params) => customer.orders(params)
|
|
422
|
+
};
|
|
423
|
+
const customers = {
|
|
424
|
+
login: auth.login,
|
|
425
|
+
register: auth.register,
|
|
426
|
+
loginWithGoogle: auth.loginWithGoogle,
|
|
427
|
+
logout: auth.logout,
|
|
428
|
+
isAuthenticated: auth.isAuthenticated,
|
|
429
|
+
setTokens: auth.setTokens,
|
|
430
|
+
me: customer.me,
|
|
431
|
+
orders: customer.orders,
|
|
432
|
+
addresses: customer.addresses
|
|
433
|
+
};
|
|
261
434
|
return {
|
|
262
435
|
store,
|
|
263
436
|
products,
|
|
@@ -268,7 +441,9 @@ function createStorefrontClient(config) {
|
|
|
268
441
|
blog,
|
|
269
442
|
bookings,
|
|
270
443
|
forms,
|
|
271
|
-
cart
|
|
444
|
+
cart,
|
|
445
|
+
orders,
|
|
446
|
+
customers
|
|
272
447
|
};
|
|
273
448
|
}
|
|
274
449
|
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":"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","file":"index.js","sourcesContent":["/**\n * ManageSales Storefront SDK\n * --------------------------\n * Resource-based client for building online storefronts.\n * Works with any framework: Next.js, Remix, Nuxt, Astro, plain HTML.\n *\n * Usage:\n * import { createStorefrontClient } from \"./managesales-storefront\";\n * const client = createStorefrontClient({ workspaceId: \"<workspace_id>\" });\n * const products = await client.products.list();\n */\n\n// ── Types ──────────────────────────────────────────────────────────\n\nexport interface StorefrontConfig {\n /** Your workspace ID (required) */\n workspaceId: string;\n /** API base URL — defaults to production */\n apiUrl?: string;\n /** Custom fetch implementation (for SSR / edge) */\n customFetch?: typeof fetch;\n /** Default locale for content */\n locale?: string;\n}\n\nexport interface ListParams {\n page?: number;\n limit?: number;\n search?: string;\n sort?: string;\n order?: \"asc\" | \"desc\";\n [key: string]: unknown;\n}\n\nexport interface PaginatedResponse<T> {\n data: T[];\n total: number;\n page: number;\n limit: number;\n totalPages: number;\n}\n\n// ── Product types ──\n\nexport interface Product {\n id: string;\n name: string;\n slug: string;\n description: string;\n status: string;\n price: number;\n compareAtPrice?: number;\n currency: string;\n images: ProductImage[];\n variants: ProductVariant[];\n category?: { id: string; name: string };\n brand?: { id: string; name: string };\n tags: string[];\n metadata: Record<string, unknown>;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface ProductImage {\n id: string;\n url: string;\n alt: string;\n position: number;\n}\n\nexport interface ProductVariant {\n id: string;\n name: string;\n sku: string;\n price: number;\n compareAtPrice?: number;\n inventory: number;\n options: Record<string, string>;\n}\n\n// ── Collection types ──\n\nexport interface Collection {\n id: string;\n name: string;\n slug: string;\n description: string;\n image?: string;\n productCount: number;\n}\n\n// ── Cart & Checkout types ──\n\nexport interface CheckoutLineItem {\n productId: string;\n variantId?: string;\n quantity: number;\n}\n\nexport interface CheckoutSession {\n id: string;\n status: \"open\" | \"completed\" | \"expired\";\n items: CheckoutLineItem[];\n subtotal: number;\n tax: number;\n shipping: number;\n discount: number;\n total: number;\n currency: string;\n couponCode?: string;\n}\n\nexport interface Order {\n id: string;\n orderNumber: string;\n status: string;\n total: number;\n currency: string;\n items: OrderItem[];\n createdAt: string;\n}\n\nexport interface OrderItem {\n productId: string;\n name: string;\n quantity: number;\n price: number;\n}\n\n// ── Customer types ──\n\nexport interface Customer {\n id: string;\n email: string;\n name: string;\n phone?: string;\n createdAt: string;\n}\n\nexport interface AuthSession {\n customer: Customer;\n accessToken: string;\n refreshToken: string;\n}\n\nexport interface Address {\n id: string;\n label?: string;\n line1: string;\n line2?: string;\n city: string;\n state: string;\n postalCode: string;\n country: string;\n isDefault: boolean;\n}\n\n// ── Blog types ──\n\nexport interface BlogPost {\n id: string;\n title: string;\n slug: string;\n excerpt: string;\n content: string;\n coverImage?: string;\n tags: string[];\n publishedAt: string;\n}\n\n// ── Booking types ──\n\nexport interface BookableService {\n id: string;\n name: string;\n duration: number;\n price: number;\n description: string;\n}\n\nexport interface TimeSlot {\n start: string;\n end: string;\n available: boolean;\n}\n\nexport interface Booking {\n id: string;\n serviceId: string;\n date: string;\n startTime: string;\n endTime: string;\n status: string;\n customer: { name: string; email: string };\n}\n\n// ── Form types ──\n\nexport interface FormDefinition {\n id: string;\n title: string;\n description: string;\n fields: FormField[];\n}\n\nexport interface FormField {\n id: string;\n type: string;\n label: string;\n required: boolean;\n options?: string[];\n placeholder?: string;\n}\n\n// ── Store config ──\n\nexport interface StoreConfig {\n name: string;\n slug: string;\n logo?: string;\n theme: Record<string, unknown>;\n currency: string;\n locale: string;\n}\n\n// ── SDK Error ──\n\nexport class StorefrontError extends Error {\n status: number;\n code: string;\n details?: unknown;\n\n constructor(status: number, code: string, message: string, details?: unknown) {\n super(message);\n this.name = \"StorefrontError\";\n this.status = status;\n this.code = code;\n this.details = details;\n }\n}\n\n// ── Core client ────────────────────────────────────────────────────\n\nexport function createStorefrontClient(config: StorefrontConfig) {\n const apiUrl = (config.apiUrl ?? \"https://managesales-backend-phase1test.up.railway.app/api/v1\").replace(/\\/$/, \"\");\n const fetchFn = config.customFetch ?? globalThis.fetch.bind(globalThis);\n let accessToken: string | null = null;\n let refreshToken: string | null = null;\n\n // ── Internal helpers ──\n\n async function request<T>(\n method: string,\n path: string,\n options?: {\n body?: unknown;\n params?: Record<string, unknown>;\n auth?: boolean;\n },\n ): Promise<T> {\n const url = new URL(`${apiUrl}${path}`);\n if (options?.params) {\n for (const [k, v] of Object.entries(options.params)) {\n if (v !== undefined && v !== null) url.searchParams.set(k, String(v));\n }\n }\n\n const headers: Record<string, string> = {\n \"X-Workspace-Id\": config.workspaceId,\n };\n\n if (options?.auth && accessToken) {\n headers[\"Authorization\"] = `Bearer ${accessToken}`;\n }\n\n if (options?.body) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n const res = await fetchFn(url.toString(), {\n method,\n headers,\n body: options?.body ? JSON.stringify(options.body) : undefined,\n });\n\n // Auto-refresh on 401\n if (res.status === 401 && options?.auth && refreshToken) {\n const refreshed = await refreshSession();\n if (refreshed) {\n headers[\"Authorization\"] = `Bearer ${accessToken}`;\n const retry = await fetchFn(url.toString(), {\n method,\n headers,\n body: options?.body ? JSON.stringify(options.body) : undefined,\n });\n if (!retry.ok) throw await parseError(retry);\n return retry.status === 204 ? (undefined as T) : retry.json();\n }\n }\n\n if (!res.ok) throw await parseError(res);\n return res.status === 204 ? (undefined as T) : res.json();\n }\n\n async function parseError(res: Response): Promise<StorefrontError> {\n try {\n const body = await res.json();\n return new StorefrontError(\n res.status,\n body.code ?? body.error ?? \"UNKNOWN\",\n body.message ?? res.statusText,\n body,\n );\n } catch {\n return new StorefrontError(res.status, \"UNKNOWN\", res.statusText);\n }\n }\n\n async function refreshSession(): Promise<boolean> {\n if (!refreshToken) return false;\n try {\n const res = await fetchFn(`${apiUrl}/storefront/auth/refresh`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Workspace-Id\": config.workspaceId,\n },\n body: JSON.stringify({ refreshToken }),\n });\n if (!res.ok) {\n accessToken = null;\n refreshToken = null;\n return false;\n }\n const data = await res.json();\n accessToken = data.accessToken;\n refreshToken = data.refreshToken;\n return true;\n } catch {\n return false;\n }\n }\n\n // ── Resource namespaces ──────────────────────────────────────────\n\n const store = {\n /** Resolve store config by workspace (returns theme, branding, currency) */\n async resolve(): Promise<StoreConfig> {\n return request(\"GET\", \"/storefront-api/resolve\");\n },\n };\n\n const products = {\n /** List published products with pagination and filters */\n async list(params?: ListParams): Promise<PaginatedResponse<Product>> {\n return request(\"GET\", \"/storefront-api/products\", { params });\n },\n /** Get a single product by ID or slug */\n async get(idOrSlug: string): Promise<Product> {\n return request(\"GET\", `/storefront-api/products/${idOrSlug}`);\n },\n /** Search products (shorthand for list with search param) */\n async search(query: string, params?: ListParams): Promise<PaginatedResponse<Product>> {\n return request(\"GET\", \"/storefront-api/search\", { params: { ...params, q: query } });\n },\n };\n\n const collections = {\n /** List published collections */\n async list(params?: ListParams): Promise<PaginatedResponse<Collection>> {\n return request(\"GET\", \"/storefront-api/collections\", { params });\n },\n /** Get a collection with its products */\n async get(idOrSlug: string): Promise<Collection & { products: Product[] }> {\n return request(\"GET\", `/storefront-api/collections/${idOrSlug}`);\n },\n };\n\n const checkout = {\n /** Create a checkout session from cart items */\n async create(items: CheckoutLineItem[]): Promise<CheckoutSession> {\n return request(\"POST\", \"/checkout\", { body: { items } });\n },\n /** Retrieve an existing checkout session */\n async get(checkoutId: string): Promise<CheckoutSession> {\n return request(\"GET\", `/checkout/${checkoutId}`);\n },\n /** Apply a coupon or discount code */\n async applyCoupon(checkoutId: string, couponCode: string): Promise<CheckoutSession> {\n return request(\"POST\", `/checkout/${checkoutId}/coupon`, { body: { couponCode } });\n },\n /** Complete checkout — process payment and create order */\n async complete(checkoutId: string, payment?: Record<string, unknown>): Promise<Order> {\n return request(\"POST\", `/checkout/${checkoutId}/complete`, { body: payment });\n },\n };\n\n const auth = {\n /** Register a new customer account */\n async register(data: {\n email: string;\n password: string;\n name: string;\n phone?: string;\n }): Promise<AuthSession> {\n const session = await request<AuthSession>(\"POST\", \"/storefront/auth/register\", { body: data });\n accessToken = session.accessToken;\n refreshToken = session.refreshToken;\n return session;\n },\n /** Log in with email + password */\n async login(email: string, password: string): Promise<AuthSession> {\n const session = await request<AuthSession>(\"POST\", \"/storefront/auth/login\", {\n body: { email, password },\n });\n accessToken = session.accessToken;\n refreshToken = session.refreshToken;\n return session;\n },\n /** Log in with Google ID token */\n async loginWithGoogle(idToken: string): Promise<AuthSession> {\n const session = await request<AuthSession>(\"POST\", \"/storefront/auth/google\", {\n body: { idToken },\n });\n accessToken = session.accessToken;\n refreshToken = session.refreshToken;\n return session;\n },\n /** Log out and clear local session */\n async logout(): Promise<void> {\n if (accessToken) {\n try {\n await request(\"POST\", \"/storefront/auth/logout\", { auth: true });\n } catch { /* ignore logout errors */ }\n }\n accessToken = null;\n refreshToken = null;\n },\n /** Refresh the access token */\n async refresh(): Promise<boolean> {\n return refreshSession();\n },\n /** Check if client has a valid session */\n isAuthenticated(): boolean {\n return accessToken !== null;\n },\n /** Manually set tokens (e.g., from SSR or cookie restoration) */\n setTokens(access: string, refresh: string): void {\n accessToken = access;\n refreshToken = refresh;\n },\n };\n\n const customer = {\n /** Get current customer profile */\n async me(): Promise<Customer> {\n return request(\"GET\", \"/storefront/auth/me\", { auth: true });\n },\n /** Get customer's order history */\n async orders(params?: ListParams): Promise<PaginatedResponse<Order>> {\n const me = await request<Customer>(\"GET\", \"/storefront/auth/me\", { auth: true });\n return request(\"GET\", `/storefront-api/customer/${me.id}/orders`, { params, auth: true });\n },\n /** Get customer's saved addresses */\n async addresses(): Promise<Address[]> {\n const me = await request<Customer>(\"GET\", \"/storefront/auth/me\", { auth: true });\n return request(\"GET\", `/storefront-api/customer/${me.id}/addresses`, { auth: true });\n },\n };\n\n const blog = {\n /** List published blog posts */\n async list(params?: ListParams & { tags?: string }): Promise<PaginatedResponse<BlogPost>> {\n return request(\"GET\", \"/storefront-api/blog/posts\", { params });\n },\n /** Get a blog post by slug */\n async get(slug: string): Promise<BlogPost> {\n return request(\"GET\", `/storefront-api/blog/posts/${slug}`);\n },\n };\n\n const bookings = {\n /** List bookable services */\n async services(): Promise<BookableService[]> {\n return request(\"GET\", \"/bookings-public/services\");\n },\n /** List bookable staff */\n async staff(): Promise<Array<{ id: string; name: string; avatar?: string }>> {\n return request(\"GET\", \"/bookings-public/staff\");\n },\n /** Check available time slots */\n async availability(params: {\n serviceId: string;\n date: string;\n staffId?: string;\n }): Promise<TimeSlot[]> {\n return request(\"GET\", \"/bookings-public/availability\", { params });\n },\n /** Create a booking */\n async book(data: {\n serviceId: string;\n date: string;\n startTime: string;\n staffId?: string;\n customer: { name: string; email: string; phone?: string };\n }): Promise<Booking> {\n return request(\"POST\", \"/bookings-public/book\", { body: data });\n },\n };\n\n const forms = {\n /** Get a published form by ID or slug */\n async get(idOrSlug: string): Promise<FormDefinition> {\n return request(\"GET\", `/public/forms/${idOrSlug}`);\n },\n /** Submit form response */\n async submit(formId: string, data: Record<string, unknown>): Promise<{ id: string }> {\n return request(\"POST\", `/public/forms/${formId}/submit`, { body: data });\n },\n /** Upload file for a form file field */\n async uploadFile(formId: string, file: File): Promise<{ url: string }> {\n const formData = new FormData();\n formData.append(\"file\", file);\n const res = await fetchFn(`${apiUrl}/public/forms/${formId}/upload`, {\n method: \"POST\",\n headers: { \"X-Workspace-Id\": config.workspaceId },\n body: formData,\n });\n if (!res.ok) throw await parseError(res);\n return res.json();\n },\n };\n\n const cart = {\n /** Recover an abandoned cart by recovery token */\n async recover(token: string): Promise<CheckoutSession> {\n return request(\"GET\", `/abandoned-carts/recover/${token}`);\n },\n };\n\n // ── Public API ───────────────────────────────────────────────────\n\n return {\n store,\n products,\n collections,\n checkout,\n auth,\n customer,\n blog,\n bookings,\n forms,\n cart,\n };\n}\n\n// ── Type export for TypeScript users ──\n\nexport type StorefrontClient = ReturnType<typeof createStorefrontClient>;\n"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts"],"names":[],"mappings":";AAqSO,IAAM,eAAA,GAAN,cAA8B,KAAA,CAAM;AAAA,EAKzC,WAAA,CAAY,MAAA,EAAgB,IAAA,EAAc,OAAA,EAAiB,OAAA,EAAmB;AAC5E,IAAA,KAAA,CAAM,OAAO,CAAA;AACb,IAAA,IAAA,CAAK,IAAA,GAAO,iBAAA;AACZ,IAAA,IAAA,CAAK,MAAA,GAAS,MAAA;AACd,IAAA,IAAA,CAAK,IAAA,GAAO,IAAA;AACZ,IAAA,IAAA,CAAK,OAAA,GAAU,OAAA;AAAA,EACjB;AACF;AAIO,SAAS,uBAAuB,MAAA,EAA0B;AAC/D,EAAA,MAAM,UAAU,MAAA,CAAO,MAAA,IAAU,8DAAA,EAAgE,OAAA,CAAQ,OAAO,EAAE,CAAA;AAClH,EAAA,MAAM,UAAU,MAAA,CAAO,WAAA,IAAe,UAAA,CAAW,KAAA,CAAM,KAAK,UAAU,CAAA;AACtE,EAAA,IAAI,WAAA,GAA6B,IAAA;AACjC,EAAA,IAAI,YAAA,GAA8B,IAAA;AAIlC,EAAA,MAAM,cAAA,GAAiB;AAAA,IACrB,OAAA,EAAS,CAAA;AAAA,IACT,WAAA,EAAa,GAAA;AAAA,IACb,UAAA,EAAY,GAAA;AAAA,IACZ,aAAA,EAAe,CAAC,GAAA,EAAK,GAAA,EAAK,KAAK,GAAA,EAAK,GAAA,EAAK,KAAK,GAAG;AAAA,GACnD;AACA,EAAA,MAAM,KACJ,MAAA,CAAO,KAAA,KAAU,KAAA,GACb,EAAE,GAAG,cAAA,EAAgB,OAAA,EAAS,CAAA,EAAE,GAChC,EAAE,GAAG,cAAA,EAAgB,GAAI,MAAA,CAAO,KAAA,IAAS,EAAC,EAAG;AACnD,EAAA,MAAM,6BAAa,IAAI,GAAA,CAAI,CAAC,KAAA,EAAO,MAAM,CAAC,CAAA;AAC1C,EAAA,MAAM,KAAA,GAAQ,CAAC,EAAA,KAAe,IAAI,OAAA,CAAc,CAAC,OAAA,KAAY,UAAA,CAAW,OAAA,EAAS,EAAE,CAAC,CAAA;AACpF,EAAA,SAAS,QAAQ,OAAA,EAAyB;AACxC,IAAA,OAAO,KAAK,GAAA,CAAI,EAAA,CAAG,UAAA,EAAY,IAAA,CAAK,MAAM,IAAA,CAAK,MAAA,EAAO,GAAI,EAAA,CAAG,cAAc,IAAA,CAAK,GAAA,CAAI,CAAA,EAAG,OAAO,CAAC,CAAC,CAAA;AAAA,EAClG;AAIA,EAAA,eAAe,OAAA,CACb,MAAA,EACA,IAAA,EACA,OAAA,EAKY;AACZ,IAAA,MAAM,MAAM,IAAI,GAAA,CAAI,GAAG,MAAM,CAAA,EAAG,IAAI,CAAA,CAAE,CAAA;AACtC,IAAA,IAAI,SAAS,MAAA,EAAQ;AACnB,MAAA,KAAA,MAAW,CAAC,GAAG,CAAC,CAAA,IAAK,OAAO,OAAA,CAAQ,OAAA,CAAQ,MAAM,CAAA,EAAG;AACnD,QAAA,IAAI,CAAA,KAAM,MAAA,IAAa,CAAA,KAAM,IAAA,EAAM,GAAA,CAAI,aAAa,GAAA,CAAI,CAAA,EAAG,MAAA,CAAO,CAAC,CAAC,CAAA;AAAA,MACtE;AAAA,IACF;AAEA,IAAA,MAAM,OAAA,GAAkC;AAAA,MACtC,kBAAkB,MAAA,CAAO;AAAA,KAC3B;AAEA,IAAA,IAAI,OAAA,EAAS,QAAQ,WAAA,EAAa;AAChC,MAAA,OAAA,CAAQ,eAAe,CAAA,GAAI,CAAA,OAAA,EAAU,WAAW,CAAA,CAAA;AAAA,IAClD;AAEA,IAAA,IAAI,SAAS,IAAA,EAAM;AACjB,MAAA,OAAA,CAAQ,cAAc,CAAA,GAAI,kBAAA;AAAA,IAC5B;AAEA,IAAA,MAAM,YAAA,GAA4B;AAAA,MAChC,MAAA;AAAA,MACA,OAAA;AAAA,MACA,MAAM,OAAA,EAAS,IAAA,GAAO,KAAK,SAAA,CAAU,OAAA,CAAQ,IAAI,CAAA,GAAI;AAAA,KACvD;AAEA,IAAA,IAAI,OAAA,GAAU,CAAA;AACd,IAAA,IAAI,GAAA;AACJ,IAAA,OAAO,IAAA,EAAM;AACX,MAAA,IAAI;AACF,QAAA,GAAA,GAAM,MAAM,OAAA,CAAQ,GAAA,CAAI,QAAA,IAAY,YAAY,CAAA;AAAA,MAClD,SAAS,GAAA,EAAK;AACZ,QAAA,IAAI,OAAA,GAAU,GAAG,OAAA,EAAS;AACxB,UAAA,MAAM,KAAA,CAAM,OAAA,CAAQ,OAAO,CAAC,CAAA;AAC5B,UAAA,OAAA,EAAA;AACA,UAAA;AAAA,QACF;AACA,QAAA,MAAM,GAAA;AAAA,MACR;AACA,MAAA,IAAI,OAAA,GAAU,EAAA,CAAG,OAAA,IAAW,EAAA,CAAG,aAAA,CAAc,QAAA,CAAS,GAAA,CAAI,MAAM,CAAA,IAAK,UAAA,CAAW,GAAA,CAAI,MAAM,CAAA,EAAG;AAC3F,QAAA,MAAM,KAAK,MAAA,CAAO,GAAA,CAAI,OAAA,CAAQ,GAAA,CAAI,aAAa,CAAC,CAAA;AAChD,QAAA,MAAM,KAAA,CAAM,MAAA,CAAO,QAAA,CAAS,EAAE,CAAA,IAAK,EAAA,GAAK,CAAA,GAAI,EAAA,GAAK,GAAA,GAAO,OAAA,CAAQ,OAAO,CAAC,CAAA;AACxE,QAAA,OAAA,EAAA;AACA,QAAA;AAAA,MACF;AACA,MAAA;AAAA,IACF;AAGA,IAAA,IAAI,GAAA,CAAI,MAAA,KAAW,GAAA,IAAO,OAAA,EAAS,QAAQ,YAAA,EAAc;AACvD,MAAA,MAAM,SAAA,GAAY,MAAM,cAAA,EAAe;AACvC,MAAA,IAAI,SAAA,EAAW;AACb,QAAA,OAAA,CAAQ,eAAe,CAAA,GAAI,CAAA,OAAA,EAAU,WAAW,CAAA,CAAA;AAChD,QAAA,MAAM,KAAA,GAAQ,MAAM,OAAA,CAAQ,GAAA,CAAI,UAAS,EAAG;AAAA,UAC1C,MAAA;AAAA,UACA,OAAA;AAAA,UACA,MAAM,OAAA,EAAS,IAAA,GAAO,KAAK,SAAA,CAAU,OAAA,CAAQ,IAAI,CAAA,GAAI;AAAA,SACtD,CAAA;AACD,QAAA,IAAI,CAAC,KAAA,CAAM,EAAA,EAAI,MAAM,MAAM,WAAW,KAAK,CAAA;AAC3C,QAAA,OAAO,KAAA,CAAM,MAAA,KAAW,GAAA,GAAO,MAAA,GAAkB,MAAM,IAAA,EAAK;AAAA,MAC9D;AAAA,IACF;AAEA,IAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,MAAM,MAAM,WAAW,GAAG,CAAA;AACvC,IAAA,OAAO,GAAA,CAAI,MAAA,KAAW,GAAA,GAAO,MAAA,GAAkB,IAAI,IAAA,EAAK;AAAA,EAC1D;AAEA,EAAA,eAAe,WAAW,GAAA,EAAyC;AACjE,IAAA,IAAI;AACF,MAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,EAAK;AAC5B,MAAA,OAAO,IAAI,eAAA;AAAA,QACT,GAAA,CAAI,MAAA;AAAA,QACJ,IAAA,CAAK,IAAA,IAAQ,IAAA,CAAK,KAAA,IAAS,SAAA;AAAA,QAC3B,IAAA,CAAK,WAAW,GAAA,CAAI,UAAA;AAAA,QACpB;AAAA,OACF;AAAA,IACF,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,IAAI,eAAA,CAAgB,GAAA,CAAI,MAAA,EAAQ,SAAA,EAAW,IAAI,UAAU,CAAA;AAAA,IAClE;AAAA,EACF;AAEA,EAAA,eAAe,cAAA,GAAmC;AAChD,IAAA,IAAI,CAAC,cAAc,OAAO,KAAA;AAC1B,IAAA,IAAI;AACF,MAAA,MAAM,GAAA,GAAM,MAAM,OAAA,CAAQ,CAAA,EAAG,MAAM,CAAA,wBAAA,CAAA,EAA4B;AAAA,QAC7D,MAAA,EAAQ,MAAA;AAAA,QACR,OAAA,EAAS;AAAA,UACP,cAAA,EAAgB,kBAAA;AAAA,UAChB,kBAAkB,MAAA,CAAO;AAAA,SAC3B;AAAA,QACA,IAAA,EAAM,IAAA,CAAK,SAAA,CAAU,EAAE,cAAc;AAAA,OACtC,CAAA;AACD,MAAA,IAAI,CAAC,IAAI,EAAA,EAAI;AACX,QAAA,WAAA,GAAc,IAAA;AACd,QAAA,YAAA,GAAe,IAAA;AACf,QAAA,OAAO,KAAA;AAAA,MACT;AACA,MAAA,MAAM,IAAA,GAAO,MAAM,GAAA,CAAI,IAAA,EAAK;AAC5B,MAAA,WAAA,GAAc,IAAA,CAAK,WAAA;AACnB,MAAA,YAAA,GAAe,IAAA,CAAK,YAAA;AACpB,MAAA,OAAO,IAAA;AAAA,IACT,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,KAAA;AAAA,IACT;AAAA,EACF;AAIA,EAAA,MAAM,KAAA,GAAQ;AAAA;AAAA,IAEZ,MAAM,OAAA,GAAgC;AACpC,MAAA,OAAO,OAAA,CAAQ,OAAO,yBAAyB,CAAA;AAAA,IACjD;AAAA,GACF;AAEA,EAAA,MAAM,QAAA,GAAW;AAAA;AAAA,IAEf,MAAM,KAAK,MAAA,EAA0D;AACnE,MAAA,OAAO,OAAA,CAAQ,KAAA,EAAO,0BAAA,EAA4B,EAAE,QAAQ,CAAA;AAAA,IAC9D,CAAA;AAAA;AAAA,IAEA,MAAM,IAAI,QAAA,EAAoC;AAC5C,MAAA,OAAO,OAAA,CAAQ,KAAA,EAAO,CAAA,yBAAA,EAA4B,QAAQ,CAAA,CAAE,CAAA;AAAA,IAC9D,CAAA;AAAA;AAAA,IAEA,MAAM,MAAA,CAAO,KAAA,EAAe,MAAA,EAA0D;AACpF,MAAA,OAAO,OAAA,CAAQ,KAAA,EAAO,wBAAA,EAA0B,EAAE,MAAA,EAAQ,EAAE,GAAG,MAAA,EAAQ,CAAA,EAAG,KAAA,EAAM,EAAG,CAAA;AAAA,IACrF;AAAA,GACF;AAEA,EAAA,MAAM,WAAA,GAAc;AAAA;AAAA,IAElB,MAAM,KAAK,MAAA,EAA6D;AACtE,MAAA,OAAO,OAAA,CAAQ,KAAA,EAAO,6BAAA,EAA+B,EAAE,QAAQ,CAAA;AAAA,IACjE,CAAA;AAAA;AAAA,IAEA,MAAM,IAAI,QAAA,EAAiE;AACzE,MAAA,OAAO,OAAA,CAAQ,KAAA,EAAO,CAAA,4BAAA,EAA+B,QAAQ,CAAA,CAAE,CAAA;AAAA,IACjE;AAAA,GACF;AAEA,EAAA,MAAM,QAAA,GAAW;AAAA;AAAA,IAEf,MAAM,OAAO,KAAA,EAAqD;AAChE,MAAA,OAAO,OAAA,CAAQ,QAAQ,WAAA,EAAa,EAAE,MAAM,EAAE,KAAA,EAAO,KAAA,EAAM,EAAG,CAAA;AAAA,IAChE,CAAA;AAAA;AAAA,IAEA,MAAM,IAAI,UAAA,EAA8C;AACtD,MAAA,OAAO,OAAA,CAAQ,KAAA,EAAO,CAAA,UAAA,EAAa,UAAU,CAAA,CAAE,CAAA;AAAA,IACjD,CAAA;AAAA;AAAA,IAEA,MAAM,WAAA,CAAY,UAAA,EAAoB,UAAA,EAA8C;AAClF,MAAA,OAAO,OAAA,CAAQ,MAAA,EAAQ,CAAA,UAAA,EAAa,UAAU,CAAA,OAAA,CAAA,EAAW,EAAE,IAAA,EAAM,EAAE,UAAA,EAAW,EAAG,CAAA;AAAA,IACnF,CAAA;AAAA;AAAA,IAEA,MAAM,QAAA,CAAS,UAAA,EAAoB,OAAA,EAAmD;AACpF,MAAA,OAAO,OAAA,CAAQ,QAAQ,CAAA,UAAA,EAAa,UAAU,aAAa,EAAE,IAAA,EAAM,SAAS,CAAA;AAAA,IAC9E;AAAA,GACF;AAEA,EAAA,MAAM,IAAA,GAAO;AAAA;AAAA,IAEX,MAAM,SAAS,IAAA,EAKU;AACvB,MAAA,MAAM,OAAA,GAAU,MAAM,OAAA,CAAqB,MAAA,EAAQ,6BAA6B,EAAE,IAAA,EAAM,MAAM,CAAA;AAC9F,MAAA,WAAA,GAAc,OAAA,CAAQ,WAAA;AACtB,MAAA,YAAA,GAAe,OAAA,CAAQ,YAAA;AACvB,MAAA,OAAO,OAAA;AAAA,IACT,CAAA;AAAA;AAAA,IAEA,MAAM,KAAA,CAAM,KAAA,EAAe,QAAA,EAAwC;AACjE,MAAA,MAAM,OAAA,GAAU,MAAM,OAAA,CAAqB,MAAA,EAAQ,wBAAA,EAA0B;AAAA,QAC3E,IAAA,EAAM,EAAE,KAAA,EAAO,QAAA;AAAS,OACzB,CAAA;AACD,MAAA,WAAA,GAAc,OAAA,CAAQ,WAAA;AACtB,MAAA,YAAA,GAAe,OAAA,CAAQ,YAAA;AACvB,MAAA,OAAO,OAAA;AAAA,IACT,CAAA;AAAA;AAAA,IAEA,MAAM,gBAAgB,OAAA,EAAuC;AAC3D,MAAA,MAAM,OAAA,GAAU,MAAM,OAAA,CAAqB,MAAA,EAAQ,yBAAA,EAA2B;AAAA,QAC5E,IAAA,EAAM,EAAE,OAAA;AAAQ,OACjB,CAAA;AACD,MAAA,WAAA,GAAc,OAAA,CAAQ,WAAA;AACtB,MAAA,YAAA,GAAe,OAAA,CAAQ,YAAA;AACvB,MAAA,OAAO,OAAA;AAAA,IACT,CAAA;AAAA;AAAA,IAEA,MAAM,MAAA,GAAwB;AAC5B,MAAA,IAAI,WAAA,EAAa;AACf,QAAA,IAAI;AACF,UAAA,MAAM,QAAQ,MAAA,EAAQ,yBAAA,EAA2B,EAAE,IAAA,EAAM,MAAM,CAAA;AAAA,QACjE,CAAA,CAAA,MAAQ;AAAA,QAA6B;AAAA,MACvC;AACA,MAAA,WAAA,GAAc,IAAA;AACd,MAAA,YAAA,GAAe,IAAA;AAAA,IACjB,CAAA;AAAA;AAAA,IAEA,MAAM,OAAA,GAA4B;AAChC,MAAA,OAAO,cAAA,EAAe;AAAA,IACxB,CAAA;AAAA;AAAA,IAEA,eAAA,GAA2B;AACzB,MAAA,OAAO,WAAA,KAAgB,IAAA;AAAA,IACzB,CAAA;AAAA;AAAA,IAEA,SAAA,CAAU,QAAgB,OAAA,EAAuB;AAC/C,MAAA,WAAA,GAAc,MAAA;AACd,MAAA,YAAA,GAAe,OAAA;AAAA,IACjB;AAAA,GACF;AAEA,EAAA,MAAM,QAAA,GAAW;AAAA;AAAA,IAEf,MAAM,EAAA,GAAwB;AAC5B,MAAA,OAAO,QAAQ,KAAA,EAAO,qBAAA,EAAuB,EAAE,IAAA,EAAM,MAAM,CAAA;AAAA,IAC7D,CAAA;AAAA;AAAA,IAEA,MAAM,OAAO,MAAA,EAAwD;AACnE,MAAA,MAAM,EAAA,GAAK,MAAM,OAAA,CAAkB,KAAA,EAAO,uBAAuB,EAAE,IAAA,EAAM,MAAM,CAAA;AAC/E,MAAA,OAAO,OAAA,CAAQ,KAAA,EAAO,CAAA,yBAAA,EAA4B,EAAA,CAAG,EAAE,WAAW,EAAE,MAAA,EAAQ,IAAA,EAAM,IAAA,EAAM,CAAA;AAAA,IAC1F,CAAA;AAAA;AAAA,IAEA,MAAM,SAAA,GAAgC;AACpC,MAAA,MAAM,EAAA,GAAK,MAAM,OAAA,CAAkB,KAAA,EAAO,uBAAuB,EAAE,IAAA,EAAM,MAAM,CAAA;AAC/E,MAAA,OAAO,OAAA,CAAQ,OAAO,CAAA,yBAAA,EAA4B,EAAA,CAAG,EAAE,CAAA,UAAA,CAAA,EAAc,EAAE,IAAA,EAAM,IAAA,EAAM,CAAA;AAAA,IACrF;AAAA,GACF;AAEA,EAAA,MAAM,IAAA,GAAO;AAAA;AAAA,IAEX,MAAM,KAAK,MAAA,EAA+E;AACxF,MAAA,OAAO,OAAA,CAAQ,KAAA,EAAO,4BAAA,EAA8B,EAAE,QAAQ,CAAA;AAAA,IAChE,CAAA;AAAA;AAAA,IAEA,MAAM,IAAI,IAAA,EAAiC;AACzC,MAAA,OAAO,OAAA,CAAQ,KAAA,EAAO,CAAA,2BAAA,EAA8B,IAAI,CAAA,CAAE,CAAA;AAAA,IAC5D;AAAA,GACF;AAEA,EAAA,MAAM,QAAA,GAAW;AAAA;AAAA,IAEf,MAAM,QAAA,GAAuC;AAC3C,MAAA,OAAO,OAAA,CAAQ,OAAO,2BAA2B,CAAA;AAAA,IACnD,CAAA;AAAA;AAAA,IAEA,MAAM,KAAA,GAAuE;AAC3E,MAAA,OAAO,OAAA,CAAQ,OAAO,wBAAwB,CAAA;AAAA,IAChD,CAAA;AAAA;AAAA,IAEA,MAAM,aAAa,MAAA,EAIK;AACtB,MAAA,OAAO,OAAA,CAAQ,KAAA,EAAO,+BAAA,EAAiC,EAAE,QAAQ,CAAA;AAAA,IACnE,CAAA;AAAA;AAAA,IAEA,MAAM,KAAK,IAAA,EAMU;AACnB,MAAA,OAAO,QAAQ,MAAA,EAAQ,uBAAA,EAAyB,EAAE,IAAA,EAAM,MAAM,CAAA;AAAA,IAChE;AAAA,GACF;AAEA,EAAA,MAAM,KAAA,GAAQ;AAAA;AAAA,IAEZ,MAAM,IAAI,QAAA,EAA2C;AACnD,MAAA,OAAO,OAAA,CAAQ,KAAA,EAAO,CAAA,cAAA,EAAiB,QAAQ,CAAA,CAAE,CAAA;AAAA,IACnD,CAAA;AAAA;AAAA,IAEA,MAAM,MAAA,CAAO,MAAA,EAAgB,IAAA,EAAwD;AACnF,MAAA,OAAO,OAAA,CAAQ,QAAQ,CAAA,cAAA,EAAiB,MAAM,WAAW,EAAE,IAAA,EAAM,MAAM,CAAA;AAAA,IACzE,CAAA;AAAA;AAAA,IAEA,MAAM,UAAA,CAAW,MAAA,EAAgB,IAAA,EAAsC;AACrE,MAAA,MAAM,QAAA,GAAW,IAAI,QAAA,EAAS;AAC9B,MAAA,QAAA,CAAS,MAAA,CAAO,QAAQ,IAAI,CAAA;AAC5B,MAAA,MAAM,MAAM,MAAM,OAAA,CAAQ,GAAG,MAAM,CAAA,cAAA,EAAiB,MAAM,CAAA,OAAA,CAAA,EAAW;AAAA,QACnE,MAAA,EAAQ,MAAA;AAAA,QACR,OAAA,EAAS,EAAE,gBAAA,EAAkB,MAAA,CAAO,WAAA,EAAY;AAAA,QAChD,IAAA,EAAM;AAAA,OACP,CAAA;AACD,MAAA,IAAI,CAAC,GAAA,CAAI,EAAA,EAAI,MAAM,MAAM,WAAW,GAAG,CAAA;AACvC,MAAA,OAAO,IAAI,IAAA,EAAK;AAAA,IAClB;AAAA,GACF;AAIA,EAAA,MAAM,QAAA,GAAW,sBAAsB,MAAA,CAAO,WAAA;AAC9C,EAAA,MAAM,QAAQ,MAAM;AAClB,IAAA,IAAI;AACF,MAAA,OAAO,MAAA,CAAO,WAAA,KAAgB,KAAA,IAAS,OAAO,YAAA,KAAiB,WAAA;AAAA,IACjE,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,KAAA;AAAA,IACT;AAAA,EACF,CAAA;AACA,EAAA,MAAM,WAAW,MAAwB;AACvC,IAAA,IAAI,CAAC,KAAA,EAAM,EAAG,OAAO,IAAA;AACrB,IAAA,IAAI;AACF,MAAA,MAAM,CAAA,GAAI,YAAA,CAAa,OAAA,CAAQ,QAAQ,CAAA;AACvC,MAAA,OAAO,CAAA,GAAI,IAAA,CAAK,KAAA,CAAM,CAAC,CAAA,GAAI,IAAA;AAAA,IAC7B,CAAA,CAAA,MAAQ;AACN,MAAA,OAAO,IAAA;AAAA,IACT;AAAA,EACF,CAAA;AACA,EAAA,MAAM,QAAA,GAAW,CAAC,EAAA,KAAwB;AACxC,IAAA,IAAI,CAAC,OAAM,EAAG;AACd,IAAA,IAAI;AACF,MAAA,YAAA,CAAa,OAAA,CAAQ,QAAA,EAAU,IAAA,CAAK,SAAA,CAAU,EAAE,CAAC,CAAA;AAAA,IACnD,CAAA,CAAA,MAAQ;AAAA,IAER;AAAA,EACF,CAAA;AACA,EAAA,IAAI,SAAA,GAAY,CAAA;AAChB,EAAA,MAAM,QAAQ,CAAC,CAAA,KACb,OAAO,MAAA,KAAW,eAAe,MAAA,CAAO,UAAA,GAAa,CAAA,GAAI,MAAA,CAAO,YAAW,GAAI,CAAA,GAAI,KAAK,GAAA,EAAI,GAAI,MAAM,EAAE,SAAA;AAC1G,EAAA,MAAM,MAAA,GAAS,MAAA,iBAAc,IAAI,IAAA,IAAO,WAAA,EAAY;AACpD,EAAA,MAAM,SAAA,GAAY,OAAkB,EAAE,EAAA,EAAI,MAAM,OAAO,CAAA,EAAG,KAAA,EAAO,IAAI,SAAA,EAAW,MAAA,EAAO,EAAG,SAAA,EAAW,QAAO,EAAE,CAAA;AAE9G,EAAA,SAAS,SAAS,KAAA,EAAkC;AAClD,IAAA,MAAM,UAAU,MAAM;AACpB,MAAA,KAAA,CAAM,YAAY,MAAA,EAAO;AACzB,MAAA,QAAA,CAAS,KAAK,CAAA;AAAA,IAChB,CAAA;AACA,IAAA,MAAM,OAAA,GAA0B;AAAA,MAC9B,IAAI,EAAA,GAAK;AACP,QAAA,OAAO,KAAA,CAAM,EAAA;AAAA,MACf,CAAA;AAAA,MACA,IAAI,KAAA,GAAQ;AACV,QAAA,OAAO,KAAA,CAAM,KAAA;AAAA,MACf,CAAA;AAAA,MACA,IAAI,SAAA,GAAY;AACd,QAAA,OAAO,KAAA,CAAM,MAAM,MAAA,CAAO,CAAC,GAAG,CAAA,KAAM,CAAA,GAAI,CAAA,CAAE,QAAA,EAAU,CAAC,CAAA;AAAA,MACvD,CAAA;AAAA,MACA,IAAI,QAAA,GAAW;AACb,QAAA,OAAO,KAAA,CAAM,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA,EAAG,CAAA,KAAM,CAAA,GAAI,CAAA,CAAE,SAAA,GAAY,CAAA,CAAE,QAAA,EAAU,CAAC,CAAA;AAAA,MACrE,CAAA;AAAA;AAAA;AAAA,MAGA,MAAM,YAAY,KAAA,EAAsD;AACtE,QAAA,IAAI,IAAA;AACJ,QAAA,IAAI,KAAA,CAAM,SAAA,IAAa,IAAA,IAAQ,KAAA,CAAM,WAAA,EAAa;AAChD,UAAA,IAAA,GAAO;AAAA,YACL,EAAA,EAAI,MAAM,OAAO,CAAA;AAAA,YACjB,WAAW,KAAA,CAAM,SAAA;AAAA,YACjB,WAAW,KAAA,CAAM,SAAA;AAAA,YACjB,UAAU,KAAA,CAAM,QAAA;AAAA,YAChB,WAAW,KAAA,CAAM,SAAA;AAAA,YACjB,aAAa,KAAA,CAAM,WAAA;AAAA,YACnB,UAAU,KAAA,CAAM;AAAA,WAClB;AAAA,QACF,CAAA,MAAO;AACL,UAAA,MAAM,CAAA,GAAI,MAAM,QAAA,CAAS,GAAA,CAAI,MAAM,SAAS,CAAA;AAC5C,UAAA,MAAM,IAAI,KAAA,CAAM,SAAA,IAAa,KAAA,CAAM,OAAA,CAAQ,EAAE,QAAQ,CAAA,GAAI,CAAA,CAAE,QAAA,CAAS,KAAK,CAAC,CAAA,KAAM,EAAE,EAAA,KAAO,KAAA,CAAM,SAAS,CAAA,GAAI,MAAA;AAC5G,UAAA,IAAA,GAAO;AAAA,YACL,EAAA,EAAI,MAAM,OAAO,CAAA;AAAA,YACjB,WAAW,KAAA,CAAM,SAAA;AAAA,YACjB,WAAW,KAAA,CAAM,SAAA;AAAA,YACjB,UAAU,KAAA,CAAM,QAAA;AAAA,YAChB,WAAW,CAAA,IAAK,CAAA,CAAE,SAAS,IAAA,GAAO,CAAA,CAAE,QAAQ,CAAA,CAAE,KAAA;AAAA,YAC9C,aAAa,CAAA,CAAE,IAAA;AAAA,YACf,QAAA,EAAU,KAAA,CAAM,OAAA,CAAQ,CAAA,CAAE,MAAM,CAAA,IAAK,CAAA,CAAE,MAAA,CAAO,MAAA,GAAS,CAAA,GAAI,CAAA,CAAE,MAAA,CAAO,CAAC,EAAE,GAAA,GAAM;AAAA,WAC/E;AAAA,QACF;AACA,QAAA,MAAM,QAAA,GAAW,KAAA,CAAM,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,SAAA,KAAc,IAAA,CAAK,SAAA,IAAa,CAAA,CAAE,SAAA,KAAc,KAAK,SAAS,CAAA;AACzG,QAAA,IAAI,QAAA,EAAU,QAAA,CAAS,QAAA,IAAY,IAAA,CAAK,QAAA;AAAA,aACnC,KAAA,CAAM,KAAA,CAAM,IAAA,CAAK,IAAI,CAAA;AAC1B,QAAA,OAAA,EAAQ;AACR,QAAA,OAAO,OAAA;AAAA,MACT,CAAA;AAAA,MACA,cAAA,CAAe,QAAgB,QAAA,EAAkC;AAC/D,QAAA,MAAM,CAAA,GAAI,MAAM,KAAA,CAAM,IAAA,CAAK,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,MAAM,CAAA;AACjD,QAAA,IAAI,CAAA,EAAG;AACL,UAAA,IAAI,QAAA,IAAY,CAAA,EAAG,KAAA,CAAM,KAAA,GAAQ,KAAA,CAAM,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,EAAA,KAAO,MAAM,CAAA;AAAA,iBACnE,QAAA,GAAW,QAAA;AAClB,UAAA,OAAA,EAAQ;AAAA,QACV;AACA,QAAA,OAAO,OAAA;AAAA,MACT,CAAA;AAAA,MACA,eAAe,MAAA,EAAgC;AAC7C,QAAA,KAAA,CAAM,KAAA,GAAQ,MAAM,KAAA,CAAM,MAAA,CAAO,CAAC,CAAA,KAAM,CAAA,CAAE,OAAO,MAAM,CAAA;AACvD,QAAA,OAAA,EAAQ;AACR,QAAA,OAAO,OAAA;AAAA,MACT,CAAA;AAAA,MACA,KAAA,GAAwB;AACtB,QAAA,KAAA,CAAM,QAAQ,EAAC;AACf,QAAA,OAAA,EAAQ;AACR,QAAA,OAAO,OAAA;AAAA,MACT,CAAA;AAAA,MACA,MAAM,SAAS,IAAA,EAA+D;AAC5E,QAAA,MAAM,OAAA,GAAU,MAAM,QAAA,CAAS,MAAA;AAAA,UAC7B,KAAA,CAAM,KAAA,CAAM,GAAA,CAAI,CAAC,CAAA,MAAO;AAAA,YACtB,WAAW,CAAA,CAAE,SAAA;AAAA,YACb,WAAW,CAAA,CAAE,SAAA;AAAA,YACb,aAAa,CAAA,CAAE,WAAA;AAAA,YACf,UAAU,CAAA,CAAE,QAAA;AAAA,YACZ,WAAW,CAAA,CAAE,SAAA;AAAA,YACb,UAAU,CAAA,CAAE;AAAA,WACd,CAAE;AAAA,SACJ;AACA,QAAA,IAAI,IAAA,IAAQ,IAAA,CAAK,cAAA,EAAgB,OAAA,CAAQ,KAAA,EAAM;AAC/C,QAAA,OAAO,OAAA;AAAA,MACT;AAAA,KACF;AACA,IAAA,OAAO,OAAA;AAAA,EACT;AAEA,EAAA,MAAM,IAAA,GAAO;AAAA;AAAA,IAEX,MAAA,GAAyB;AACvB,MAAA,MAAM,KAAK,SAAA,EAAU;AACrB,MAAA,QAAA,CAAS,EAAE,CAAA;AACX,MAAA,OAAO,SAAS,EAAE,CAAA;AAAA,IACpB,CAAA;AAAA;AAAA,IAEA,OAAA,GAA0B;AACxB,MAAA,OAAO,QAAA,CAAS,QAAA,EAAS,IAAK,SAAA,EAAW,CAAA;AAAA,IAC3C,CAAA;AAAA;AAAA,IAEA,MAAM,QAAQ,KAAA,EAAyC;AACrD,MAAA,OAAO,OAAA,CAAQ,KAAA,EAAO,CAAA,yBAAA,EAA4B,KAAK,CAAA,CAAE,CAAA;AAAA,IAC3D;AAAA,GACF;AAKA,EAAA,MAAM,MAAA,GAAS;AAAA;AAAA,IAEb,IAAA,EAAM,CAAC,MAAA,KAA2D,QAAA,CAAS,OAAO,MAAM;AAAA,GAC1F;AAGA,EAAA,MAAM,SAAA,GAAY;AAAA,IAChB,OAAO,IAAA,CAAK,KAAA;AAAA,IACZ,UAAU,IAAA,CAAK,QAAA;AAAA,IACf,iBAAiB,IAAA,CAAK,eAAA;AAAA,IACtB,QAAQ,IAAA,CAAK,MAAA;AAAA,IACb,iBAAiB,IAAA,CAAK,eAAA;AAAA,IACtB,WAAW,IAAA,CAAK,SAAA;AAAA,IAChB,IAAI,QAAA,CAAS,EAAA;AAAA,IACb,QAAQ,QAAA,CAAS,MAAA;AAAA,IACjB,WAAW,QAAA,CAAS;AAAA,GACtB;AAIA,EAAA,OAAO;AAAA,IACL,KAAA;AAAA,IACA,QAAA;AAAA,IACA,WAAA;AAAA,IACA,QAAA;AAAA,IACA,IAAA;AAAA,IACA,QAAA;AAAA,IACA,IAAA;AAAA,IACA,QAAA;AAAA,IACA,KAAA;AAAA,IACA,IAAA;AAAA,IACA,MAAA;AAAA,IACA;AAAA,GACF;AACF","file":"index.js","sourcesContent":["/**\n * ManageSales Storefront SDK\n * --------------------------\n * Resource-based client for building online storefronts.\n * Works with any framework: Next.js, Remix, Nuxt, Astro, plain HTML.\n *\n * Usage:\n * import { createStorefrontClient } from \"./managesales-storefront\";\n * const client = createStorefrontClient({ workspaceId: \"<workspace_id>\" });\n * const products = await client.products.list();\n */\n\n// ── Types ──────────────────────────────────────────────────────────\n\nexport interface RetryConfig {\n /** Max retry attempts for a failed request (default: 2) */\n retries?: number;\n /** Base delay in ms used for exponential backoff (default: 300) */\n baseDelayMs?: number;\n /** Upper bound for any single backoff delay, in ms (default: 4000) */\n maxDelayMs?: number;\n /** HTTP status codes that are retried, GET/HEAD only (default: [408,425,429,500,502,503,504]) */\n retryOnStatus?: number[];\n}\n\nexport interface StorefrontConfig {\n /** Your workspace ID (required) */\n workspaceId: string;\n /** API base URL — defaults to production */\n apiUrl?: string;\n /** Custom fetch implementation (for SSR / edge) */\n customFetch?: typeof fetch;\n /** Default locale for content */\n locale?: string;\n /**\n * Retry behavior for transient failures (network errors, and 5xx/408/425/429\n * responses on idempotent GET/HEAD requests). Pass `false` to disable retries\n * entirely. Defaults to 2 retries with exponential backoff.\n */\n retry?: RetryConfig | false;\n /**\n * Persist the stateful cart (`client.cart`) to `localStorage` so it survives\n * page reloads. Defaults to `true`. Has no effect in environments without\n * `localStorage` (e.g. Node/SSR) — the cart simply stays in-memory.\n */\n persistCart?: boolean;\n}\n\nexport interface ListParams {\n page?: number;\n limit?: number;\n search?: string;\n sort?: string;\n order?: \"asc\" | \"desc\";\n [key: string]: unknown;\n}\n\nexport interface PaginatedResponse<T> {\n data: T[];\n total: number;\n page: number;\n limit: number;\n totalPages: number;\n}\n\n// ── Product types ──\n\nexport interface Product {\n id: string;\n name: string;\n slug: string;\n description: string;\n status: string;\n price: number;\n compareAtPrice?: number;\n currency: string;\n images: ProductImage[];\n variants: ProductVariant[];\n category?: { id: string; name: string };\n brand?: { id: string; name: string };\n tags: string[];\n metadata: Record<string, unknown>;\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface ProductImage {\n id: string;\n url: string;\n alt: string;\n position: number;\n}\n\nexport interface ProductVariant {\n id: string;\n name: string;\n sku: string;\n price: number;\n compareAtPrice?: number;\n inventory: number;\n options: Record<string, string>;\n}\n\n// ── Collection types ──\n\nexport interface Collection {\n id: string;\n name: string;\n slug: string;\n description: string;\n image?: string;\n productCount: number;\n}\n\n// ── Cart & Checkout types ──\n\nexport interface CheckoutLineItem {\n productId: string;\n variantId?: string;\n productName: string;\n quantity: number;\n unitPrice: number;\n imageUrl?: string;\n}\n\nexport interface CheckoutSession {\n id: string;\n status: \"open\" | \"completed\" | \"expired\";\n items: CheckoutLineItem[];\n subtotal: number;\n tax: number;\n shipping: number;\n discount: number;\n total: number;\n currency: string;\n couponCode?: string;\n}\n\nexport interface CartLine {\n id: string;\n productId: string;\n variantId?: string;\n quantity: number;\n unitPrice: number;\n productName: string;\n imageUrl?: string;\n}\n\nexport interface CartState {\n id: string;\n lines: CartLine[];\n createdAt: string;\n updatedAt: string;\n}\n\nexport interface AddCartLineItemInput {\n productId: string;\n variantId?: string;\n quantity: number;\n /** Skip the `products.get` price lookup by supplying these directly */\n unitPrice?: number;\n productName?: string;\n imageUrl?: string;\n}\n\nexport interface StorefrontCart {\n readonly id: string;\n readonly lines: CartLine[];\n readonly itemCount: number;\n readonly subtotal: number;\n /** Adds a line item. Supplying `unitPrice` also requires `productName`, otherwise this falls through to a `products.get` network lookup. */\n addLineItem(input: AddCartLineItemInput): Promise<StorefrontCart>;\n updateLineItem(lineId: string, quantity: number): StorefrontCart;\n removeLineItem(lineId: string): StorefrontCart;\n clear(): StorefrontCart;\n checkout(opts?: { clearOnSuccess?: boolean }): Promise<CheckoutSession>;\n}\n\nexport interface Order {\n id: string;\n orderNumber: string;\n status: string;\n total: number;\n currency: string;\n items: OrderItem[];\n createdAt: string;\n}\n\nexport interface OrderItem {\n productId: string;\n name: string;\n quantity: number;\n price: number;\n}\n\n// ── Customer types ──\n\nexport interface Customer {\n id: string;\n email: string;\n name: string;\n phone?: string;\n createdAt: string;\n}\n\nexport interface AuthSession {\n customer: Customer;\n accessToken: string;\n refreshToken: string;\n}\n\nexport interface Address {\n id: string;\n label?: string;\n line1: string;\n line2?: string;\n city: string;\n state: string;\n postalCode: string;\n country: string;\n isDefault: boolean;\n}\n\n// ── Blog types ──\n\nexport interface BlogPost {\n id: string;\n title: string;\n slug: string;\n excerpt: string;\n content: string;\n coverImage?: string;\n tags: string[];\n publishedAt: string;\n}\n\n// ── Booking types ──\n\nexport interface BookableService {\n id: string;\n name: string;\n duration: number;\n price: number;\n description: string;\n}\n\nexport interface TimeSlot {\n start: string;\n end: string;\n available: boolean;\n}\n\nexport interface Booking {\n id: string;\n serviceId: string;\n date: string;\n startTime: string;\n endTime: string;\n status: string;\n customer: { name: string; email: string };\n}\n\n// ── Form types ──\n\nexport interface FormDefinition {\n id: string;\n title: string;\n description: string;\n fields: FormField[];\n}\n\nexport interface FormField {\n id: string;\n type: string;\n label: string;\n required: boolean;\n options?: string[];\n placeholder?: string;\n}\n\n// ── Store config ──\n\nexport interface StoreConfig {\n name: string;\n slug: string;\n logo?: string;\n theme: Record<string, unknown>;\n currency: string;\n locale: string;\n}\n\n// ── SDK Error ──\n\nexport class StorefrontError extends Error {\n status: number;\n code: string;\n details?: unknown;\n\n constructor(status: number, code: string, message: string, details?: unknown) {\n super(message);\n this.name = \"StorefrontError\";\n this.status = status;\n this.code = code;\n this.details = details;\n }\n}\n\n// ── Core client ────────────────────────────────────────────────────\n\nexport function createStorefrontClient(config: StorefrontConfig) {\n const apiUrl = (config.apiUrl ?? \"https://managesales-backend-phase1test.up.railway.app/api/v1\").replace(/\\/$/, \"\");\n const fetchFn = config.customFetch ?? globalThis.fetch.bind(globalThis);\n let accessToken: string | null = null;\n let refreshToken: string | null = null;\n\n // ── Retry configuration ──\n\n const RETRY_DEFAULTS = {\n retries: 2,\n baseDelayMs: 300,\n maxDelayMs: 4000,\n retryOnStatus: [408, 425, 429, 500, 502, 503, 504],\n };\n const rc =\n config.retry === false\n ? { ...RETRY_DEFAULTS, retries: 0 }\n : { ...RETRY_DEFAULTS, ...(config.retry || {}) };\n const IDEMPOTENT = new Set([\"GET\", \"HEAD\"]);\n const sleep = (ms: number) => new Promise<void>((resolve) => setTimeout(resolve, ms));\n function backoff(attempt: number): number {\n return Math.min(rc.maxDelayMs, Math.round(Math.random() * rc.baseDelayMs * Math.pow(2, attempt)));\n }\n\n // ── Internal helpers ──\n\n async function request<T>(\n method: string,\n path: string,\n options?: {\n body?: unknown;\n params?: Record<string, unknown>;\n auth?: boolean;\n },\n ): Promise<T> {\n const url = new URL(`${apiUrl}${path}`);\n if (options?.params) {\n for (const [k, v] of Object.entries(options.params)) {\n if (v !== undefined && v !== null) url.searchParams.set(k, String(v));\n }\n }\n\n const headers: Record<string, string> = {\n \"X-Workspace-Id\": config.workspaceId,\n };\n\n if (options?.auth && accessToken) {\n headers[\"Authorization\"] = `Bearer ${accessToken}`;\n }\n\n if (options?.body) {\n headers[\"Content-Type\"] = \"application/json\";\n }\n\n const fetchOptions: RequestInit = {\n method,\n headers,\n body: options?.body ? JSON.stringify(options.body) : undefined,\n };\n\n let attempt = 0;\n let res: Response;\n while (true) {\n try {\n res = await fetchFn(url.toString(), fetchOptions);\n } catch (err) {\n if (attempt < rc.retries) {\n await sleep(backoff(attempt));\n attempt++;\n continue;\n }\n throw err;\n }\n if (attempt < rc.retries && rc.retryOnStatus.includes(res.status) && IDEMPOTENT.has(method)) {\n const ra = Number(res.headers.get(\"retry-after\"));\n await sleep(Number.isFinite(ra) && ra > 0 ? ra * 1000 : backoff(attempt));\n attempt++;\n continue;\n }\n break;\n }\n\n // Auto-refresh on 401\n if (res.status === 401 && options?.auth && refreshToken) {\n const refreshed = await refreshSession();\n if (refreshed) {\n headers[\"Authorization\"] = `Bearer ${accessToken}`;\n const retry = await fetchFn(url.toString(), {\n method,\n headers,\n body: options?.body ? JSON.stringify(options.body) : undefined,\n });\n if (!retry.ok) throw await parseError(retry);\n return retry.status === 204 ? (undefined as T) : retry.json();\n }\n }\n\n if (!res.ok) throw await parseError(res);\n return res.status === 204 ? (undefined as T) : res.json();\n }\n\n async function parseError(res: Response): Promise<StorefrontError> {\n try {\n const body = await res.json();\n return new StorefrontError(\n res.status,\n body.code ?? body.error ?? \"UNKNOWN\",\n body.message ?? res.statusText,\n body,\n );\n } catch {\n return new StorefrontError(res.status, \"UNKNOWN\", res.statusText);\n }\n }\n\n async function refreshSession(): Promise<boolean> {\n if (!refreshToken) return false;\n try {\n const res = await fetchFn(`${apiUrl}/storefront/auth/refresh`, {\n method: \"POST\",\n headers: {\n \"Content-Type\": \"application/json\",\n \"X-Workspace-Id\": config.workspaceId,\n },\n body: JSON.stringify({ refreshToken }),\n });\n if (!res.ok) {\n accessToken = null;\n refreshToken = null;\n return false;\n }\n const data = await res.json();\n accessToken = data.accessToken;\n refreshToken = data.refreshToken;\n return true;\n } catch {\n return false;\n }\n }\n\n // ── Resource namespaces ──────────────────────────────────────────\n\n const store = {\n /** Resolve store config by workspace (returns theme, branding, currency) */\n async resolve(): Promise<StoreConfig> {\n return request(\"GET\", \"/storefront-api/resolve\");\n },\n };\n\n const products = {\n /** List published products with pagination and filters */\n async list(params?: ListParams): Promise<PaginatedResponse<Product>> {\n return request(\"GET\", \"/storefront-api/products\", { params });\n },\n /** Get a single product by ID or slug */\n async get(idOrSlug: string): Promise<Product> {\n return request(\"GET\", `/storefront-api/products/${idOrSlug}`);\n },\n /** Search products (shorthand for list with search param) */\n async search(query: string, params?: ListParams): Promise<PaginatedResponse<Product>> {\n return request(\"GET\", \"/storefront-api/search\", { params: { ...params, q: query } });\n },\n };\n\n const collections = {\n /** List published collections */\n async list(params?: ListParams): Promise<PaginatedResponse<Collection>> {\n return request(\"GET\", \"/storefront-api/collections\", { params });\n },\n /** Get a collection with its products */\n async get(idOrSlug: string): Promise<Collection & { products: Product[] }> {\n return request(\"GET\", `/storefront-api/collections/${idOrSlug}`);\n },\n };\n\n const checkout = {\n /** Create a checkout session from cart items */\n async create(items: CheckoutLineItem[]): Promise<CheckoutSession> {\n return request(\"POST\", \"/checkout\", { body: { lines: items } });\n },\n /** Retrieve an existing checkout session */\n async get(checkoutId: string): Promise<CheckoutSession> {\n return request(\"GET\", `/checkout/${checkoutId}`);\n },\n /** Apply a coupon or discount code */\n async applyCoupon(checkoutId: string, couponCode: string): Promise<CheckoutSession> {\n return request(\"POST\", `/checkout/${checkoutId}/coupon`, { body: { couponCode } });\n },\n /** Complete checkout — process payment and create order */\n async complete(checkoutId: string, payment?: Record<string, unknown>): Promise<Order> {\n return request(\"POST\", `/checkout/${checkoutId}/complete`, { body: payment });\n },\n };\n\n const auth = {\n /** Register a new customer account */\n async register(data: {\n email: string;\n password: string;\n name: string;\n phone?: string;\n }): Promise<AuthSession> {\n const session = await request<AuthSession>(\"POST\", \"/storefront/auth/register\", { body: data });\n accessToken = session.accessToken;\n refreshToken = session.refreshToken;\n return session;\n },\n /** Log in with email + password */\n async login(email: string, password: string): Promise<AuthSession> {\n const session = await request<AuthSession>(\"POST\", \"/storefront/auth/login\", {\n body: { email, password },\n });\n accessToken = session.accessToken;\n refreshToken = session.refreshToken;\n return session;\n },\n /** Log in with Google ID token */\n async loginWithGoogle(idToken: string): Promise<AuthSession> {\n const session = await request<AuthSession>(\"POST\", \"/storefront/auth/google\", {\n body: { idToken },\n });\n accessToken = session.accessToken;\n refreshToken = session.refreshToken;\n return session;\n },\n /** Log out and clear local session */\n async logout(): Promise<void> {\n if (accessToken) {\n try {\n await request(\"POST\", \"/storefront/auth/logout\", { auth: true });\n } catch { /* ignore logout errors */ }\n }\n accessToken = null;\n refreshToken = null;\n },\n /** Refresh the access token */\n async refresh(): Promise<boolean> {\n return refreshSession();\n },\n /** Check if client has a valid session */\n isAuthenticated(): boolean {\n return accessToken !== null;\n },\n /** Manually set tokens (e.g., from SSR or cookie restoration) */\n setTokens(access: string, refresh: string): void {\n accessToken = access;\n refreshToken = refresh;\n },\n };\n\n const customer = {\n /** Get current customer profile */\n async me(): Promise<Customer> {\n return request(\"GET\", \"/storefront/auth/me\", { auth: true });\n },\n /** Get customer's order history */\n async orders(params?: ListParams): Promise<PaginatedResponse<Order>> {\n const me = await request<Customer>(\"GET\", \"/storefront/auth/me\", { auth: true });\n return request(\"GET\", `/storefront-api/customer/${me.id}/orders`, { params, auth: true });\n },\n /** Get customer's saved addresses */\n async addresses(): Promise<Address[]> {\n const me = await request<Customer>(\"GET\", \"/storefront/auth/me\", { auth: true });\n return request(\"GET\", `/storefront-api/customer/${me.id}/addresses`, { auth: true });\n },\n };\n\n const blog = {\n /** List published blog posts */\n async list(params?: ListParams & { tags?: string }): Promise<PaginatedResponse<BlogPost>> {\n return request(\"GET\", \"/storefront-api/blog/posts\", { params });\n },\n /** Get a blog post by slug */\n async get(slug: string): Promise<BlogPost> {\n return request(\"GET\", `/storefront-api/blog/posts/${slug}`);\n },\n };\n\n const bookings = {\n /** List bookable services */\n async services(): Promise<BookableService[]> {\n return request(\"GET\", \"/bookings-public/services\");\n },\n /** List bookable staff */\n async staff(): Promise<Array<{ id: string; name: string; avatar?: string }>> {\n return request(\"GET\", \"/bookings-public/staff\");\n },\n /** Check available time slots */\n async availability(params: {\n serviceId: string;\n date: string;\n staffId?: string;\n }): Promise<TimeSlot[]> {\n return request(\"GET\", \"/bookings-public/availability\", { params });\n },\n /** Create a booking */\n async book(data: {\n serviceId: string;\n date: string;\n startTime: string;\n staffId?: string;\n customer: { name: string; email: string; phone?: string };\n }): Promise<Booking> {\n return request(\"POST\", \"/bookings-public/book\", { body: data });\n },\n };\n\n const forms = {\n /** Get a published form by ID or slug */\n async get(idOrSlug: string): Promise<FormDefinition> {\n return request(\"GET\", `/public/forms/${idOrSlug}`);\n },\n /** Submit form response */\n async submit(formId: string, data: Record<string, unknown>): Promise<{ id: string }> {\n return request(\"POST\", `/public/forms/${formId}/submit`, { body: data });\n },\n /** Upload file for a form file field */\n async uploadFile(formId: string, file: File): Promise<{ url: string }> {\n const formData = new FormData();\n formData.append(\"file\", file);\n const res = await fetchFn(`${apiUrl}/public/forms/${formId}/upload`, {\n method: \"POST\",\n headers: { \"X-Workspace-Id\": config.workspaceId },\n body: formData,\n });\n if (!res.ok) throw await parseError(res);\n return res.json();\n },\n };\n\n // ── Stateful cart (client-side, localStorage-persisted) ──\n\n const CART_KEY = \"managesales_cart_\" + config.workspaceId;\n const hasLS = () => {\n try {\n return config.persistCart !== false && typeof localStorage !== \"undefined\";\n } catch {\n return false;\n }\n };\n const loadCart = (): CartState | null => {\n if (!hasLS()) return null;\n try {\n const s = localStorage.getItem(CART_KEY);\n return s ? JSON.parse(s) : null;\n } catch {\n return null;\n }\n };\n const saveCart = (st: CartState): void => {\n if (!hasLS()) return;\n try {\n localStorage.setItem(CART_KEY, JSON.stringify(st));\n } catch {\n /* ignore persistence errors (quota, privacy mode, etc.) */\n }\n };\n let __lineSeq = 0;\n const genId = (p: string): string =>\n typeof crypto !== \"undefined\" && crypto.randomUUID ? p + crypto.randomUUID() : p + Date.now() + \"_\" + ++__lineSeq;\n const nowIso = (): string => new Date().toISOString();\n const freshCart = (): CartState => ({ id: genId(\"cart_\"), lines: [], createdAt: nowIso(), updatedAt: nowIso() });\n\n function makeCart(state: CartState): StorefrontCart {\n const persist = () => {\n state.updatedAt = nowIso();\n saveCart(state);\n };\n const cartObj: StorefrontCart = {\n get id() {\n return state.id;\n },\n get lines() {\n return state.lines;\n },\n get itemCount() {\n return state.lines.reduce((n, l) => n + l.quantity, 0);\n },\n get subtotal() {\n return state.lines.reduce((s, l) => s + l.unitPrice * l.quantity, 0);\n },\n // Supplying unitPrice alone does not skip the lookup — productName must also be\n // provided, otherwise this falls through to the products.get() network lookup below.\n async addLineItem(input: AddCartLineItemInput): Promise<StorefrontCart> {\n let line: CartLine;\n if (input.unitPrice != null && input.productName) {\n line = {\n id: genId(\"line_\"),\n productId: input.productId,\n variantId: input.variantId,\n quantity: input.quantity,\n unitPrice: input.unitPrice,\n productName: input.productName,\n imageUrl: input.imageUrl,\n };\n } else {\n const p = await products.get(input.productId);\n const v = input.variantId && Array.isArray(p.variants) ? p.variants.find((x) => x.id === input.variantId) : undefined;\n line = {\n id: genId(\"line_\"),\n productId: input.productId,\n variantId: input.variantId,\n quantity: input.quantity,\n unitPrice: v && v.price != null ? v.price : p.price,\n productName: p.name,\n imageUrl: Array.isArray(p.images) && p.images.length > 0 ? p.images[0].url : undefined,\n };\n }\n const existing = state.lines.find((l) => l.productId === line.productId && l.variantId === line.variantId);\n if (existing) existing.quantity += line.quantity;\n else state.lines.push(line);\n persist();\n return cartObj;\n },\n updateLineItem(lineId: string, quantity: number): StorefrontCart {\n const l = state.lines.find((x) => x.id === lineId);\n if (l) {\n if (quantity <= 0) state.lines = state.lines.filter((x) => x.id !== lineId);\n else l.quantity = quantity;\n persist();\n }\n return cartObj;\n },\n removeLineItem(lineId: string): StorefrontCart {\n state.lines = state.lines.filter((x) => x.id !== lineId);\n persist();\n return cartObj;\n },\n clear(): StorefrontCart {\n state.lines = [];\n persist();\n return cartObj;\n },\n async checkout(opts?: { clearOnSuccess?: boolean }): Promise<CheckoutSession> {\n const session = await checkout.create(\n state.lines.map((l) => ({\n productId: l.productId,\n variantId: l.variantId,\n productName: l.productName,\n quantity: l.quantity,\n unitPrice: l.unitPrice,\n imageUrl: l.imageUrl,\n })),\n );\n if (opts && opts.clearOnSuccess) cartObj.clear();\n return session;\n },\n };\n return cartObj;\n }\n\n const cart = {\n /** Create a new, empty cart (replaces any persisted cart) */\n create(): StorefrontCart {\n const st = freshCart();\n saveCart(st);\n return makeCart(st);\n },\n /** Get the current cart — loaded from storage if persisted, otherwise a fresh one */\n current(): StorefrontCart {\n return makeCart(loadCart() || freshCart());\n },\n /** Recover an abandoned cart by recovery token */\n async recover(token: string): Promise<CheckoutSession> {\n return request(\"GET\", `/abandoned-carts/recover/${token}`);\n },\n };\n\n // ── Alias namespaces (convenience wrappers over existing methods) ──\n\n /** Alias for `client.customer.orders` — order history for the logged-in customer */\n const orders = {\n /** Get customer's order history (delegates to `customer.orders`) */\n list: (params?: ListParams): Promise<PaginatedResponse<Order>> => customer.orders(params),\n };\n\n /** Convenience namespace bundling `auth` + `customer` under one name */\n const customers = {\n login: auth.login,\n register: auth.register,\n loginWithGoogle: auth.loginWithGoogle,\n logout: auth.logout,\n isAuthenticated: auth.isAuthenticated,\n setTokens: auth.setTokens,\n me: customer.me,\n orders: customer.orders,\n addresses: customer.addresses,\n };\n\n // ── Public API ───────────────────────────────────────────────────\n\n return {\n store,\n products,\n collections,\n checkout,\n auth,\n customer,\n blog,\n bookings,\n forms,\n cart,\n orders,\n customers,\n };\n}\n\n// ── Type export for TypeScript users ──\n\nexport type StorefrontClient = ReturnType<typeof createStorefrontClient>;\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@managesales/storefront",
|
|
3
|
-
"version": "1.0
|
|
3
|
+
"version": "1.1.0",
|
|
4
4
|
"description": "Resource-based TypeScript client for building storefronts on the ManageSales Storefront API. Zero runtime dependencies, built-in auth and token refresh.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/index.cjs",
|