@commerce-blocks/sdk 1.2.0 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -61,16 +61,29 @@ Storefront API is opt-in. Enable it for richer product data (metafields, full va
61
61
  | `layersBaseUrl` | `string` | No | Custom API URL |
62
62
  | `fetch` | `CustomFetch` | No | Custom fetch implementation (SSR, testing, proxying) |
63
63
 
64
+ Layers API supports identity tracking for personalization. Pass identity fields via request context:
65
+
66
+ ```typescript
67
+ // Identity fields available in browse/search contexts
68
+ interface LayersIdentity {
69
+ deviceId?: string // Device identifier
70
+ sessionId?: string // Session identifier
71
+ customerId?: string // Customer identifier
72
+ }
73
+ ```
74
+
64
75
  ### Product
65
76
 
66
- | Option | Type | Description |
67
- | ------------------- | ------------------------------ | ----------------------------- |
68
- | `currencyCode` | `string` | Currency for price formatting |
69
- | `formatPrice` | `(amount, currency) => string` | Custom price formatter |
70
- | `swatches` | `Swatch[]` | Color swatch definitions |
71
- | `options` | `string[]` | Product options to expose |
72
- | `productMetafields` | `{ namespace, key }[]` | Product metafields to fetch |
73
- | `variantMetafields` | `{ namespace, key }[]` | Variant metafields to fetch |
77
+ | Option | Type | Description |
78
+ | ---------------------- | ------------------------------ | ------------------------------ |
79
+ | `currencyCode` | `string` | Currency for price formatting |
80
+ | `formatPrice` | `(amount, currency) => string` | Custom price formatter |
81
+ | `swatches` | `Swatch[]` | Color swatch definitions |
82
+ | `options` | `string[]` | Product options to expose |
83
+ | `productMetafields` | `{ namespace, key }[]` | Product metafields to fetch |
84
+ | `variantMetafields` | `{ namespace, key }[]` | Variant metafields to fetch |
85
+ | `collectionMetafields` | `{ namespace, key }[]` | Collection metafields to fetch |
86
+ | `pageMetafields` | `{ namespace, key }[]` | Page metafields to fetch |
74
87
 
75
88
  ### Extensibility
76
89
 
@@ -79,6 +92,7 @@ Storefront API is opt-in. Enable it for richer product data (metafields, full va
79
92
  | `extendProduct` | `({ base, raw, shopify }) => P` | Transform products after hydration |
80
93
  | `extendCollection` | `(result, raw) => result` | Transform collection results |
81
94
  | `extendSearch` | `(result, raw) => result` | Transform search results |
95
+ | `extendBlock` | `(result, raw) => result` | Transform blocks results |
82
96
  | `transformFilters` | `(filters) => FilterGroup` | Custom filter transformation |
83
97
  | `filterMap` | `FilterMap` | URL-friendly filter key mapping |
84
98
 
@@ -113,6 +127,13 @@ await collection.execute({ filters: { color: 'Red' } }) // filterMap applied
113
127
  | `cacheMaxEntries` | `number` | Max query entries in cache |
114
128
  | `cacheTtl` | `number` | TTL in milliseconds |
115
129
 
130
+ ### Data Hydration
131
+
132
+ | Option | Type | Description |
133
+ | -------------------- | --------------------------------------- | ----------------------------------------- |
134
+ | `initialData` | `{ products?, queries?, collections? }` | Pre-populate cache at init |
135
+ | `restoreFromStorage` | `boolean` | Auto-restore from storage (default: true) |
136
+
116
137
  ## SDK Methods
117
138
 
118
139
  ### `sdk.collection()` - Browse Collections
@@ -162,13 +183,18 @@ collection.dispose()
162
183
 
163
184
  **Execute parameters:**
164
185
 
165
- | Parameter | Type | Description |
166
- | --------------- | ------------- | ------------------------------- |
167
- | `page` | `number` | Page number (default: 1) |
168
- | `limit` | `number` | Products per page (default: 24) |
169
- | `sortOrderCode` | `string` | Sort option code |
170
- | `filters` | `unknown` | Filter criteria |
171
- | `signal` | `AbortSignal` | External abort signal |
186
+ | Parameter | Type | Description |
187
+ | ---------------- | ------------------------- | ------------------------------------- |
188
+ | `page` | `number` | Page number (default: 1) |
189
+ | `limit` | `number` | Products per page (default: 24) |
190
+ | `sortOrderCode` | `string` | Sort option code |
191
+ | `filters` | `unknown` | Filter criteria |
192
+ | `signal` | `AbortSignal` | External abort signal |
193
+ | `includeMeta` | `boolean` | Fetch collection metadata |
194
+ | `includeFilters` | `boolean` | Include filter counts in response |
195
+ | `dynamicLinking` | `Record<string, unknown>` | Custom dynamic linking parameters |
196
+ | `params` | `Record<string, unknown>` | Additional request parameters |
197
+ | `transformBody` | `(body) => body` | Custom request body mutation function |
172
198
 
173
199
  ### `sdk.blocks()` - Product Recommendations
174
200
 
@@ -220,14 +246,17 @@ blocks.dispose()
220
246
 
221
247
  **Execute parameters:**
222
248
 
223
- | Parameter | Type | Description |
224
- | ---------------------- | ----------------------- | ------------------------------- |
225
- | `page` | `number` | Page number (default: 1) |
226
- | `limit` | `number` | Products per page (default: 24) |
227
- | `filters` | `unknown` | Filter criteria |
228
- | `signal` | `AbortSignal` | External abort signal |
229
- | `discountEntitlements` | `DiscountEntitlement[]` | Discount entitlements to apply |
230
- | `context` | `BlocksContext` | Cart, geo, and custom context |
249
+ | Parameter | Type | Description |
250
+ | ---------------------- | ------------------------- | ------------------------------------- |
251
+ | `page` | `number` | Page number (default: 1) |
252
+ | `limit` | `number` | Products per page (default: 24) |
253
+ | `filters` | `unknown` | Filter criteria |
254
+ | `signal` | `AbortSignal` | External abort signal |
255
+ | `discountEntitlements` | `DiscountEntitlement[]` | Discount entitlements to apply |
256
+ | `context` | `BlocksContext` | Cart, geo, and custom context |
257
+ | `dynamicLinking` | `Record<string, unknown>` | Custom dynamic linking parameters |
258
+ | `params` | `Record<string, unknown>` | Additional request parameters |
259
+ | `transformBody` | `(body) => body` | Custom request body mutation function |
231
260
 
232
261
  **`BlocksContext`:**
233
262
 
@@ -237,6 +266,23 @@ blocks.dispose()
237
266
  | `geo` | `{ country?, province?, city? }` | Geographic context |
238
267
  | `custom` | `Record<string, unknown>` | Custom context data |
239
268
 
269
+ **`DiscountEntitlement`:**
270
+
271
+ ```typescript
272
+ interface DiscountEntitlement {
273
+ entitled: {
274
+ all?: boolean // Apply to all products
275
+ products?: string[] // Product IDs
276
+ variants?: (string | number)[] // Variant IDs
277
+ collections?: string[] // Collection handles
278
+ }
279
+ discount: {
280
+ type: 'PERCENTAGE' | 'FIXED_AMOUNT'
281
+ value: number
282
+ }
283
+ }
284
+ ```
285
+
240
286
  ### `sdk.autocomplete()` - Predictive Search
241
287
 
242
288
  Creates a standalone autocomplete controller with debounced search and local caching. Only full words (completed by a trailing space) are cached — partial input filters cached results client-side.
@@ -316,14 +362,26 @@ search.dispose()
316
362
 
317
363
  **Search parameters:**
318
364
 
319
- | Parameter | Type | Description |
320
- | --------- | -------------- | ------------------------------- |
321
- | `query` | `string` | Search query |
322
- | `page` | `number` | Page number (default: 1) |
323
- | `limit` | `number` | Products per page (default: 24) |
324
- | `filters` | `unknown` | Filter criteria |
325
- | `tuning` | `LayersTuning` | Search tuning parameters |
326
- | `signal` | `AbortSignal` | External abort signal |
365
+ | Parameter | Type | Description |
366
+ | ---------------- | ------------------------- | ------------------------------------- |
367
+ | `query` | `string` | Search query |
368
+ | `page` | `number` | Page number (default: 1) |
369
+ | `limit` | `number` | Products per page (default: 24) |
370
+ | `filters` | `unknown` | Filter criteria |
371
+ | `tuning` | `LayersTuning` | Search tuning parameters |
372
+ | `signal` | `AbortSignal` | External abort signal |
373
+ | `dynamicLinking` | `Record<string, unknown>` | Custom dynamic linking parameters |
374
+ | `params` | `Record<string, unknown>` | Additional request parameters |
375
+ | `transformBody` | `(body) => body` | Custom request body mutation function |
376
+
377
+ **`LayersTuning`:**
378
+
379
+ | Property | Type | Description |
380
+ | ---------------- | -------- | ------------------------------------------- |
381
+ | `textualWeight` | `number` | Weight for text-based matching (0-1) |
382
+ | `visualWeight` | `number` | Weight for visual similarity matching (0-1) |
383
+ | `multipleFactor` | `number` | Factor for multiple keyword matching |
384
+ | `minimumMatch` | `number` | Minimum match threshold |
327
385
 
328
386
  ### `sdk.uploadImage()` - Upload Image for Search
329
387
 
@@ -441,7 +499,10 @@ interface CollectionResult {
441
499
  totalResults: number
442
500
  totalPages: number
443
501
  page: number
502
+ resultsPerPage?: number
444
503
  facets: Record<string, Record<string, number>>
504
+ facetRanges?: Record<string, { min: number; max: number }>
505
+ attributionToken: string
445
506
  collection?: ShopifyCollection
446
507
  }
447
508
 
@@ -450,7 +511,10 @@ interface SearchResult {
450
511
  totalResults: number
451
512
  totalPages: number
452
513
  page: number
514
+ resultsPerPage?: number
453
515
  facets: Record<string, Record<string, number>>
516
+ facetRanges?: Record<string, { min: number; max: number }>
517
+ attributionToken: string
454
518
  }
455
519
 
456
520
  interface BlocksResult {
@@ -458,7 +522,10 @@ interface BlocksResult {
458
522
  totalResults: number
459
523
  totalPages: number
460
524
  page: number
525
+ resultsPerPage?: number
461
526
  facets: Record<string, Record<string, number>>
527
+ facetRanges?: Record<string, { min: number; max: number }>
528
+ attributionToken: string
462
529
  block?: BlocksInfo // { id, title, anchor_type, strategy_type, strategy_key }
463
530
  }
464
531
 
@@ -703,13 +770,54 @@ store.queries.invalidate('browse:*') // invalidate by pattern
703
770
  // Collection metadata
704
771
  const meta = store.collections.get('shirts')
705
772
 
773
+ // Page metadata
774
+ const page = store.pages.get('about')
775
+ store.pages.set('about', { title: 'About Us', body: '...' })
776
+ store.pages.delete('about')
777
+
706
778
  // Persistence
707
- store.persist() // save to localStorage
708
- store.restore() // restore from localStorage
779
+ store.persist() // save to storage
780
+ store.restore() // restore from storage
709
781
  store.clear() // clear all caches
710
782
 
711
783
  // Stats
712
- console.log(store.stats) // { products, queries, collections }
784
+ console.log(store.stats) // { products, queries, collections, pages }
785
+ ```
786
+
787
+ ## Storage Adapters
788
+
789
+ Configure how the SDK persists cache data. By default, the SDK uses `localStorage` in browsers.
790
+
791
+ ### Built-in Adapters
792
+
793
+ ```typescript
794
+ import { localStorageAdapter, jsonFileAdapter } from '@commerce-blocks/sdk'
795
+
796
+ // Browser: localStorage (returns null if unavailable)
797
+ const browserAdapter = localStorageAdapter('my-cache-key')
798
+
799
+ // Node.js: JSON file
800
+ import fs from 'fs'
801
+ const nodeAdapter = jsonFileAdapter('./cache.json', fs)
802
+ ```
803
+
804
+ ### Custom Adapter
805
+
806
+ Implement the `StorageAdapter` interface:
807
+
808
+ ```typescript
809
+ interface StorageAdapter {
810
+ read(): string | null
811
+ write(data: string): void
812
+ remove(): void
813
+ }
814
+
815
+ // Example: sessionStorage adapter
816
+ const sessionAdapter: StorageAdapter = {
817
+ read: () => sessionStorage.getItem('my-key'),
818
+ write: (data) => sessionStorage.setItem('my-key', data),
819
+ remove: () => sessionStorage.removeItem('my-key'),
820
+ }
713
821
  ```
714
822
 
715
823
  ## Standalone Utilities
package/dist/index.d.ts CHANGED
@@ -68,9 +68,12 @@ declare interface BaseProduct {
68
68
  calculated?: Record<string, unknown>;
69
69
  isGiftCard: boolean;
70
70
  onlineStoreUrl: string | null;
71
+ /** @deprecated Use variant `inventoryLevels` for location-specific inventory */
71
72
  totalInventory: number;
72
73
  }
73
74
 
75
+ declare type BaseResult<P extends Product = Product> = QueryResult<P>;
76
+
74
77
  export { batch }
75
78
 
76
79
  declare interface BlocksContext {
@@ -124,10 +127,7 @@ export declare interface BlocksOptions {
124
127
  anchorHandle?: string;
125
128
  }
126
129
 
127
- export declare interface BlocksResult<P extends Product = Product> extends LayersPagination {
128
- products: P[];
129
- facets: Record<string, Record<string, number>>;
130
- _meta?: LayersMeta;
130
+ export declare interface BlocksResult<P extends Product = Product> extends BaseResult<P> {
131
131
  block?: BlocksInfo;
132
132
  }
133
133
 
@@ -183,13 +183,12 @@ export declare interface CollectionOptions {
183
183
  defaultSort?: string;
184
184
  }
185
185
 
186
- export declare interface CollectionResult<P extends Product = Product> extends LayersPagination {
187
- products: P[];
188
- facets: Record<string, Record<string, number>>;
189
- _meta?: LayersMeta;
186
+ export declare interface CollectionResult<P extends Product = Product> extends BaseResult<P> {
190
187
  collection?: ShopifyCollection;
191
188
  }
192
189
 
190
+ declare type Collections = Record<string, Omit<CollectionMeta, 'handle'>>;
191
+
193
192
  declare type CommerceBlocksID = `${CommerceBlocksType}-${OptionSegment}` | `${CommerceBlocksType}-${OptionSegment}-${OptionSegment}` | `${CommerceBlocksType}-${OptionSegment}-${OptionSegment}-${OptionSegment}`;
194
193
 
195
194
  declare type CommerceBlocksType = 'Product' | 'ProductVariant';
@@ -244,6 +243,13 @@ declare interface FeaturedMedia {
244
243
  height: number;
245
244
  }
246
245
 
246
+ declare interface FileSystem_2 {
247
+ readFileSync(path: string, encoding: 'utf-8'): string;
248
+ writeFileSync(path: string, data: string, encoding: 'utf-8'): void;
249
+ unlinkSync(path: string): void;
250
+ }
251
+ export { FileSystem_2 as FileSystem }
252
+
247
253
  export declare function filter(expression: FilterExpression | FilterGroup): FilterGroupParam;
248
254
 
249
255
  declare enum FilterConditional {
@@ -363,12 +369,17 @@ export declare interface ImageSearchOptions {
363
369
 
364
370
  export declare function inValues(property: string, values: FilterValue[]): FilterExpression;
365
371
 
372
+ /** Map of location ID to available quantity */
373
+ declare type InventoryLevels = Record<string, number>;
374
+
366
375
  export declare function isInitialized(): boolean;
367
376
 
368
377
  export declare function isRetryable(error: SdkError): boolean;
369
378
 
370
379
  export declare function isSdkError(error: unknown): error is SdkError;
371
380
 
381
+ export declare function jsonFileAdapter(filePath: string, fs: FileSystem_2): StorageAdapter;
382
+
372
383
  declare interface LayersClientConfig extends LayersFetchConfig {
373
384
  sorts: Sort[];
374
385
  facets: string[];
@@ -395,11 +406,7 @@ declare interface LayersMeta {
395
406
  group: 'control' | 'variation';
396
407
  }[];
397
408
  appliedRules?: string[];
398
- variantBreakouts?: {
399
- id: string;
400
- name: string;
401
- optionCode: string;
402
- }[];
409
+ variantBreakouts?: Record<string, string | number>[];
403
410
  }
404
411
 
405
412
  declare interface LayersPagination {
@@ -413,8 +420,9 @@ declare type LayersProduct = ProductResult;
413
420
 
414
421
  declare interface LayersResponse extends LayersPagination, LayersFacets {
415
422
  results: (ProductResult | VariantResult_2)[];
416
- attributionToken: string;
423
+ attributionToken?: string;
417
424
  _meta?: LayersMeta;
425
+ _workflow?: unknown;
418
426
  }
419
427
 
420
428
  declare interface LayersTuning {
@@ -445,9 +453,12 @@ declare interface LayersVariant {
445
453
  updated_at?: string;
446
454
  requires_selling_plan?: boolean;
447
455
  has_selling_plan?: boolean;
448
- inventory_levels?: Record<number, number>;
456
+ inventory_levels?: InventoryLevels;
457
+ in_stock_location_ids?: number[];
449
458
  }
450
459
 
460
+ export declare function localStorageAdapter(key: string): StorageAdapter | null;
461
+
451
462
  export declare function lt(property: string, value: number): FilterExpression;
452
463
 
453
464
  export declare function lte(property: string, value: number): FilterExpression;
@@ -457,6 +468,7 @@ declare interface MatchedVariant extends LayersVariant {
457
468
  inventory_policy?: string;
458
469
  inventory_quantity?: number;
459
470
  product_id?: number;
471
+ rn?: number;
460
472
  }
461
473
 
462
474
  export declare interface Metafield extends MetafieldIdentifier {
@@ -666,6 +678,7 @@ export declare interface ProductVariant {
666
678
  title: string;
667
679
  availableForSale: boolean;
668
680
  currentlyNotInStock: boolean;
681
+ /** @deprecated Use `inventoryLevels` for location-specific inventory */
669
682
  quantityAvailable: number;
670
683
  sku: string | null;
671
684
  barcode: string | null;
@@ -677,15 +690,26 @@ export declare interface ProductVariant {
677
690
  image: Image_2 | null;
678
691
  selectedOptions: ProductOption[];
679
692
  metafields: Metafield[];
680
- inventoryLevels: Record<number, number>;
693
+ inventoryLevels: InventoryLevels;
694
+ inStockLocationIds: number[];
681
695
  }
682
696
 
697
+ declare type Queries<P extends Product = Product> = Record<string, QueryResult<P>>;
698
+
683
699
  declare interface QueryResult<P extends Product = Product> {
684
700
  products: P[];
685
701
  totalResults: number;
686
702
  totalPages: number;
687
703
  page: number;
688
- facets?: Record<string, Record<string, number>>;
704
+ resultsPerPage?: number;
705
+ facets: Record<string, Record<string, number>>;
706
+ facetRanges?: Record<string, {
707
+ min: number;
708
+ max: number;
709
+ }>;
710
+ attributionToken?: string;
711
+ _meta?: LayersMeta;
712
+ _workflow?: unknown;
689
713
  }
690
714
 
691
715
  export declare interface QueryState<T> {
@@ -760,12 +784,22 @@ export declare interface SdkConfig<P extends Product = Product> extends LayersCl
760
784
  cacheMaxProducts?: number;
761
785
  cacheMaxEntries?: number;
762
786
  cacheTtl?: number;
787
+ /** Custom storage adapter for cache persistence. Defaults to localStorage in browser. */
788
+ storageAdapter?: StorageAdapter;
763
789
  extendProduct?: ProductExtender<P>;
764
790
  transformFilters?: FilterTransformer;
765
791
  filterMap?: FilterMap;
766
792
  extendCollection?: CollectionExtender<P>;
767
793
  extendSearch?: SearchExtender<P>;
768
794
  extendBlock?: BlocksExtender<P>;
795
+ /** Initial data to hydrate into the store */
796
+ initialData?: {
797
+ products?: P[];
798
+ queries?: Queries<P>;
799
+ collections?: Collections;
800
+ };
801
+ /** Restore cache from storage on init (default: true) */
802
+ restoreFromStorage?: boolean;
769
803
  }
770
804
 
771
805
  export declare type SdkError = NetworkError | ApiError | ValidationError | ConfigError;
@@ -793,11 +827,7 @@ export declare interface SearchQuery {
793
827
  transformBody?: BodyTransformer;
794
828
  }
795
829
 
796
- export declare interface SearchResult<P extends Product = Product> extends LayersPagination {
797
- products: P[];
798
- facets: Record<string, Record<string, number>>;
799
- _meta?: LayersMeta;
800
- }
830
+ export declare type SearchResult<P extends Product = Product> = BaseResult<P>;
801
831
 
802
832
  declare interface SelectedOptionResult {
803
833
  name: string;
@@ -877,6 +907,12 @@ declare interface Sort {
877
907
  code: string;
878
908
  }
879
909
 
910
+ export declare interface StorageAdapter {
911
+ read(): string | null;
912
+ write(data: string): void;
913
+ remove(): void;
914
+ }
915
+
880
916
  declare interface Store<P extends Product = Product> {
881
917
  products: {
882
918
  get(gid: ProductGID): P | undefined;
@@ -884,7 +920,7 @@ declare interface Store<P extends Product = Product> {
884
920
  cached: P[];
885
921
  missing: ProductGID[];
886
922
  };
887
- set(products: P[]): void;
923
+ set(products: P | P[]): void;
888
924
  has(gid: ProductGID): boolean;
889
925
  };
890
926
  queries: {
@@ -953,6 +989,15 @@ export declare interface StorefrontResult<P extends Product = Product> {
953
989
  page?: ShopifyPage;
954
990
  }
955
991
 
992
+ export declare interface StoreOptions {
993
+ ttl?: number;
994
+ maxProducts?: number;
995
+ maxQueries?: number;
996
+ storageKey?: string;
997
+ /** Custom storage adapter. Defaults to localStorage in browser. */
998
+ storageAdapter?: StorageAdapter;
999
+ }
1000
+
956
1001
  export declare interface Swatch {
957
1002
  name: string | null;
958
1003
  value: string;