shop-client 3.8.2 → 3.9.1

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.
Files changed (60) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +158 -1
  3. package/dist/ai/enrich.d.ts +93 -0
  4. package/dist/ai/enrich.js +25 -0
  5. package/dist/checkout.js +5 -114
  6. package/dist/{chunk-2KBOKOAD.mjs → chunk-2MF53V33.js} +32 -13
  7. package/dist/{chunk-BWKBRM2Z.mjs → chunk-CN7L3BHG.js} +12 -1
  8. package/dist/chunk-CXUCPK6X.js +460 -0
  9. package/dist/{chunk-QCTICSBE.mjs → chunk-MOBWPEY4.js} +29 -7
  10. package/dist/chunk-ROH545KI.js +274 -0
  11. package/dist/{chunk-QL5OUZGP.mjs → chunk-RR6YTQWP.js} +0 -1
  12. package/dist/{chunk-O4BPIIQ6.mjs → chunk-V52MFQZE.js} +11 -281
  13. package/dist/{chunk-WTK5HUFI.mjs → chunk-VPPCOJC3.js} +13 -435
  14. package/dist/collections.d.ts +2 -1
  15. package/dist/collections.js +7 -539
  16. package/dist/index.d.ts +28 -87
  17. package/dist/index.js +109 -2597
  18. package/dist/products.d.ts +2 -1
  19. package/dist/products.js +7 -1205
  20. package/dist/store.d.ts +53 -1
  21. package/dist/store.js +8 -697
  22. package/dist/{store-CJVUz2Yb.d.ts → types-luPg5O08.d.ts} +1 -208
  23. package/dist/utils/detect-country.d.ts +32 -0
  24. package/dist/utils/detect-country.js +6 -0
  25. package/dist/utils/func.d.ts +61 -0
  26. package/dist/utils/func.js +24 -0
  27. package/dist/utils/rate-limit.d.ts +5 -0
  28. package/dist/utils/rate-limit.js +7 -200
  29. package/package.json +21 -10
  30. package/dist/checkout.d.mts +0 -31
  31. package/dist/checkout.js.map +0 -1
  32. package/dist/checkout.mjs +0 -7
  33. package/dist/checkout.mjs.map +0 -1
  34. package/dist/chunk-2KBOKOAD.mjs.map +0 -1
  35. package/dist/chunk-BWKBRM2Z.mjs.map +0 -1
  36. package/dist/chunk-O4BPIIQ6.mjs.map +0 -1
  37. package/dist/chunk-QCTICSBE.mjs.map +0 -1
  38. package/dist/chunk-QL5OUZGP.mjs.map +0 -1
  39. package/dist/chunk-WTK5HUFI.mjs.map +0 -1
  40. package/dist/collections.d.mts +0 -64
  41. package/dist/collections.js.map +0 -1
  42. package/dist/collections.mjs +0 -9
  43. package/dist/collections.mjs.map +0 -1
  44. package/dist/index.d.mts +0 -233
  45. package/dist/index.js.map +0 -1
  46. package/dist/index.mjs +0 -702
  47. package/dist/index.mjs.map +0 -1
  48. package/dist/products.d.mts +0 -63
  49. package/dist/products.js.map +0 -1
  50. package/dist/products.mjs +0 -9
  51. package/dist/products.mjs.map +0 -1
  52. package/dist/store-CJVUz2Yb.d.mts +0 -608
  53. package/dist/store.d.mts +0 -1
  54. package/dist/store.js.map +0 -1
  55. package/dist/store.mjs +0 -9
  56. package/dist/store.mjs.map +0 -1
  57. package/dist/utils/rate-limit.d.mts +0 -25
  58. package/dist/utils/rate-limit.js.map +0 -1
  59. package/dist/utils/rate-limit.mjs +0 -11
  60. package/dist/utils/rate-limit.mjs.map +0 -1
@@ -1,540 +1,8 @@
1
- "use strict";
2
- var __defProp = Object.defineProperty;
3
- var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
- var __getOwnPropNames = Object.getOwnPropertyNames;
5
- var __hasOwnProp = Object.prototype.hasOwnProperty;
6
- var __export = (target, all) => {
7
- for (var name in all)
8
- __defProp(target, name, { get: all[name], enumerable: true });
9
- };
10
- var __copyProps = (to, from, except, desc) => {
11
- if (from && typeof from === "object" || typeof from === "function") {
12
- for (let key of __getOwnPropNames(from))
13
- if (!__hasOwnProp.call(to, key) && key !== except)
14
- __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
- }
16
- return to;
17
- };
18
- var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
-
20
- // src/collections.ts
21
- var collections_exports = {};
22
- __export(collections_exports, {
23
- createCollectionOperations: () => createCollectionOperations
24
- });
25
- module.exports = __toCommonJS(collections_exports);
26
- var import_remeda = require("remeda");
27
-
28
- // src/utils/func.ts
29
- var import_tldts = require("tldts");
30
- function formatPrice(amountInCents, currency) {
31
- try {
32
- return new Intl.NumberFormat(void 0, {
33
- style: "currency",
34
- currency
35
- }).format((amountInCents || 0) / 100);
36
- } catch {
37
- const val = (amountInCents || 0) / 100;
38
- return `${val} ${currency}`;
39
- }
40
- }
41
-
42
- // src/utils/rate-limit.ts
43
- var RateLimiter = class {
44
- constructor(options) {
45
- this.queue = [];
46
- this.inFlight = 0;
47
- this.refillTimer = null;
48
- this.options = options;
49
- this.tokens = options.maxRequestsPerInterval;
50
- }
51
- startRefill() {
52
- if (this.refillTimer) return;
53
- this.refillTimer = setInterval(() => {
54
- this.tokens = this.options.maxRequestsPerInterval;
55
- this.tryRun();
56
- }, this.options.intervalMs);
57
- if (this.refillTimer && typeof this.refillTimer.unref === "function") {
58
- this.refillTimer.unref();
59
- }
60
- }
61
- stopRefill() {
62
- if (this.refillTimer) {
63
- clearInterval(this.refillTimer);
64
- this.refillTimer = null;
65
- }
66
- }
67
- ensureRefillStarted() {
68
- if (!this.refillTimer) {
69
- this.startRefill();
70
- }
71
- }
72
- configure(next) {
73
- this.options = { ...this.options, ...next };
74
- this.options.maxRequestsPerInterval = Math.max(
75
- 1,
76
- this.options.maxRequestsPerInterval
77
- );
78
- this.options.intervalMs = Math.max(10, this.options.intervalMs);
79
- this.options.maxConcurrency = Math.max(1, this.options.maxConcurrency);
80
- }
81
- schedule(fn) {
82
- return new Promise((resolve, reject) => {
83
- this.ensureRefillStarted();
84
- this.queue.push({ fn, resolve, reject });
85
- this.tryRun();
86
- });
87
- }
88
- tryRun() {
89
- while (this.queue.length > 0 && this.inFlight < this.options.maxConcurrency && this.tokens > 0) {
90
- const task = this.queue.shift();
91
- this.tokens -= 1;
92
- this.inFlight += 1;
93
- Promise.resolve().then(task.fn).then((result) => task.resolve(result)).catch((err) => task.reject(err)).finally(() => {
94
- this.inFlight -= 1;
95
- setTimeout(() => this.tryRun(), 0);
96
- });
97
- }
98
- }
99
- };
100
- var enabled = false;
101
- var defaultOptions = {
102
- maxRequestsPerInterval: 5,
103
- // 5 requests
104
- intervalMs: 1e3,
105
- // per second
106
- maxConcurrency: 5
107
- // up to 5 in parallel
108
- };
109
- var limiter = new RateLimiter(defaultOptions);
110
- var hostLimiters = /* @__PURE__ */ new Map();
111
- var classLimiters = /* @__PURE__ */ new Map();
112
- function getHost(input) {
113
- try {
114
- if (typeof input === "string") {
115
- return new URL(input).host;
116
- }
117
- if (input instanceof URL) {
118
- return input.host;
119
- }
120
- const url = input.url;
121
- if (url) {
122
- return new URL(url).host;
123
- }
124
- } catch {
125
- }
126
- return void 0;
127
- }
128
- function getHostLimiter(host) {
129
- if (!host) return void 0;
130
- const exact = hostLimiters.get(host);
131
- if (exact) return exact;
132
- for (const [key, lim] of hostLimiters.entries()) {
133
- if (key.startsWith("*.") && host.endsWith(key.slice(2))) {
134
- return lim;
135
- }
136
- }
137
- return void 0;
138
- }
139
- async function rateLimitedFetch(input, init) {
140
- var _a;
141
- if (!enabled) {
142
- return fetch(input, init);
143
- }
144
- const klass = init == null ? void 0 : init.rateLimitClass;
145
- const byClass = klass ? classLimiters.get(klass) : void 0;
146
- const byHost = getHostLimiter(getHost(input));
147
- const eff = (_a = byClass != null ? byClass : byHost) != null ? _a : limiter;
148
- return eff.schedule(() => fetch(input, init));
149
- }
150
-
151
- // src/collections.ts
152
- function createCollectionOperations(baseUrl, storeDomain, fetchCollections, collectionsDto, fetchPaginatedProductsFromCollection, getStoreInfo, findCollection) {
153
- function applyCurrencyOverride(product, currency) {
154
- var _a, _b, _c, _d, _e, _f;
155
- const priceMin = (_b = (_a = product.priceMin) != null ? _a : product.price) != null ? _b : 0;
156
- const priceMax = (_d = (_c = product.priceMax) != null ? _c : product.price) != null ? _d : 0;
157
- const compareAtMin = (_f = (_e = product.compareAtPriceMin) != null ? _e : product.compareAtPrice) != null ? _f : 0;
158
- return {
159
- ...product,
160
- currency,
161
- localizedPricing: {
162
- currency,
163
- priceFormatted: formatPrice(priceMin, currency),
164
- priceMinFormatted: formatPrice(priceMin, currency),
165
- priceMaxFormatted: formatPrice(priceMax, currency),
166
- compareAtPriceFormatted: formatPrice(compareAtMin, currency)
167
- }
168
- };
169
- }
170
- function maybeOverrideProductsCurrency(products, currency) {
171
- if (!products || !currency) return products;
172
- return products.map((p) => applyCurrencyOverride(p, currency));
173
- }
174
- return {
175
- /**
176
- * Fetches collections with pagination support.
177
- *
178
- * @param options - Pagination options
179
- * @param options.page - Page number (default: 1)
180
- * @param options.limit - Number of collections per page (default: 10, max: 250)
181
- *
182
- * @returns {Promise<Collection[] | null>} Collections for the requested page, or null on error
183
- */
184
- paginated: async (options) => {
185
- var _a, _b;
186
- const page = (_a = options == null ? void 0 : options.page) != null ? _a : 1;
187
- const limit = (_b = options == null ? void 0 : options.limit) != null ? _b : 10;
188
- if (page < 1 || limit < 1 || limit > 250) {
189
- throw new Error(
190
- "Invalid pagination parameters: page must be >= 1, limit must be between 1 and 250"
191
- );
192
- }
193
- try {
194
- const collections = await fetchCollections(page, limit);
195
- return collections != null ? collections : null;
196
- } catch (error) {
197
- console.error(
198
- "Failed to fetch paginated collections:",
199
- storeDomain,
200
- error
201
- );
202
- return null;
203
- }
204
- },
205
- /**
206
- * Fetches all collections from the store across all pages.
207
- *
208
- * @returns {Promise<Collection[]>} Array of all collections
209
- *
210
- * @throws {Error} When there's a network error or API failure
211
- *
212
- * @example
213
- * ```typescript
214
- * const shop = new ShopClient('https://exampleshop.com');
215
- * const allCollections = await shop.collections.all();
216
- *
217
- * console.log(`Found ${allCollections.length} collections`);
218
- * allCollections.forEach(collection => {
219
- * console.log(collection.title, collection.handle);
220
- * });
221
- * ```
222
- */
223
- all: async () => {
224
- const limit = 250;
225
- const allCollections = [];
226
- async function fetchAll() {
227
- let currentPage = 1;
228
- while (true) {
229
- const collections = await fetchCollections(currentPage, limit);
230
- if (!collections || collections.length === 0 || collections.length < limit) {
231
- if (!collections) {
232
- console.warn(
233
- "fetchCollections returned null, treating as empty array."
234
- );
235
- break;
236
- }
237
- if (collections && collections.length > 0) {
238
- allCollections.push(...collections);
239
- }
240
- break;
241
- }
242
- allCollections.push(...collections);
243
- currentPage++;
244
- }
245
- return allCollections;
246
- }
247
- try {
248
- const collections = await fetchAll();
249
- return collections || [];
250
- } catch (error) {
251
- console.error("Failed to fetch all collections:", storeDomain, error);
252
- throw error;
253
- }
254
- },
255
- /**
256
- * Finds a specific collection by its handle.
257
- *
258
- * @param collectionHandle - The collection handle (URL slug) to search for
259
- *
260
- * @returns {Promise<Collection | null>} The collection if found, null if not found
261
- *
262
- * @throws {Error} When the handle is invalid or there's a network error
263
- *
264
- * @example
265
- * ```typescript
266
- * const shop = new ShopClient('https://example.myshopify.com');
267
- * const collection = await shop.collections.find('summer-collection');
268
- * if (collection) {
269
- * console.log(collection.title); // "Summer Collection"
270
- * }
271
- * ```
272
- */
273
- find: async (collectionHandle) => {
274
- var _a, _b;
275
- if (!collectionHandle || typeof collectionHandle !== "string") {
276
- throw new Error("Collection handle is required and must be a string");
277
- }
278
- const sanitizedHandle = collectionHandle.trim().replace(/[^a-zA-Z0-9\-_]/g, "");
279
- if (!sanitizedHandle) {
280
- throw new Error("Invalid collection handle format");
281
- }
282
- if (sanitizedHandle.length > 255) {
283
- throw new Error("Collection handle is too long");
284
- }
285
- try {
286
- const url = `${baseUrl}collections/${encodeURIComponent(sanitizedHandle)}.json`;
287
- const response = await rateLimitedFetch(url);
288
- if (!response.ok) {
289
- if (response.status === 404) {
290
- return null;
291
- }
292
- throw new Error(`HTTP error! status: ${response.status}`);
293
- }
294
- const result = await response.json();
295
- let collectionImage = result.collection.image;
296
- if (!collectionImage) {
297
- const collectionProduct = (_a = await fetchPaginatedProductsFromCollection(
298
- result.collection.handle,
299
- {
300
- limit: 1,
301
- page: 1
302
- }
303
- )) == null ? void 0 : _a.at(0);
304
- const collectionProductImage = (_b = collectionProduct == null ? void 0 : collectionProduct.images) == null ? void 0 : _b[0];
305
- if (collectionProduct && collectionProductImage) {
306
- collectionImage = {
307
- id: collectionProductImage.id,
308
- src: collectionProductImage.src,
309
- alt: collectionProductImage.alt || collectionProduct.title,
310
- created_at: collectionProductImage.createdAt || (/* @__PURE__ */ new Date()).toISOString()
311
- };
312
- }
313
- }
314
- const collectionData = collectionsDto([
315
- {
316
- ...result.collection,
317
- image: collectionImage
318
- }
319
- ]);
320
- return collectionData[0] || null;
321
- } catch (error) {
322
- if (error instanceof Error) {
323
- console.error(
324
- `Error fetching collection ${sanitizedHandle}:`,
325
- baseUrl,
326
- error.message
327
- );
328
- }
329
- throw error;
330
- }
331
- },
332
- /**
333
- * Fetches collections that are showcased/featured on the store's homepage.
334
- *
335
- * @returns {Promise<Collection[]>} Array of showcased collections found on the homepage
336
- *
337
- * @throws {Error} When there's a network error or API failure
338
- *
339
- * @example
340
- * ```typescript
341
- * const shop = new ShopClient('https://exampleshop.com');
342
- * const showcasedCollections = await shop.collections.showcased();
343
- *
344
- * console.log(`Found ${showcasedCollections.length} showcased collections`);
345
- * showcasedCollections.forEach(collection => {
346
- * console.log(`Featured: ${collection.title} - ${collection.productsCount} products`);
347
- * });
348
- * ```
349
- */
350
- showcased: async () => {
351
- const storeInfo = await getStoreInfo();
352
- const collections = await Promise.all(
353
- storeInfo.showcase.collections.map(
354
- (collectionHandle) => findCollection(collectionHandle)
355
- )
356
- );
357
- return (0, import_remeda.filter)(collections, import_remeda.isNonNullish);
358
- },
359
- products: {
360
- /**
361
- * Fetches products from a specific collection with pagination support.
362
- *
363
- * @param collectionHandle - The collection handle to fetch products from
364
- * @param options - Pagination options
365
- * @param options.page - Page number (default: 1)
366
- * @param options.limit - Number of products per page (default: 250, max: 250)
367
- *
368
- * @returns {Promise<Product[] | null>} Array of products from the collection or null if error occurs
369
- *
370
- * @throws {Error} When the collection handle is invalid or there's a network error
371
- *
372
- * @example
373
- * ```typescript
374
- * const shop = new ShopClient('https://example.myshopify.com');
375
- *
376
- * // Get first page of products from a collection
377
- * const products = await shop.collections.products.paginated('summer-collection');
378
- *
379
- * // Get second page with custom limit
380
- * const moreProducts = await shop.collections.products.paginated(
381
- * 'summer-collection',
382
- * { page: 2, limit: 50 }
383
- * );
384
- * ```
385
- */
386
- paginated: async (collectionHandle, options) => {
387
- var _a, _b;
388
- if (!collectionHandle || typeof collectionHandle !== "string") {
389
- throw new Error("Collection handle is required and must be a string");
390
- }
391
- const sanitizedHandle = collectionHandle.trim().replace(/[^a-zA-Z0-9\-_]/g, "");
392
- if (!sanitizedHandle) {
393
- throw new Error("Invalid collection handle format");
394
- }
395
- if (sanitizedHandle.length > 255) {
396
- throw new Error("Collection handle is too long");
397
- }
398
- const page = (_a = options == null ? void 0 : options.page) != null ? _a : 1;
399
- const limit = (_b = options == null ? void 0 : options.limit) != null ? _b : 250;
400
- if (page < 1 || limit < 1 || limit > 250) {
401
- throw new Error(
402
- "Invalid pagination parameters: page must be >= 1, limit must be between 1 and 250"
403
- );
404
- }
405
- const products = await fetchPaginatedProductsFromCollection(
406
- sanitizedHandle,
407
- { page, limit }
408
- );
409
- return maybeOverrideProductsCurrency(products, options == null ? void 0 : options.currency);
410
- },
411
- /**
412
- * Fetches all products from a specific collection.
413
- *
414
- * @param collectionHandle - The collection handle to fetch products from
415
- *
416
- * @returns {Promise<Product[] | null>} Array of all products from the collection or null if error occurs
417
- *
418
- * @throws {Error} When the collection handle is invalid or there's a network error
419
- *
420
- * @example
421
- * ```typescript
422
- * const shop = new ShopClient('https://exampleshop.com');
423
- * const allProducts = await shop.collections.products.all('summer-collection');
424
- *
425
- * if (allProducts) {
426
- * console.log(`Found ${allProducts.length} products in the collection`);
427
- * allProducts.forEach(product => {
428
- * console.log(`${product.title} - $${product.price}`);
429
- * });
430
- * }
431
- * ```
432
- */
433
- all: async (collectionHandle, options) => {
434
- if (!collectionHandle || typeof collectionHandle !== "string") {
435
- throw new Error("Collection handle is required and must be a string");
436
- }
437
- const sanitizedHandle = collectionHandle.trim().replace(/[^a-zA-Z0-9\-_]/g, "");
438
- if (!sanitizedHandle) {
439
- throw new Error("Invalid collection handle format");
440
- }
441
- if (sanitizedHandle.length > 255) {
442
- throw new Error("Collection handle is too long");
443
- }
444
- try {
445
- const limit = 250;
446
- const allProducts = [];
447
- let currentPage = 1;
448
- while (true) {
449
- const products = await fetchPaginatedProductsFromCollection(
450
- sanitizedHandle,
451
- {
452
- page: currentPage,
453
- limit
454
- }
455
- );
456
- if (!products || products.length === 0 || products.length < limit) {
457
- if (products && products.length > 0) {
458
- allProducts.push(...products);
459
- }
460
- break;
461
- }
462
- allProducts.push(...products);
463
- currentPage++;
464
- }
465
- return maybeOverrideProductsCurrency(allProducts, options == null ? void 0 : options.currency);
466
- } catch (error) {
467
- console.error(
468
- `Error fetching all products for collection ${sanitizedHandle}:`,
469
- baseUrl,
470
- error
471
- );
472
- return null;
473
- }
474
- },
475
- /**
476
- * Fetches all product slugs from a specific collection.
477
- *
478
- * @param collectionHandle - The collection handle to fetch product slugs from
479
- *
480
- * @returns {Promise<string[] | null>} Array of product slugs from the collection or null if error occurs
481
- *
482
- * @throws {Error} When the collection handle is invalid or there's a network error
483
- *
484
- * @example
485
- * ```typescript
486
- * const shop = new ShopClient('https://exampleshop.com');
487
- * const productSlugs = await shop.collections.products.slugs('summer-collection');
488
- * console.log(productSlugs);
489
- * ```
490
- */
491
- slugs: async (collectionHandle) => {
492
- if (!collectionHandle || typeof collectionHandle !== "string") {
493
- throw new Error("Collection handle is required and must be a string");
494
- }
495
- const sanitizedHandle = collectionHandle.trim().replace(/[^a-zA-Z0-9\-_]/g, "");
496
- if (!sanitizedHandle) {
497
- throw new Error("Invalid collection handle format");
498
- }
499
- if (sanitizedHandle.length > 255) {
500
- throw new Error("Collection handle is too long");
501
- }
502
- try {
503
- const limit = 250;
504
- const slugs = [];
505
- let currentPage = 1;
506
- while (true) {
507
- const products = await fetchPaginatedProductsFromCollection(
508
- sanitizedHandle,
509
- {
510
- page: currentPage,
511
- limit
512
- }
513
- );
514
- if (!products || products.length === 0 || products.length < limit) {
515
- if (products && products.length > 0) {
516
- slugs.push(...products.map((p) => p.slug));
517
- }
518
- break;
519
- }
520
- slugs.push(...products.map((p) => p.slug));
521
- currentPage++;
522
- }
523
- return slugs;
524
- } catch (error) {
525
- console.error(
526
- `Error fetching product slugs for collection ${sanitizedHandle}:`,
527
- baseUrl,
528
- error
529
- );
530
- return null;
531
- }
532
- }
533
- }
534
- };
535
- }
536
- // Annotate the CommonJS export names for ESM import in node:
537
- 0 && (module.exports = {
1
+ import {
2
+ createCollectionOperations
3
+ } from "./chunk-MOBWPEY4.js";
4
+ import "./chunk-2MF53V33.js";
5
+ import "./chunk-CN7L3BHG.js";
6
+ export {
538
7
  createCollectionOperations
539
- });
540
- //# sourceMappingURL=collections.js.map
8
+ };