@managesales/storefront 1.0.0 → 1.0.2
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 +26 -0
- package/README.md +327 -38
- package/package.json +2 -1
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to `@managesales/storefront`. This project adheres to
|
|
4
|
+
[Semantic Versioning](https://semver.org).
|
|
5
|
+
|
|
6
|
+
## 1.0.2 — 2026-07-11
|
|
7
|
+
|
|
8
|
+
### Docs
|
|
9
|
+
- Rewrote the README as a full developer landing page: human-readable title and tagline, feature
|
|
10
|
+
overview, all-package-manager install, a copy-paste quick start, a complete **API organization**
|
|
11
|
+
reference, framework examples (React / Next.js / Node.js), server-side auth (SSR) guide, error
|
|
12
|
+
handling, TypeScript guide, a **Versioning** policy, and this changelog.
|
|
13
|
+
|
|
14
|
+
## 1.0.1 — 2026-07-11
|
|
15
|
+
|
|
16
|
+
### Docs
|
|
17
|
+
- Expanded the README with per-resource examples and browser/Node runtime support notes.
|
|
18
|
+
|
|
19
|
+
## 1.0.0 — 2026-07-11
|
|
20
|
+
|
|
21
|
+
### Added
|
|
22
|
+
- Initial public release. Resource-based Storefront SDK with namespaces for products, collections,
|
|
23
|
+
checkout, cart recovery, auth, customer, blog, bookings, and forms.
|
|
24
|
+
- Built-in customer auth with automatic access-token refresh on `401`.
|
|
25
|
+
- `StorefrontError` class exposing `status`, `code`, `message`, and `details`.
|
|
26
|
+
- Dual ESM + CommonJS builds with bundled TypeScript declarations. Zero runtime dependencies.
|
package/README.md
CHANGED
|
@@ -1,77 +1,366 @@
|
|
|
1
|
-
#
|
|
1
|
+
# ManageSales Storefront SDK
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
API** — like Shopify's Storefront API client. Built-in auth, automatic token refresh, typed
|
|
5
|
-
responses, and **zero runtime dependencies**.
|
|
3
|
+
**Build custom storefronts on top of the ManageSales Commerce Platform.**
|
|
6
4
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
5
|
+
[](https://www.npmjs.com/package/@managesales/storefront)
|
|
6
|
+
[](./LICENSE)
|
|
7
|
+
[](#typescript-support)
|
|
8
|
+
[](#browser--node-support)
|
|
10
9
|
|
|
11
|
-
|
|
10
|
+
`@managesales/storefront` is a typed, resource-based client for the ManageSales Storefront API —
|
|
11
|
+
like Shopify's Storefront client, for ManageSales. Everything you need to ship a storefront:
|
|
12
|
+
|
|
13
|
+
- 🛍️ **Products** — list, search, and fetch details
|
|
14
|
+
- 🗂️ **Collections** — browse categories and their products
|
|
15
|
+
- 👤 **Customers** — registration, login, profile, order history
|
|
16
|
+
- 🛒 **Checkout** — line items, coupons, complete purchases (+ abandoned-cart recovery)
|
|
17
|
+
- 🔎 **Search** — full-text product search
|
|
18
|
+
- 🟦 **TypeScript** — first-class types, no `@types` needed
|
|
19
|
+
- ⚡ **SSR-ready** — inject a custom `fetch`; server-side auth pattern built in
|
|
20
|
+
- 📦 **Zero dependencies** — one small file, dual ESM + CommonJS
|
|
21
|
+
|
|
22
|
+
---
|
|
23
|
+
|
|
24
|
+
## Table of contents
|
|
25
|
+
|
|
26
|
+
- [Installation](#installation)
|
|
27
|
+
- [Quick start](#quick-start)
|
|
28
|
+
- [API organization](#api-organization)
|
|
29
|
+
- [Create a client](#create-a-client)
|
|
30
|
+
- [Products & search](#products--search)
|
|
31
|
+
- [Collections](#collections)
|
|
32
|
+
- [Cart & checkout](#cart--checkout)
|
|
33
|
+
- [Authentication](#authentication)
|
|
34
|
+
- [Customer account](#customer-account)
|
|
35
|
+
- [Error handling](#error-handling)
|
|
36
|
+
- [TypeScript support](#typescript-support)
|
|
37
|
+
- [Framework examples](#framework-examples)
|
|
38
|
+
- [Browser & Node support](#browser--node-support)
|
|
39
|
+
- [Versioning](#versioning)
|
|
40
|
+
- [Changelog](#changelog)
|
|
41
|
+
- [No-npm alternative](#no-npm-alternative)
|
|
42
|
+
- [License](#license)
|
|
43
|
+
|
|
44
|
+
## Installation
|
|
12
45
|
|
|
13
46
|
```bash
|
|
14
47
|
npm install @managesales/storefront
|
|
15
|
-
|
|
48
|
+
pnpm add @managesales/storefront
|
|
49
|
+
yarn add @managesales/storefront
|
|
50
|
+
bun add @managesales/storefront
|
|
16
51
|
```
|
|
17
52
|
|
|
18
|
-
##
|
|
53
|
+
## Quick start
|
|
54
|
+
|
|
55
|
+
Copy-paste, add your workspace ID, and you're fetching live catalog data — no auth required:
|
|
19
56
|
|
|
20
57
|
```ts
|
|
21
58
|
import { createStorefrontClient } from "@managesales/storefront";
|
|
22
59
|
|
|
23
60
|
const client = createStorefrontClient({
|
|
24
|
-
workspaceId: "ws_123", //
|
|
61
|
+
workspaceId: "ws_123", // your store — Settings → API
|
|
25
62
|
});
|
|
26
63
|
|
|
27
|
-
const products = await client.products.list();
|
|
64
|
+
const { data: products } = await client.products.list({ limit: 12 });
|
|
65
|
+
console.log(products);
|
|
28
66
|
```
|
|
29
67
|
|
|
30
|
-
|
|
31
|
-
|
|
68
|
+
## API organization
|
|
69
|
+
|
|
70
|
+
Every resource is a namespace on the client. Public resources need no auth; customer resources
|
|
71
|
+
require a logged-in session.
|
|
72
|
+
|
|
73
|
+
```ts
|
|
74
|
+
// Catalog (public)
|
|
75
|
+
client.products.list(params?) // paginated list
|
|
76
|
+
client.products.get(idOrSlug) // product details
|
|
77
|
+
client.products.search(query, params?) // full-text search
|
|
78
|
+
client.collections.list(params?)
|
|
79
|
+
client.collections.get(idOrSlug) // collection + its products
|
|
80
|
+
|
|
81
|
+
// Cart & checkout (public)
|
|
82
|
+
client.checkout.create(lineItems) // start a checkout from line items
|
|
83
|
+
client.checkout.get(checkoutId)
|
|
84
|
+
client.checkout.applyCoupon(checkoutId, code)
|
|
85
|
+
client.checkout.complete(checkoutId, payment?)
|
|
86
|
+
client.cart.recover(token) // rebuild a checkout from a recovery link
|
|
87
|
+
|
|
88
|
+
// Auth & customer (customer session)
|
|
89
|
+
client.auth.register({ email, password, name })
|
|
90
|
+
client.auth.login(email, password)
|
|
91
|
+
client.auth.loginWithGoogle(idToken)
|
|
92
|
+
client.auth.logout()
|
|
93
|
+
client.auth.isAuthenticated()
|
|
94
|
+
client.auth.setTokens(access, refresh) // restore a session (e.g. from cookies)
|
|
95
|
+
client.customer.me()
|
|
96
|
+
client.customer.orders(params?) // order history
|
|
97
|
+
client.customer.addresses()
|
|
98
|
+
|
|
99
|
+
// Content (public)
|
|
100
|
+
client.blog.list(params?) / client.blog.get(slug)
|
|
101
|
+
client.bookings.services() / client.bookings.availability(params) / client.bookings.book(data)
|
|
102
|
+
client.forms.get(idOrSlug) / client.forms.submit(formId, data) / client.forms.uploadFile(formId, file)
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
## Create a client
|
|
106
|
+
|
|
107
|
+
```ts
|
|
108
|
+
const client = createStorefrontClient({
|
|
109
|
+
workspaceId: "ws_123",
|
|
110
|
+
});
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
| Option | Type | Required | Description |
|
|
114
|
+
|--------|------|----------|-------------|
|
|
115
|
+
| `workspaceId` | `string` | ✅ | Identifies your store. Found in **Settings → API**. |
|
|
116
|
+
| `apiUrl` | `string` | — | Backend base URL **including** the `/api/v1` prefix. Defaults to ManageSales cloud; override for self-hosted / staging / local. |
|
|
117
|
+
| `customFetch` | `typeof fetch` | — | Inject a `fetch` for SSR / edge runtimes or Node < 18. |
|
|
118
|
+
| `locale` | `string` | — | Default locale for localized content (e.g. `"en"`). |
|
|
32
119
|
|
|
33
120
|
```ts
|
|
34
121
|
const client = createStorefrontClient({
|
|
35
122
|
workspaceId: "ws_123",
|
|
36
|
-
apiUrl: "https://
|
|
123
|
+
apiUrl: "https://api.mystore.com/api/v1",
|
|
124
|
+
customFetch: fetch,
|
|
125
|
+
locale: "en",
|
|
126
|
+
});
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
## Products & search
|
|
130
|
+
|
|
131
|
+
```ts
|
|
132
|
+
// List with pagination, search, and sorting
|
|
133
|
+
const { data: products, total } = await client.products.list({
|
|
134
|
+
page: 1,
|
|
135
|
+
limit: 20,
|
|
136
|
+
search: "red shoes",
|
|
137
|
+
sort: "price",
|
|
138
|
+
order: "asc",
|
|
37
139
|
});
|
|
140
|
+
|
|
141
|
+
// Product details — by ID or slug
|
|
142
|
+
const product = await client.products.get("leather-jacket");
|
|
143
|
+
console.log(product.name, product.price, product.variants);
|
|
144
|
+
|
|
145
|
+
// Full-text search
|
|
146
|
+
const { data: results } = await client.products.search("organic cotton");
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
## Collections
|
|
150
|
+
|
|
151
|
+
```ts
|
|
152
|
+
const { data: collections } = await client.collections.list();
|
|
153
|
+
|
|
154
|
+
const summer = await client.collections.get("summer-sale");
|
|
155
|
+
console.log(summer.name, summer.products.length); // collection + its products
|
|
156
|
+
```
|
|
157
|
+
|
|
158
|
+
## Cart & checkout
|
|
159
|
+
|
|
160
|
+
Assemble line items in your UI, create a checkout session, optionally apply a coupon, then complete
|
|
161
|
+
it:
|
|
162
|
+
|
|
163
|
+
```ts
|
|
164
|
+
// 1. Create a checkout from line items
|
|
165
|
+
const session = await client.checkout.create([
|
|
166
|
+
{ productId: "prod_123", variantId: "var_456", quantity: 2 },
|
|
167
|
+
{ productId: "prod_789", quantity: 1 },
|
|
168
|
+
]);
|
|
169
|
+
|
|
170
|
+
// 2. Re-fetch a session (e.g. after a reload)
|
|
171
|
+
const current = await client.checkout.get(session.id);
|
|
172
|
+
|
|
173
|
+
// 3. Apply a coupon
|
|
174
|
+
const discounted = await client.checkout.applyCoupon(session.id, "SAVE10");
|
|
175
|
+
console.log(discounted.discount, discounted.total);
|
|
176
|
+
|
|
177
|
+
// 4. Complete the purchase
|
|
178
|
+
const order = await client.checkout.complete(session.id, {
|
|
179
|
+
paymentMethod: "card",
|
|
180
|
+
token: "tok_visa_xxx",
|
|
181
|
+
});
|
|
182
|
+
console.log("Order placed:", order.orderNumber);
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
**Abandoned-cart recovery** — turn a recovery link back into a checkout session:
|
|
186
|
+
|
|
187
|
+
```ts
|
|
188
|
+
const token = new URLSearchParams(location.search).get("token")!;
|
|
189
|
+
const recovered = await client.cart.recover(token);
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
## Authentication
|
|
193
|
+
|
|
194
|
+
Most pages need **no auth**. When a customer logs in, the SDK stores the access + refresh tokens and
|
|
195
|
+
**auto-refreshes on `401`** — you never handle tokens by hand.
|
|
196
|
+
|
|
197
|
+
```ts
|
|
198
|
+
await client.auth.register({ email: "jane@example.com", password: "SecurePass123!", name: "Jane Doe" });
|
|
199
|
+
await client.auth.login("jane@example.com", "SecurePass123!");
|
|
200
|
+
await client.auth.loginWithGoogle(googleIdToken); // pass a Google ID token
|
|
201
|
+
|
|
202
|
+
if (client.auth.isAuthenticated()) { /* show account menu */ }
|
|
203
|
+
await client.auth.logout();
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Server-side (recommended)
|
|
207
|
+
|
|
208
|
+
For production, run the SDK on your server with httpOnly cookies so tokens never reach the browser.
|
|
209
|
+
Restore a session with `setTokens`:
|
|
210
|
+
|
|
211
|
+
```ts
|
|
212
|
+
// app/api/storefront/[...path]/route.ts (Next.js)
|
|
213
|
+
import { createStorefrontClient } from "@managesales/storefront";
|
|
214
|
+
import { cookies } from "next/headers";
|
|
215
|
+
|
|
216
|
+
export async function GET() {
|
|
217
|
+
const client = createStorefrontClient({ workspaceId: "ws_123" });
|
|
218
|
+
|
|
219
|
+
const access = cookies().get("sf_access_token")?.value;
|
|
220
|
+
const refresh = cookies().get("sf_refresh_token")?.value;
|
|
221
|
+
if (access && refresh) client.auth.setTokens(access, refresh);
|
|
222
|
+
|
|
223
|
+
const products = await client.products.list({ limit: 20 });
|
|
224
|
+
return Response.json(products);
|
|
225
|
+
}
|
|
226
|
+
```
|
|
227
|
+
|
|
228
|
+
## Customer account
|
|
229
|
+
|
|
230
|
+
Requires a logged-in session.
|
|
231
|
+
|
|
232
|
+
```ts
|
|
233
|
+
const me = await client.customer.me(); // profile
|
|
234
|
+
const { data: orders } = await client.customer.orders({ page: 1 }); // order history
|
|
235
|
+
const addresses = await client.customer.addresses(); // saved addresses
|
|
236
|
+
```
|
|
237
|
+
|
|
238
|
+
## Error handling
|
|
239
|
+
|
|
240
|
+
Every non-2xx response throws a `StorefrontError` with structured fields:
|
|
241
|
+
|
|
242
|
+
```ts
|
|
243
|
+
import { StorefrontError } from "@managesales/storefront";
|
|
244
|
+
|
|
245
|
+
try {
|
|
246
|
+
await client.products.get("does-not-exist");
|
|
247
|
+
} catch (err) {
|
|
248
|
+
if (err instanceof StorefrontError) {
|
|
249
|
+
console.log(err.status); // 404
|
|
250
|
+
console.log(err.code); // "NOT_FOUND"
|
|
251
|
+
console.log(err.message); // "Product not found"
|
|
252
|
+
console.log(err.details); // full error body from the API
|
|
253
|
+
} else {
|
|
254
|
+
throw err; // network / unexpected
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
> **Resilience:** the client automatically retries a request **once** with a refreshed token when a
|
|
260
|
+
> `401` indicates an expired access token. It does not retry on network/5xx errors — wrap calls in
|
|
261
|
+
> your own retry policy if you need that.
|
|
262
|
+
|
|
263
|
+
## TypeScript support
|
|
264
|
+
|
|
265
|
+
Written in TypeScript and shipped with declaration files — no `@types/*` package. Params and
|
|
266
|
+
responses are fully typed, including generics like `PaginatedResponse<Product>`:
|
|
267
|
+
|
|
268
|
+
```ts
|
|
269
|
+
import {
|
|
270
|
+
createStorefrontClient,
|
|
271
|
+
StorefrontError,
|
|
272
|
+
type StorefrontConfig,
|
|
273
|
+
type Product,
|
|
274
|
+
type PaginatedResponse,
|
|
275
|
+
} from "@managesales/storefront";
|
|
276
|
+
|
|
277
|
+
const config: StorefrontConfig = { workspaceId: "ws_123" };
|
|
278
|
+
const client = createStorefrontClient(config);
|
|
279
|
+
|
|
280
|
+
const page: PaginatedResponse<Product> = await client.products.list();
|
|
38
281
|
```
|
|
39
282
|
|
|
40
|
-
|
|
283
|
+
## Framework examples
|
|
284
|
+
|
|
285
|
+
**React**
|
|
286
|
+
|
|
287
|
+
```tsx
|
|
288
|
+
import { useEffect, useState } from "react";
|
|
289
|
+
import { createStorefrontClient, type Product } from "@managesales/storefront";
|
|
290
|
+
|
|
291
|
+
const client = createStorefrontClient({ workspaceId: "ws_123" });
|
|
292
|
+
|
|
293
|
+
export function ProductGrid() {
|
|
294
|
+
const [products, setProducts] = useState<Product[]>([]);
|
|
295
|
+
useEffect(() => {
|
|
296
|
+
client.products.list({ limit: 12 }).then((res) => setProducts(res.data));
|
|
297
|
+
}, []);
|
|
298
|
+
return <ul>{products.map((p) => <li key={p.id}>{p.name}</li>)}</ul>;
|
|
299
|
+
}
|
|
300
|
+
```
|
|
301
|
+
|
|
302
|
+
**Next.js (Server Component)**
|
|
303
|
+
|
|
304
|
+
```tsx
|
|
305
|
+
// app/page.tsx
|
|
306
|
+
import { createStorefrontClient } from "@managesales/storefront";
|
|
307
|
+
|
|
308
|
+
const client = createStorefrontClient({ workspaceId: "ws_123" });
|
|
309
|
+
|
|
310
|
+
export default async function Home() {
|
|
311
|
+
const { data: products } = await client.products.list({ limit: 12 });
|
|
312
|
+
return <ProductGrid products={products} />;
|
|
313
|
+
}
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
**Node.js**
|
|
41
317
|
|
|
42
318
|
```ts
|
|
43
|
-
|
|
319
|
+
import { createStorefrontClient } from "@managesales/storefront";
|
|
320
|
+
|
|
321
|
+
const client = createStorefrontClient({ workspaceId: "ws_123" });
|
|
322
|
+
|
|
323
|
+
const { data } = await client.products.list({ page: 1, limit: 20 });
|
|
324
|
+
console.log(data);
|
|
44
325
|
```
|
|
45
326
|
|
|
46
|
-
|
|
47
|
-
|
|
327
|
+
## Browser & Node support
|
|
328
|
+
|
|
329
|
+
- **Zero dependencies** — uses the platform's global `fetch`.
|
|
330
|
+
- **Node.js ≥ 18** (global `fetch`). On older Node, pass `customFetch` (e.g. `undici`).
|
|
331
|
+
- **Browsers** — all modern evergreen browsers.
|
|
332
|
+
- **Deno, Bun, edge runtimes** (Cloudflare Workers, Vercel Edge) — supported; inject `customFetch`
|
|
333
|
+
where a global isn't available.
|
|
334
|
+
- **Module formats** — ships both **ESM** (`import`) and **CommonJS** (`require`).
|
|
335
|
+
|
|
336
|
+
## Versioning
|
|
337
|
+
|
|
338
|
+
Follows [Semantic Versioning](https://semver.org):
|
|
339
|
+
|
|
340
|
+
- **patch** (`x.y.Z`) — docs and bug fixes, no API change
|
|
341
|
+
- **minor** (`x.Y.z`) — additive, backward-compatible API
|
|
342
|
+
- **major** (`X.y.z`) — breaking changes to the client surface
|
|
343
|
+
|
|
344
|
+
The SDK version tracks the ManageSales Storefront API contract it targets. See the
|
|
345
|
+
[Changelog](#changelog).
|
|
48
346
|
|
|
49
|
-
##
|
|
347
|
+
## Changelog
|
|
50
348
|
|
|
51
|
-
|
|
52
|
-
Astro, plain Node (`import` or `require`), Vite, and the browser. Requires Node ≥ 18 (for global
|
|
53
|
-
`fetch`).
|
|
349
|
+
See [CHANGELOG.md](./CHANGELOG.md) for release notes.
|
|
54
350
|
|
|
55
|
-
##
|
|
351
|
+
## No-npm alternative
|
|
56
352
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
(`backend/src/modules/storefront/sdk/storefront-sdk-source.ts`) — the exact same function that
|
|
60
|
-
serves `GET /api/v1/sdk/storefront.ts`. This guarantees the npm package, the curl endpoint, and the
|
|
61
|
-
in-app Download never drift.
|
|
353
|
+
Prefer no dependency? The identical client is served as a single file — download it from
|
|
354
|
+
**Developers → SDKs** in the app, or curl it:
|
|
62
355
|
|
|
63
356
|
```bash
|
|
64
|
-
|
|
65
|
-
|
|
357
|
+
curl -o managesales-storefront.ts \
|
|
358
|
+
"https://managesales-backend-phase1test.up.railway.app/api/v1/sdk/storefront.ts"
|
|
66
359
|
```
|
|
67
360
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
- Override the baked default backend URL at build time with `PUBLIC_API_URL` (must include
|
|
71
|
-
`/api/v1`).
|
|
72
|
-
- The build imports the generator across the monorepo. If this package is ever extracted to its own
|
|
73
|
-
repo, vendor `storefront-sdk-source.ts` into `scripts/` instead.
|
|
361
|
+
Then `import { createStorefrontClient } from "./managesales-storefront"`. The npm package, the curl
|
|
362
|
+
endpoint, and the in-app download are generated from one source, so they're always identical.
|
|
74
363
|
|
|
75
364
|
## License
|
|
76
365
|
|
|
77
|
-
Apache-2.0
|
|
366
|
+
[Apache-2.0](./LICENSE) © ManageSales
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@managesales/storefront",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.2",
|
|
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",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"files": [
|
|
17
17
|
"dist",
|
|
18
18
|
"README.md",
|
|
19
|
+
"CHANGELOG.md",
|
|
19
20
|
"LICENSE"
|
|
20
21
|
],
|
|
21
22
|
"sideEffects": false,
|