@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/dist/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ErrorResponse, LoginCredentials, AuthTokens, RegisterParams, StoreStore, ProductListParams, PaginatedResponse, StoreProduct, ProductFiltersParams, ProductFiltersResponse, ListParams, StoreTaxonomy, TaxonListParams, StoreTaxon, StoreCountry, StoreCurrency, StoreLocale, StoreMarket, StoreOrder, CreateCartParams, UpdateOrderParams, AddLineItemParams, UpdateLineItemParams, StorePayment, StorePaymentMethod, CreatePaymentSessionParams, StorePaymentSession, UpdatePaymentSessionParams, CompletePaymentSessionParams, StoreShipment, StoreCustomer, StoreAddress, AddressParams, StoreCreditCard, StoreGiftCard, OrderListParams, CreatePaymentSetupSessionParams, StorePaymentSetupSession, CompletePaymentSetupSessionParams, StoreWishlist, StoreWishedItem } from './types/index.js';
1
+ import { ErrorResponse, LoginCredentials, AuthTokens, RegisterParams, ProductListParams, PaginatedResponse, StoreProduct, ProductFiltersParams, ProductFiltersResponse, ListParams, StoreTaxonomy, TaxonListParams, StoreTaxon, ListResponse, StoreCountry, StoreCurrency, StoreLocale, StoreMarket, StoreOrder, CreateCartParams, UpdateOrderParams, AddLineItemParams, UpdateLineItemParams, StorePayment, StorePaymentMethod, CreatePaymentSessionParams, StorePaymentSession, UpdatePaymentSessionParams, CompletePaymentSessionParams, StoreShipment, StoreCustomer, StoreAddress, AddressParams, StoreCreditCard, StoreGiftCard, OrderListParams, CreatePaymentSetupSessionParams, StorePaymentSetupSession, CompletePaymentSetupSessionParams, StoreWishlist, StoreWishedItem } from './types/index.js';
2
2
  export { AdminCustomer, AdminLineItem, AdminMetafield, AdminOrder, AdminPrice, AdminProduct, AdminTaxon, AdminTaxonomy, AdminVariant, AvailabilityFilter, FilterOption, LocaleDefaults, OptionFilter, OptionFilterOption, PaginationMeta, PriceRangeFilter, ProductFilter, SortOption, StoreAsset, StoreBase, StoreCustomerReturn, StoreDigital, StoreDigitalLink, StoreExport, StoreGiftCardBatch, StoreImage, StoreImport, StoreImportRow, StoreInvitation, StoreLineItem, StoreMetafield, StoreNewsletterSubscriber, StoreOptionType, StoreOptionValue, StoreOrderPromotion, StorePaymentSource, StorePrice, StorePromotion, StoreRefund, StoreReimbursement, StoreReport, StoreReturnAuthorization, StoreReturnItem, StoreShippingMethod, StoreShippingRate, StoreState, StoreStockItem, StoreStockLocation, StoreStockMovement, StoreStockTransfer, StoreStoreCredit, StoreVariant, TaxonFilter, TaxonFilterOption } from './types/index.js';
3
3
 
4
4
  interface RetryConfig {
@@ -24,6 +24,8 @@ interface RequestOptions {
24
24
  currency?: string;
25
25
  /** Country ISO code for market resolution (e.g., 'US', 'DE') */
26
26
  country?: string;
27
+ /** Idempotency key for safe retries of mutating requests (max 255 characters) */
28
+ idempotencyKey?: string;
27
29
  /** Custom headers */
28
30
  headers?: Record<string, string>;
29
31
  }
@@ -56,12 +58,6 @@ declare class StoreClient {
56
58
  */
57
59
  refresh: (options: RequestOptions) => Promise<AuthTokens>;
58
60
  };
59
- readonly store: {
60
- /**
61
- * Get current store information
62
- */
63
- get: (options?: RequestOptions) => Promise<StoreStore>;
64
- };
65
61
  readonly products: {
66
62
  /**
67
63
  * List products
@@ -71,7 +67,7 @@ declare class StoreClient {
71
67
  * Get a product by ID or slug
72
68
  */
73
69
  get: (idOrSlug: string, params?: {
74
- expand?: string;
70
+ expand?: string[];
75
71
  }, options?: RequestOptions) => Promise<StoreProduct>;
76
72
  /**
77
73
  * Get available filters for products
@@ -88,7 +84,7 @@ declare class StoreClient {
88
84
  * Get a taxonomy by ID
89
85
  */
90
86
  get: (id: string, params?: {
91
- expand?: string;
87
+ expand?: string[];
92
88
  }, options?: RequestOptions) => Promise<StoreTaxonomy>;
93
89
  };
94
90
  readonly taxons: {
@@ -100,7 +96,7 @@ declare class StoreClient {
100
96
  * Get a taxon by ID or permalink
101
97
  */
102
98
  get: (idOrPermalink: string, params?: {
103
- expand?: string;
99
+ expand?: string[];
104
100
  }, options?: RequestOptions) => Promise<StoreTaxon>;
105
101
  /**
106
102
  * Nested resource: Products in a taxon
@@ -118,41 +114,33 @@ declare class StoreClient {
118
114
  * List countries available in the store
119
115
  * Each country includes currency and default_locale derived from its market
120
116
  */
121
- list: (options?: RequestOptions) => Promise<{
122
- data: StoreCountry[];
123
- }>;
117
+ list: (options?: RequestOptions) => Promise<ListResponse<StoreCountry>>;
124
118
  /**
125
119
  * Get a country by ISO code
126
120
  * Use `?expand=states` to expand states for address forms
127
121
  * @param iso - ISO 3166-1 alpha-2 code (e.g., "US", "DE")
128
122
  */
129
123
  get: (iso: string, params?: {
130
- expand?: string;
124
+ expand?: string[];
131
125
  }, options?: RequestOptions) => Promise<StoreCountry>;
132
126
  };
133
127
  readonly currencies: {
134
128
  /**
135
129
  * List currencies supported by the store (derived from markets)
136
130
  */
137
- list: (options?: RequestOptions) => Promise<{
138
- data: StoreCurrency[];
139
- }>;
131
+ list: (options?: RequestOptions) => Promise<ListResponse<StoreCurrency>>;
140
132
  };
141
133
  readonly locales: {
142
134
  /**
143
135
  * List locales supported by the store (derived from markets)
144
136
  */
145
- list: (options?: RequestOptions) => Promise<{
146
- data: StoreLocale[];
147
- }>;
137
+ list: (options?: RequestOptions) => Promise<ListResponse<StoreLocale>>;
148
138
  };
149
139
  readonly markets: {
150
140
  /**
151
141
  * List all markets for the current store
152
142
  */
153
- list: (options?: RequestOptions) => Promise<{
154
- data: StoreMarket[];
155
- }>;
143
+ list: (options?: RequestOptions) => Promise<ListResponse<StoreMarket>>;
156
144
  /**
157
145
  * Get a market by prefixed ID
158
146
  * @param id - Market prefixed ID (e.g., "mkt_k5nR8xLq")
@@ -171,16 +159,14 @@ declare class StoreClient {
171
159
  * List countries belonging to a market
172
160
  * @param marketId - Market prefixed ID
173
161
  */
174
- list: (marketId: string, options?: RequestOptions) => Promise<{
175
- data: StoreCountry[];
176
- }>;
162
+ list: (marketId: string, options?: RequestOptions) => Promise<ListResponse<StoreCountry>>;
177
163
  /**
178
164
  * Get a country by ISO code within a market
179
165
  * @param marketId - Market prefixed ID
180
166
  * @param iso - Country ISO code (e.g., "DE")
181
167
  */
182
168
  get: (marketId: string, iso: string, params?: {
183
- expand?: string;
169
+ expand?: string[];
184
170
  }, options?: RequestOptions) => Promise<StoreCountry>;
185
171
  };
186
172
  };
@@ -213,7 +199,7 @@ declare class StoreClient {
213
199
  * Get an order by ID or number
214
200
  */
215
201
  get: (idOrNumber: string, params?: {
216
- expand?: string;
202
+ expand?: string[];
217
203
  }, options?: RequestOptions) => Promise<StoreOrder>;
218
204
  /**
219
205
  * Update an order
@@ -266,10 +252,7 @@ declare class StoreClient {
266
252
  /**
267
253
  * List payments for an order
268
254
  */
269
- list: (orderId: string, options?: RequestOptions) => Promise<{
270
- data: StorePayment[];
271
- meta: object;
272
- }>;
255
+ list: (orderId: string, options?: RequestOptions) => Promise<PaginatedResponse<StorePayment>>;
273
256
  /**
274
257
  * Get a payment by ID
275
258
  */
@@ -282,7 +265,7 @@ declare class StoreClient {
282
265
  /**
283
266
  * List available payment methods for an order
284
267
  */
285
- list: (orderId: string, options?: RequestOptions) => Promise<PaginatedResponse<StorePaymentMethod>>;
268
+ list: (orderId: string, options?: RequestOptions) => Promise<ListResponse<StorePaymentMethod>>;
286
269
  };
287
270
  /**
288
271
  * Nested resource: Payment sessions
@@ -329,9 +312,7 @@ declare class StoreClient {
329
312
  /**
330
313
  * List shipments for an order
331
314
  */
332
- list: (orderId: string, options?: RequestOptions) => Promise<{
333
- data: StoreShipment[];
334
- }>;
315
+ list: (orderId: string, options?: RequestOptions) => Promise<ListResponse<StoreShipment>>;
335
316
  /**
336
317
  * Select a shipping rate for a shipment.
337
318
  * Returns the updated order with recalculated totals.
@@ -456,7 +437,7 @@ declare class StoreClient {
456
437
  * Get a wishlist by ID
457
438
  */
458
439
  get: (id: string, params?: {
459
- expand?: string;
440
+ expand?: string[];
460
441
  }, options?: RequestOptions) => Promise<StoreWishlist>;
461
442
  /**
462
443
  * Create a wishlist
@@ -512,8 +493,8 @@ declare class AdminClient {
512
493
  interface SpreeClientConfig {
513
494
  /** Base URL of the Spree API (e.g., 'https://api.mystore.com') */
514
495
  baseUrl: string;
515
- /** Publishable API key for Store API access */
516
- publishableKey: string;
496
+ /** Publishable API key for Store API access (required for Store API) */
497
+ publishableKey?: string;
517
498
  /** Secret API key for Admin API access (optional) */
518
499
  secretKey?: string;
519
500
  /** Custom fetch implementation (optional, defaults to global fetch) */
@@ -546,4 +527,4 @@ declare class SpreeClient {
546
527
  */
547
528
  declare function createSpreeClient(config: SpreeClientConfig): SpreeClient;
548
529
 
549
- export { AddLineItemParams, AddressParams, AdminClient, AuthTokens, CompletePaymentSessionParams, CompletePaymentSetupSessionParams, CreateCartParams, CreatePaymentSessionParams, CreatePaymentSetupSessionParams, ErrorResponse, ListParams, LoginCredentials, OrderListParams, PaginatedResponse, ProductFiltersParams, ProductFiltersResponse, ProductListParams, RegisterParams, type RequestOptions, type RetryConfig, SpreeClient, type SpreeClientConfig, SpreeError, StoreAddress, StoreClient, StoreCountry, StoreCreditCard, StoreCurrency, StoreCustomer, StoreGiftCard, StoreLocale, StoreMarket, StoreOrder, StorePayment, StorePaymentMethod, StorePaymentSession, StorePaymentSetupSession, StoreProduct, StoreShipment, StoreStore, StoreTaxon, StoreTaxonomy, StoreWishedItem, StoreWishlist, TaxonListParams, UpdateLineItemParams, UpdateOrderParams, UpdatePaymentSessionParams, createSpreeClient };
530
+ export { AddLineItemParams, AddressParams, AdminClient, AuthTokens, CompletePaymentSessionParams, CompletePaymentSetupSessionParams, CreateCartParams, CreatePaymentSessionParams, CreatePaymentSetupSessionParams, ErrorResponse, ListParams, ListResponse, LoginCredentials, OrderListParams, PaginatedResponse, ProductFiltersParams, ProductFiltersResponse, ProductListParams, RegisterParams, type RequestOptions, type RetryConfig, SpreeClient, type SpreeClientConfig, SpreeError, StoreAddress, StoreClient, StoreCountry, StoreCreditCard, StoreCurrency, StoreCustomer, StoreGiftCard, StoreLocale, StoreMarket, StoreOrder, StorePayment, StorePaymentMethod, StorePaymentSession, StorePaymentSetupSession, StoreProduct, StoreShipment, StoreTaxon, StoreTaxonomy, StoreWishedItem, StoreWishlist, TaxonListParams, UpdateLineItemParams, UpdateOrderParams, UpdatePaymentSessionParams, createSpreeClient };
package/dist/index.js CHANGED
@@ -19,20 +19,23 @@ function calculateDelay(attempt, config) {
19
19
  function sleep(ms) {
20
20
  return new Promise((resolve) => setTimeout(resolve, ms));
21
21
  }
22
- function shouldRetryOnStatus(method, status, config) {
23
- const isIdempotent = method === "GET" || method === "HEAD";
22
+ function generateIdempotencyKey() {
23
+ return `spree-sdk-retry-${crypto.randomUUID()}`;
24
+ }
25
+ function shouldRetryOnStatus(method, status, config, hasIdempotencyKey) {
26
+ const isIdempotent = method === "GET" || method === "HEAD" || hasIdempotencyKey;
24
27
  if (isIdempotent) {
25
28
  return config.retryOnStatus.includes(status);
26
29
  }
27
30
  return status === 429;
28
31
  }
29
- function shouldRetryOnNetworkError(method, config) {
32
+ function shouldRetryOnNetworkError(method, config, hasIdempotencyKey) {
30
33
  if (!config.retryOnNetworkError) return false;
31
- return method === "GET" || method === "HEAD";
34
+ return method === "GET" || method === "HEAD" || hasIdempotencyKey;
32
35
  }
33
36
  function createRequestFn(config, basePath, auth, defaults) {
34
37
  return async function request(method, path, options = {}) {
35
- const { token, orderToken, headers = {}, body, params } = options;
38
+ const { token, orderToken, idempotencyKey, headers = {}, body, params } = options;
36
39
  const locale = options.locale ?? defaults?.locale;
37
40
  const currency = options.currency ?? defaults?.currency;
38
41
  const country = options.country ?? defaults?.country;
@@ -48,14 +51,13 @@ function createRequestFn(config, basePath, auth, defaults) {
48
51
  }
49
52
  });
50
53
  }
51
- if (orderToken) {
52
- url.searchParams.set("order_token", orderToken);
53
- }
54
54
  const requestHeaders = {
55
55
  "Content-Type": "application/json",
56
- [auth.headerName]: auth.headerValue,
57
56
  ...headers
58
57
  };
58
+ if (auth.headerName && auth.headerValue) {
59
+ requestHeaders[auth.headerName] = auth.headerValue;
60
+ }
59
61
  if (token) {
60
62
  requestHeaders["Authorization"] = `Bearer ${token}`;
61
63
  }
@@ -71,6 +73,12 @@ function createRequestFn(config, basePath, auth, defaults) {
71
73
  if (country) {
72
74
  requestHeaders["x-spree-country"] = country;
73
75
  }
76
+ const isMutating = method !== "GET" && method !== "HEAD";
77
+ const effectiveIdempotencyKey = idempotencyKey ?? (isMutating && config.retryConfig ? generateIdempotencyKey() : void 0);
78
+ if (effectiveIdempotencyKey) {
79
+ requestHeaders["Idempotency-Key"] = effectiveIdempotencyKey;
80
+ }
81
+ const hasIdempotencyKey = !!effectiveIdempotencyKey;
74
82
  const maxAttempts = config.retryConfig ? config.retryConfig.maxRetries + 1 : 1;
75
83
  for (let attempt = 0; attempt < maxAttempts; attempt++) {
76
84
  try {
@@ -81,7 +89,7 @@ function createRequestFn(config, basePath, auth, defaults) {
81
89
  });
82
90
  if (!response.ok) {
83
91
  const isLastAttempt = attempt >= maxAttempts - 1;
84
- if (!isLastAttempt && config.retryConfig && shouldRetryOnStatus(method, response.status, config.retryConfig)) {
92
+ if (!isLastAttempt && config.retryConfig && shouldRetryOnStatus(method, response.status, config.retryConfig, hasIdempotencyKey)) {
85
93
  const retryAfter = response.headers.get("Retry-After");
86
94
  const delay = retryAfter ? Math.min(parseInt(retryAfter, 10) * 1e3, config.retryConfig.maxDelay) : calculateDelay(attempt, config.retryConfig);
87
95
  await sleep(delay);
@@ -99,7 +107,7 @@ function createRequestFn(config, basePath, auth, defaults) {
99
107
  throw error;
100
108
  }
101
109
  const isLastAttempt = attempt >= maxAttempts - 1;
102
- if (!isLastAttempt && config.retryConfig && shouldRetryOnNetworkError(method, config.retryConfig)) {
110
+ if (!isLastAttempt && config.retryConfig && shouldRetryOnNetworkError(method, config.retryConfig, hasIdempotencyKey)) {
103
111
  const delay = calculateDelay(attempt, config.retryConfig);
104
112
  await sleep(delay);
105
113
  continue;
@@ -136,6 +144,10 @@ function transformListParams(params) {
136
144
  }
137
145
 
138
146
  // src/store-client.ts
147
+ function expandParams(params) {
148
+ if (!params?.expand?.length) return void 0;
149
+ return { expand: params.expand.join(",") };
150
+ }
139
151
  var StoreClient = class {
140
152
  request;
141
153
  constructor(request) {
@@ -159,15 +171,6 @@ var StoreClient = class {
159
171
  refresh: (options) => this.request("POST", "/auth/refresh", options)
160
172
  };
161
173
  // ============================================
162
- // Store
163
- // ============================================
164
- store = {
165
- /**
166
- * Get current store information
167
- */
168
- get: (options) => this.request("GET", "/store", options)
169
- };
170
- // ============================================
171
174
  // Products
172
175
  // ============================================
173
176
  products = {
@@ -176,14 +179,14 @@ var StoreClient = class {
176
179
  */
177
180
  list: (params, options) => this.request("GET", "/products", {
178
181
  ...options,
179
- params: params ? transformListParams(params) : void 0
182
+ params: transformListParams({ ...params })
180
183
  }),
181
184
  /**
182
185
  * Get a product by ID or slug
183
186
  */
184
187
  get: (idOrSlug, params, options) => this.request("GET", `/products/${idOrSlug}`, {
185
188
  ...options,
186
- params
189
+ params: expandParams(params)
187
190
  }),
188
191
  /**
189
192
  * Get available filters for products
@@ -203,14 +206,14 @@ var StoreClient = class {
203
206
  */
204
207
  list: (params, options) => this.request("GET", "/taxonomies", {
205
208
  ...options,
206
- params
209
+ params: transformListParams({ ...params })
207
210
  }),
208
211
  /**
209
212
  * Get a taxonomy by ID
210
213
  */
211
214
  get: (id, params, options) => this.request("GET", `/taxonomies/${id}`, {
212
215
  ...options,
213
- params
216
+ params: expandParams(params)
214
217
  })
215
218
  };
216
219
  taxons = {
@@ -219,14 +222,14 @@ var StoreClient = class {
219
222
  */
220
223
  list: (params, options) => this.request("GET", "/taxons", {
221
224
  ...options,
222
- params: params ? transformListParams(params) : void 0
225
+ params: transformListParams({ ...params })
223
226
  }),
224
227
  /**
225
228
  * Get a taxon by ID or permalink
226
229
  */
227
230
  get: (idOrPermalink, params, options) => this.request("GET", `/taxons/${idOrPermalink}`, {
228
231
  ...options,
229
- params
232
+ params: expandParams(params)
230
233
  }),
231
234
  /**
232
235
  * Nested resource: Products in a taxon
@@ -241,7 +244,7 @@ var StoreClient = class {
241
244
  `/taxons/${taxonId}/products`,
242
245
  {
243
246
  ...options,
244
- params: params ? transformListParams(params) : void 0
247
+ params: transformListParams({ ...params })
245
248
  }
246
249
  )
247
250
  }
@@ -262,7 +265,7 @@ var StoreClient = class {
262
265
  */
263
266
  get: (iso, params, options) => this.request("GET", `/countries/${iso}`, {
264
267
  ...options,
265
- params
268
+ params: expandParams(params)
266
269
  })
267
270
  };
268
271
  currencies = {
@@ -319,7 +322,7 @@ var StoreClient = class {
319
322
  get: (marketId, iso, params, options) => this.request(
320
323
  "GET",
321
324
  `/markets/${marketId}/countries/${iso}`,
322
- { ...options, params }
325
+ { ...options, params: expandParams(params) }
323
326
  )
324
327
  }
325
328
  };
@@ -356,7 +359,7 @@ var StoreClient = class {
356
359
  */
357
360
  get: (idOrNumber, params, options) => this.request("GET", `/orders/${idOrNumber}`, {
358
361
  ...options,
359
- params
362
+ params: expandParams(params)
360
363
  }),
361
364
  /**
362
365
  * Update an order
@@ -574,7 +577,7 @@ var StoreClient = class {
574
577
  list: (params, options) => this.request(
575
578
  "GET",
576
579
  "/customer/addresses",
577
- { ...options, params }
580
+ { ...options, params: transformListParams({ ...params }) }
578
581
  ),
579
582
  /**
580
583
  * Get an address by ID
@@ -616,7 +619,7 @@ var StoreClient = class {
616
619
  list: (params, options) => this.request(
617
620
  "GET",
618
621
  "/customer/credit_cards",
619
- { ...options, params }
622
+ { ...options, params: transformListParams({ ...params }) }
620
623
  ),
621
624
  /**
622
625
  * Get a credit card by ID
@@ -638,7 +641,7 @@ var StoreClient = class {
638
641
  list: (params, options) => this.request(
639
642
  "GET",
640
643
  "/customer/gift_cards",
641
- { ...options, params }
644
+ { ...options, params: transformListParams({ ...params }) }
642
645
  ),
643
646
  /**
644
647
  * Get a gift card by ID
@@ -654,7 +657,7 @@ var StoreClient = class {
654
657
  */
655
658
  list: (params, options) => this.request("GET", "/customer/orders", {
656
659
  ...options,
657
- params: params ? transformListParams(params) : void 0
660
+ params: transformListParams({ ...params })
658
661
  })
659
662
  },
660
663
  /**
@@ -694,14 +697,14 @@ var StoreClient = class {
694
697
  */
695
698
  list: (params, options) => this.request("GET", "/wishlists", {
696
699
  ...options,
697
- params
700
+ params: transformListParams({ ...params })
698
701
  }),
699
702
  /**
700
703
  * Get a wishlist by ID
701
704
  */
702
705
  get: (id, params, options) => this.request("GET", `/wishlists/${id}`, {
703
706
  ...options,
704
- params
707
+ params: expandParams(params)
705
708
  }),
706
709
  /**
707
710
  * Create a wishlist
@@ -770,6 +773,9 @@ var SpreeClient = class {
770
773
  admin;
771
774
  _defaults;
772
775
  constructor(config) {
776
+ if (!config.publishableKey && !config.secretKey) {
777
+ throw new Error("SpreeClient requires at least one of publishableKey or secretKey");
778
+ }
773
779
  const baseUrl = config.baseUrl.replace(/\/$/, "");
774
780
  const fetchFn = config.fetch || fetch.bind(globalThis);
775
781
  this._defaults = {
@@ -793,7 +799,7 @@ var SpreeClient = class {
793
799
  const storeRequestFn = createRequestFn(
794
800
  requestConfig,
795
801
  "/api/v3/store",
796
- { headerName: "x-spree-api-key", headerValue: config.publishableKey },
802
+ config.publishableKey ? { headerName: "x-spree-api-key", headerValue: config.publishableKey } : { headerName: "", headerValue: "" },
797
803
  this._defaults
798
804
  );
799
805
  const adminRequestFn = createRequestFn(