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
@@ -1,503 +0,0 @@
1
- import {
2
- extractDomainWithoutSuffix,
3
- generateStoreSlug,
4
- sanitizeDomain
5
- } from "./chunk-BWKBRM2Z.mjs";
6
- import {
7
- rateLimitedFetch
8
- } from "./chunk-2KBOKOAD.mjs";
9
-
10
- // src/client/get-info.ts
11
- import { unique } from "remeda";
12
-
13
- // src/utils/detect-country.ts
14
- var COUNTRY_CODES = {
15
- "+1": "US",
16
- // United States (primary) / Canada also uses +1
17
- "+44": "GB",
18
- // United Kingdom
19
- "+61": "AU",
20
- // Australia
21
- "+65": "SG",
22
- // Singapore
23
- "+91": "IN",
24
- // India
25
- "+81": "JP",
26
- // Japan
27
- "+49": "DE",
28
- // Germany
29
- "+33": "FR",
30
- // France
31
- "+971": "AE",
32
- // United Arab Emirates
33
- "+39": "IT",
34
- // Italy
35
- "+34": "ES",
36
- // Spain
37
- "+82": "KR",
38
- // South Korea
39
- "+55": "BR",
40
- // Brazil
41
- "+62": "ID",
42
- // Indonesia
43
- "+92": "PK",
44
- // Pakistan
45
- "+7": "RU"
46
- // Russia
47
- };
48
- var CURRENCY_SYMBOLS = {
49
- Rs: "IN",
50
- // India
51
- "\u20B9": "IN",
52
- // India
53
- $: "US",
54
- // United States (primary, though many countries use $)
55
- CA$: "CA",
56
- // Canada
57
- A$: "AU",
58
- // Australia
59
- "\xA3": "GB",
60
- // United Kingdom
61
- "\u20AC": "EU",
62
- // European Union (not a country code, but commonly used)
63
- AED: "AE",
64
- // United Arab Emirates
65
- "\u20A9": "KR",
66
- // South Korea
67
- "\xA5": "JP"
68
- // Japan (primary, though China also uses ¥)
69
- };
70
- var CURRENCY_SYMBOL_TO_CODE = {
71
- Rs: "INR",
72
- "\u20B9": "INR",
73
- $: "USD",
74
- CA$: "CAD",
75
- A$: "AUD",
76
- "\xA3": "GBP",
77
- "\u20AC": "EUR",
78
- AED: "AED",
79
- "\u20A9": "KRW",
80
- "\xA5": "JPY"
81
- };
82
- var CURRENCY_CODE_TO_COUNTRY = {
83
- INR: "IN",
84
- USD: "US",
85
- CAD: "CA",
86
- AUD: "AU",
87
- GBP: "GB",
88
- EUR: "EU",
89
- AED: "AE",
90
- KRW: "KR",
91
- JPY: "JP"
92
- };
93
- function scoreCountry(countryScores, country, weight, reason) {
94
- if (!country) return;
95
- if (!countryScores[country])
96
- countryScores[country] = { score: 0, reasons: [] };
97
- countryScores[country].score += weight;
98
- countryScores[country].reasons.push(reason);
99
- }
100
- async function detectShopifyCountry(html) {
101
- var _a, _b;
102
- const countryScores = {};
103
- let detectedCurrencyCode;
104
- const shopifyFeaturesMatch = html.match(
105
- /<script[^>]+id=["']shopify-features["'][^>]*>([\s\S]*?)<\/script>/
106
- );
107
- if (shopifyFeaturesMatch) {
108
- try {
109
- const json = shopifyFeaturesMatch[1];
110
- if (!json) {
111
- } else {
112
- const data = JSON.parse(json);
113
- if (data.country)
114
- scoreCountry(
115
- countryScores,
116
- data.country,
117
- 1,
118
- "shopify-features.country"
119
- );
120
- if ((_a = data.locale) == null ? void 0 : _a.includes("-")) {
121
- const [, localeCountry] = data.locale.split("-");
122
- if (localeCountry) {
123
- scoreCountry(
124
- countryScores,
125
- localeCountry.toUpperCase(),
126
- 0.7,
127
- "shopify-features.locale"
128
- );
129
- }
130
- }
131
- if (data.moneyFormat) {
132
- for (const symbol in CURRENCY_SYMBOLS) {
133
- if (data.moneyFormat.includes(symbol)) {
134
- const iso = CURRENCY_SYMBOLS[symbol];
135
- if (typeof iso === "string") {
136
- scoreCountry(countryScores, iso, 0.6, "moneyFormat symbol");
137
- }
138
- const code = CURRENCY_SYMBOL_TO_CODE[symbol];
139
- if (!detectedCurrencyCode && typeof code === "string") {
140
- detectedCurrencyCode = code;
141
- }
142
- }
143
- }
144
- }
145
- }
146
- } catch (_error) {
147
- }
148
- }
149
- const currencyJsonMatch = html.match(/Shopify\.currency\s*=\s*(\{[^}]*\})/);
150
- if (currencyJsonMatch) {
151
- try {
152
- const raw = currencyJsonMatch[1];
153
- const obj = JSON.parse(raw || "{}");
154
- const activeCode = typeof (obj == null ? void 0 : obj.active) === "string" ? obj.active.toUpperCase() : void 0;
155
- const iso = activeCode ? CURRENCY_CODE_TO_COUNTRY[activeCode] : void 0;
156
- if (activeCode) {
157
- detectedCurrencyCode = activeCode;
158
- }
159
- if (typeof iso === "string") {
160
- scoreCountry(countryScores, iso, 0.8, "Shopify.currency.active");
161
- }
162
- } catch (_error) {
163
- }
164
- } else {
165
- const currencyActiveAssignMatch = html.match(
166
- /Shopify\.currency\.active\s*=\s*['"]([A-Za-z]{3})['"]/i
167
- );
168
- if (currencyActiveAssignMatch) {
169
- const captured = currencyActiveAssignMatch[1];
170
- const code = typeof captured === "string" ? captured.toUpperCase() : void 0;
171
- const iso = code ? CURRENCY_CODE_TO_COUNTRY[code] : void 0;
172
- if (code) {
173
- detectedCurrencyCode = code;
174
- }
175
- if (typeof iso === "string") {
176
- scoreCountry(countryScores, iso, 0.8, "Shopify.currency.active");
177
- }
178
- }
179
- }
180
- const shopifyCountryMatch = html.match(
181
- /Shopify\.country\s*=\s*['"]([A-Za-z]{2})['"]/i
182
- );
183
- if (shopifyCountryMatch) {
184
- const captured = shopifyCountryMatch[1];
185
- const iso = typeof captured === "string" ? captured.toUpperCase() : void 0;
186
- if (typeof iso === "string") {
187
- scoreCountry(countryScores, iso, 1, "Shopify.country");
188
- }
189
- }
190
- const phones = html.match(/\+\d{1,3}[\s\-()0-9]{5,}/g);
191
- if (phones) {
192
- for (const phone of phones) {
193
- const prefix = (_b = phone.match(/^\+\d{1,3}/)) == null ? void 0 : _b[0];
194
- if (prefix && COUNTRY_CODES[prefix])
195
- scoreCountry(
196
- countryScores,
197
- COUNTRY_CODES[prefix],
198
- 0.8,
199
- `phone prefix ${prefix}`
200
- );
201
- }
202
- }
203
- const jsonLdRegex = /<script[^>]+application\/ld\+json[^>]*>(.*?)<\/script>/g;
204
- let jsonLdMatch = jsonLdRegex.exec(html);
205
- while (jsonLdMatch !== null) {
206
- try {
207
- const json = jsonLdMatch[1];
208
- if (!json) {
209
- } else {
210
- const raw = JSON.parse(json);
211
- const collectAddressCountries = (node, results = []) => {
212
- if (Array.isArray(node)) {
213
- for (const item of node) collectAddressCountries(item, results);
214
- return results;
215
- }
216
- if (node && typeof node === "object") {
217
- const obj = node;
218
- const address = obj.address;
219
- if (address && typeof address === "object") {
220
- const country = address.addressCountry;
221
- if (typeof country === "string") results.push(country);
222
- }
223
- const graph = obj["@graph"];
224
- if (graph) collectAddressCountries(graph, results);
225
- }
226
- return results;
227
- };
228
- const countries = collectAddressCountries(raw);
229
- for (const country of countries) {
230
- scoreCountry(countryScores, country, 1, "JSON-LD addressCountry");
231
- }
232
- }
233
- } catch (_error) {
234
- }
235
- jsonLdMatch = jsonLdRegex.exec(html);
236
- }
237
- const footerMatch = html.match(/<footer[^>]*>(.*?)<\/footer>/i);
238
- if (footerMatch) {
239
- const footerTextGroup = footerMatch[1];
240
- const footerText = footerTextGroup ? footerTextGroup.toLowerCase() : "";
241
- const countryNameToISO = {
242
- india: "IN",
243
- "united states": "US",
244
- canada: "CA",
245
- australia: "AU",
246
- "united kingdom": "GB",
247
- britain: "GB",
248
- uk: "GB",
249
- japan: "JP",
250
- "south korea": "KR",
251
- korea: "KR",
252
- germany: "DE",
253
- france: "FR",
254
- italy: "IT",
255
- spain: "ES",
256
- brazil: "BR",
257
- russia: "RU",
258
- singapore: "SG",
259
- indonesia: "ID",
260
- pakistan: "PK"
261
- };
262
- for (const [countryName, isoCode] of Object.entries(countryNameToISO)) {
263
- if (footerText.includes(countryName))
264
- scoreCountry(countryScores, isoCode, 0.4, "footer mention");
265
- }
266
- }
267
- const sorted = Object.entries(countryScores).sort(
268
- (a, b) => b[1].score - a[1].score
269
- );
270
- const best = sorted[0];
271
- return best ? {
272
- country: best[0],
273
- confidence: Math.min(1, best[1].score / 2),
274
- signals: best[1].reasons,
275
- currencyCode: detectedCurrencyCode
276
- } : {
277
- country: "Unknown",
278
- confidence: 0,
279
- signals: [],
280
- currencyCode: detectedCurrencyCode
281
- };
282
- }
283
-
284
- // src/client/get-info.ts
285
- async function getInfoForStore(args) {
286
- var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m;
287
- const {
288
- baseUrl,
289
- storeDomain,
290
- validateProductExists,
291
- validateCollectionExists,
292
- validateLinksInBatches
293
- } = args;
294
- const response = await rateLimitedFetch(baseUrl);
295
- if (!response.ok) {
296
- throw new Error(`HTTP error! status: ${response.status}`);
297
- }
298
- const html = await response.text();
299
- const getMetaTag = (name2) => {
300
- const regex = new RegExp(
301
- `<meta[^>]*name=["']${name2}["'][^>]*content=["'](.*?)["']`
302
- );
303
- const match = html.match(regex);
304
- return match ? match[1] : null;
305
- };
306
- const getPropertyMetaTag = (property) => {
307
- const regex = new RegExp(
308
- `<meta[^>]*property=["']${property}["'][^>]*content=["'](.*?)["']`
309
- );
310
- const match = html.match(regex);
311
- return match ? match[1] : null;
312
- };
313
- const name = (_a = getMetaTag("og:site_name")) != null ? _a : extractDomainWithoutSuffix(baseUrl);
314
- const title = (_b = getMetaTag("og:title")) != null ? _b : getMetaTag("twitter:title");
315
- const description = getMetaTag("description") || getPropertyMetaTag("og:description");
316
- const shopifyWalletId = (_c = getMetaTag("shopify-digital-wallet")) == null ? void 0 : _c.split("/")[1];
317
- const myShopifySubdomainMatch = html.match(/['"](.*?\.myshopify\.com)['"]/);
318
- const myShopifySubdomain = myShopifySubdomainMatch ? myShopifySubdomainMatch[1] : null;
319
- let logoUrl = getPropertyMetaTag("og:image") || getPropertyMetaTag("og:image:secure_url");
320
- if (!logoUrl) {
321
- const logoMatch = html.match(
322
- /<img[^>]+src=["']([^"']+\/cdn\/shop\/[^"']+)["']/
323
- );
324
- const matchedUrl = logoMatch == null ? void 0 : logoMatch[1];
325
- logoUrl = matchedUrl ? matchedUrl.replace("http://", "https://") : null;
326
- } else {
327
- logoUrl = logoUrl.replace("http://", "https://");
328
- }
329
- const socialLinks = {};
330
- const socialRegex = /<a[^>]+href=["']([^"']*(?:facebook|twitter|instagram|pinterest|youtube|linkedin|tiktok|vimeo)\.com[^"']*)["']/g;
331
- for (const match of html.matchAll(socialRegex)) {
332
- const str = match[1];
333
- if (!str) continue;
334
- let href = str;
335
- try {
336
- if (href.startsWith("//")) {
337
- href = `https:${href}`;
338
- } else if (href.startsWith("/")) {
339
- href = new URL(href, baseUrl).toString();
340
- }
341
- const parsed = new URL(href);
342
- const domain = parsed.hostname.replace("www.", "").split(".")[0];
343
- if (domain) {
344
- socialLinks[domain] = parsed.toString();
345
- }
346
- } catch {
347
- }
348
- }
349
- const contactLinks = {
350
- tel: null,
351
- email: null,
352
- contactPage: null
353
- };
354
- for (const match of html.matchAll(/href=["']tel:([^"']+)["']/g)) {
355
- contactLinks.tel = ((_d = match == null ? void 0 : match[1]) == null ? void 0 : _d.trim()) || null;
356
- }
357
- for (const match of html.matchAll(/href=["']mailto:([^"']+)["']/g)) {
358
- contactLinks.email = ((_e = match == null ? void 0 : match[1]) == null ? void 0 : _e.trim()) || null;
359
- }
360
- for (const match of html.matchAll(
361
- /href=["']([^"']*(?:\/contact|\/pages\/contact)[^"']*)["']/g
362
- )) {
363
- contactLinks.contactPage = (match == null ? void 0 : match[1]) || null;
364
- }
365
- const extractedProductLinks = ((_g = (_f = html.match(/href=["']([^"']*\/products\/[^"']+)["']/g)) == null ? void 0 : _f.map(
366
- (match) => {
367
- var _a2, _b2;
368
- return (_b2 = (_a2 = match == null ? void 0 : match.split("href=")[1]) == null ? void 0 : _a2.replace(/["']/g, "")) == null ? void 0 : _b2.split("/").at(-1);
369
- }
370
- )) == null ? void 0 : _g.filter(Boolean)) || [];
371
- const extractedCollectionLinks = ((_i = (_h = html.match(/href=["']([^"']*\/collections\/[^"']+)["']/g)) == null ? void 0 : _h.map(
372
- (match) => {
373
- var _a2, _b2;
374
- return (_b2 = (_a2 = match == null ? void 0 : match.split("href=")[1]) == null ? void 0 : _a2.replace(/["']/g, "")) == null ? void 0 : _b2.split("/").at(-1);
375
- }
376
- )) == null ? void 0 : _i.filter(Boolean)) || [];
377
- const headerLinks = (_k = (_j = html.match(
378
- /<(header|nav|div|section)\b[^>]*\b(?:id|class)=["'][^"']*(?=.*shopify-section)(?=.*\b(header|navigation|nav|menu)\b)[^"']*["'][^>]*>[\s\S]*?<\/\1>/gi
379
- )) == null ? void 0 : _j.flatMap((header) => {
380
- var _a2, _b2;
381
- const links = (_a2 = header.match(/href=["']([^"']+)["']/g)) == null ? void 0 : _a2.filter(
382
- (link) => link.includes("/products/") || link.includes("/collections/") || link.includes("/pages/")
383
- );
384
- return (_b2 = links == null ? void 0 : links.map((link) => {
385
- var _a3;
386
- const href = (_a3 = link.match(/href=["']([^"']+)["']/)) == null ? void 0 : _a3[1];
387
- if (href && !href.startsWith("#") && !href.startsWith("javascript:")) {
388
- try {
389
- const url = new URL(href, storeDomain);
390
- return url.pathname.replace(/^\/|\/$/g, "");
391
- } catch {
392
- return href.replace(/^\/|\/$/g, "");
393
- }
394
- }
395
- return null;
396
- }).filter((item) => Boolean(item))) != null ? _b2 : [];
397
- })) != null ? _k : [];
398
- const slug = generateStoreSlug(baseUrl);
399
- const countryDetection = await detectShopifyCountry(html);
400
- const [homePageProductLinks, homePageCollectionLinks] = await Promise.all([
401
- validateLinksInBatches(
402
- extractedProductLinks.filter(
403
- (handle) => Boolean(handle)
404
- ),
405
- (handle) => validateProductExists(handle)
406
- ),
407
- validateLinksInBatches(
408
- extractedCollectionLinks.filter(
409
- (handle) => Boolean(handle)
410
- ),
411
- (handle) => validateCollectionExists(handle)
412
- )
413
- ]);
414
- const info = {
415
- name: name || slug,
416
- domain: sanitizeDomain(baseUrl),
417
- slug,
418
- title: title || null,
419
- description: description || null,
420
- logoUrl,
421
- socialLinks,
422
- contactLinks,
423
- headerLinks,
424
- showcase: {
425
- products: unique(homePageProductLinks != null ? homePageProductLinks : []),
426
- collections: unique(homePageCollectionLinks != null ? homePageCollectionLinks : [])
427
- },
428
- jsonLdData: ((_m = (_l = html.match(
429
- /<script[^>]*type="application\/ld\+json"[^>]*>([^<]+)<\/script>/g
430
- )) == null ? void 0 : _l.map(
431
- (match) => {
432
- var _a2;
433
- return ((_a2 = match == null ? void 0 : match.split(">")[1]) == null ? void 0 : _a2.replace(/<\/script/g, "")) || null;
434
- }
435
- )) == null ? void 0 : _m.map((json) => json ? JSON.parse(json) : null)) || [],
436
- techProvider: {
437
- name: "shopify",
438
- walletId: shopifyWalletId,
439
- subDomain: myShopifySubdomain != null ? myShopifySubdomain : null
440
- },
441
- country: countryDetection.country
442
- };
443
- const currencyCode = countryDetection == null ? void 0 : countryDetection.currencyCode;
444
- return { info, currencyCode };
445
- }
446
-
447
- // src/store.ts
448
- function createStoreOperations(context) {
449
- return {
450
- /**
451
- * Fetches comprehensive store information including metadata, social links, and showcase content.
452
- *
453
- * @returns {Promise<StoreInfo>} Store information object containing:
454
- * - `name` - Store name from meta tags or domain
455
- * - `domain` - Store domain URL
456
- * - `slug` - Generated store slug
457
- * - `title` - Store title from meta tags
458
- * - `description` - Store description from meta tags
459
- * - `logoUrl` - Store logo URL from Open Graph or CDN
460
- * - `socialLinks` - Object with social media links (facebook, twitter, instagram, etc.)
461
- * - `contactLinks` - Object with contact information (tel, email, contactPage)
462
- * - `headerLinks` - Array of navigation links from header
463
- * - `showcase` - Object with featured products and collections from homepage
464
- * - `jsonLdData` - Structured data from JSON-LD scripts
465
- * - `techProvider` - Shopify-specific information (walletId, subDomain)
466
- * - `country` - Country detection results with ISO 3166-1 alpha-2 codes (e.g., "US", "GB")
467
- *
468
- * @throws {Error} When the store URL is unreachable or returns an error
469
- *
470
- * @example
471
- * ```typescript
472
- * const shop = new ShopClient('https://exampleshop.com');
473
- * const storeInfo = await shop.getInfo();
474
- *
475
- * console.log(storeInfo.name); // "Example Store"
476
- * console.log(storeInfo.socialLinks.instagram); // "https://instagram.com/example"
477
- * console.log(storeInfo.showcase.products); // ["product-handle-1", "product-handle-2"]
478
- * console.log(storeInfo.country); // "US"
479
- * ```
480
- */
481
- info: async () => {
482
- try {
483
- const { info } = await getInfoForStore({
484
- baseUrl: context.baseUrl,
485
- storeDomain: context.storeDomain,
486
- validateProductExists: context.validateProductExists,
487
- validateCollectionExists: context.validateCollectionExists,
488
- validateLinksInBatches: context.validateLinksInBatches
489
- });
490
- return info;
491
- } catch (error) {
492
- context.handleFetchError(error, "fetching store info", context.baseUrl);
493
- }
494
- }
495
- };
496
- }
497
-
498
- export {
499
- detectShopifyCountry,
500
- getInfoForStore,
501
- createStoreOperations
502
- };
503
- //# sourceMappingURL=chunk-O4BPIIQ6.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/client/get-info.ts","../src/utils/detect-country.ts","../src/store.ts"],"sourcesContent":["import { unique } from \"remeda\";\nimport type { StoreInfo } from \"../store\";\nimport { detectShopifyCountry } from \"../utils/detect-country\";\nimport {\n extractDomainWithoutSuffix,\n generateStoreSlug,\n sanitizeDomain,\n} from \"../utils/func\";\nimport { rateLimitedFetch } from \"../utils/rate-limit\";\n\ntype Args = {\n baseUrl: string;\n storeDomain: string;\n validateProductExists: (handle: string) => Promise<boolean>;\n validateCollectionExists: (handle: string) => Promise<boolean>;\n validateLinksInBatches: <T>(\n items: T[],\n validator: (item: T) => Promise<boolean>,\n batchSize?: number\n ) => Promise<T[]>;\n};\n\n/**\n * Fetches comprehensive store information including metadata, social links, and showcase content.\n * Returns the structured StoreInfo and detected currency code (if available).\n */\nexport async function getInfoForStore(\n args: Args\n): Promise<{ info: StoreInfo; currencyCode?: string }> {\n const {\n baseUrl,\n storeDomain,\n validateProductExists,\n validateCollectionExists,\n validateLinksInBatches,\n } = args;\n\n const response = await rateLimitedFetch(baseUrl);\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n const html = await response.text();\n\n const getMetaTag = (name: string) => {\n const regex = new RegExp(\n `<meta[^>]*name=[\"']${name}[\"'][^>]*content=[\"'](.*?)[\"']`\n );\n const match = html.match(regex);\n return match ? match[1] : null;\n };\n\n const getPropertyMetaTag = (property: string) => {\n const regex = new RegExp(\n `<meta[^>]*property=[\"']${property}[\"'][^>]*content=[\"'](.*?)[\"']`\n );\n const match = html.match(regex);\n return match ? match[1] : null;\n };\n\n const name =\n getMetaTag(\"og:site_name\") ?? extractDomainWithoutSuffix(baseUrl);\n const title = getMetaTag(\"og:title\") ?? getMetaTag(\"twitter:title\");\n const description =\n getMetaTag(\"description\") || getPropertyMetaTag(\"og:description\");\n\n const shopifyWalletId = getMetaTag(\"shopify-digital-wallet\")?.split(\"/\")[1];\n\n const myShopifySubdomainMatch = html.match(/['\"](.*?\\.myshopify\\.com)['\"]/);\n const myShopifySubdomain = myShopifySubdomainMatch\n ? myShopifySubdomainMatch[1]\n : null;\n\n let logoUrl =\n getPropertyMetaTag(\"og:image\") || getPropertyMetaTag(\"og:image:secure_url\");\n if (!logoUrl) {\n const logoMatch = html.match(\n /<img[^>]+src=[\"']([^\"']+\\/cdn\\/shop\\/[^\"']+)[\"']/\n );\n const matchedUrl = logoMatch?.[1];\n logoUrl = matchedUrl ? matchedUrl.replace(\"http://\", \"https://\") : null;\n } else {\n logoUrl = logoUrl.replace(\"http://\", \"https://\");\n }\n\n const socialLinks: Record<string, string> = {};\n const socialRegex =\n /<a[^>]+href=[\"']([^\"']*(?:facebook|twitter|instagram|pinterest|youtube|linkedin|tiktok|vimeo)\\.com[^\"']*)[\"']/g;\n for (const match of html.matchAll(socialRegex)) {\n const str = match[1];\n if (!str) continue;\n let href: string = str;\n try {\n if (href.startsWith(\"//\")) {\n href = `https:${href}`;\n } else if (href.startsWith(\"/\")) {\n href = new URL(href, baseUrl).toString();\n }\n const parsed = new URL(href);\n const domain = parsed.hostname.replace(\"www.\", \"\").split(\".\")[0];\n if (domain) {\n socialLinks[domain] = parsed.toString();\n }\n } catch {\n // Skip invalid URLs without failing\n }\n }\n\n const contactLinks = {\n tel: null as string | null,\n email: null as string | null,\n contactPage: null as string | null,\n };\n\n for (const match of html.matchAll(/href=[\"']tel:([^\"']+)[\"']/g)) {\n contactLinks.tel = match?.[1]?.trim() || null;\n }\n for (const match of html.matchAll(/href=[\"']mailto:([^\"']+)[\"']/g)) {\n contactLinks.email = match?.[1]?.trim() || null;\n }\n for (const match of html.matchAll(\n /href=[\"']([^\"']*(?:\\/contact|\\/pages\\/contact)[^\"']*)[\"']/g\n )) {\n contactLinks.contactPage = match?.[1] || null;\n }\n\n const extractedProductLinks =\n html\n .match(/href=[\"']([^\"']*\\/products\\/[^\"']+)[\"']/g)\n ?.map((match) =>\n match?.split(\"href=\")[1]?.replace(/[\"']/g, \"\")?.split(\"/\").at(-1)\n )\n ?.filter(Boolean) || [];\n\n const extractedCollectionLinks =\n html\n .match(/href=[\"']([^\"']*\\/collections\\/[^\"']+)[\"']/g)\n ?.map((match) =>\n match?.split(\"href=\")[1]?.replace(/[\"']/g, \"\")?.split(\"/\").at(-1)\n )\n ?.filter(Boolean) || [];\n\n const headerLinks =\n html\n .match(\n /<(header|nav|div|section)\\b[^>]*\\b(?:id|class)=[\"'][^\"']*(?=.*shopify-section)(?=.*\\b(header|navigation|nav|menu)\\b)[^\"']*[\"'][^>]*>[\\s\\S]*?<\\/\\1>/gi\n )\n ?.flatMap((header) => {\n const links = header\n .match(/href=[\"']([^\"']+)[\"']/g)\n ?.filter(\n (link) =>\n link.includes(\"/products/\") ||\n link.includes(\"/collections/\") ||\n link.includes(\"/pages/\")\n );\n return (\n links\n ?.map((link) => {\n const href = link.match(/href=[\"']([^\"']+)[\"']/)?.[1];\n if (\n href &&\n !href.startsWith(\"#\") &&\n !href.startsWith(\"javascript:\")\n ) {\n try {\n const url = new URL(href, storeDomain);\n return url.pathname.replace(/^\\/|\\/$/g, \"\");\n } catch {\n return href.replace(/^\\/|\\/$/g, \"\");\n }\n }\n return null;\n })\n .filter((item): item is string => Boolean(item)) ?? []\n );\n }) ?? [];\n\n const slug = generateStoreSlug(baseUrl);\n\n const countryDetection = await detectShopifyCountry(html);\n\n const [homePageProductLinks, homePageCollectionLinks] = await Promise.all([\n validateLinksInBatches(\n extractedProductLinks.filter((handle): handle is string =>\n Boolean(handle)\n ),\n (handle) => validateProductExists(handle)\n ),\n validateLinksInBatches(\n extractedCollectionLinks.filter((handle): handle is string =>\n Boolean(handle)\n ),\n (handle) => validateCollectionExists(handle)\n ),\n ]);\n\n const info: StoreInfo = {\n name: name || slug,\n domain: sanitizeDomain(baseUrl),\n slug,\n title: title || null,\n description: description || null,\n logoUrl: logoUrl,\n socialLinks,\n contactLinks,\n headerLinks,\n showcase: {\n products: unique(homePageProductLinks ?? []),\n collections: unique(homePageCollectionLinks ?? []),\n },\n jsonLdData:\n html\n .match(\n /<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([^<]+)<\\/script>/g\n )\n ?.map(\n (match) => match?.split(\">\")[1]?.replace(/<\\/script/g, \"\") || null\n )\n ?.map((json) => (json ? JSON.parse(json) : null)) || [],\n techProvider: {\n name: \"shopify\",\n walletId: shopifyWalletId,\n subDomain: myShopifySubdomain ?? null,\n },\n country: countryDetection.country,\n };\n\n const currencyCode = (countryDetection as any)?.currencyCode;\n return { info, currencyCode };\n}\n","import type {\n CountryDetectionResult,\n CountryScores,\n ShopifyFeaturesData,\n} from \"../types\";\n\nconst COUNTRY_CODES: Record<string, string> = {\n \"+1\": \"US\", // United States (primary) / Canada also uses +1\n \"+44\": \"GB\", // United Kingdom\n \"+61\": \"AU\", // Australia\n \"+65\": \"SG\", // Singapore\n \"+91\": \"IN\", // India\n \"+81\": \"JP\", // Japan\n \"+49\": \"DE\", // Germany\n \"+33\": \"FR\", // France\n \"+971\": \"AE\", // United Arab Emirates\n \"+39\": \"IT\", // Italy\n \"+34\": \"ES\", // Spain\n \"+82\": \"KR\", // South Korea\n \"+55\": \"BR\", // Brazil\n \"+62\": \"ID\", // Indonesia\n \"+92\": \"PK\", // Pakistan\n \"+7\": \"RU\", // Russia\n};\n\nconst CURRENCY_SYMBOLS: Record<string, string> = {\n Rs: \"IN\", // India\n \"₹\": \"IN\", // India\n $: \"US\", // United States (primary, though many countries use $)\n CA$: \"CA\", // Canada\n A$: \"AU\", // Australia\n \"£\": \"GB\", // United Kingdom\n \"€\": \"EU\", // European Union (not a country code, but commonly used)\n AED: \"AE\", // United Arab Emirates\n \"₩\": \"KR\", // South Korea\n \"¥\": \"JP\", // Japan (primary, though China also uses ¥)\n};\n\n// Map currency symbols commonly found in Shopify money formats to ISO currency codes\nconst CURRENCY_SYMBOL_TO_CODE: Record<string, string> = {\n Rs: \"INR\",\n \"₹\": \"INR\",\n $: \"USD\",\n CA$: \"CAD\",\n A$: \"AUD\",\n \"£\": \"GBP\",\n \"€\": \"EUR\",\n AED: \"AED\",\n \"₩\": \"KRW\",\n \"¥\": \"JPY\",\n};\n\n// Map Shopify currency codes to likely ISO country codes\n// Note: Some codes (e.g., USD, EUR) are used by multiple countries; we treat them as signals.\nconst CURRENCY_CODE_TO_COUNTRY: Record<string, string> = {\n INR: \"IN\",\n USD: \"US\",\n CAD: \"CA\",\n AUD: \"AU\",\n GBP: \"GB\",\n EUR: \"EU\",\n AED: \"AE\",\n KRW: \"KR\",\n JPY: \"JP\",\n};\n\nfunction scoreCountry(\n countryScores: CountryScores,\n country: string,\n weight: number,\n reason: string\n): void {\n if (!country) return;\n if (!countryScores[country])\n countryScores[country] = { score: 0, reasons: [] };\n countryScores[country].score += weight;\n countryScores[country].reasons.push(reason);\n}\n\n/**\n * Detects the country of a Shopify store by analyzing various signals in the HTML content.\n *\n * This function examines multiple data sources within the HTML to determine the store's country:\n * - Shopify features JSON data (country, locale, money format)\n * - Phone number prefixes in contact information\n * - JSON-LD structured data with address information\n * - Footer mentions of country names\n * - Currency symbols in money formatting\n *\n * @param html - The HTML content of the Shopify store's homepage\n * @returns Promise resolving to country detection results containing:\n * - `country` - The detected country ISO 3166-1 alpha-2 code (e.g., \"US\", \"GB\") or \"Unknown\" if no reliable detection\n * - `confidence` - Confidence score between 0 and 1 (higher = more confident)\n * - `signals` - Array of detection signals that contributed to the result\n *\n * @example\n * ```typescript\n * const response = await fetch('https://exampleshop.com');\n * const html = await response.text();\n * const result = await detectShopifyCountry(html);\n *\n * console.log(result.country); // \"US\" (ISO code for United States)\n * console.log(result.confidence); // 0.85\n * console.log(result.signals); // [\"shopify-features.country\", \"phone prefix +1\"]\n * ```\n */\nexport async function detectShopifyCountry(\n html: string\n): Promise<CountryDetectionResult> {\n const countryScores: CountryScores = {};\n let detectedCurrencyCode: string | undefined;\n\n // 1️⃣ Extract Shopify features JSON\n const shopifyFeaturesMatch = html.match(\n /<script[^>]+id=[\"']shopify-features[\"'][^>]*>([\\s\\S]*?)<\\/script>/\n );\n if (shopifyFeaturesMatch) {\n try {\n const json = shopifyFeaturesMatch[1];\n if (!json) {\n // no content in capture group; skip\n } else {\n const data: ShopifyFeaturesData = JSON.parse(json);\n if (data.country)\n scoreCountry(\n countryScores,\n data.country,\n 1,\n \"shopify-features.country\"\n );\n if (data.locale?.includes(\"-\")) {\n const [, localeCountry] = data.locale.split(\"-\");\n if (localeCountry) {\n scoreCountry(\n countryScores,\n localeCountry.toUpperCase(),\n 0.7,\n \"shopify-features.locale\"\n );\n }\n }\n if (data.moneyFormat) {\n for (const symbol in CURRENCY_SYMBOLS) {\n if (data.moneyFormat.includes(symbol)) {\n const iso =\n CURRENCY_SYMBOLS[symbol as keyof typeof CURRENCY_SYMBOLS];\n if (typeof iso === \"string\") {\n scoreCountry(countryScores, iso, 0.6, \"moneyFormat symbol\");\n }\n // Also capture currency code if symbol is recognized\n const code =\n CURRENCY_SYMBOL_TO_CODE[\n symbol as keyof typeof CURRENCY_SYMBOL_TO_CODE\n ];\n if (!detectedCurrencyCode && typeof code === \"string\") {\n detectedCurrencyCode = code;\n }\n }\n }\n }\n }\n } catch (_error) {\n // Silently handle JSON parsing errors\n }\n }\n\n // 1️⃣ b) Detect Shopify.currency active code (common across many Shopify themes)\n // Example: Shopify.currency = {\"active\":\"INR\",\"rate\":\"1.0\"};\n // Fallback pattern: Shopify.currency.active = 'INR';\n const currencyJsonMatch = html.match(/Shopify\\.currency\\s*=\\s*(\\{[^}]*\\})/);\n if (currencyJsonMatch) {\n try {\n const raw = currencyJsonMatch[1];\n const obj = JSON.parse(raw || \"{}\") as any;\n const activeCode =\n typeof obj?.active === \"string\" ? obj.active.toUpperCase() : undefined;\n const iso = activeCode ? CURRENCY_CODE_TO_COUNTRY[activeCode] : undefined;\n if (activeCode) {\n detectedCurrencyCode = activeCode;\n }\n if (typeof iso === \"string\") {\n // Treat as a strong signal\n scoreCountry(countryScores, iso, 0.8, \"Shopify.currency.active\");\n }\n } catch (_error) {\n // ignore malformed objects\n }\n } else {\n const currencyActiveAssignMatch = html.match(\n /Shopify\\.currency\\.active\\s*=\\s*['\"]([A-Za-z]{3})['\"]/i\n );\n if (currencyActiveAssignMatch) {\n const captured = currencyActiveAssignMatch[1];\n const code =\n typeof captured === \"string\" ? captured.toUpperCase() : undefined;\n const iso = code ? CURRENCY_CODE_TO_COUNTRY[code] : undefined;\n if (code) {\n detectedCurrencyCode = code;\n }\n if (typeof iso === \"string\") {\n scoreCountry(countryScores, iso, 0.8, \"Shopify.currency.active\");\n }\n }\n }\n\n // 1️⃣ c) Detect explicit Shopify.country assignment\n // Example: Shopify.country = \"IN\";\n const shopifyCountryMatch = html.match(\n /Shopify\\.country\\s*=\\s*['\"]([A-Za-z]{2})['\"]/i\n );\n if (shopifyCountryMatch) {\n const captured = shopifyCountryMatch[1];\n const iso =\n typeof captured === \"string\" ? captured.toUpperCase() : undefined;\n if (typeof iso === \"string\") {\n // Treat as strongest signal\n scoreCountry(countryScores, iso, 1, \"Shopify.country\");\n }\n }\n\n // 2️⃣ Extract phone numbers\n const phones = html.match(/\\+\\d{1,3}[\\s\\-()0-9]{5,}/g);\n if (phones) {\n for (const phone of phones) {\n const prefix = phone.match(/^\\+\\d{1,3}/)?.[0];\n if (prefix && COUNTRY_CODES[prefix])\n scoreCountry(\n countryScores,\n COUNTRY_CODES[prefix],\n 0.8,\n `phone prefix ${prefix}`\n );\n }\n }\n\n // 3️⃣ Extract JSON-LD addressCountry fields\n const jsonLdRegex = /<script[^>]+application\\/ld\\+json[^>]*>(.*?)<\\/script>/g;\n let jsonLdMatch: RegExpExecArray | null = jsonLdRegex.exec(html);\n while (jsonLdMatch !== null) {\n try {\n const json = jsonLdMatch[1];\n if (!json) {\n // skip empty capture\n } else {\n const raw = JSON.parse(json) as unknown;\n\n const collectAddressCountries = (\n node: unknown,\n results: string[] = []\n ): string[] => {\n if (Array.isArray(node)) {\n for (const item of node) collectAddressCountries(item, results);\n return results;\n }\n if (node && typeof node === \"object\") {\n const obj = node as Record<string, unknown>;\n const address = obj.address;\n if (address && typeof address === \"object\") {\n const country = (address as Record<string, unknown>)\n .addressCountry;\n if (typeof country === \"string\") results.push(country);\n }\n // Support nested graphs\n const graph = obj[\"@graph\"];\n if (graph) collectAddressCountries(graph, results);\n }\n return results;\n };\n\n const countries = collectAddressCountries(raw);\n for (const country of countries) {\n scoreCountry(countryScores, country, 1, \"JSON-LD addressCountry\");\n }\n }\n } catch (_error) {\n // Silently handle JSON parsing errors\n }\n // advance to next match\n jsonLdMatch = jsonLdRegex.exec(html);\n }\n\n // 4️⃣ Footer country mentions - now using ISO codes\n const footerMatch = html.match(/<footer[^>]*>(.*?)<\\/footer>/i);\n if (footerMatch) {\n const footerTextGroup = footerMatch[1];\n const footerText = footerTextGroup ? footerTextGroup.toLowerCase() : \"\";\n // Create a mapping of country names to ISO codes for footer detection\n const countryNameToISO: Record<string, string> = {\n india: \"IN\",\n \"united states\": \"US\",\n canada: \"CA\",\n australia: \"AU\",\n \"united kingdom\": \"GB\",\n britain: \"GB\",\n uk: \"GB\",\n japan: \"JP\",\n \"south korea\": \"KR\",\n korea: \"KR\",\n germany: \"DE\",\n france: \"FR\",\n italy: \"IT\",\n spain: \"ES\",\n brazil: \"BR\",\n russia: \"RU\",\n singapore: \"SG\",\n indonesia: \"ID\",\n pakistan: \"PK\",\n };\n\n for (const [countryName, isoCode] of Object.entries(countryNameToISO)) {\n if (footerText.includes(countryName))\n scoreCountry(countryScores, isoCode, 0.4, \"footer mention\");\n }\n }\n\n // Pick best guess\n const sorted = Object.entries(countryScores).sort(\n (a, b) => b[1].score - a[1].score\n );\n const best = sorted[0];\n\n return best\n ? {\n country: best[0],\n confidence: Math.min(1, best[1].score / 2),\n signals: best[1].reasons,\n currencyCode: detectedCurrencyCode,\n }\n : {\n country: \"Unknown\",\n confidence: 0,\n signals: [],\n currencyCode: detectedCurrencyCode,\n };\n}\n","import { getInfoForStore } from \"./client/get-info\";\nimport type { CountryDetectionResult, JsonLdEntry } from \"./types\";\n\n/**\n * Store operations interface for managing store-related functionality.\n * Provides methods to fetch comprehensive store information and metadata.\n */\nexport interface StoreOperations {\n info(): Promise<StoreInfo>;\n}\n\n/**\n * Comprehensive store information structure returned by the info method.\n * Contains all metadata, branding, social links, and showcase content for a Shopify store.\n */\nexport interface StoreInfo {\n name: string;\n domain: string;\n slug: string;\n title: string | null;\n description: string | null;\n logoUrl: string | null;\n socialLinks: Record<string, string>;\n contactLinks: {\n tel: string | null;\n email: string | null;\n contactPage: string | null;\n };\n headerLinks: string[];\n showcase: {\n products: string[];\n collections: string[];\n };\n jsonLdData: JsonLdEntry[] | undefined;\n techProvider: {\n name: string;\n walletId: string | undefined;\n subDomain: string | null;\n };\n country: CountryDetectionResult[\"country\"];\n}\n\n/**\n * Creates store operations for a ShopClient instance.\n * @param context - ShopClient context containing necessary methods and properties for store operations\n */\nexport function createStoreOperations(context: {\n baseUrl: string;\n storeDomain: string;\n validateProductExists: (handle: string) => Promise<boolean>;\n validateCollectionExists: (handle: string) => Promise<boolean>;\n validateLinksInBatches: <T>(\n items: T[],\n validator: (item: T) => Promise<boolean>,\n batchSize?: number\n ) => Promise<T[]>;\n handleFetchError: (error: unknown, context: string, url: string) => never;\n}): StoreOperations {\n return {\n /**\n * Fetches comprehensive store information including metadata, social links, and showcase content.\n *\n * @returns {Promise<StoreInfo>} Store information object containing:\n * - `name` - Store name from meta tags or domain\n * - `domain` - Store domain URL\n * - `slug` - Generated store slug\n * - `title` - Store title from meta tags\n * - `description` - Store description from meta tags\n * - `logoUrl` - Store logo URL from Open Graph or CDN\n * - `socialLinks` - Object with social media links (facebook, twitter, instagram, etc.)\n * - `contactLinks` - Object with contact information (tel, email, contactPage)\n * - `headerLinks` - Array of navigation links from header\n * - `showcase` - Object with featured products and collections from homepage\n * - `jsonLdData` - Structured data from JSON-LD scripts\n * - `techProvider` - Shopify-specific information (walletId, subDomain)\n * - `country` - Country detection results with ISO 3166-1 alpha-2 codes (e.g., \"US\", \"GB\")\n *\n * @throws {Error} When the store URL is unreachable or returns an error\n *\n * @example\n * ```typescript\n * const shop = new ShopClient('https://exampleshop.com');\n * const storeInfo = await shop.getInfo();\n *\n * console.log(storeInfo.name); // \"Example Store\"\n * console.log(storeInfo.socialLinks.instagram); // \"https://instagram.com/example\"\n * console.log(storeInfo.showcase.products); // [\"product-handle-1\", \"product-handle-2\"]\n * console.log(storeInfo.country); // \"US\"\n * ```\n */\n info: async (): Promise<StoreInfo> => {\n try {\n // Delegate to shared client parser to avoid redundancy\n const { info } = await getInfoForStore({\n baseUrl: context.baseUrl,\n storeDomain: context.storeDomain,\n validateProductExists: context.validateProductExists,\n validateCollectionExists: context.validateCollectionExists,\n validateLinksInBatches: context.validateLinksInBatches,\n });\n return info;\n /* const response = await rateLimitedFetch(context.baseUrl);\n if (!response.ok) {\n throw new Error(`HTTP error! status: ${response.status}`);\n }\n const html = await response.text();\n\n const getMetaTag = (name: string) => {\n const regex = new RegExp(\n `<meta[^>]*name=[\"']${name}[\"'][^>]*content=[\"'](.*?)[\"']`\n );\n const match = html.match(regex);\n return match ? match[1] : null;\n };\n\n const getPropertyMetaTag = (property: string) => {\n const regex = new RegExp(\n `<meta[^>]*property=[\"']${property}[\"'][^>]*content=[\"'](.*?)[\"']`\n );\n const match = html.match(regex);\n return match ? match[1] : null;\n };\n\n const name =\n getMetaTag(\"og:site_name\") ??\n extractDomainWithoutSuffix(context.baseUrl);\n const title = getMetaTag(\"og:title\") ?? getMetaTag(\"twitter:title\");\n\n const description =\n getMetaTag(\"description\") || getPropertyMetaTag(\"og:description\");\n\n const shopifyWalletId = getMetaTag(\"shopify-digital-wallet\")?.split(\n \"/\"\n )[1];\n\n const myShopifySubdomainMatch = html.match(\n /['\"](.*?\\.myshopify\\.com)['\"]/\n );\n const myShopifySubdomain = myShopifySubdomainMatch\n ? myShopifySubdomainMatch[1]\n : null;\n\n let logoUrl =\n getPropertyMetaTag(\"og:image\") ||\n getPropertyMetaTag(\"og:image:secure_url\");\n if (!logoUrl) {\n const logoMatch = html.match(\n /<img[^>]+src=[\"']([^\"']+\\/cdn\\/shop\\/[^\"']+)[\"']/\n );\n const group = logoMatch?.[1];\n logoUrl = group ? group.replace(\"http://\", \"https://\") : null;\n } else {\n logoUrl = logoUrl.replace(\"http://\", \"https://\");\n }\n\n const socialLinks: Record<string, string> = {};\n const socialRegex =\n /<a[^>]+href=[\"']([^\"']*(?:facebook|twitter|instagram|pinterest|youtube|linkedin|tiktok|vimeo)\\.com[^\"']*)[\"']/g;\n for (const match of html.matchAll(socialRegex)) {\n const hrefGroup = match[1];\n if (!hrefGroup) continue;\n let href: string = hrefGroup;\n try {\n if (href.startsWith(\"//\")) {\n href = `https:${href}`;\n } else if (href.startsWith(\"/\")) {\n href = new URL(href, context.baseUrl).toString();\n }\n const parsed = new URL(href);\n const domain = parsed.hostname.replace(\"www.\", \"\").split(\".\")[0];\n if (domain) {\n socialLinks[domain] = parsed.toString();\n }\n } catch {\n // Skip invalid URL entries silently\n }\n }\n\n const contactLinks = {\n tel: null as string | null,\n email: null as string | null,\n contactPage: null as string | null,\n };\n\n // Extract contact details using focused regexes to avoid parser pitfalls\n for (const match of html.matchAll(/href=[\"']tel:([^\"']+)[\"']/g)) {\n const group = match[1];\n if (group) contactLinks.tel = group.trim();\n }\n for (const match of html.matchAll(/href=[\"']mailto:([^\"']+)[\"']/g)) {\n const group = match[1];\n if (group) contactLinks.email = group.trim();\n }\n for (const match of html.matchAll(\n /href=[\"']([^\"']*(?:\\/contact|\\/pages\\/contact)[^\"']*)[\"']/g\n )) {\n const group = match[1];\n if (group) contactLinks.contactPage = group;\n }\n\n const extractedProductLinks =\n html\n .match(/href=[\"']([^\"']*\\/products\\/[^\"']+)[\"']/g)\n ?.map((match) => {\n const afterHref = match.split(\"href=\")[1];\n if (!afterHref) return null;\n const last = afterHref.replace(/[\\'\"]/g, \"\").split(\"/\").at(-1);\n return last ?? null;\n })\n ?.filter((x): x is string => Boolean(x)) || [];\n\n const extractedCollectionLinks =\n html\n .match(/href=[\"']([^\"']*\\/collections\\/[^\"']+)[\"']/g)\n ?.map((match) => {\n const afterHref = match.split(\"href=\")[1];\n if (!afterHref) return null;\n const last = afterHref.replace(/[\\'\"]/g, \"\").split(\"/\").at(-1);\n return last ?? null;\n })\n ?.filter((x): x is string => Boolean(x)) || [];\n\n // Validate links in batches for better performance\n const [homePageProductLinks, homePageCollectionLinks] =\n await Promise.all([\n context.validateLinksInBatches(\n extractedProductLinks.filter((handle): handle is string =>\n Boolean(handle)\n ),\n (handle) => context.validateProductExists(handle)\n ),\n context.validateLinksInBatches(\n extractedCollectionLinks.filter((handle): handle is string =>\n Boolean(handle)\n ),\n (handle) => context.validateCollectionExists(handle)\n ),\n ]);\n\n const jsonLd = html\n .match(\n /<script[^>]*type=\"application\\/ld\\+json\"[^>]*>([^<]+)<\\/script>/g\n )\n ?.map((match) => {\n const afterGt = match.split(\">\")[1];\n return afterGt ? afterGt.replace(/<\\/script/g, \"\") : \"\";\n });\n const jsonLdData: JsonLdEntry[] | undefined = jsonLd?.map(\n (json) => JSON.parse(json) as JsonLdEntry\n );\n\n const headerLinks =\n html\n .match(\n /<(header|nav|div|section)\\b[^>]*\\b(?:id|class)=[\"'][^\"']*(?=.*shopify-section)(?=.*\\b(header|navigation|nav|menu)\\b)[^\"']*[\"'][^>]*>[\\s\\S]*?<\\/\\1>/gi\n )\n ?.flatMap((header) => {\n const links = header\n .match(/href=[\"']([^\"']+)[\"']/g)\n ?.filter(\n (link) =>\n link.includes(\"/products/\") ||\n link.includes(\"/collections/\") ||\n link.includes(\"/pages/\")\n );\n return (\n links\n ?.map((link) => {\n const href = link.match(/href=[\"']([^\"']+)[\"']/)?.[1];\n if (\n href &&\n !href.startsWith(\"#\") &&\n !href.startsWith(\"javascript:\")\n ) {\n try {\n const url = new URL(href, context.storeDomain);\n return url.pathname.replace(/^\\/|\\/$/g, \"\");\n } catch {\n return href.replace(/^\\/|\\/$/g, \"\");\n }\n }\n return null;\n })\n .filter((item): item is string => Boolean(item)) ?? []\n );\n }) ?? [];\n\n const slug = generateStoreSlug(context.baseUrl);\n\n // Detect country information\n const countryDetection = await detectShopifyCountry(html);\n\n return {\n name: name || slug,\n domain: context.baseUrl,\n slug,\n title: title ?? null,\n description: description ?? null,\n logoUrl,\n socialLinks,\n contactLinks,\n headerLinks,\n showcase: {\n products: unique(homePageProductLinks ?? []),\n collections: unique(homePageCollectionLinks ?? []),\n },\n jsonLdData,\n techProvider: {\n name: \"shopify\",\n walletId: shopifyWalletId,\n subDomain: myShopifySubdomain ?? null,\n },\n country: countryDetection?.country || \"\",\n };\n */\n } catch (error) {\n context.handleFetchError(error, \"fetching store info\", context.baseUrl);\n }\n },\n };\n}\n"],"mappings":"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","names":["name","_a","_b"]}