shop-client 3.8.2 → 3.9.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.
Files changed (60) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +93 -1
  3. package/dist/checkout.mjs +1 -7
  4. package/dist/chunk-6GPWNCDO.mjs +130 -0
  5. package/dist/chunk-EJO5U4BT.mjs +2 -0
  6. package/dist/chunk-FFKWCNLU.mjs +1 -0
  7. package/dist/chunk-KYLPIEU3.mjs +2 -0
  8. package/dist/chunk-MB2INNNP.mjs +1 -0
  9. package/dist/chunk-MI7754VX.mjs +2 -0
  10. package/dist/chunk-SZQPMLZG.mjs +1 -0
  11. package/dist/collections.d.ts +1 -1
  12. package/dist/collections.mjs +1 -9
  13. package/dist/enrich-OZHBXKK6.mjs +1 -0
  14. package/dist/index.d.ts +24 -6
  15. package/dist/index.mjs +2 -702
  16. package/dist/products.d.ts +1 -1
  17. package/dist/products.mjs +1 -9
  18. package/dist/{store-CJVUz2Yb.d.mts → store-iQARl6J3.d.ts} +3 -3
  19. package/dist/store.d.ts +1 -1
  20. package/dist/store.mjs +1 -9
  21. package/dist/utils/rate-limit.d.ts +5 -0
  22. package/dist/utils/rate-limit.mjs +1 -11
  23. package/package.json +8 -10
  24. package/dist/checkout.d.mts +0 -31
  25. package/dist/checkout.js +0 -115
  26. package/dist/checkout.js.map +0 -1
  27. package/dist/checkout.mjs.map +0 -1
  28. package/dist/chunk-2KBOKOAD.mjs +0 -177
  29. package/dist/chunk-2KBOKOAD.mjs.map +0 -1
  30. package/dist/chunk-BWKBRM2Z.mjs +0 -136
  31. package/dist/chunk-BWKBRM2Z.mjs.map +0 -1
  32. package/dist/chunk-O4BPIIQ6.mjs +0 -503
  33. package/dist/chunk-O4BPIIQ6.mjs.map +0 -1
  34. package/dist/chunk-QCTICSBE.mjs +0 -398
  35. package/dist/chunk-QCTICSBE.mjs.map +0 -1
  36. package/dist/chunk-QL5OUZGP.mjs +0 -91
  37. package/dist/chunk-QL5OUZGP.mjs.map +0 -1
  38. package/dist/chunk-WTK5HUFI.mjs +0 -1287
  39. package/dist/chunk-WTK5HUFI.mjs.map +0 -1
  40. package/dist/collections.d.mts +0 -64
  41. package/dist/collections.js +0 -540
  42. package/dist/collections.js.map +0 -1
  43. package/dist/collections.mjs.map +0 -1
  44. package/dist/index.d.mts +0 -233
  45. package/dist/index.js +0 -3241
  46. package/dist/index.js.map +0 -1
  47. package/dist/index.mjs.map +0 -1
  48. package/dist/products.d.mts +0 -63
  49. package/dist/products.js +0 -1206
  50. package/dist/products.js.map +0 -1
  51. package/dist/products.mjs.map +0 -1
  52. package/dist/store-CJVUz2Yb.d.ts +0 -608
  53. package/dist/store.d.mts +0 -1
  54. package/dist/store.js +0 -698
  55. package/dist/store.js.map +0 -1
  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 +0 -203
  59. package/dist/utils/rate-limit.js.map +0 -1
  60. package/dist/utils/rate-limit.mjs.map +0 -1
package/dist/store.js DELETED
@@ -1,698 +0,0 @@
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/store.ts
21
- var store_exports = {};
22
- __export(store_exports, {
23
- createStoreOperations: () => createStoreOperations
24
- });
25
- module.exports = __toCommonJS(store_exports);
26
-
27
- // src/client/get-info.ts
28
- var import_remeda = require("remeda");
29
-
30
- // src/utils/detect-country.ts
31
- var COUNTRY_CODES = {
32
- "+1": "US",
33
- // United States (primary) / Canada also uses +1
34
- "+44": "GB",
35
- // United Kingdom
36
- "+61": "AU",
37
- // Australia
38
- "+65": "SG",
39
- // Singapore
40
- "+91": "IN",
41
- // India
42
- "+81": "JP",
43
- // Japan
44
- "+49": "DE",
45
- // Germany
46
- "+33": "FR",
47
- // France
48
- "+971": "AE",
49
- // United Arab Emirates
50
- "+39": "IT",
51
- // Italy
52
- "+34": "ES",
53
- // Spain
54
- "+82": "KR",
55
- // South Korea
56
- "+55": "BR",
57
- // Brazil
58
- "+62": "ID",
59
- // Indonesia
60
- "+92": "PK",
61
- // Pakistan
62
- "+7": "RU"
63
- // Russia
64
- };
65
- var CURRENCY_SYMBOLS = {
66
- Rs: "IN",
67
- // India
68
- "\u20B9": "IN",
69
- // India
70
- $: "US",
71
- // United States (primary, though many countries use $)
72
- CA$: "CA",
73
- // Canada
74
- A$: "AU",
75
- // Australia
76
- "\xA3": "GB",
77
- // United Kingdom
78
- "\u20AC": "EU",
79
- // European Union (not a country code, but commonly used)
80
- AED: "AE",
81
- // United Arab Emirates
82
- "\u20A9": "KR",
83
- // South Korea
84
- "\xA5": "JP"
85
- // Japan (primary, though China also uses ¥)
86
- };
87
- var CURRENCY_SYMBOL_TO_CODE = {
88
- Rs: "INR",
89
- "\u20B9": "INR",
90
- $: "USD",
91
- CA$: "CAD",
92
- A$: "AUD",
93
- "\xA3": "GBP",
94
- "\u20AC": "EUR",
95
- AED: "AED",
96
- "\u20A9": "KRW",
97
- "\xA5": "JPY"
98
- };
99
- var CURRENCY_CODE_TO_COUNTRY = {
100
- INR: "IN",
101
- USD: "US",
102
- CAD: "CA",
103
- AUD: "AU",
104
- GBP: "GB",
105
- EUR: "EU",
106
- AED: "AE",
107
- KRW: "KR",
108
- JPY: "JP"
109
- };
110
- function scoreCountry(countryScores, country, weight, reason) {
111
- if (!country) return;
112
- if (!countryScores[country])
113
- countryScores[country] = { score: 0, reasons: [] };
114
- countryScores[country].score += weight;
115
- countryScores[country].reasons.push(reason);
116
- }
117
- async function detectShopifyCountry(html) {
118
- var _a, _b;
119
- const countryScores = {};
120
- let detectedCurrencyCode;
121
- const shopifyFeaturesMatch = html.match(
122
- /<script[^>]+id=["']shopify-features["'][^>]*>([\s\S]*?)<\/script>/
123
- );
124
- if (shopifyFeaturesMatch) {
125
- try {
126
- const json = shopifyFeaturesMatch[1];
127
- if (!json) {
128
- } else {
129
- const data = JSON.parse(json);
130
- if (data.country)
131
- scoreCountry(
132
- countryScores,
133
- data.country,
134
- 1,
135
- "shopify-features.country"
136
- );
137
- if ((_a = data.locale) == null ? void 0 : _a.includes("-")) {
138
- const [, localeCountry] = data.locale.split("-");
139
- if (localeCountry) {
140
- scoreCountry(
141
- countryScores,
142
- localeCountry.toUpperCase(),
143
- 0.7,
144
- "shopify-features.locale"
145
- );
146
- }
147
- }
148
- if (data.moneyFormat) {
149
- for (const symbol in CURRENCY_SYMBOLS) {
150
- if (data.moneyFormat.includes(symbol)) {
151
- const iso = CURRENCY_SYMBOLS[symbol];
152
- if (typeof iso === "string") {
153
- scoreCountry(countryScores, iso, 0.6, "moneyFormat symbol");
154
- }
155
- const code = CURRENCY_SYMBOL_TO_CODE[symbol];
156
- if (!detectedCurrencyCode && typeof code === "string") {
157
- detectedCurrencyCode = code;
158
- }
159
- }
160
- }
161
- }
162
- }
163
- } catch (_error) {
164
- }
165
- }
166
- const currencyJsonMatch = html.match(/Shopify\.currency\s*=\s*(\{[^}]*\})/);
167
- if (currencyJsonMatch) {
168
- try {
169
- const raw = currencyJsonMatch[1];
170
- const obj = JSON.parse(raw || "{}");
171
- const activeCode = typeof (obj == null ? void 0 : obj.active) === "string" ? obj.active.toUpperCase() : void 0;
172
- const iso = activeCode ? CURRENCY_CODE_TO_COUNTRY[activeCode] : void 0;
173
- if (activeCode) {
174
- detectedCurrencyCode = activeCode;
175
- }
176
- if (typeof iso === "string") {
177
- scoreCountry(countryScores, iso, 0.8, "Shopify.currency.active");
178
- }
179
- } catch (_error) {
180
- }
181
- } else {
182
- const currencyActiveAssignMatch = html.match(
183
- /Shopify\.currency\.active\s*=\s*['"]([A-Za-z]{3})['"]/i
184
- );
185
- if (currencyActiveAssignMatch) {
186
- const captured = currencyActiveAssignMatch[1];
187
- const code = typeof captured === "string" ? captured.toUpperCase() : void 0;
188
- const iso = code ? CURRENCY_CODE_TO_COUNTRY[code] : void 0;
189
- if (code) {
190
- detectedCurrencyCode = code;
191
- }
192
- if (typeof iso === "string") {
193
- scoreCountry(countryScores, iso, 0.8, "Shopify.currency.active");
194
- }
195
- }
196
- }
197
- const shopifyCountryMatch = html.match(
198
- /Shopify\.country\s*=\s*['"]([A-Za-z]{2})['"]/i
199
- );
200
- if (shopifyCountryMatch) {
201
- const captured = shopifyCountryMatch[1];
202
- const iso = typeof captured === "string" ? captured.toUpperCase() : void 0;
203
- if (typeof iso === "string") {
204
- scoreCountry(countryScores, iso, 1, "Shopify.country");
205
- }
206
- }
207
- const phones = html.match(/\+\d{1,3}[\s\-()0-9]{5,}/g);
208
- if (phones) {
209
- for (const phone of phones) {
210
- const prefix = (_b = phone.match(/^\+\d{1,3}/)) == null ? void 0 : _b[0];
211
- if (prefix && COUNTRY_CODES[prefix])
212
- scoreCountry(
213
- countryScores,
214
- COUNTRY_CODES[prefix],
215
- 0.8,
216
- `phone prefix ${prefix}`
217
- );
218
- }
219
- }
220
- const jsonLdRegex = /<script[^>]+application\/ld\+json[^>]*>(.*?)<\/script>/g;
221
- let jsonLdMatch = jsonLdRegex.exec(html);
222
- while (jsonLdMatch !== null) {
223
- try {
224
- const json = jsonLdMatch[1];
225
- if (!json) {
226
- } else {
227
- const raw = JSON.parse(json);
228
- const collectAddressCountries = (node, results = []) => {
229
- if (Array.isArray(node)) {
230
- for (const item of node) collectAddressCountries(item, results);
231
- return results;
232
- }
233
- if (node && typeof node === "object") {
234
- const obj = node;
235
- const address = obj.address;
236
- if (address && typeof address === "object") {
237
- const country = address.addressCountry;
238
- if (typeof country === "string") results.push(country);
239
- }
240
- const graph = obj["@graph"];
241
- if (graph) collectAddressCountries(graph, results);
242
- }
243
- return results;
244
- };
245
- const countries = collectAddressCountries(raw);
246
- for (const country of countries) {
247
- scoreCountry(countryScores, country, 1, "JSON-LD addressCountry");
248
- }
249
- }
250
- } catch (_error) {
251
- }
252
- jsonLdMatch = jsonLdRegex.exec(html);
253
- }
254
- const footerMatch = html.match(/<footer[^>]*>(.*?)<\/footer>/i);
255
- if (footerMatch) {
256
- const footerTextGroup = footerMatch[1];
257
- const footerText = footerTextGroup ? footerTextGroup.toLowerCase() : "";
258
- const countryNameToISO = {
259
- india: "IN",
260
- "united states": "US",
261
- canada: "CA",
262
- australia: "AU",
263
- "united kingdom": "GB",
264
- britain: "GB",
265
- uk: "GB",
266
- japan: "JP",
267
- "south korea": "KR",
268
- korea: "KR",
269
- germany: "DE",
270
- france: "FR",
271
- italy: "IT",
272
- spain: "ES",
273
- brazil: "BR",
274
- russia: "RU",
275
- singapore: "SG",
276
- indonesia: "ID",
277
- pakistan: "PK"
278
- };
279
- for (const [countryName, isoCode] of Object.entries(countryNameToISO)) {
280
- if (footerText.includes(countryName))
281
- scoreCountry(countryScores, isoCode, 0.4, "footer mention");
282
- }
283
- }
284
- const sorted = Object.entries(countryScores).sort(
285
- (a, b) => b[1].score - a[1].score
286
- );
287
- const best = sorted[0];
288
- return best ? {
289
- country: best[0],
290
- confidence: Math.min(1, best[1].score / 2),
291
- signals: best[1].reasons,
292
- currencyCode: detectedCurrencyCode
293
- } : {
294
- country: "Unknown",
295
- confidence: 0,
296
- signals: [],
297
- currencyCode: detectedCurrencyCode
298
- };
299
- }
300
-
301
- // src/utils/func.ts
302
- var import_tldts = require("tldts");
303
- function extractDomainWithoutSuffix(domain) {
304
- const parsedDomain = (0, import_tldts.parse)(domain);
305
- return parsedDomain.domainWithoutSuffix;
306
- }
307
- function generateStoreSlug(domain) {
308
- var _a;
309
- const input = new URL(domain);
310
- const parsedDomain = (0, import_tldts.parse)(input.href);
311
- const domainName = (_a = parsedDomain.domainWithoutSuffix) != null ? _a : input.hostname.split(".")[0];
312
- return (domainName || "").toLowerCase().replace(/[^a-z0-9]/g, "-").replace(/-+/g, "-").replace(/^-+|-+$/g, "");
313
- }
314
- function sanitizeDomain(input, opts) {
315
- var _a;
316
- if (typeof input !== "string") {
317
- throw new Error("sanitizeDomain: input must be a string");
318
- }
319
- let raw = input.trim();
320
- if (!raw) {
321
- throw new Error("sanitizeDomain: input cannot be empty");
322
- }
323
- const hasProtocol = /^[a-z]+:\/\//i.test(raw);
324
- if (!hasProtocol && !raw.startsWith("//")) {
325
- raw = `https://${raw}`;
326
- }
327
- const stripWWW = (_a = opts == null ? void 0 : opts.stripWWW) != null ? _a : true;
328
- try {
329
- let url;
330
- if (raw.startsWith("//")) {
331
- url = new URL(`https:${raw}`);
332
- } else if (raw.includes("://")) {
333
- url = new URL(raw);
334
- } else {
335
- url = new URL(`https://${raw}`);
336
- }
337
- let hostname = url.hostname.toLowerCase();
338
- const hadWWW = /^www\./i.test(url.hostname);
339
- if (stripWWW) hostname = hostname.replace(/^www\./, "");
340
- if (!hostname.includes(".")) {
341
- throw new Error("sanitizeDomain: invalid domain (missing suffix)");
342
- }
343
- const parsed = (0, import_tldts.parse)(hostname);
344
- if (!parsed.publicSuffix || parsed.isIcann === false) {
345
- throw new Error("sanitizeDomain: invalid domain (missing suffix)");
346
- }
347
- if (!stripWWW && hadWWW) {
348
- return `www.${parsed.domain || hostname}`;
349
- }
350
- return parsed.domain || hostname;
351
- } catch {
352
- let hostname = raw.toLowerCase();
353
- hostname = hostname.replace(/^[a-z]+:\/\//, "");
354
- hostname = hostname.replace(/^\/\//, "");
355
- hostname = hostname.replace(/[/:#?].*$/, "");
356
- const hadWWW = /^www\./i.test(hostname);
357
- if (stripWWW) hostname = hostname.replace(/^www\./, "");
358
- if (!hostname.includes(".")) {
359
- throw new Error("sanitizeDomain: invalid domain (missing suffix)");
360
- }
361
- const parsed = (0, import_tldts.parse)(hostname);
362
- if (!parsed.publicSuffix || parsed.isIcann === false) {
363
- throw new Error("sanitizeDomain: invalid domain (missing suffix)");
364
- }
365
- if (!stripWWW && hadWWW) {
366
- return `www.${parsed.domain || hostname}`;
367
- }
368
- return parsed.domain || hostname;
369
- }
370
- }
371
-
372
- // src/utils/rate-limit.ts
373
- var RateLimiter = class {
374
- constructor(options) {
375
- this.queue = [];
376
- this.inFlight = 0;
377
- this.refillTimer = null;
378
- this.options = options;
379
- this.tokens = options.maxRequestsPerInterval;
380
- }
381
- startRefill() {
382
- if (this.refillTimer) return;
383
- this.refillTimer = setInterval(() => {
384
- this.tokens = this.options.maxRequestsPerInterval;
385
- this.tryRun();
386
- }, this.options.intervalMs);
387
- if (this.refillTimer && typeof this.refillTimer.unref === "function") {
388
- this.refillTimer.unref();
389
- }
390
- }
391
- stopRefill() {
392
- if (this.refillTimer) {
393
- clearInterval(this.refillTimer);
394
- this.refillTimer = null;
395
- }
396
- }
397
- ensureRefillStarted() {
398
- if (!this.refillTimer) {
399
- this.startRefill();
400
- }
401
- }
402
- configure(next) {
403
- this.options = { ...this.options, ...next };
404
- this.options.maxRequestsPerInterval = Math.max(
405
- 1,
406
- this.options.maxRequestsPerInterval
407
- );
408
- this.options.intervalMs = Math.max(10, this.options.intervalMs);
409
- this.options.maxConcurrency = Math.max(1, this.options.maxConcurrency);
410
- }
411
- schedule(fn) {
412
- return new Promise((resolve, reject) => {
413
- this.ensureRefillStarted();
414
- this.queue.push({ fn, resolve, reject });
415
- this.tryRun();
416
- });
417
- }
418
- tryRun() {
419
- while (this.queue.length > 0 && this.inFlight < this.options.maxConcurrency && this.tokens > 0) {
420
- const task = this.queue.shift();
421
- this.tokens -= 1;
422
- this.inFlight += 1;
423
- Promise.resolve().then(task.fn).then((result) => task.resolve(result)).catch((err) => task.reject(err)).finally(() => {
424
- this.inFlight -= 1;
425
- setTimeout(() => this.tryRun(), 0);
426
- });
427
- }
428
- }
429
- };
430
- var enabled = false;
431
- var defaultOptions = {
432
- maxRequestsPerInterval: 5,
433
- // 5 requests
434
- intervalMs: 1e3,
435
- // per second
436
- maxConcurrency: 5
437
- // up to 5 in parallel
438
- };
439
- var limiter = new RateLimiter(defaultOptions);
440
- var hostLimiters = /* @__PURE__ */ new Map();
441
- var classLimiters = /* @__PURE__ */ new Map();
442
- function getHost(input) {
443
- try {
444
- if (typeof input === "string") {
445
- return new URL(input).host;
446
- }
447
- if (input instanceof URL) {
448
- return input.host;
449
- }
450
- const url = input.url;
451
- if (url) {
452
- return new URL(url).host;
453
- }
454
- } catch {
455
- }
456
- return void 0;
457
- }
458
- function getHostLimiter(host) {
459
- if (!host) return void 0;
460
- const exact = hostLimiters.get(host);
461
- if (exact) return exact;
462
- for (const [key, lim] of hostLimiters.entries()) {
463
- if (key.startsWith("*.") && host.endsWith(key.slice(2))) {
464
- return lim;
465
- }
466
- }
467
- return void 0;
468
- }
469
- async function rateLimitedFetch(input, init) {
470
- var _a;
471
- if (!enabled) {
472
- return fetch(input, init);
473
- }
474
- const klass = init == null ? void 0 : init.rateLimitClass;
475
- const byClass = klass ? classLimiters.get(klass) : void 0;
476
- const byHost = getHostLimiter(getHost(input));
477
- const eff = (_a = byClass != null ? byClass : byHost) != null ? _a : limiter;
478
- return eff.schedule(() => fetch(input, init));
479
- }
480
-
481
- // src/client/get-info.ts
482
- async function getInfoForStore(args) {
483
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
484
- const {
485
- baseUrl,
486
- storeDomain,
487
- validateProductExists,
488
- validateCollectionExists,
489
- validateLinksInBatches
490
- } = args;
491
- const response = await rateLimitedFetch(baseUrl);
492
- if (!response.ok) {
493
- throw new Error(`HTTP error! status: ${response.status}`);
494
- }
495
- const html = await response.text();
496
- const getMetaTag = (name2) => {
497
- const regex = new RegExp(
498
- `<meta[^>]*name=["']${name2}["'][^>]*content=["'](.*?)["']`
499
- );
500
- const match = html.match(regex);
501
- return match ? match[1] : null;
502
- };
503
- const getPropertyMetaTag = (property) => {
504
- const regex = new RegExp(
505
- `<meta[^>]*property=["']${property}["'][^>]*content=["'](.*?)["']`
506
- );
507
- const match = html.match(regex);
508
- return match ? match[1] : null;
509
- };
510
- const name = (_a = getMetaTag("og:site_name")) != null ? _a : extractDomainWithoutSuffix(baseUrl);
511
- const title = (_b = getMetaTag("og:title")) != null ? _b : getMetaTag("twitter:title");
512
- const description = getMetaTag("description") || getPropertyMetaTag("og:description");
513
- const shopifyWalletId = (_c = getMetaTag("shopify-digital-wallet")) == null ? void 0 : _c.split("/")[1];
514
- const myShopifySubdomainMatch = html.match(/['"](.*?\.myshopify\.com)['"]/);
515
- const myShopifySubdomain = myShopifySubdomainMatch ? myShopifySubdomainMatch[1] : null;
516
- let logoUrl = getPropertyMetaTag("og:image") || getPropertyMetaTag("og:image:secure_url");
517
- if (!logoUrl) {
518
- const logoMatch = html.match(
519
- /<img[^>]+src=["']([^"']+\/cdn\/shop\/[^"']+)["']/
520
- );
521
- const matchedUrl = logoMatch == null ? void 0 : logoMatch[1];
522
- logoUrl = matchedUrl ? matchedUrl.replace("http://", "https://") : null;
523
- } else {
524
- logoUrl = logoUrl.replace("http://", "https://");
525
- }
526
- const socialLinks = {};
527
- const socialRegex = /<a[^>]+href=["']([^"']*(?:facebook|twitter|instagram|pinterest|youtube|linkedin|tiktok|vimeo)\.com[^"']*)["']/g;
528
- for (const match of html.matchAll(socialRegex)) {
529
- const str = match[1];
530
- if (!str) continue;
531
- let href = str;
532
- try {
533
- if (href.startsWith("//")) {
534
- href = `https:${href}`;
535
- } else if (href.startsWith("/")) {
536
- href = new URL(href, baseUrl).toString();
537
- }
538
- const parsed = new URL(href);
539
- const domain = parsed.hostname.replace("www.", "").split(".")[0];
540
- if (domain) {
541
- socialLinks[domain] = parsed.toString();
542
- }
543
- } catch {
544
- }
545
- }
546
- const contactLinks = {
547
- tel: null,
548
- email: null,
549
- contactPage: null
550
- };
551
- for (const match of html.matchAll(/href=["']tel:([^"']+)["']/g)) {
552
- contactLinks.tel = ((_d = match == null ? void 0 : match[1]) == null ? void 0 : _d.trim()) || null;
553
- }
554
- for (const match of html.matchAll(/href=["']mailto:([^"']+)["']/g)) {
555
- contactLinks.email = ((_e = match == null ? void 0 : match[1]) == null ? void 0 : _e.trim()) || null;
556
- }
557
- for (const match of html.matchAll(
558
- /href=["']([^"']*(?:\/contact|\/pages\/contact)[^"']*)["']/g
559
- )) {
560
- contactLinks.contactPage = (match == null ? void 0 : match[1]) || null;
561
- }
562
- const extractedProductLinks = ((_g = (_f = html.match(/href=["']([^"']*\/products\/[^"']+)["']/g)) == null ? void 0 : _f.map(
563
- (match) => {
564
- var _a2, _b2;
565
- return (_b2 = (_a2 = match == null ? void 0 : match.split("href=")[1]) == null ? void 0 : _a2.replace(/["']/g, "")) == null ? void 0 : _b2.split("/").at(-1);
566
- }
567
- )) == null ? void 0 : _g.filter(Boolean)) || [];
568
- const extractedCollectionLinks = ((_i = (_h = html.match(/href=["']([^"']*\/collections\/[^"']+)["']/g)) == null ? void 0 : _h.map(
569
- (match) => {
570
- var _a2, _b2;
571
- return (_b2 = (_a2 = match == null ? void 0 : match.split("href=")[1]) == null ? void 0 : _a2.replace(/["']/g, "")) == null ? void 0 : _b2.split("/").at(-1);
572
- }
573
- )) == null ? void 0 : _i.filter(Boolean)) || [];
574
- const headerLinks = (_k = (_j = html.match(
575
- /<(header|nav|div|section)\b[^>]*\b(?:id|class)=["'][^"']*(?=.*shopify-section)(?=.*\b(header|navigation|nav|menu)\b)[^"']*["'][^>]*>[\s\S]*?<\/\1>/gi
576
- )) == null ? void 0 : _j.flatMap((header) => {
577
- var _a2, _b2;
578
- const links = (_a2 = header.match(/href=["']([^"']+)["']/g)) == null ? void 0 : _a2.filter(
579
- (link) => link.includes("/products/") || link.includes("/collections/") || link.includes("/pages/")
580
- );
581
- return (_b2 = links == null ? void 0 : links.map((link) => {
582
- var _a3;
583
- const href = (_a3 = link.match(/href=["']([^"']+)["']/)) == null ? void 0 : _a3[1];
584
- if (href && !href.startsWith("#") && !href.startsWith("javascript:")) {
585
- try {
586
- const url = new URL(href, storeDomain);
587
- return url.pathname.replace(/^\/|\/$/g, "");
588
- } catch {
589
- return href.replace(/^\/|\/$/g, "");
590
- }
591
- }
592
- return null;
593
- }).filter((item) => Boolean(item))) != null ? _b2 : [];
594
- })) != null ? _k : [];
595
- const slug = generateStoreSlug(baseUrl);
596
- const countryDetection = await detectShopifyCountry(html);
597
- const [homePageProductLinks, homePageCollectionLinks] = await Promise.all([
598
- validateLinksInBatches(
599
- extractedProductLinks.filter(
600
- (handle) => Boolean(handle)
601
- ),
602
- (handle) => validateProductExists(handle)
603
- ),
604
- validateLinksInBatches(
605
- extractedCollectionLinks.filter(
606
- (handle) => Boolean(handle)
607
- ),
608
- (handle) => validateCollectionExists(handle)
609
- )
610
- ]);
611
- const info = {
612
- name: name || slug,
613
- domain: sanitizeDomain(baseUrl),
614
- slug,
615
- title: title || null,
616
- description: description || null,
617
- logoUrl,
618
- socialLinks,
619
- contactLinks,
620
- headerLinks,
621
- showcase: {
622
- products: (0, import_remeda.unique)(homePageProductLinks != null ? homePageProductLinks : []),
623
- collections: (0, import_remeda.unique)(homePageCollectionLinks != null ? homePageCollectionLinks : [])
624
- },
625
- jsonLdData: ((_m = (_l = html.match(
626
- /<script[^>]*type="application\/ld\+json"[^>]*>([^<]+)<\/script>/g
627
- )) == null ? void 0 : _l.map(
628
- (match) => {
629
- var _a2;
630
- return ((_a2 = match == null ? void 0 : match.split(">")[1]) == null ? void 0 : _a2.replace(/<\/script/g, "")) || null;
631
- }
632
- )) == null ? void 0 : _m.map((json) => json ? JSON.parse(json) : null)) || [],
633
- techProvider: {
634
- name: "shopify",
635
- walletId: shopifyWalletId,
636
- subDomain: myShopifySubdomain != null ? myShopifySubdomain : null
637
- },
638
- country: countryDetection.country
639
- };
640
- const currencyCode = countryDetection == null ? void 0 : countryDetection.currencyCode;
641
- return { info, currencyCode };
642
- }
643
-
644
- // src/store.ts
645
- function createStoreOperations(context) {
646
- return {
647
- /**
648
- * Fetches comprehensive store information including metadata, social links, and showcase content.
649
- *
650
- * @returns {Promise<StoreInfo>} Store information object containing:
651
- * - `name` - Store name from meta tags or domain
652
- * - `domain` - Store domain URL
653
- * - `slug` - Generated store slug
654
- * - `title` - Store title from meta tags
655
- * - `description` - Store description from meta tags
656
- * - `logoUrl` - Store logo URL from Open Graph or CDN
657
- * - `socialLinks` - Object with social media links (facebook, twitter, instagram, etc.)
658
- * - `contactLinks` - Object with contact information (tel, email, contactPage)
659
- * - `headerLinks` - Array of navigation links from header
660
- * - `showcase` - Object with featured products and collections from homepage
661
- * - `jsonLdData` - Structured data from JSON-LD scripts
662
- * - `techProvider` - Shopify-specific information (walletId, subDomain)
663
- * - `country` - Country detection results with ISO 3166-1 alpha-2 codes (e.g., "US", "GB")
664
- *
665
- * @throws {Error} When the store URL is unreachable or returns an error
666
- *
667
- * @example
668
- * ```typescript
669
- * const shop = new ShopClient('https://exampleshop.com');
670
- * const storeInfo = await shop.getInfo();
671
- *
672
- * console.log(storeInfo.name); // "Example Store"
673
- * console.log(storeInfo.socialLinks.instagram); // "https://instagram.com/example"
674
- * console.log(storeInfo.showcase.products); // ["product-handle-1", "product-handle-2"]
675
- * console.log(storeInfo.country); // "US"
676
- * ```
677
- */
678
- info: async () => {
679
- try {
680
- const { info } = await getInfoForStore({
681
- baseUrl: context.baseUrl,
682
- storeDomain: context.storeDomain,
683
- validateProductExists: context.validateProductExists,
684
- validateCollectionExists: context.validateCollectionExists,
685
- validateLinksInBatches: context.validateLinksInBatches
686
- });
687
- return info;
688
- } catch (error) {
689
- context.handleFetchError(error, "fetching store info", context.baseUrl);
690
- }
691
- }
692
- };
693
- }
694
- // Annotate the CommonJS export names for ESM import in node:
695
- 0 && (module.exports = {
696
- createStoreOperations
697
- });
698
- //# sourceMappingURL=store.js.map