shop-client 3.11.0 → 3.13.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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # Shop Search
1
+ # Shop Client
2
2
 
3
3
  [![npm version](https://badge.fury.io/js/shop-client.svg)](https://badge.fury.io/js/shop-client)
4
4
  [![TypeScript](https://img.shields.io/badge/TypeScript-Ready-blue.svg)](https://www.typescriptlang.org/)
@@ -205,17 +205,8 @@ Notes:
205
205
 
206
206
  ### Migration: Barrel → Subpath Imports
207
207
 
208
- #### Package Rename: `shop-search` → `shop-client` (v3.8.2)
209
- - Install: `npm i shop-client` (replaces `shop-search`)
210
- - Update imports to `shop-client` (API unchanged)
211
-
212
208
  TypeScript:
213
209
  ```ts
214
- // Before (pre-rename: shop-search)
215
- import { Store } from 'shop-search';
216
- const store = new Store("your-store.myshopify.com");
217
-
218
- // After (post-rename: shop-client v3.8.2+)
219
210
  import { ShopClient } from 'shop-client';
220
211
  const client = new ShopClient("your-store.myshopify.com");
221
212
  ```
@@ -354,6 +345,8 @@ const storeInfo = await shop.getInfo();
354
345
  - `headerLinks`: Navigation menu links
355
346
  - `showcase`: Featured products and collections
356
347
  - `jsonLdData`: Structured data from the store
348
+ - `country`: ISO 3166-1 alpha-2 code (e.g., `US`, `GB`)
349
+ - `currency`: ISO 4217 currency code (e.g., `USD`, `EUR`)
357
350
 
358
351
  ### Products
359
352
 
@@ -444,6 +437,43 @@ Object.entries(filters || {}).forEach(([optionName, values]) => {
444
437
  - Handles products with multiple variant options
445
438
  - Returns empty object `{}` if no products have variants
446
439
 
440
+ ### Predictive Search
441
+
442
+ #### `products.predictiveSearch(query, options?)`
443
+
444
+ Locale-aware Shopify Ajax predictive search for products.
445
+
446
+ ```typescript
447
+ const results = await shop.products.predictiveSearch("dress", {
448
+ limit: 10, // clamps 1–10
449
+ locale: "en", // defaults to "en"
450
+ // unavailableProducts defaults to "hide"
451
+ currency: "USD", // optional override
452
+ });
453
+ ```
454
+
455
+ - Hides unavailable items by default
456
+ - Extracts handles from Ajax results, fetches full products via `find`
457
+ - Falls back to non-locale path when locale returns 404/417
458
+
459
+ ### Recommendations
460
+
461
+ #### `products.recommendations(productId, options?)`
462
+
463
+ Shopify Ajax product recommendations for a given product.
464
+
465
+ ```typescript
466
+ const recos = await shop.products.recommendations(1234567890, {
467
+ limit: 6, // clamps 1–10 (default 10)
468
+ intent: "related", // or "complementary" (default: related)
469
+ locale: "en", // defaults to "en"
470
+ currency: "USD", // optional override
471
+ });
472
+ ```
473
+
474
+ - Returns normalized `Product[]`
475
+ - Locale-aware endpoint `/{locale}/recommendations/products.json`
476
+
447
477
  ### Collections
448
478
 
449
479
  #### `collections.all()`
@@ -192,7 +192,8 @@ async function getInfoForStore(args, options) {
192
192
  walletId: shopifyWalletId,
193
193
  subDomain: myShopifySubdomain != null ? myShopifySubdomain : null
194
194
  },
195
- country: countryDetection.country
195
+ country: countryDetection.country,
196
+ currency: (countryDetection == null ? void 0 : countryDetection.currencyCode) || null
196
197
  };
197
198
  const currencyCode = countryDetection == null ? void 0 : countryDetection.currencyCode;
198
199
  return { info, currencyCode };
@@ -218,6 +219,7 @@ function createStoreOperations(context) {
218
219
  * - `jsonLdData` - Structured data from JSON-LD scripts
219
220
  * - `techProvider` - Shopify-specific information (walletId, subDomain)
220
221
  * - `country` - Country detection results with ISO 3166-1 alpha-2 codes (e.g., "US", "GB")
222
+ * - `currency` - ISO 4217 currency code inferred from store (e.g., "USD")
221
223
  *
222
224
  * @throws {Error} When the store URL is unreachable or returns an error
223
225
  *
@@ -450,6 +450,74 @@ function createProductOperations(baseUrl, storeDomain, fetchProducts, productsDt
450
450
  console.error("Failed to create product filters:", storeDomain, error);
451
451
  throw error;
452
452
  }
453
+ },
454
+ predictiveSearch: async (query, options) => {
455
+ var _a, _b, _c, _d, _e;
456
+ if (!query || typeof query !== "string") {
457
+ throw new Error("Query is required and must be a string");
458
+ }
459
+ const limit = Math.max(1, Math.min((_a = options == null ? void 0 : options.limit) != null ? _a : 10, 10));
460
+ const unavailable = (options == null ? void 0 : options.unavailableProducts) === "show" || (options == null ? void 0 : options.unavailableProducts) === "hide" ? options.unavailableProducts : "hide";
461
+ const localeValue = (options == null ? void 0 : options.locale) && options.locale.trim() || "en";
462
+ const localePrefix = `${localeValue.replace(/^\/|\/$/g, "")}/`;
463
+ const url = `${baseUrl}${localePrefix}search/suggest.json?q=${encodeURIComponent(query)}&resources[type]=product&resources[limit]=${limit}&resources[options][unavailable_products]=${unavailable}`;
464
+ const response = await rateLimitedFetch(url, {
465
+ rateLimitClass: "search:predictive",
466
+ timeoutMs: 7e3,
467
+ retry: { maxRetries: 2, baseDelayMs: 300 }
468
+ });
469
+ let resp = response;
470
+ if (!resp.ok && (resp.status === 404 || resp.status === 417)) {
471
+ const fallbackUrl = `${baseUrl}search/suggest.json?q=${encodeURIComponent(query)}&resources[type]=product&resources[limit]=${limit}&resources[options][unavailable_products]=${unavailable}`;
472
+ resp = await rateLimitedFetch(fallbackUrl, {
473
+ rateLimitClass: "search:predictive",
474
+ timeoutMs: 7e3,
475
+ retry: { maxRetries: 2, baseDelayMs: 300 }
476
+ });
477
+ }
478
+ if (!resp.ok) {
479
+ throw new Error(`HTTP ${resp.status}: ${resp.statusText}`);
480
+ }
481
+ const data = await resp.json();
482
+ const raw = (_d = (_c = (_b = data == null ? void 0 : data.resources) == null ? void 0 : _b.results) == null ? void 0 : _c.products) != null ? _d : [];
483
+ const handles = raw.filter((p) => {
484
+ var _a2;
485
+ return Boolean((_a2 = p == null ? void 0 : p.available) != null ? _a2 : true);
486
+ }).map((p) => {
487
+ var _a2;
488
+ return String((_a2 = p == null ? void 0 : p.handle) != null ? _a2 : "");
489
+ }).filter((h) => h.length > 0).slice(0, limit);
490
+ const fetched = await Promise.all(handles.map((h) => findProduct(h)));
491
+ const results = filter(fetched, isNonNullish);
492
+ const finalProducts = (_e = maybeOverrideProductsCurrency(results, options == null ? void 0 : options.currency)) != null ? _e : [];
493
+ return finalProducts;
494
+ },
495
+ recommendations: async (productId, options) => {
496
+ var _a, _b;
497
+ if (!Number.isFinite(productId) || productId <= 0) {
498
+ throw new Error("Valid productId is required");
499
+ }
500
+ const limit = Math.max(1, Math.min((_a = options == null ? void 0 : options.limit) != null ? _a : 10, 10));
501
+ const intent = (options == null ? void 0 : options.intent) === "complementary" ? "complementary" : "related";
502
+ const localeValue = (options == null ? void 0 : options.locale) && options.locale.trim() || "en";
503
+ const localePrefix = `${localeValue.replace(/^\/|\/$/g, "")}/`;
504
+ const url = `${baseUrl}${localePrefix}recommendations/products.json?product_id=${encodeURIComponent(String(productId))}&limit=${limit}&intent=${intent}`;
505
+ const resp = await rateLimitedFetch(url, {
506
+ rateLimitClass: "products:recommendations",
507
+ timeoutMs: 7e3,
508
+ retry: { maxRetries: 2, baseDelayMs: 300 }
509
+ });
510
+ if (!resp.ok) {
511
+ if (resp.status === 404) {
512
+ return [];
513
+ }
514
+ throw new Error(`HTTP ${resp.status}: ${resp.statusText}`);
515
+ }
516
+ const data = await resp.json();
517
+ const productsArray = Array.isArray(data) ? data : Array.isArray(data == null ? void 0 : data.products) ? data.products : [];
518
+ const normalized = productsDto(productsArray) || [];
519
+ const finalProducts = (_b = maybeOverrideProductsCurrency(normalized, options == null ? void 0 : options.currency)) != null ? _b : [];
520
+ return finalProducts;
453
521
  }
454
522
  };
455
523
  return operations;
package/dist/index.d.ts CHANGED
@@ -132,6 +132,7 @@ declare class ShopClient {
132
132
  * - `jsonLdData` - Structured data from JSON-LD scripts
133
133
  * - `techProvider` - Shopify-specific information (walletId, subDomain)
134
134
  * - `country` - Country detection results with ISO 3166-1 alpha-2 codes (e.g., "US", "GB")
135
+ * - `currency` - ISO 4217 currency code inferred from store (e.g., "USD")
135
136
  *
136
137
  * @throws {Error} When the store URL is unreachable or returns an error
137
138
  *
package/dist/index.mjs CHANGED
@@ -6,11 +6,11 @@ import {
6
6
  } from "./chunk-554O5ED6.mjs";
7
7
  import {
8
8
  createProductOperations
9
- } from "./chunk-CNJRHWIK.mjs";
9
+ } from "./chunk-ZF4M6GMB.mjs";
10
10
  import {
11
11
  createStoreOperations,
12
12
  getInfoForStore
13
- } from "./chunk-EUAKMCAX.mjs";
13
+ } from "./chunk-CUL7ZM2W.mjs";
14
14
  import {
15
15
  classifyProduct,
16
16
  determineStoreType,
@@ -656,6 +656,7 @@ var ShopClient = class {
656
656
  * - `jsonLdData` - Structured data from JSON-LD scripts
657
657
  * - `techProvider` - Shopify-specific information (walletId, subDomain)
658
658
  * - `country` - Country detection results with ISO 3166-1 alpha-2 codes (e.g., "US", "GB")
659
+ * - `currency` - ISO 4217 currency code inferred from store (e.g., "USD")
659
660
  *
660
661
  * @throws {Error} When the store URL is unreachable or returns an error
661
662
  *
@@ -55,6 +55,24 @@ interface ProductOperations {
55
55
  * Creates a filter map of variant options and their distinct values from all products.
56
56
  */
57
57
  filter(): Promise<Record<string, string[]> | null>;
58
+ /**
59
+ * Predictive product search using Shopify Ajax API.
60
+ */
61
+ predictiveSearch(query: string, options?: {
62
+ limit?: number;
63
+ locale?: string;
64
+ currency?: CurrencyCode;
65
+ unavailableProducts?: "show" | "hide" | "last";
66
+ }): Promise<Product[]>;
67
+ /**
68
+ * Product recommendations for a given product ID using Shopify Ajax API.
69
+ */
70
+ recommendations(productId: number, options?: {
71
+ limit?: number;
72
+ intent?: "related" | "complementary";
73
+ locale?: string;
74
+ currency?: CurrencyCode;
75
+ }): Promise<Product[] | null>;
58
76
  }
59
77
  /**
60
78
  * Creates product operations for a store instance
package/dist/products.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createProductOperations
3
- } from "./chunk-CNJRHWIK.mjs";
3
+ } from "./chunk-ZF4M6GMB.mjs";
4
4
  import "./chunk-D5MTUWFO.mjs";
5
5
  import "./chunk-U3RQRBXZ.mjs";
6
6
  export {
package/dist/store.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { J as JsonLdEntry, d as CountryDetectionResult } from './types-luPg5O08.js';
1
+ import { J as JsonLdEntry, d as CountryDetectionResult, f as CurrencyCode } from './types-luPg5O08.js';
2
2
 
3
3
  /**
4
4
  * Store operations interface for managing store-related functionality.
@@ -36,6 +36,7 @@ interface StoreInfo {
36
36
  subDomain: string | null;
37
37
  };
38
38
  country: CountryDetectionResult["country"];
39
+ currency: CurrencyCode | null;
39
40
  }
40
41
  /**
41
42
  * Creates store operations for a ShopClient instance.
package/dist/store.mjs CHANGED
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  createStoreOperations
3
- } from "./chunk-EUAKMCAX.mjs";
3
+ } from "./chunk-CUL7ZM2W.mjs";
4
4
  import "./chunk-D5MTUWFO.mjs";
5
5
  import "./chunk-G7OCMGA6.mjs";
6
6
  import "./chunk-U3RQRBXZ.mjs";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shop-client",
3
- "version": "3.11.0",
3
+ "version": "3.13.0",
4
4
  "type": "module",
5
5
  "main": "./dist/index.mjs",
6
6
  "module": "./dist/index.mjs",