@spree/sdk 0.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/LICENSE +21 -0
- package/README.md +649 -0
- package/dist/index.cjs +534 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +389 -0
- package/dist/index.d.ts +389 -0
- package/dist/index.js +530 -0
- package/dist/index.js.map +1 -0
- package/dist/types/index.cjs +4 -0
- package/dist/types/index.cjs.map +1 -0
- package/dist/types/index.d.cts +786 -0
- package/dist/types/index.d.ts +786 -0
- package/dist/types/index.js +3 -0
- package/dist/types/index.js.map +1 -0
- package/package.json +91 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Vendo Connect Inc., Vendo Sp. z o.o.
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,649 @@
|
|
|
1
|
+
# @spree/sdk
|
|
2
|
+
|
|
3
|
+
Official TypeScript SDK for Spree Commerce API v3.
|
|
4
|
+
|
|
5
|
+
## Installation
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
npm install @spree/sdk
|
|
9
|
+
# or
|
|
10
|
+
yarn add @spree/sdk
|
|
11
|
+
# or
|
|
12
|
+
pnpm add @spree/sdk
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Quick Start
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
import { createSpreeClient } from '@spree/sdk';
|
|
19
|
+
|
|
20
|
+
// Initialize the client
|
|
21
|
+
const client = createSpreeClient({
|
|
22
|
+
baseUrl: 'https://api.mystore.com',
|
|
23
|
+
apiKey: 'your-publishable-api-key',
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
// Browse products
|
|
27
|
+
const products = await client.products.list({
|
|
28
|
+
per_page: 10,
|
|
29
|
+
includes: 'variants,images',
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
// Get a single product
|
|
33
|
+
const product = await client.products.get('ruby-on-rails-tote');
|
|
34
|
+
|
|
35
|
+
// Authentication
|
|
36
|
+
const { token, user } = await client.auth.login({
|
|
37
|
+
email: 'customer@example.com',
|
|
38
|
+
password: 'password123',
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
// Create a cart and add items
|
|
42
|
+
const cart = await client.cart.create();
|
|
43
|
+
await client.orders.lineItems.create(cart.id, {
|
|
44
|
+
variant_id: 'var_abc123',
|
|
45
|
+
quantity: 2,
|
|
46
|
+
}, { orderToken: cart.token });
|
|
47
|
+
|
|
48
|
+
// Checkout flow
|
|
49
|
+
await client.orders.next(cart.id, { orderToken: cart.token });
|
|
50
|
+
await client.orders.complete(cart.id, { orderToken: cart.token });
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
## Authentication
|
|
54
|
+
|
|
55
|
+
The SDK supports multiple authentication modes:
|
|
56
|
+
|
|
57
|
+
### 1. API Key Only (Guest/Public Access)
|
|
58
|
+
|
|
59
|
+
```typescript
|
|
60
|
+
const client = createSpreeClient({
|
|
61
|
+
baseUrl: 'https://api.mystore.com',
|
|
62
|
+
apiKey: 'pk_your-publishable-key',
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
// Public endpoints work without user authentication
|
|
66
|
+
const products = await client.products.list();
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
### 2. API Key + JWT (Authenticated Customer)
|
|
70
|
+
|
|
71
|
+
```typescript
|
|
72
|
+
// Login to get tokens
|
|
73
|
+
const { token, user } = await client.auth.login({
|
|
74
|
+
email: 'customer@example.com',
|
|
75
|
+
password: 'password123',
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// Use token for authenticated requests
|
|
79
|
+
const orders = await client.orders.list({}, { token });
|
|
80
|
+
|
|
81
|
+
// Refresh token when needed
|
|
82
|
+
const newTokens = await client.auth.refresh({ token });
|
|
83
|
+
```
|
|
84
|
+
|
|
85
|
+
### 3. Register New Customer
|
|
86
|
+
|
|
87
|
+
```typescript
|
|
88
|
+
const { token, user } = await client.auth.register({
|
|
89
|
+
email: 'new@example.com',
|
|
90
|
+
password: 'password123',
|
|
91
|
+
password_confirmation: 'password123',
|
|
92
|
+
first_name: 'John',
|
|
93
|
+
last_name: 'Doe',
|
|
94
|
+
});
|
|
95
|
+
```
|
|
96
|
+
|
|
97
|
+
## Guest Checkout
|
|
98
|
+
|
|
99
|
+
For guest checkout, use the `token` (or `order_token`) returned when creating a cart:
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
// Create a cart (guest)
|
|
103
|
+
const cart = await client.cart.create();
|
|
104
|
+
|
|
105
|
+
// Use orderToken for all cart operations
|
|
106
|
+
const options = { orderToken: cart.token };
|
|
107
|
+
|
|
108
|
+
// Add items
|
|
109
|
+
await client.orders.lineItems.create(cart.id, {
|
|
110
|
+
variant_id: 'var_abc123',
|
|
111
|
+
quantity: 1,
|
|
112
|
+
}, options);
|
|
113
|
+
|
|
114
|
+
// Update order with email
|
|
115
|
+
await client.orders.update(cart.id, {
|
|
116
|
+
email: 'guest@example.com',
|
|
117
|
+
}, options);
|
|
118
|
+
|
|
119
|
+
// Complete checkout
|
|
120
|
+
await client.orders.complete(cart.id, options);
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## API Reference
|
|
124
|
+
|
|
125
|
+
### Store
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
// Get current store information
|
|
129
|
+
const store = await client.store.get();
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
### Products
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
// List products with filtering
|
|
136
|
+
const products = await client.products.list({
|
|
137
|
+
page: 1,
|
|
138
|
+
per_page: 25,
|
|
139
|
+
'q[name_cont]': 'shirt',
|
|
140
|
+
includes: 'variants,images,taxons',
|
|
141
|
+
});
|
|
142
|
+
|
|
143
|
+
// Get single product by ID or slug
|
|
144
|
+
const product = await client.products.get('ruby-on-rails-tote', {
|
|
145
|
+
includes: 'variants,images',
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Get available filters (price range, availability, options, taxons)
|
|
149
|
+
const filters = await client.products.filters({
|
|
150
|
+
taxon_id: 'txn_abc123', // Optional: scope filters to a taxon
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
### Categories (Taxonomies & Taxons)
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
// List taxonomies
|
|
158
|
+
const taxonomies = await client.taxonomies.list({
|
|
159
|
+
includes: 'taxons',
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Get taxonomy with taxons
|
|
163
|
+
const categories = await client.taxonomies.get('tax_123', {
|
|
164
|
+
includes: 'root,taxons',
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
// List taxons with filtering
|
|
168
|
+
const taxons = await client.taxons.list({
|
|
169
|
+
'q[depth_eq]': 1, // Top-level categories only
|
|
170
|
+
'q[taxonomy_id_eq]': '123', // Filter by taxonomy
|
|
171
|
+
});
|
|
172
|
+
|
|
173
|
+
// Get single taxon by ID or permalink
|
|
174
|
+
const taxon = await client.taxons.get('categories/clothing', {
|
|
175
|
+
includes: 'ancestors,children', // For breadcrumbs and subcategories
|
|
176
|
+
});
|
|
177
|
+
|
|
178
|
+
// List products in a category
|
|
179
|
+
const categoryProducts = await client.taxons.products.list('categories/clothing', {
|
|
180
|
+
page: 1,
|
|
181
|
+
per_page: 12,
|
|
182
|
+
includes: 'images,default_variant',
|
|
183
|
+
});
|
|
184
|
+
```
|
|
185
|
+
|
|
186
|
+
### Cart
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
// Get current cart
|
|
190
|
+
const cart = await client.cart.get({ orderToken: 'xxx' });
|
|
191
|
+
|
|
192
|
+
// Create a new cart
|
|
193
|
+
const newCart = await client.cart.create();
|
|
194
|
+
|
|
195
|
+
// Associate guest cart with authenticated user
|
|
196
|
+
// (after user logs in, merge their guest cart with their account)
|
|
197
|
+
await client.cart.associate({
|
|
198
|
+
token: jwtToken, // User's JWT token
|
|
199
|
+
orderToken: cart.token, // Guest cart token
|
|
200
|
+
});
|
|
201
|
+
```
|
|
202
|
+
|
|
203
|
+
### Orders & Checkout
|
|
204
|
+
|
|
205
|
+
```typescript
|
|
206
|
+
// List orders for authenticated customer
|
|
207
|
+
const orders = await client.orders.list({}, { token });
|
|
208
|
+
|
|
209
|
+
// Create a new order (cart)
|
|
210
|
+
const cart = await client.orders.create();
|
|
211
|
+
const options = { orderToken: cart.order_token };
|
|
212
|
+
|
|
213
|
+
// Get order by ID or number
|
|
214
|
+
const order = await client.orders.get('R123456789', {
|
|
215
|
+
includes: 'line_items,shipments',
|
|
216
|
+
}, options);
|
|
217
|
+
|
|
218
|
+
// Update order (email, addresses)
|
|
219
|
+
await client.orders.update(cart.id, {
|
|
220
|
+
email: 'customer@example.com',
|
|
221
|
+
ship_address: {
|
|
222
|
+
firstname: 'John',
|
|
223
|
+
lastname: 'Doe',
|
|
224
|
+
address1: '123 Main St',
|
|
225
|
+
city: 'New York',
|
|
226
|
+
zipcode: '10001',
|
|
227
|
+
phone: '+1 555 123 4567',
|
|
228
|
+
country_iso: 'US',
|
|
229
|
+
state_abbr: 'NY',
|
|
230
|
+
},
|
|
231
|
+
bill_address_id: 'addr_xxx', // Or use existing address by ID
|
|
232
|
+
}, options);
|
|
233
|
+
|
|
234
|
+
// Checkout flow
|
|
235
|
+
await client.orders.next(cart.id, options); // Move to next step
|
|
236
|
+
await client.orders.advance(cart.id, options); // Advance through all steps
|
|
237
|
+
await client.orders.complete(cart.id, options); // Complete the order
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
### Line Items
|
|
241
|
+
|
|
242
|
+
```typescript
|
|
243
|
+
const options = { orderToken: cart.token };
|
|
244
|
+
|
|
245
|
+
// Add item
|
|
246
|
+
await client.orders.lineItems.create(cart.id, {
|
|
247
|
+
variant_id: 'var_123',
|
|
248
|
+
quantity: 2,
|
|
249
|
+
}, options);
|
|
250
|
+
|
|
251
|
+
// Update item quantity
|
|
252
|
+
await client.orders.lineItems.update(cart.id, lineItemId, {
|
|
253
|
+
quantity: 3,
|
|
254
|
+
}, options);
|
|
255
|
+
|
|
256
|
+
// Remove item
|
|
257
|
+
await client.orders.lineItems.delete(cart.id, lineItemId, options);
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### Coupon Codes
|
|
261
|
+
|
|
262
|
+
```typescript
|
|
263
|
+
const options = { orderToken: cart.token };
|
|
264
|
+
|
|
265
|
+
// Apply a coupon code
|
|
266
|
+
await client.orders.couponCodes.apply(cart.id, 'SAVE20', options);
|
|
267
|
+
|
|
268
|
+
// Remove a coupon code
|
|
269
|
+
await client.orders.couponCodes.remove(cart.id, 'promo_xxx', options);
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
### Store Credits
|
|
273
|
+
|
|
274
|
+
```typescript
|
|
275
|
+
const options = { orderToken: cart.token };
|
|
276
|
+
|
|
277
|
+
// Apply store credit to order (applies maximum available by default)
|
|
278
|
+
await client.orders.addStoreCredit(cart.id, undefined, options);
|
|
279
|
+
|
|
280
|
+
// Apply specific amount of store credit
|
|
281
|
+
await client.orders.addStoreCredit(cart.id, 25.00, options);
|
|
282
|
+
|
|
283
|
+
// Remove store credit from order
|
|
284
|
+
await client.orders.removeStoreCredit(cart.id, options);
|
|
285
|
+
```
|
|
286
|
+
|
|
287
|
+
### Shipments
|
|
288
|
+
|
|
289
|
+
```typescript
|
|
290
|
+
const options = { orderToken: cart.token };
|
|
291
|
+
|
|
292
|
+
// List shipments for an order
|
|
293
|
+
const shipments = await client.orders.shipments.list(cart.id, options);
|
|
294
|
+
|
|
295
|
+
// Select a shipping rate
|
|
296
|
+
await client.orders.shipments.update(cart.id, shipmentId, {
|
|
297
|
+
selected_shipping_rate_id: 'rate_xxx',
|
|
298
|
+
}, options);
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Payments
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
const options = { orderToken: cart.token };
|
|
305
|
+
|
|
306
|
+
// Get available payment methods for an order
|
|
307
|
+
const methods = await client.orders.paymentMethods.list(cart.id, options);
|
|
308
|
+
|
|
309
|
+
// List payments on an order
|
|
310
|
+
const payments = await client.orders.payments.list(cart.id, options);
|
|
311
|
+
|
|
312
|
+
// Get a specific payment
|
|
313
|
+
const payment = await client.orders.payments.get(cart.id, paymentId, options);
|
|
314
|
+
```
|
|
315
|
+
|
|
316
|
+
### Geography
|
|
317
|
+
|
|
318
|
+
```typescript
|
|
319
|
+
// List countries available for checkout
|
|
320
|
+
const { data: countries } = await client.countries.list();
|
|
321
|
+
|
|
322
|
+
// Get country by ISO code (includes states)
|
|
323
|
+
const usa = await client.countries.get('US');
|
|
324
|
+
console.log(usa.states); // Array of states
|
|
325
|
+
```
|
|
326
|
+
|
|
327
|
+
### Customer Account
|
|
328
|
+
|
|
329
|
+
```typescript
|
|
330
|
+
const options = { token: jwtToken };
|
|
331
|
+
|
|
332
|
+
// Get profile
|
|
333
|
+
const profile = await client.customer.get(options);
|
|
334
|
+
|
|
335
|
+
// Update profile
|
|
336
|
+
await client.customer.update({
|
|
337
|
+
first_name: 'John',
|
|
338
|
+
last_name: 'Doe',
|
|
339
|
+
}, options);
|
|
340
|
+
```
|
|
341
|
+
|
|
342
|
+
### Customer Addresses
|
|
343
|
+
|
|
344
|
+
```typescript
|
|
345
|
+
const options = { token: jwtToken };
|
|
346
|
+
|
|
347
|
+
// List addresses
|
|
348
|
+
const { data: addresses } = await client.customer.addresses.list({}, options);
|
|
349
|
+
|
|
350
|
+
// Get address by ID
|
|
351
|
+
const address = await client.customer.addresses.get('addr_xxx', options);
|
|
352
|
+
|
|
353
|
+
// Create address
|
|
354
|
+
await client.customer.addresses.create({
|
|
355
|
+
firstname: 'John',
|
|
356
|
+
lastname: 'Doe',
|
|
357
|
+
address1: '123 Main St',
|
|
358
|
+
city: 'New York',
|
|
359
|
+
zipcode: '10001',
|
|
360
|
+
country_iso: 'US',
|
|
361
|
+
state_abbr: 'NY',
|
|
362
|
+
}, options);
|
|
363
|
+
|
|
364
|
+
// Update address
|
|
365
|
+
await client.customer.addresses.update('addr_xxx', { city: 'Brooklyn' }, options);
|
|
366
|
+
|
|
367
|
+
// Delete address
|
|
368
|
+
await client.customer.addresses.delete('addr_xxx', options);
|
|
369
|
+
```
|
|
370
|
+
|
|
371
|
+
### Customer Credit Cards
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
const options = { token: jwtToken };
|
|
375
|
+
|
|
376
|
+
// List saved credit cards
|
|
377
|
+
const { data: cards } = await client.customer.creditCards.list({}, options);
|
|
378
|
+
|
|
379
|
+
// Get credit card by ID
|
|
380
|
+
const card = await client.customer.creditCards.get('cc_xxx', options);
|
|
381
|
+
|
|
382
|
+
// Delete credit card
|
|
383
|
+
await client.customer.creditCards.delete('cc_xxx', options);
|
|
384
|
+
```
|
|
385
|
+
|
|
386
|
+
### Wishlists
|
|
387
|
+
|
|
388
|
+
```typescript
|
|
389
|
+
const options = { token: jwtToken };
|
|
390
|
+
|
|
391
|
+
// List wishlists
|
|
392
|
+
const { data: wishlists } = await client.wishlists.list({}, options);
|
|
393
|
+
|
|
394
|
+
// Get wishlist by ID
|
|
395
|
+
const wishlist = await client.wishlists.get('wl_xxx', {
|
|
396
|
+
includes: 'wished_items',
|
|
397
|
+
}, options);
|
|
398
|
+
|
|
399
|
+
// Create wishlist
|
|
400
|
+
const newWishlist = await client.wishlists.create({
|
|
401
|
+
name: 'Birthday Ideas',
|
|
402
|
+
is_private: true,
|
|
403
|
+
}, options);
|
|
404
|
+
|
|
405
|
+
// Update wishlist
|
|
406
|
+
await client.wishlists.update('wl_xxx', {
|
|
407
|
+
name: 'Updated Name',
|
|
408
|
+
}, options);
|
|
409
|
+
|
|
410
|
+
// Delete wishlist
|
|
411
|
+
await client.wishlists.delete('wl_xxx', options);
|
|
412
|
+
```
|
|
413
|
+
|
|
414
|
+
### Wishlist Items
|
|
415
|
+
|
|
416
|
+
```typescript
|
|
417
|
+
const options = { token: jwtToken };
|
|
418
|
+
|
|
419
|
+
// Add item to wishlist
|
|
420
|
+
await client.wishlists.items.create('wl_xxx', {
|
|
421
|
+
variant_id: 'var_123',
|
|
422
|
+
quantity: 1,
|
|
423
|
+
}, options);
|
|
424
|
+
|
|
425
|
+
// Update item quantity
|
|
426
|
+
await client.wishlists.items.update('wl_xxx', 'wi_xxx', {
|
|
427
|
+
quantity: 2,
|
|
428
|
+
}, options);
|
|
429
|
+
|
|
430
|
+
// Remove item from wishlist
|
|
431
|
+
await client.wishlists.items.delete('wl_xxx', 'wi_xxx', options);
|
|
432
|
+
```
|
|
433
|
+
|
|
434
|
+
## Nested Resources
|
|
435
|
+
|
|
436
|
+
The SDK uses a resource builder pattern for nested resources:
|
|
437
|
+
|
|
438
|
+
| Parent Resource | Nested Resource | Available Methods |
|
|
439
|
+
|-----------------|-----------------|-------------------|
|
|
440
|
+
| `orders` | `lineItems` | `create`, `update`, `delete` |
|
|
441
|
+
| `orders` | `payments` | `list`, `get` |
|
|
442
|
+
| `orders` | `paymentMethods` | `list` |
|
|
443
|
+
| `orders` | `shipments` | `list`, `update` |
|
|
444
|
+
| `orders` | `couponCodes` | `apply`, `remove` |
|
|
445
|
+
| `customer` | `addresses` | `list`, `get`, `create`, `update`, `delete` |
|
|
446
|
+
| `customer` | `creditCards` | `list`, `get`, `delete` |
|
|
447
|
+
| `taxons` | `products` | `list` |
|
|
448
|
+
| `wishlists` | `items` | `create`, `update`, `delete` |
|
|
449
|
+
|
|
450
|
+
Example:
|
|
451
|
+
```typescript
|
|
452
|
+
// Nested resources follow the pattern: client.parent.nested.method(parentId, ...)
|
|
453
|
+
await client.orders.lineItems.create(orderId, params, options);
|
|
454
|
+
await client.orders.payments.list(orderId, options);
|
|
455
|
+
await client.orders.shipments.update(orderId, shipmentId, params, options);
|
|
456
|
+
await client.customer.addresses.list({}, options);
|
|
457
|
+
await client.taxons.products.list(taxonId, params, options);
|
|
458
|
+
await client.wishlists.items.create(wishlistId, params, options);
|
|
459
|
+
```
|
|
460
|
+
|
|
461
|
+
## Localization & Currency
|
|
462
|
+
|
|
463
|
+
Pass locale and currency headers with any request:
|
|
464
|
+
|
|
465
|
+
```typescript
|
|
466
|
+
// Set locale and currency per request
|
|
467
|
+
const products = await client.products.list({}, {
|
|
468
|
+
locale: 'fr',
|
|
469
|
+
currency: 'EUR',
|
|
470
|
+
});
|
|
471
|
+
|
|
472
|
+
// Works with all endpoints
|
|
473
|
+
const taxon = await client.taxons.get('categories/clothing', {
|
|
474
|
+
includes: 'ancestors',
|
|
475
|
+
}, {
|
|
476
|
+
locale: 'de',
|
|
477
|
+
currency: 'EUR',
|
|
478
|
+
});
|
|
479
|
+
```
|
|
480
|
+
|
|
481
|
+
## Error Handling
|
|
482
|
+
|
|
483
|
+
```typescript
|
|
484
|
+
import { SpreeError } from '@spree/sdk';
|
|
485
|
+
|
|
486
|
+
try {
|
|
487
|
+
await client.products.get('non-existent');
|
|
488
|
+
} catch (error) {
|
|
489
|
+
if (error instanceof SpreeError) {
|
|
490
|
+
console.log(error.code); // 'record_not_found'
|
|
491
|
+
console.log(error.message); // 'Product not found'
|
|
492
|
+
console.log(error.status); // 404
|
|
493
|
+
console.log(error.details); // Validation errors (if any)
|
|
494
|
+
}
|
|
495
|
+
}
|
|
496
|
+
```
|
|
497
|
+
|
|
498
|
+
## TypeScript Support
|
|
499
|
+
|
|
500
|
+
The SDK includes full TypeScript support with generated types from the API serializers:
|
|
501
|
+
|
|
502
|
+
```typescript
|
|
503
|
+
import type {
|
|
504
|
+
StoreProduct,
|
|
505
|
+
StoreOrder,
|
|
506
|
+
StoreVariant,
|
|
507
|
+
StoreTaxon,
|
|
508
|
+
StoreTaxonomy,
|
|
509
|
+
StoreLineItem,
|
|
510
|
+
StoreAddress,
|
|
511
|
+
StoreUser,
|
|
512
|
+
PaginatedResponse,
|
|
513
|
+
} from '@spree/sdk';
|
|
514
|
+
|
|
515
|
+
// All responses are fully typed
|
|
516
|
+
const products: PaginatedResponse<StoreProduct> = await client.products.list();
|
|
517
|
+
const taxon: StoreTaxon = await client.taxons.get('clothing');
|
|
518
|
+
```
|
|
519
|
+
|
|
520
|
+
## Available Types
|
|
521
|
+
|
|
522
|
+
The SDK exports all Store API types:
|
|
523
|
+
|
|
524
|
+
### Core Types
|
|
525
|
+
- `StoreProduct` - Product data
|
|
526
|
+
- `StoreVariant` - Variant data
|
|
527
|
+
- `StoreOrder` - Order/cart data
|
|
528
|
+
- `StoreLineItem` - Line item in cart
|
|
529
|
+
- `StoreTaxonomy` - Category group
|
|
530
|
+
- `StoreTaxon` - Individual category
|
|
531
|
+
- `StoreCountry` - Country with states
|
|
532
|
+
- `StoreState` - State/province
|
|
533
|
+
- `StoreAddress` - Customer address
|
|
534
|
+
- `StoreUser` - Customer profile
|
|
535
|
+
- `StoreStore` - Store configuration
|
|
536
|
+
|
|
537
|
+
### Commerce Types
|
|
538
|
+
- `StorePayment` - Payment record
|
|
539
|
+
- `StorePaymentMethod` - Payment method
|
|
540
|
+
- `StoreShipment` - Shipment record
|
|
541
|
+
- `StoreShippingRate` - Shipping rate option
|
|
542
|
+
- `StoreShippingMethod` - Shipping method
|
|
543
|
+
- `StoreCreditCard` - Saved credit card
|
|
544
|
+
|
|
545
|
+
### Product Types
|
|
546
|
+
- `StoreImage` - Product image
|
|
547
|
+
- `StorePrice` - Price data
|
|
548
|
+
- `StoreOptionType` - Option type (e.g., Size, Color)
|
|
549
|
+
- `StoreOptionValue` - Option value (e.g., Small, Red)
|
|
550
|
+
- `StoreDigitalLink` - Digital download link
|
|
551
|
+
|
|
552
|
+
### Wishlist Types
|
|
553
|
+
- `StoreWishlist` - Wishlist
|
|
554
|
+
- `StoreWishedItem` - Wishlist item
|
|
555
|
+
|
|
556
|
+
### Utility Types
|
|
557
|
+
- `PaginatedResponse<T>` - Paginated API response
|
|
558
|
+
- `AuthTokens` - JWT tokens from login
|
|
559
|
+
- `AddressParams` - Address input parameters
|
|
560
|
+
- `ProductFiltersResponse` - Product filters response
|
|
561
|
+
|
|
562
|
+
## Custom Fetch
|
|
563
|
+
|
|
564
|
+
You can provide a custom fetch implementation:
|
|
565
|
+
|
|
566
|
+
```typescript
|
|
567
|
+
import { createSpreeClient } from '@spree/sdk';
|
|
568
|
+
|
|
569
|
+
const client = createSpreeClient({
|
|
570
|
+
baseUrl: 'https://api.mystore.com',
|
|
571
|
+
apiKey: 'your-api-key',
|
|
572
|
+
fetch: customFetchImplementation,
|
|
573
|
+
});
|
|
574
|
+
```
|
|
575
|
+
|
|
576
|
+
## Development
|
|
577
|
+
|
|
578
|
+
### Setup
|
|
579
|
+
|
|
580
|
+
```bash
|
|
581
|
+
cd sdk
|
|
582
|
+
npm install
|
|
583
|
+
```
|
|
584
|
+
|
|
585
|
+
### Scripts
|
|
586
|
+
|
|
587
|
+
| Command | Description |
|
|
588
|
+
|---------|-------------|
|
|
589
|
+
| `npm test` | Run tests once |
|
|
590
|
+
| `npm run test:watch` | Run tests in watch mode |
|
|
591
|
+
| `npm run test:coverage` | Run tests with coverage report |
|
|
592
|
+
| `npm run typecheck` | Type-check with `tsc --noEmit` |
|
|
593
|
+
| `npm run build` | Build CJS + ESM bundles with `tsup` |
|
|
594
|
+
| `npm run dev` | Build in watch mode |
|
|
595
|
+
| `npm run console` | Interactive REPL for testing the SDK |
|
|
596
|
+
|
|
597
|
+
### Testing
|
|
598
|
+
|
|
599
|
+
Tests use [Vitest](https://vitest.dev/) with [MSW](https://mswjs.io/) (Mock Service Worker) for API mocking at the network level.
|
|
600
|
+
|
|
601
|
+
```bash
|
|
602
|
+
# Run all tests
|
|
603
|
+
npm test
|
|
604
|
+
|
|
605
|
+
# Run in watch mode during development
|
|
606
|
+
npm run test:watch
|
|
607
|
+
|
|
608
|
+
# Run with coverage
|
|
609
|
+
npm run test:coverage
|
|
610
|
+
```
|
|
611
|
+
|
|
612
|
+
Test files live in `tests/` and follow the structure:
|
|
613
|
+
|
|
614
|
+
- `tests/mocks/handlers.ts` - MSW request handlers with fixture data
|
|
615
|
+
- `tests/mocks/server.ts` - MSW server instance
|
|
616
|
+
- `tests/setup.ts` - Server lifecycle (listen/reset/close)
|
|
617
|
+
- `tests/helpers.ts` - `createTestClient()` and constants
|
|
618
|
+
- `tests/*.test.ts` - Test suites per resource (auth, products, orders, etc.)
|
|
619
|
+
|
|
620
|
+
To add tests for a new endpoint, add an MSW handler in `handlers.ts` and create a corresponding test file.
|
|
621
|
+
|
|
622
|
+
### Releasing
|
|
623
|
+
|
|
624
|
+
This package uses [Changesets](https://github.com/changesets/changesets) for version management and publishing.
|
|
625
|
+
|
|
626
|
+
**After making changes:**
|
|
627
|
+
|
|
628
|
+
```bash
|
|
629
|
+
npx changeset
|
|
630
|
+
```
|
|
631
|
+
|
|
632
|
+
This prompts you to select a semver bump type (patch/minor/major) and write a summary. A changeset file is created in `.changeset/`.
|
|
633
|
+
|
|
634
|
+
**How releases work:**
|
|
635
|
+
|
|
636
|
+
1. Changeset files are committed with your PR
|
|
637
|
+
2. When merged to `main`, a GitHub Action creates a "Version Packages" PR that bumps the version and updates the CHANGELOG
|
|
638
|
+
3. When that PR is merged, the package is automatically published to npm
|
|
639
|
+
|
|
640
|
+
**Manual release (if needed):**
|
|
641
|
+
|
|
642
|
+
```bash
|
|
643
|
+
npm run version # Apply changesets and bump version
|
|
644
|
+
npm run release # Build and publish to npm
|
|
645
|
+
```
|
|
646
|
+
|
|
647
|
+
## License
|
|
648
|
+
|
|
649
|
+
BSD-3-Clause
|