shop-client 3.10.0 → 3.12.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/dist/{chunk-MSIVKUGN.mjs → chunk-VK5666EK.mjs} +55 -32
- package/dist/{chunk-CNJRHWIK.mjs → chunk-ZF4M6GMB.mjs} +68 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.mjs +20 -11
- package/dist/products.d.ts +18 -0
- package/dist/products.mjs +1 -1
- package/dist/store.d.ts +2 -1
- package/dist/store.mjs +1 -1
- package/package.json +1 -1
|
@@ -12,8 +12,8 @@ import {
|
|
|
12
12
|
|
|
13
13
|
// src/client/get-info.ts
|
|
14
14
|
import { unique } from "remeda";
|
|
15
|
-
async function getInfoForStore(args) {
|
|
16
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
|
|
15
|
+
async function getInfoForStore(args, options) {
|
|
16
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
|
|
17
17
|
const {
|
|
18
18
|
baseUrl,
|
|
19
19
|
storeDomain,
|
|
@@ -22,7 +22,8 @@ async function getInfoForStore(args) {
|
|
|
22
22
|
validateLinksInBatches
|
|
23
23
|
} = args;
|
|
24
24
|
const response = await rateLimitedFetch(baseUrl, {
|
|
25
|
-
rateLimitClass: "store:info"
|
|
25
|
+
rateLimitClass: "store:info",
|
|
26
|
+
timeoutMs: 7e3
|
|
26
27
|
});
|
|
27
28
|
if (!response.ok) {
|
|
28
29
|
throw new Error(`HTTP error! status: ${response.status}`);
|
|
@@ -94,18 +95,22 @@ async function getInfoForStore(args) {
|
|
|
94
95
|
)) {
|
|
95
96
|
contactLinks.contactPage = (match == null ? void 0 : match[1]) || null;
|
|
96
97
|
}
|
|
97
|
-
const extractedProductLinks = (
|
|
98
|
-
(match)
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
98
|
+
const extractedProductLinks = unique(
|
|
99
|
+
((_g = (_f = html.match(/href=["']([^"']*\/products\/[^"']+)["']/g)) == null ? void 0 : _f.map(
|
|
100
|
+
(match) => {
|
|
101
|
+
var _a2, _b2;
|
|
102
|
+
return (_b2 = (_a2 = match == null ? void 0 : match.split("href=")[1]) == null ? void 0 : _a2.replace(/["']/g, "")) == null ? void 0 : _b2.split("/").at(-1);
|
|
103
|
+
}
|
|
104
|
+
)) == null ? void 0 : _g.filter(Boolean)) || []
|
|
105
|
+
).slice(0, 8);
|
|
106
|
+
const extractedCollectionLinks = unique(
|
|
107
|
+
((_i = (_h = html.match(/href=["']([^"']*\/collections\/[^"']+)["']/g)) == null ? void 0 : _h.map(
|
|
108
|
+
(match) => {
|
|
109
|
+
var _a2, _b2;
|
|
110
|
+
return (_b2 = (_a2 = match == null ? void 0 : match.split("href=")[1]) == null ? void 0 : _a2.replace(/["']/g, "")) == null ? void 0 : _b2.split("/").at(-1);
|
|
111
|
+
}
|
|
112
|
+
)) == null ? void 0 : _i.filter(Boolean)) || []
|
|
113
|
+
).slice(0, 8);
|
|
109
114
|
const headerLinks = (_k = (_j = html.match(
|
|
110
115
|
/<(header|nav|div|section)\b[^>]*\b(?:id|class)=["'][^"']*(?=.*shopify-section)(?=.*\b(header|navigation|nav|menu)\b)[^"']*["'][^>]*>[\s\S]*?<\/\1>/gi
|
|
111
116
|
)) == null ? void 0 : _j.flatMap((header) => {
|
|
@@ -129,20 +134,37 @@ async function getInfoForStore(args) {
|
|
|
129
134
|
})) != null ? _k : [];
|
|
130
135
|
const slug = generateStoreSlug(baseUrl);
|
|
131
136
|
const countryDetection = await detectShopCountry(html);
|
|
132
|
-
const
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
137
|
+
const doValidate = (options == null ? void 0 : options.validateShowcase) === true;
|
|
138
|
+
let homePageProductLinks = [];
|
|
139
|
+
let homePageCollectionLinks = [];
|
|
140
|
+
if (doValidate) {
|
|
141
|
+
const batchSize = (_l = options == null ? void 0 : options.validationBatchSize) != null ? _l : 5;
|
|
142
|
+
const validated = await Promise.all([
|
|
143
|
+
validateLinksInBatches(
|
|
144
|
+
extractedProductLinks.filter(
|
|
145
|
+
(handle) => Boolean(handle)
|
|
146
|
+
),
|
|
147
|
+
(handle) => validateProductExists(handle),
|
|
148
|
+
batchSize
|
|
136
149
|
),
|
|
137
|
-
(
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
(handle) =>
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
)
|
|
145
|
-
|
|
150
|
+
validateLinksInBatches(
|
|
151
|
+
extractedCollectionLinks.filter(
|
|
152
|
+
(handle) => Boolean(handle)
|
|
153
|
+
),
|
|
154
|
+
(handle) => validateCollectionExists(handle),
|
|
155
|
+
batchSize
|
|
156
|
+
)
|
|
157
|
+
]);
|
|
158
|
+
homePageProductLinks = (_m = validated[0]) != null ? _m : [];
|
|
159
|
+
homePageCollectionLinks = (_n = validated[1]) != null ? _n : [];
|
|
160
|
+
} else {
|
|
161
|
+
homePageProductLinks = extractedProductLinks.filter(
|
|
162
|
+
(handle) => Boolean(handle)
|
|
163
|
+
);
|
|
164
|
+
homePageCollectionLinks = extractedCollectionLinks.filter(
|
|
165
|
+
(handle) => Boolean(handle)
|
|
166
|
+
);
|
|
167
|
+
}
|
|
146
168
|
const info = {
|
|
147
169
|
name: name || slug,
|
|
148
170
|
domain: sanitizeDomain(baseUrl),
|
|
@@ -157,20 +179,21 @@ async function getInfoForStore(args) {
|
|
|
157
179
|
products: unique(homePageProductLinks != null ? homePageProductLinks : []),
|
|
158
180
|
collections: unique(homePageCollectionLinks != null ? homePageCollectionLinks : [])
|
|
159
181
|
},
|
|
160
|
-
jsonLdData: ((
|
|
182
|
+
jsonLdData: ((_p = (_o = html.match(
|
|
161
183
|
/<script[^>]*type="application\/ld\+json"[^>]*>([^<]+)<\/script>/g
|
|
162
|
-
)) == null ? void 0 :
|
|
184
|
+
)) == null ? void 0 : _o.map(
|
|
163
185
|
(match) => {
|
|
164
186
|
var _a2;
|
|
165
187
|
return ((_a2 = match == null ? void 0 : match.split(">")[1]) == null ? void 0 : _a2.replace(/<\/script/g, "")) || null;
|
|
166
188
|
}
|
|
167
|
-
)) == null ? void 0 :
|
|
189
|
+
)) == null ? void 0 : _p.map((json) => json ? JSON.parse(json) : null)) || [],
|
|
168
190
|
techProvider: {
|
|
169
191
|
name: "shopify",
|
|
170
192
|
walletId: shopifyWalletId,
|
|
171
193
|
subDomain: myShopifySubdomain != null ? myShopifySubdomain : null
|
|
172
194
|
},
|
|
173
|
-
country: countryDetection.country
|
|
195
|
+
country: countryDetection.country,
|
|
196
|
+
currency: (countryDetection == null ? void 0 : countryDetection.currencyCode) || null
|
|
174
197
|
};
|
|
175
198
|
const currencyCode = countryDetection == null ? void 0 : countryDetection.currencyCode;
|
|
176
199
|
return { info, currencyCode };
|
|
@@ -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
|
*
|
|
@@ -153,6 +154,8 @@ declare class ShopClient {
|
|
|
153
154
|
*/
|
|
154
155
|
getInfo(options?: {
|
|
155
156
|
force?: boolean;
|
|
157
|
+
validateShowcase?: boolean;
|
|
158
|
+
validationBatchSize?: number;
|
|
156
159
|
}): Promise<StoreInfo>;
|
|
157
160
|
/**
|
|
158
161
|
* Manually clear the cached store info.
|
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-
|
|
9
|
+
} from "./chunk-ZF4M6GMB.mjs";
|
|
10
10
|
import {
|
|
11
11
|
createStoreOperations,
|
|
12
12
|
getInfoForStore
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-VK5666EK.mjs";
|
|
14
14
|
import {
|
|
15
15
|
classifyProduct,
|
|
16
16
|
determineStoreType,
|
|
@@ -568,7 +568,8 @@ var ShopClient = class {
|
|
|
568
568
|
const url = `${this.baseUrl}products/${handle}.js`;
|
|
569
569
|
const response = await rateLimitedFetch(url, {
|
|
570
570
|
method: "HEAD",
|
|
571
|
-
rateLimitClass: "validate:product"
|
|
571
|
+
rateLimitClass: "validate:product",
|
|
572
|
+
timeoutMs: 5e3
|
|
572
573
|
});
|
|
573
574
|
const exists = response.ok;
|
|
574
575
|
this.setCacheValue(cacheKey, exists);
|
|
@@ -590,7 +591,8 @@ var ShopClient = class {
|
|
|
590
591
|
const url = `${this.baseUrl}collections/${handle}.json`;
|
|
591
592
|
const response = await rateLimitedFetch(url, {
|
|
592
593
|
method: "HEAD",
|
|
593
|
-
rateLimitClass: "validate:collection"
|
|
594
|
+
rateLimitClass: "validate:collection",
|
|
595
|
+
timeoutMs: 5e3
|
|
594
596
|
});
|
|
595
597
|
const exists = response.ok;
|
|
596
598
|
this.setCacheValue(cacheKey, exists);
|
|
@@ -654,6 +656,7 @@ var ShopClient = class {
|
|
|
654
656
|
* - `jsonLdData` - Structured data from JSON-LD scripts
|
|
655
657
|
* - `techProvider` - Shopify-specific information (walletId, subDomain)
|
|
656
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")
|
|
657
660
|
*
|
|
658
661
|
* @throws {Error} When the store URL is unreachable or returns an error
|
|
659
662
|
*
|
|
@@ -685,13 +688,19 @@ var ShopClient = class {
|
|
|
685
688
|
return await this.infoInFlight;
|
|
686
689
|
}
|
|
687
690
|
this.infoInFlight = (async () => {
|
|
688
|
-
const { info, currencyCode } = await getInfoForStore(
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
691
|
+
const { info, currencyCode } = await getInfoForStore(
|
|
692
|
+
{
|
|
693
|
+
baseUrl: this.baseUrl,
|
|
694
|
+
storeDomain: this.storeDomain,
|
|
695
|
+
validateProductExists: (handle) => this.validateProductExists(handle),
|
|
696
|
+
validateCollectionExists: (handle) => this.validateCollectionExists(handle),
|
|
697
|
+
validateLinksInBatches: (items, validator, batchSize) => this.validateLinksInBatches(items, validator, batchSize)
|
|
698
|
+
},
|
|
699
|
+
{
|
|
700
|
+
validateShowcase: (options == null ? void 0 : options.validateShowcase) === true,
|
|
701
|
+
validationBatchSize: options == null ? void 0 : options.validationBatchSize
|
|
702
|
+
}
|
|
703
|
+
);
|
|
695
704
|
if (typeof currencyCode === "string") {
|
|
696
705
|
this.storeCurrency = currencyCode;
|
|
697
706
|
}
|
package/dist/products.d.ts
CHANGED
|
@@ -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
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