shop-client 3.14.0 → 3.14.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,7 +12,7 @@ import {
12
12
 
13
13
  // src/client/get-info.ts
14
14
  import { unique } from "remeda";
15
- async function getInfoForStore(args, options) {
15
+ async function getInfoForShop(args, options) {
16
16
  var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n, _o, _p;
17
17
  const {
18
18
  baseUrl,
@@ -206,7 +206,7 @@ async function getInfoForStore(args, options) {
206
206
  }
207
207
 
208
208
  // src/store.ts
209
- function createStoreOperations(context) {
209
+ function createShopOperations(context) {
210
210
  return {
211
211
  /**
212
212
  * Fetches comprehensive store information including metadata, social links, and showcase content.
@@ -242,7 +242,7 @@ function createStoreOperations(context) {
242
242
  */
243
243
  info: async () => {
244
244
  try {
245
- const { info } = await getInfoForStore({
245
+ const { info } = await getInfoForShop({
246
246
  baseUrl: context.baseUrl,
247
247
  storeDomain: context.storeDomain,
248
248
  validateProductExists: context.validateProductExists,
@@ -253,11 +253,134 @@ function createStoreOperations(context) {
253
253
  } catch (error) {
254
254
  context.handleFetchError(error, "fetching store info", context.baseUrl);
255
255
  }
256
+ },
257
+ getMetaData: async () => {
258
+ try {
259
+ const response = await rateLimitedFetch(context.baseUrl, {
260
+ rateLimitClass: "store:metadata",
261
+ timeoutMs: 7e3
262
+ });
263
+ if (!response.ok) {
264
+ throw new Error(`HTTP error! status: ${response.status}`);
265
+ }
266
+ const html = await response.text();
267
+ const getPropertyMetaTag = (property) => {
268
+ const regex = new RegExp(
269
+ `<meta[^>]*property=["']${property}["'][^>]*content=["'](.*?)["']`
270
+ );
271
+ const match = html.match(regex);
272
+ return match ? match[1] : null;
273
+ };
274
+ const siteName = getPropertyMetaTag("og:site_name");
275
+ const title = getPropertyMetaTag("og:title");
276
+ const description = getPropertyMetaTag("og:description");
277
+ const url = getPropertyMetaTag("og:url");
278
+ const type = getPropertyMetaTag("og:type");
279
+ const image = getPropertyMetaTag("og:image");
280
+ const imageSecureUrl = getPropertyMetaTag("og:image:secure_url");
281
+ return {
282
+ siteName: siteName != null ? siteName : null,
283
+ title: title != null ? title : null,
284
+ description: description != null ? description : null,
285
+ url: url != null ? url : null,
286
+ type: type != null ? type : null,
287
+ image: image != null ? image : null,
288
+ imageSecureUrl: imageSecureUrl != null ? imageSecureUrl : null
289
+ };
290
+ } catch (error) {
291
+ context.handleFetchError(
292
+ error,
293
+ "fetching store metadata",
294
+ context.baseUrl
295
+ );
296
+ }
297
+ },
298
+ getJsonLd: async () => {
299
+ var _a, _b;
300
+ try {
301
+ const response = await rateLimitedFetch(context.baseUrl, {
302
+ rateLimitClass: "store:jsonld",
303
+ timeoutMs: 7e3
304
+ });
305
+ if (!response.ok) {
306
+ throw new Error(`HTTP error! status: ${response.status}`);
307
+ }
308
+ const html = await response.text();
309
+ const scripts = (_b = (_a = html.match(
310
+ /<script[^>]*type="application\/ld\+json"[^>]*>([\s\S]*?)<\/script>/g
311
+ )) == null ? void 0 : _a.map(
312
+ (match) => match.replace(/^.*?>/, "").replace(/<\/script>$/i, "")
313
+ )) != null ? _b : [];
314
+ const parsed = scripts.map((json) => {
315
+ try {
316
+ return JSON.parse(json);
317
+ } catch {
318
+ return void 0;
319
+ }
320
+ }).filter((x) => !!x);
321
+ return parsed.length ? parsed : void 0;
322
+ } catch (error) {
323
+ context.handleFetchError(
324
+ error,
325
+ "fetching store JSON-LD",
326
+ context.baseUrl
327
+ );
328
+ }
329
+ },
330
+ getHeaderLinks: async () => {
331
+ var _a;
332
+ try {
333
+ const response = await rateLimitedFetch(context.baseUrl, {
334
+ rateLimitClass: "store:header",
335
+ timeoutMs: 7e3
336
+ });
337
+ if (!response.ok) {
338
+ throw new Error(`HTTP error! status: ${response.status}`);
339
+ }
340
+ const html = await response.text();
341
+ const sections = (_a = html.match(
342
+ /<(header|nav|div|section)\b[^>]*\b(?:id|class)=["'][^"']*(?=.*shopify-section)(?=.*\b(header|navigation|nav|menu)\b)[^"']*["'][^>]*>[\s\S]*?<\/\1>/gi
343
+ )) != null ? _a : [];
344
+ const links = sections.flatMap((section) => {
345
+ var _a2;
346
+ return (_a2 = section.match(/href=["']([^"']+)["']/g)) != null ? _a2 : [];
347
+ }).map((link) => {
348
+ var _a2;
349
+ return (_a2 = link.match(/href=["']([^"']+)["']/)) == null ? void 0 : _a2[1];
350
+ }).filter((href) => !!href).filter(
351
+ (href) => href.includes("/products/") || href.includes("/collections/") || href.includes("/pages/")
352
+ ).map((href) => {
353
+ try {
354
+ if (href.startsWith("//"))
355
+ return new URL(`https:${href}`).pathname;
356
+ if (href.startsWith("/"))
357
+ return new URL(href, context.storeDomain).pathname;
358
+ return new URL(href).pathname;
359
+ } catch {
360
+ return href;
361
+ }
362
+ }).map((p) => p.replace(/^\/|\/$/g, ""));
363
+ const seen = /* @__PURE__ */ new Set();
364
+ const deduped = [];
365
+ for (const l of links) {
366
+ if (!seen.has(l)) {
367
+ seen.add(l);
368
+ deduped.push(l);
369
+ }
370
+ }
371
+ return deduped;
372
+ } catch (error) {
373
+ context.handleFetchError(
374
+ error,
375
+ "fetching header links",
376
+ context.baseUrl
377
+ );
378
+ }
256
379
  }
257
380
  };
258
381
  }
259
382
 
260
383
  export {
261
- getInfoForStore,
262
- createStoreOperations
384
+ getInfoForShop,
385
+ createShopOperations
263
386
  };
@@ -1,4 +1,4 @@
1
- import { StoreInfo } from './store.js';
1
+ import { ShopInfo } from './store.js';
2
2
  import { C as Collection, f as CurrencyCode, P as Product, b as ShopifyCollection } from './types-luPg5O08.js';
3
3
 
4
4
  /**
@@ -60,6 +60,6 @@ interface CollectionOperations {
60
60
  declare function createCollectionOperations(baseUrl: string, storeDomain: string, fetchCollections: (page: number, limit: number) => Promise<Collection[] | null>, collectionsDto: (collections: ShopifyCollection[]) => Collection[], fetchPaginatedProductsFromCollection: (collectionHandle: string, options?: {
61
61
  page?: number;
62
62
  limit?: number;
63
- }) => Promise<Product[] | null>, getStoreInfo: () => Promise<StoreInfo>, findCollection: (handle: string) => Promise<Collection | null>): CollectionOperations;
63
+ }) => Promise<Product[] | null>, getStoreInfo: () => Promise<ShopInfo>, findCollection: (handle: string) => Promise<Collection | null>): CollectionOperations;
64
64
 
65
65
  export { type CollectionOperations, createCollectionOperations };
package/dist/index.d.ts CHANGED
@@ -1,9 +1,9 @@
1
+ import { S as ShopifyProduct, P as Product, a as ShopifySingleProduct, b as ShopifyCollection, C as Collection, J as JsonLdEntry, c as StoreTypeBreakdown } from './types-luPg5O08.js';
2
+ export { d as CountryDetectionResult, e as CountryScores, f as CurrencyCode, L as LocalizedPricing, M as MetaTag, g as ProductImage, h as ProductOption, i as ProductVariant, j as ProductVariantImage } from './types-luPg5O08.js';
1
3
  import { CheckoutOperations } from './checkout.js';
2
4
  import { CollectionOperations } from './collections.js';
3
5
  import { ProductOperations } from './products.js';
4
- import { StoreOperations, StoreInfo } from './store.js';
5
- import { S as ShopifyProduct, P as Product, a as ShopifySingleProduct, b as ShopifyCollection, C as Collection, c as StoreTypeBreakdown } from './types-luPg5O08.js';
6
- export { d as CountryDetectionResult, e as CountryScores, f as CurrencyCode, L as LocalizedPricing, M as MetaTag, g as ProductImage, h as ProductOption, i as ProductVariant, j as ProductVariantImage } from './types-luPg5O08.js';
6
+ import { ShopOperations, ShopInfo, OpenGraphMeta } from './store.js';
7
7
  export { classifyProduct, generateSEOContent } from './ai/enrich.js';
8
8
  export { detectShopCountry } from './utils/detect-country.js';
9
9
  export { calculateDiscount, extractDomainWithoutSuffix, genProductSlug, generateStoreSlug, safeParseDate, sanitizeDomain } from './utils/func.js';
@@ -40,10 +40,13 @@ declare class ShopClient {
40
40
  private infoCacheValue?;
41
41
  private infoCacheTimestamp?;
42
42
  private infoInFlight?;
43
+ private metaCacheValue?;
44
+ private metaCacheTimestamp?;
45
+ private metaInFlight?;
43
46
  products: ProductOperations;
44
47
  collections: CollectionOperations;
45
48
  checkout: CheckoutOperations;
46
- storeOperations: StoreOperations;
49
+ shopOperations: ShopOperations;
47
50
  /**
48
51
  * Creates a new ShopClient instance for interacting with a Shopify store.
49
52
  *
@@ -156,12 +159,21 @@ declare class ShopClient {
156
159
  force?: boolean;
157
160
  validateShowcase?: boolean;
158
161
  validationBatchSize?: number;
159
- }): Promise<StoreInfo>;
162
+ }): Promise<ShopInfo>;
160
163
  /**
161
164
  * Manually clear the cached store info.
162
165
  * The next call to `getInfo()` will fetch fresh data regardless of TTL.
163
166
  */
164
167
  clearInfoCache(): void;
168
+ /**
169
+ * Fetch OpenGraph metadata from the store homepage.
170
+ * Returns only `og:*` fields without additional parsing or validation.
171
+ */
172
+ getMetaData(options?: {
173
+ force?: boolean;
174
+ }): Promise<OpenGraphMeta>;
175
+ getJsonLd(): Promise<JsonLdEntry[] | undefined>;
176
+ getHeaderLinks(): Promise<string[]>;
165
177
  /**
166
178
  * Determine the store's primary vertical and target audience.
167
179
  * Uses `getInfo()` internally; no input required.
@@ -174,4 +186,4 @@ declare class ShopClient {
174
186
  }): Promise<StoreTypeBreakdown>;
175
187
  }
176
188
 
177
- export { CheckoutOperations, Collection, CollectionOperations, Product, ProductOperations, ShopClient, type ShopClientOptions, StoreInfo, StoreOperations, StoreTypeBreakdown };
189
+ export { CheckoutOperations, Collection, CollectionOperations, OpenGraphMeta, Product, ProductOperations, ShopClient, type ShopClientOptions, ShopInfo, ShopOperations, StoreTypeBreakdown };
package/dist/index.mjs CHANGED
@@ -8,9 +8,9 @@ import {
8
8
  createProductOperations
9
9
  } from "./chunk-ZF4M6GMB.mjs";
10
10
  import {
11
- createStoreOperations,
12
- getInfoForStore
13
- } from "./chunk-RLVH7LEG.mjs";
11
+ createShopOperations,
12
+ getInfoForShop
13
+ } from "./chunk-7IMI76JZ.mjs";
14
14
  import {
15
15
  classifyProduct,
16
16
  determineStoreType,
@@ -353,7 +353,7 @@ var ShopClient = class {
353
353
  if (typeof (options == null ? void 0 : options.cacheTTL) === "number" && options.cacheTTL > 0) {
354
354
  this.cacheExpiry = options.cacheTTL;
355
355
  }
356
- this.storeOperations = createStoreOperations({
356
+ this.shopOperations = createShopOperations({
357
357
  baseUrl: this.baseUrl,
358
358
  storeDomain: this.storeDomain,
359
359
  validateProductExists: this.validateProductExists.bind(this),
@@ -688,7 +688,7 @@ var ShopClient = class {
688
688
  return await this.infoInFlight;
689
689
  }
690
690
  this.infoInFlight = (async () => {
691
- const { info, currencyCode } = await getInfoForStore(
691
+ const { info, currencyCode } = await getInfoForShop(
692
692
  {
693
693
  baseUrl: this.baseUrl,
694
694
  storeDomain: this.storeDomain,
@@ -726,6 +726,60 @@ var ShopClient = class {
726
726
  this.infoCacheValue = void 0;
727
727
  this.infoCacheTimestamp = void 0;
728
728
  }
729
+ /**
730
+ * Fetch OpenGraph metadata from the store homepage.
731
+ * Returns only `og:*` fields without additional parsing or validation.
732
+ */
733
+ async getMetaData(options) {
734
+ try {
735
+ if ((options == null ? void 0 : options.force) === true) {
736
+ this.metaCacheValue = void 0;
737
+ this.metaCacheTimestamp = void 0;
738
+ }
739
+ if (this.metaCacheValue && this.metaCacheTimestamp !== void 0 && Date.now() - this.metaCacheTimestamp < this.cacheExpiry) {
740
+ return this.metaCacheValue;
741
+ }
742
+ if (this.metaInFlight) {
743
+ return await this.metaInFlight;
744
+ }
745
+ this.metaInFlight = (async () => {
746
+ const meta = await this.shopOperations.getMetaData();
747
+ this.metaCacheValue = meta;
748
+ this.metaCacheTimestamp = Date.now();
749
+ return meta;
750
+ })();
751
+ try {
752
+ const result = await this.metaInFlight;
753
+ return result;
754
+ } finally {
755
+ this.metaInFlight = void 0;
756
+ }
757
+ } catch (error) {
758
+ throw this.handleFetchError(
759
+ error,
760
+ "fetching store metadata",
761
+ this.baseUrl
762
+ );
763
+ }
764
+ }
765
+ async getJsonLd() {
766
+ try {
767
+ return await this.shopOperations.getJsonLd();
768
+ } catch (error) {
769
+ throw this.handleFetchError(
770
+ error,
771
+ "fetching store JSON-LD",
772
+ this.baseUrl
773
+ );
774
+ }
775
+ }
776
+ async getHeaderLinks() {
777
+ try {
778
+ return await this.shopOperations.getHeaderLinks();
779
+ } catch (error) {
780
+ throw this.handleFetchError(error, "fetching header links", this.baseUrl);
781
+ }
782
+ }
729
783
  /**
730
784
  * Determine the store's primary vertical and target audience.
731
785
  * Uses `getInfo()` internally; no input required.
@@ -1,4 +1,4 @@
1
- import { StoreInfo } from './store.js';
1
+ import { ShopInfo } from './store.js';
2
2
  import { f as CurrencyCode, P as Product, k as ProductClassification, l as SEOContent, S as ShopifyProduct, a as ShopifySingleProduct } from './types-luPg5O08.js';
3
3
 
4
4
  /**
@@ -77,6 +77,6 @@ interface ProductOperations {
77
77
  /**
78
78
  * Creates product operations for a store instance
79
79
  */
80
- declare function createProductOperations(baseUrl: string, storeDomain: string, fetchProducts: (page: number, limit: number) => Promise<Product[] | null>, productsDto: (products: ShopifyProduct[]) => Product[] | null, productDto: (product: ShopifySingleProduct) => Product, getStoreInfo: () => Promise<StoreInfo>, findProduct: (handle: string) => Promise<Product | null>): ProductOperations;
80
+ declare function createProductOperations(baseUrl: string, storeDomain: string, fetchProducts: (page: number, limit: number) => Promise<Product[] | null>, productsDto: (products: ShopifyProduct[]) => Product[] | null, productDto: (product: ShopifySingleProduct) => Product, getStoreInfo: () => Promise<ShopInfo>, findProduct: (handle: string) => Promise<Product | null>): ProductOperations;
81
81
 
82
82
  export { type ProductOperations, createProductOperations };
package/dist/store.d.ts CHANGED
@@ -4,14 +4,17 @@ import { J as JsonLdEntry, d as CountryDetectionResult, f as CurrencyCode } from
4
4
  * Store operations interface for managing store-related functionality.
5
5
  * Provides methods to fetch comprehensive store information and metadata.
6
6
  */
7
- interface StoreOperations {
8
- info(): Promise<StoreInfo>;
7
+ interface ShopOperations {
8
+ info(): Promise<ShopInfo>;
9
+ getMetaData(): Promise<OpenGraphMeta>;
10
+ getJsonLd(): Promise<JsonLdEntry[] | undefined>;
11
+ getHeaderLinks(): Promise<string[]>;
9
12
  }
10
13
  /**
11
14
  * Comprehensive store information structure returned by the info method.
12
15
  * Contains all metadata, branding, social links, and showcase content for a Shopify store.
13
16
  */
14
- interface StoreInfo {
17
+ interface ShopInfo {
15
18
  name: string;
16
19
  domain: string;
17
20
  slug: string;
@@ -38,17 +41,26 @@ interface StoreInfo {
38
41
  country: CountryDetectionResult["country"];
39
42
  currency: CurrencyCode | null;
40
43
  }
44
+ interface OpenGraphMeta {
45
+ siteName: string | null;
46
+ title: string | null;
47
+ description: string | null;
48
+ url: string | null;
49
+ type: string | null;
50
+ image: string | null;
51
+ imageSecureUrl: string | null;
52
+ }
41
53
  /**
42
54
  * Creates store operations for a ShopClient instance.
43
55
  * @param context - ShopClient context containing necessary methods and properties for store operations
44
56
  */
45
- declare function createStoreOperations(context: {
57
+ declare function createShopOperations(context: {
46
58
  baseUrl: string;
47
59
  storeDomain: string;
48
60
  validateProductExists: (handle: string) => Promise<boolean>;
49
61
  validateCollectionExists: (handle: string) => Promise<boolean>;
50
62
  validateLinksInBatches: <T>(items: T[], validator: (item: T) => Promise<boolean>, batchSize?: number) => Promise<T[]>;
51
63
  handleFetchError: (error: unknown, context: string, url: string) => never;
52
- }): StoreOperations;
64
+ }): ShopOperations;
53
65
 
54
- export { type StoreInfo, type StoreOperations, createStoreOperations };
66
+ export { type OpenGraphMeta, type ShopInfo, type ShopOperations, createShopOperations };
package/dist/store.mjs CHANGED
@@ -1,9 +1,9 @@
1
1
  import {
2
- createStoreOperations
3
- } from "./chunk-RLVH7LEG.mjs";
2
+ createShopOperations
3
+ } from "./chunk-7IMI76JZ.mjs";
4
4
  import "./chunk-D5MTUWFO.mjs";
5
5
  import "./chunk-G7OCMGA6.mjs";
6
6
  import "./chunk-U3RQRBXZ.mjs";
7
7
  export {
8
- createStoreOperations
8
+ createShopOperations
9
9
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "shop-client",
3
- "version": "3.14.0",
3
+ "version": "3.14.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.mjs",
6
6
  "module": "./dist/index.mjs",