@spree/sdk 0.6.6 → 0.6.8
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +37 -11
- package/dist/index.cjs +43 -37
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +21 -40
- package/dist/index.d.ts +21 -40
- package/dist/index.js +43 -37
- package/dist/index.js.map +1 -1
- package/dist/types/index.d.cts +20 -33
- package/dist/types/index.d.ts +20 -33
- package/dist/zod/index.cjs +19 -39
- package/dist/zod/index.cjs.map +1 -1
- package/dist/zod/index.d.cts +19 -77
- package/dist/zod/index.d.ts +19 -77
- package/dist/zod/index.js +20 -39
- package/dist/zod/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ const client = createSpreeClient({
|
|
|
27
27
|
// Browse products (Store API)
|
|
28
28
|
const products = await client.store.products.list({
|
|
29
29
|
limit: 10,
|
|
30
|
-
expand: 'variants,images',
|
|
30
|
+
expand: ['variants', 'images'],
|
|
31
31
|
});
|
|
32
32
|
|
|
33
33
|
// Get a single product
|
|
@@ -150,12 +150,12 @@ const products = await client.store.products.list({
|
|
|
150
150
|
limit: 25,
|
|
151
151
|
name_cont: 'shirt',
|
|
152
152
|
sort: 'price asc',
|
|
153
|
-
expand: 'variants,images,taxons',
|
|
153
|
+
expand: ['variants', 'images', 'taxons'],
|
|
154
154
|
});
|
|
155
155
|
|
|
156
156
|
// Get single product by ID or slug
|
|
157
157
|
const product = await client.store.products.get('spree-tote', {
|
|
158
|
-
expand: 'variants,images',
|
|
158
|
+
expand: ['variants', 'images'],
|
|
159
159
|
});
|
|
160
160
|
|
|
161
161
|
// Get available filters (price range, availability, options, taxons)
|
|
@@ -169,12 +169,12 @@ const filters = await client.store.products.filters({
|
|
|
169
169
|
```typescript
|
|
170
170
|
// List taxonomies
|
|
171
171
|
const taxonomies = await client.store.taxonomies.list({
|
|
172
|
-
expand: 'taxons',
|
|
172
|
+
expand: ['taxons'],
|
|
173
173
|
});
|
|
174
174
|
|
|
175
175
|
// Get taxonomy with taxons
|
|
176
176
|
const categories = await client.store.taxonomies.get('tax_123', {
|
|
177
|
-
expand: 'root,taxons',
|
|
177
|
+
expand: ['root', 'taxons'],
|
|
178
178
|
});
|
|
179
179
|
|
|
180
180
|
// List taxons with filtering
|
|
@@ -185,14 +185,14 @@ const taxons = await client.store.taxons.list({
|
|
|
185
185
|
|
|
186
186
|
// Get single taxon by ID or permalink
|
|
187
187
|
const taxon = await client.store.taxons.get('categories/clothing', {
|
|
188
|
-
expand: 'ancestors,children', // For breadcrumbs and subcategories
|
|
188
|
+
expand: ['ancestors', 'children'], // For breadcrumbs and subcategories
|
|
189
189
|
});
|
|
190
190
|
|
|
191
191
|
// List products in a category
|
|
192
192
|
const categoryProducts = await client.store.taxons.products.list('categories/clothing', {
|
|
193
193
|
page: 1,
|
|
194
194
|
limit: 12,
|
|
195
|
-
expand: 'images,default_variant',
|
|
195
|
+
expand: ['images', 'default_variant'],
|
|
196
196
|
});
|
|
197
197
|
```
|
|
198
198
|
|
|
@@ -225,7 +225,7 @@ const options = { orderToken: cart.order_token };
|
|
|
225
225
|
|
|
226
226
|
// Get order by ID or number
|
|
227
227
|
const order = await client.store.orders.get('R123456789', {
|
|
228
|
-
expand: 'line_items,shipments',
|
|
228
|
+
expand: ['line_items', 'shipments'],
|
|
229
229
|
}, options);
|
|
230
230
|
|
|
231
231
|
// Update order (email, addresses)
|
|
@@ -364,14 +364,37 @@ const completed = await client.store.orders.paymentSessions.complete(
|
|
|
364
364
|
console.log(completed.status); // 'completed'
|
|
365
365
|
```
|
|
366
366
|
|
|
367
|
+
### Markets
|
|
368
|
+
|
|
369
|
+
```typescript
|
|
370
|
+
// List all markets
|
|
371
|
+
const { data: markets } = await client.store.markets.list();
|
|
372
|
+
// [{ id: "mkt_xxx", name: "North America", currency: "USD", default_locale: "en", ... }]
|
|
373
|
+
|
|
374
|
+
// Get a single market
|
|
375
|
+
const market = await client.store.markets.get('mkt_xxx');
|
|
376
|
+
|
|
377
|
+
// Resolve which market applies for a country
|
|
378
|
+
const market = await client.store.markets.resolve('DE');
|
|
379
|
+
// => { id: "mkt_xxx", name: "Europe", currency: "EUR", default_locale: "de", ... }
|
|
380
|
+
|
|
381
|
+
// List countries in a market
|
|
382
|
+
const { data: countries } = await client.store.markets.countries.list('mkt_xxx');
|
|
383
|
+
|
|
384
|
+
// Get a country in a market (with states for address forms)
|
|
385
|
+
const country = await client.store.markets.countries.get('mkt_xxx', 'DE', {
|
|
386
|
+
expand: ['states'],
|
|
387
|
+
});
|
|
388
|
+
```
|
|
389
|
+
|
|
367
390
|
### Geography
|
|
368
391
|
|
|
369
392
|
```typescript
|
|
370
393
|
// List countries available for checkout
|
|
371
394
|
const { data: countries } = await client.store.countries.list();
|
|
372
395
|
|
|
373
|
-
// Get country by ISO code (
|
|
374
|
-
const usa = await client.store.countries.get('US');
|
|
396
|
+
// Get country by ISO code (with states)
|
|
397
|
+
const usa = await client.store.countries.get('US', { expand: ['states'] });
|
|
375
398
|
console.log(usa.states); // Array of states
|
|
376
399
|
```
|
|
377
400
|
|
|
@@ -448,7 +471,7 @@ const { data: wishlists } = await client.store.wishlists.list({}, options);
|
|
|
448
471
|
|
|
449
472
|
// Get wishlist by ID
|
|
450
473
|
const wishlist = await client.store.wishlists.get('wl_xxx', {
|
|
451
|
-
expand: 'wished_items',
|
|
474
|
+
expand: ['wished_items'],
|
|
452
475
|
}, options);
|
|
453
476
|
|
|
454
477
|
// Create wishlist
|
|
@@ -501,6 +524,7 @@ The SDK uses a resource builder pattern for nested resources:
|
|
|
501
524
|
| `store.customer` | `addresses` | `list`, `get`, `create`, `update`, `delete`, `markAsDefault` |
|
|
502
525
|
| `store.customer` | `creditCards` | `list`, `get`, `delete` |
|
|
503
526
|
| `store.customer` | `giftCards` | `list`, `get` |
|
|
527
|
+
| `store.markets` | `countries` | `list`, `get` |
|
|
504
528
|
| `store.taxons` | `products` | `list` |
|
|
505
529
|
| `store.wishlists` | `items` | `create`, `update`, `delete` |
|
|
506
530
|
|
|
@@ -511,6 +535,7 @@ await client.store.orders.lineItems.create(orderId, params, options);
|
|
|
511
535
|
await client.store.orders.payments.list(orderId, options);
|
|
512
536
|
await client.store.orders.shipments.update(orderId, shipmentId, params, options);
|
|
513
537
|
await client.store.customer.addresses.list({}, options);
|
|
538
|
+
await client.store.markets.countries.list(marketId);
|
|
514
539
|
await client.store.taxons.products.list(taxonId, params, options);
|
|
515
540
|
await client.store.wishlists.items.create(wishlistId, params, options);
|
|
516
541
|
```
|
|
@@ -608,6 +633,7 @@ The SDK exports all Store API types:
|
|
|
608
633
|
- `StoreState` - State/province
|
|
609
634
|
- `StoreAddress` - Customer address
|
|
610
635
|
- `StoreCustomer` - Customer profile
|
|
636
|
+
- `StoreMarket` - Market configuration (currency, locales, countries)
|
|
611
637
|
- `StoreStore` - Store configuration
|
|
612
638
|
|
|
613
639
|
### Commerce Types
|
package/dist/index.cjs
CHANGED
|
@@ -21,20 +21,23 @@ function calculateDelay(attempt, config) {
|
|
|
21
21
|
function sleep(ms) {
|
|
22
22
|
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
23
23
|
}
|
|
24
|
-
function
|
|
25
|
-
|
|
24
|
+
function generateIdempotencyKey() {
|
|
25
|
+
return `spree-sdk-retry-${crypto.randomUUID()}`;
|
|
26
|
+
}
|
|
27
|
+
function shouldRetryOnStatus(method, status, config, hasIdempotencyKey) {
|
|
28
|
+
const isIdempotent = method === "GET" || method === "HEAD" || hasIdempotencyKey;
|
|
26
29
|
if (isIdempotent) {
|
|
27
30
|
return config.retryOnStatus.includes(status);
|
|
28
31
|
}
|
|
29
32
|
return status === 429;
|
|
30
33
|
}
|
|
31
|
-
function shouldRetryOnNetworkError(method, config) {
|
|
34
|
+
function shouldRetryOnNetworkError(method, config, hasIdempotencyKey) {
|
|
32
35
|
if (!config.retryOnNetworkError) return false;
|
|
33
|
-
return method === "GET" || method === "HEAD";
|
|
36
|
+
return method === "GET" || method === "HEAD" || hasIdempotencyKey;
|
|
34
37
|
}
|
|
35
38
|
function createRequestFn(config, basePath, auth, defaults) {
|
|
36
39
|
return async function request(method, path, options = {}) {
|
|
37
|
-
const { token, orderToken, headers = {}, body, params } = options;
|
|
40
|
+
const { token, orderToken, idempotencyKey, headers = {}, body, params } = options;
|
|
38
41
|
const locale = options.locale ?? defaults?.locale;
|
|
39
42
|
const currency = options.currency ?? defaults?.currency;
|
|
40
43
|
const country = options.country ?? defaults?.country;
|
|
@@ -50,14 +53,13 @@ function createRequestFn(config, basePath, auth, defaults) {
|
|
|
50
53
|
}
|
|
51
54
|
});
|
|
52
55
|
}
|
|
53
|
-
if (orderToken) {
|
|
54
|
-
url.searchParams.set("order_token", orderToken);
|
|
55
|
-
}
|
|
56
56
|
const requestHeaders = {
|
|
57
57
|
"Content-Type": "application/json",
|
|
58
|
-
[auth.headerName]: auth.headerValue,
|
|
59
58
|
...headers
|
|
60
59
|
};
|
|
60
|
+
if (auth.headerName && auth.headerValue) {
|
|
61
|
+
requestHeaders[auth.headerName] = auth.headerValue;
|
|
62
|
+
}
|
|
61
63
|
if (token) {
|
|
62
64
|
requestHeaders["Authorization"] = `Bearer ${token}`;
|
|
63
65
|
}
|
|
@@ -73,6 +75,12 @@ function createRequestFn(config, basePath, auth, defaults) {
|
|
|
73
75
|
if (country) {
|
|
74
76
|
requestHeaders["x-spree-country"] = country;
|
|
75
77
|
}
|
|
78
|
+
const isMutating = method !== "GET" && method !== "HEAD";
|
|
79
|
+
const effectiveIdempotencyKey = idempotencyKey ?? (isMutating && config.retryConfig ? generateIdempotencyKey() : void 0);
|
|
80
|
+
if (effectiveIdempotencyKey) {
|
|
81
|
+
requestHeaders["Idempotency-Key"] = effectiveIdempotencyKey;
|
|
82
|
+
}
|
|
83
|
+
const hasIdempotencyKey = !!effectiveIdempotencyKey;
|
|
76
84
|
const maxAttempts = config.retryConfig ? config.retryConfig.maxRetries + 1 : 1;
|
|
77
85
|
for (let attempt = 0; attempt < maxAttempts; attempt++) {
|
|
78
86
|
try {
|
|
@@ -83,7 +91,7 @@ function createRequestFn(config, basePath, auth, defaults) {
|
|
|
83
91
|
});
|
|
84
92
|
if (!response.ok) {
|
|
85
93
|
const isLastAttempt = attempt >= maxAttempts - 1;
|
|
86
|
-
if (!isLastAttempt && config.retryConfig && shouldRetryOnStatus(method, response.status, config.retryConfig)) {
|
|
94
|
+
if (!isLastAttempt && config.retryConfig && shouldRetryOnStatus(method, response.status, config.retryConfig, hasIdempotencyKey)) {
|
|
87
95
|
const retryAfter = response.headers.get("Retry-After");
|
|
88
96
|
const delay = retryAfter ? Math.min(parseInt(retryAfter, 10) * 1e3, config.retryConfig.maxDelay) : calculateDelay(attempt, config.retryConfig);
|
|
89
97
|
await sleep(delay);
|
|
@@ -101,7 +109,7 @@ function createRequestFn(config, basePath, auth, defaults) {
|
|
|
101
109
|
throw error;
|
|
102
110
|
}
|
|
103
111
|
const isLastAttempt = attempt >= maxAttempts - 1;
|
|
104
|
-
if (!isLastAttempt && config.retryConfig && shouldRetryOnNetworkError(method, config.retryConfig)) {
|
|
112
|
+
if (!isLastAttempt && config.retryConfig && shouldRetryOnNetworkError(method, config.retryConfig, hasIdempotencyKey)) {
|
|
105
113
|
const delay = calculateDelay(attempt, config.retryConfig);
|
|
106
114
|
await sleep(delay);
|
|
107
115
|
continue;
|
|
@@ -138,6 +146,10 @@ function transformListParams(params) {
|
|
|
138
146
|
}
|
|
139
147
|
|
|
140
148
|
// src/store-client.ts
|
|
149
|
+
function expandParams(params) {
|
|
150
|
+
if (!params?.expand?.length) return void 0;
|
|
151
|
+
return { expand: params.expand.join(",") };
|
|
152
|
+
}
|
|
141
153
|
var StoreClient = class {
|
|
142
154
|
request;
|
|
143
155
|
constructor(request) {
|
|
@@ -161,15 +173,6 @@ var StoreClient = class {
|
|
|
161
173
|
refresh: (options) => this.request("POST", "/auth/refresh", options)
|
|
162
174
|
};
|
|
163
175
|
// ============================================
|
|
164
|
-
// Store
|
|
165
|
-
// ============================================
|
|
166
|
-
store = {
|
|
167
|
-
/**
|
|
168
|
-
* Get current store information
|
|
169
|
-
*/
|
|
170
|
-
get: (options) => this.request("GET", "/store", options)
|
|
171
|
-
};
|
|
172
|
-
// ============================================
|
|
173
176
|
// Products
|
|
174
177
|
// ============================================
|
|
175
178
|
products = {
|
|
@@ -178,14 +181,14 @@ var StoreClient = class {
|
|
|
178
181
|
*/
|
|
179
182
|
list: (params, options) => this.request("GET", "/products", {
|
|
180
183
|
...options,
|
|
181
|
-
params:
|
|
184
|
+
params: transformListParams({ ...params })
|
|
182
185
|
}),
|
|
183
186
|
/**
|
|
184
187
|
* Get a product by ID or slug
|
|
185
188
|
*/
|
|
186
189
|
get: (idOrSlug, params, options) => this.request("GET", `/products/${idOrSlug}`, {
|
|
187
190
|
...options,
|
|
188
|
-
params
|
|
191
|
+
params: expandParams(params)
|
|
189
192
|
}),
|
|
190
193
|
/**
|
|
191
194
|
* Get available filters for products
|
|
@@ -205,14 +208,14 @@ var StoreClient = class {
|
|
|
205
208
|
*/
|
|
206
209
|
list: (params, options) => this.request("GET", "/taxonomies", {
|
|
207
210
|
...options,
|
|
208
|
-
params
|
|
211
|
+
params: transformListParams({ ...params })
|
|
209
212
|
}),
|
|
210
213
|
/**
|
|
211
214
|
* Get a taxonomy by ID
|
|
212
215
|
*/
|
|
213
216
|
get: (id, params, options) => this.request("GET", `/taxonomies/${id}`, {
|
|
214
217
|
...options,
|
|
215
|
-
params
|
|
218
|
+
params: expandParams(params)
|
|
216
219
|
})
|
|
217
220
|
};
|
|
218
221
|
taxons = {
|
|
@@ -221,14 +224,14 @@ var StoreClient = class {
|
|
|
221
224
|
*/
|
|
222
225
|
list: (params, options) => this.request("GET", "/taxons", {
|
|
223
226
|
...options,
|
|
224
|
-
params:
|
|
227
|
+
params: transformListParams({ ...params })
|
|
225
228
|
}),
|
|
226
229
|
/**
|
|
227
230
|
* Get a taxon by ID or permalink
|
|
228
231
|
*/
|
|
229
232
|
get: (idOrPermalink, params, options) => this.request("GET", `/taxons/${idOrPermalink}`, {
|
|
230
233
|
...options,
|
|
231
|
-
params
|
|
234
|
+
params: expandParams(params)
|
|
232
235
|
}),
|
|
233
236
|
/**
|
|
234
237
|
* Nested resource: Products in a taxon
|
|
@@ -243,7 +246,7 @@ var StoreClient = class {
|
|
|
243
246
|
`/taxons/${taxonId}/products`,
|
|
244
247
|
{
|
|
245
248
|
...options,
|
|
246
|
-
params:
|
|
249
|
+
params: transformListParams({ ...params })
|
|
247
250
|
}
|
|
248
251
|
)
|
|
249
252
|
}
|
|
@@ -264,7 +267,7 @@ var StoreClient = class {
|
|
|
264
267
|
*/
|
|
265
268
|
get: (iso, params, options) => this.request("GET", `/countries/${iso}`, {
|
|
266
269
|
...options,
|
|
267
|
-
params
|
|
270
|
+
params: expandParams(params)
|
|
268
271
|
})
|
|
269
272
|
};
|
|
270
273
|
currencies = {
|
|
@@ -321,7 +324,7 @@ var StoreClient = class {
|
|
|
321
324
|
get: (marketId, iso, params, options) => this.request(
|
|
322
325
|
"GET",
|
|
323
326
|
`/markets/${marketId}/countries/${iso}`,
|
|
324
|
-
{ ...options, params }
|
|
327
|
+
{ ...options, params: expandParams(params) }
|
|
325
328
|
)
|
|
326
329
|
}
|
|
327
330
|
};
|
|
@@ -358,7 +361,7 @@ var StoreClient = class {
|
|
|
358
361
|
*/
|
|
359
362
|
get: (idOrNumber, params, options) => this.request("GET", `/orders/${idOrNumber}`, {
|
|
360
363
|
...options,
|
|
361
|
-
params
|
|
364
|
+
params: expandParams(params)
|
|
362
365
|
}),
|
|
363
366
|
/**
|
|
364
367
|
* Update an order
|
|
@@ -576,7 +579,7 @@ var StoreClient = class {
|
|
|
576
579
|
list: (params, options) => this.request(
|
|
577
580
|
"GET",
|
|
578
581
|
"/customer/addresses",
|
|
579
|
-
{ ...options, params }
|
|
582
|
+
{ ...options, params: transformListParams({ ...params }) }
|
|
580
583
|
),
|
|
581
584
|
/**
|
|
582
585
|
* Get an address by ID
|
|
@@ -618,7 +621,7 @@ var StoreClient = class {
|
|
|
618
621
|
list: (params, options) => this.request(
|
|
619
622
|
"GET",
|
|
620
623
|
"/customer/credit_cards",
|
|
621
|
-
{ ...options, params }
|
|
624
|
+
{ ...options, params: transformListParams({ ...params }) }
|
|
622
625
|
),
|
|
623
626
|
/**
|
|
624
627
|
* Get a credit card by ID
|
|
@@ -640,7 +643,7 @@ var StoreClient = class {
|
|
|
640
643
|
list: (params, options) => this.request(
|
|
641
644
|
"GET",
|
|
642
645
|
"/customer/gift_cards",
|
|
643
|
-
{ ...options, params }
|
|
646
|
+
{ ...options, params: transformListParams({ ...params }) }
|
|
644
647
|
),
|
|
645
648
|
/**
|
|
646
649
|
* Get a gift card by ID
|
|
@@ -656,7 +659,7 @@ var StoreClient = class {
|
|
|
656
659
|
*/
|
|
657
660
|
list: (params, options) => this.request("GET", "/customer/orders", {
|
|
658
661
|
...options,
|
|
659
|
-
params:
|
|
662
|
+
params: transformListParams({ ...params })
|
|
660
663
|
})
|
|
661
664
|
},
|
|
662
665
|
/**
|
|
@@ -696,14 +699,14 @@ var StoreClient = class {
|
|
|
696
699
|
*/
|
|
697
700
|
list: (params, options) => this.request("GET", "/wishlists", {
|
|
698
701
|
...options,
|
|
699
|
-
params
|
|
702
|
+
params: transformListParams({ ...params })
|
|
700
703
|
}),
|
|
701
704
|
/**
|
|
702
705
|
* Get a wishlist by ID
|
|
703
706
|
*/
|
|
704
707
|
get: (id, params, options) => this.request("GET", `/wishlists/${id}`, {
|
|
705
708
|
...options,
|
|
706
|
-
params
|
|
709
|
+
params: expandParams(params)
|
|
707
710
|
}),
|
|
708
711
|
/**
|
|
709
712
|
* Create a wishlist
|
|
@@ -772,6 +775,9 @@ var SpreeClient = class {
|
|
|
772
775
|
admin;
|
|
773
776
|
_defaults;
|
|
774
777
|
constructor(config) {
|
|
778
|
+
if (!config.publishableKey && !config.secretKey) {
|
|
779
|
+
throw new Error("SpreeClient requires at least one of publishableKey or secretKey");
|
|
780
|
+
}
|
|
775
781
|
const baseUrl = config.baseUrl.replace(/\/$/, "");
|
|
776
782
|
const fetchFn = config.fetch || fetch.bind(globalThis);
|
|
777
783
|
this._defaults = {
|
|
@@ -795,7 +801,7 @@ var SpreeClient = class {
|
|
|
795
801
|
const storeRequestFn = createRequestFn(
|
|
796
802
|
requestConfig,
|
|
797
803
|
"/api/v3/store",
|
|
798
|
-
{ headerName: "x-spree-api-key", headerValue: config.publishableKey },
|
|
804
|
+
config.publishableKey ? { headerName: "x-spree-api-key", headerValue: config.publishableKey } : { headerName: "", headerValue: "" },
|
|
799
805
|
this._defaults
|
|
800
806
|
);
|
|
801
807
|
const adminRequestFn = createRequestFn(
|