@commerce-blocks/sdk 2.0.0-alpha.3 → 2.0.2

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
@@ -40,31 +40,48 @@ collection.dispose()
40
40
 
41
41
  ## Configuration
42
42
 
43
- | Option | Type | Required | Description |
44
- | ------------------- | ------------------------------ | -------- | ------------------------------------ |
45
- | `token` | `string` | Yes | Layers API public token |
46
- | `sorts` | `Sort[]` | Yes | Sort options `{ name, code }` |
47
- | `facets` | `Facet[]` | Yes | Facet fields `{ name, code }` |
48
- | `attributes` | `string[]` | No | Product attributes to fetch |
49
- | `baseUrl` | `string` | No | Custom API URL |
50
- | `fetch` | `CustomFetch` | No | Custom fetch (SSR, testing) |
51
- | `currency` | `string` | No | Currency for price formatting |
52
- | `formatPrice` | `(amount, currency) => string` | No | Custom price formatter |
53
- | `swatches` | `Swatch[]` | No | Color swatch definitions |
54
- | `options` | `string[]` | No | Product options to expose |
55
- | `productMetafields` | `{ namespace, key }[]` | No | Product metafields to fetch |
56
- | `variantMetafields` | `{ namespace, key }[]` | No | Variant metafields to fetch |
57
- | `transforms` | `Transforms` | No | Post-process results (see below) |
58
- | `filterAliases` | `FilterAliases` | No | URL-friendly filter key mapping |
59
- | `cacheLimit` | `number` | No | Max entries in cache |
60
- | `cacheLifetime` | `number` | No | TTL in milliseconds |
61
- | `storage` | `StorageAdapter` | No | Custom storage adapter |
62
- | `initialData` | `CacheData` | No | Pre-populate cache at init |
63
- | `restoreCache` | `boolean` | No | Restore from storage (default: true) |
43
+ | Option | Type | Required | Description |
44
+ | --------------- | ------------------------------ | -------- | ------------------------------------ |
45
+ | `token` | `string` | Yes | Layers API public token |
46
+ | `sorts` | `Sort[]` | Yes | Sort options `{ name, code }` |
47
+ | `facets` | `Facet[]` | Yes | Facet fields `{ name, code }` |
48
+ | `attributes` | `string[]` | No | Product attributes to fetch |
49
+ | `baseUrl` | `string` | No | Custom API URL |
50
+ | `fetch` | `CustomFetch` | No | Custom fetch (SSR, testing) |
51
+ | `currency` | `string` | No | Currency for price formatting |
52
+ | `formatPrice` | `(amount, currency) => string` | No | Custom price formatter |
53
+ | `swatches` | `Swatch[]` | No | Color swatch definitions |
54
+ | `transforms` | `Transforms` | No | Post-process results (see below) |
55
+ | `filterAliases` | `FilterAliases` | No | URL-friendly filter key mapping |
56
+ | `cacheLimit` | `number` | No | Max entries in cache |
57
+ | `cacheLifetime` | `number` | No | TTL in milliseconds |
58
+ | `storage` | `StorageAdapter` | No | Custom storage adapter |
59
+ | `initialData` | `CacheData` | No | Pre-populate cache at init |
60
+ | `restoreCache` | `boolean` | No | Restore from storage (default: true) |
61
+ | `flags` | `Flags` | No | Feature flags (see below) |
62
+
63
+ ### Flags
64
+
65
+ Control which product data the SDK requests from the API.
66
+
67
+ ```typescript
68
+ createClient({
69
+ // ...config
70
+ flags: { variants: true }, // opt-in for full variants array
71
+ })
72
+ ```
73
+
74
+ | Flag | Default | Description |
75
+ | ---------- | ------- | ----------------------------------------------------------------------------- |
76
+ | `variants` | `false` | Request full variants array per product. Enable for PDPs or option selectors. |
77
+
78
+ By default, products are **shallow** — `variants` contains only the matched variant, and `price`/`compareAtPrice`/`featuredMedia`/`availableForSale` are sourced from `first_or_matched_variant`. This significantly reduces API payload size.
79
+
80
+ When `flags.variants` is `true`, the full `variants` array is requested and `selectedVariant` is looked up from it. Use this for product detail pages or cards with variant/option selectors.
64
81
 
65
82
  ## Controllers
66
83
 
67
- All controllers (`collection`, `blocks`, `search`, `suggest`) follow the same pattern:
84
+ All controllers (`collection`, `blocks`, `search`, `suggest`, `searchContent`) follow the same pattern:
68
85
 
69
86
  - **`state`** — a `ReadonlySignal<QueryState<T>>` with `{ data, error, isFetching }`
70
87
  - **`execute()`** — runs the query, returns `Result<T, ClientError>`
@@ -210,6 +227,56 @@ suggest.dispose()
210
227
  | `excludeQueries` | `string[]` | Custom strings to filter from suggestions |
211
228
  | `signal` | `AbortSignal` | Shared abort signal (acts like `dispose()`) |
212
229
 
230
+ ### Content Search
231
+
232
+ Search articles and other content types.
233
+
234
+ ```typescript
235
+ const content = client.searchContent({ query: 'styling tips' })
236
+
237
+ const { data } = await content.execute()
238
+ // data.articles, data.totalResults, data.page
239
+
240
+ await content.execute({ query: 'new arrivals', contentType: 'blog' })
241
+ await content.execute({ page: 2, temporary: true })
242
+
243
+ content.dispose()
244
+ ```
245
+
246
+ | Option | Type | Description |
247
+ | ------------------ | ---------------- | ----------------------------------------- |
248
+ | `query` | `string` | Search query |
249
+ | `contentType` | `string` | Filter by content type |
250
+ | `page` | `number` | Page number |
251
+ | `limit` | `number` | Results per page |
252
+ | `tuning` | `SearchTuning` | Matching weight configuration |
253
+ | `signal` | `AbortSignal` | Abort signal |
254
+ | `transformRequest` | `(body) => body` | Custom request body transformation |
255
+ | `temporary` | `boolean` | Override without persisting for next call |
256
+
257
+ Result shape:
258
+
259
+ ```typescript
260
+ interface SearchContentResult {
261
+ articles: Article[]
262
+ query: string
263
+ contentType: string | null
264
+ totalResults: number
265
+ page: number
266
+ resultsPerPage: number
267
+ }
268
+
269
+ interface Article {
270
+ title: string
271
+ handle: string
272
+ summary: string
273
+ author: string
274
+ tags: string[]
275
+ image: { url: string; width: number; height: number } | null
276
+ publishedAt: string
277
+ }
278
+ ```
279
+
213
280
  ### Image Search
214
281
 
215
282
  Upload an image, then search by it.
@@ -398,7 +465,7 @@ if (result.error) {
398
465
  }
399
466
  ```
400
467
 
401
- Retryable errors (`TIMEOUT`, `CONNECTION_FAILED`, `RATE_LIMITED`, `SERVICE_UNAVAILABLE`) expose `retryable` and `retryAfterMs`:
468
+ `isRetryable` classifies errors by tag, code, and status — use it standalone or as a `shouldRetry` predicate:
402
469
 
403
470
  ```typescript
404
471
  import { isRetryable } from '@commerce-blocks/sdk'
@@ -417,7 +484,7 @@ The client exposes a reactive cache:
417
484
  const { cache } = client
418
485
 
419
486
  cache.get('cache-key') // CacheEntry<QueryResult> | null
420
- cache.invalidate('browse:*') // invalidate by pattern
487
+ cache.invalidate('browse') // invalidate keys containing 'browse'
421
488
  cache.persist() // save to storage
422
489
  cache.restore() // restore from storage
423
490
  cache.clear() // clear all
package/dist/index.d.ts CHANGED
@@ -19,7 +19,6 @@ export declare interface ApiError {
19
19
  readonly source: ApiSource;
20
20
  readonly message: string;
21
21
  readonly status?: number;
22
- readonly retryable: boolean;
23
22
  readonly retryAfterMs?: number;
24
23
  readonly cause?: unknown;
25
24
  }
@@ -34,6 +33,22 @@ declare interface ApiFetchConfig {
34
33
 
35
34
  declare type ApiSource = 'layers';
36
35
 
36
+ export declare interface Article {
37
+ title: string;
38
+ handle: string;
39
+ summary: string;
40
+ author: string;
41
+ tags: string[];
42
+ image: ArticleImage | null;
43
+ publishedAt: string;
44
+ }
45
+
46
+ declare interface ArticleImage {
47
+ url: string;
48
+ width: number;
49
+ height: number;
50
+ }
51
+
37
52
  export { batch }
38
53
 
39
54
  declare interface BlocksContext {
@@ -76,8 +91,8 @@ export declare interface BlocksResult extends QueryResult {
76
91
  }
77
92
 
78
93
  declare interface Cache_2 {
79
- get(key: string): CacheEntry<QueryResult> | null;
80
- set(key: string, result: QueryResult): void;
94
+ get(key: string): CacheEntry<CacheValue> | null;
95
+ set(key: string, result: CacheValue): void;
81
96
  isExpired(key: string): boolean;
82
97
  invalidate(pattern: string): void;
83
98
  persist(): void;
@@ -88,11 +103,12 @@ declare interface Cache_2 {
88
103
  };
89
104
  }
90
105
 
91
- declare type CacheData = Record<string, QueryResult>;
106
+ declare type CacheData = Record<string, CacheValue>;
92
107
 
93
108
  declare interface CacheEntry<T> {
94
109
  data: T;
95
110
  timestamp: number;
111
+ createdAt: number;
96
112
  }
97
113
 
98
114
  export declare interface CacheOptions {
@@ -103,6 +119,8 @@ export declare interface CacheOptions {
103
119
  storageAdapter?: StorageAdapter;
104
120
  }
105
121
 
122
+ declare type CacheValue = QueryResult | ContentResult;
123
+
106
124
  export declare interface Client {
107
125
  collection: (options: CollectionOptions) => CollectionController;
108
126
  blocks: (options: BlocksOptions) => BlocksController;
@@ -110,6 +128,7 @@ export declare interface Client {
110
128
  search: (options?: SearchQuery) => SearchController;
111
129
  uploadImage: (options: UploadImageOptions) => UploadImageController;
112
130
  searchByImage: (options: SearchByImageQuery) => SearchByImageController;
131
+ searchContent: (options?: SearchContentQuery) => SearchContentController;
113
132
  config: ClientConfig;
114
133
  cache: Cache_2;
115
134
  }
@@ -117,17 +136,8 @@ export declare interface Client {
117
136
  export declare interface ClientConfig extends ApiClientConfig {
118
137
  includeMeta?: boolean;
119
138
  currency?: string;
120
- formatPrice?: (amount: number, currencyCode: string) => string;
139
+ formatPrice?: FormatPriceFn;
121
140
  swatches?: Swatch[];
122
- options?: string[];
123
- productMetafields?: {
124
- namespace: string;
125
- key: string;
126
- }[];
127
- variantMetafields?: {
128
- namespace: string;
129
- key: string;
130
- }[];
131
141
  cacheLimit?: number;
132
142
  cacheLifetime?: number;
133
143
  /** Custom storage adapter for cache persistence. Defaults to localStorage in browser. */
@@ -173,6 +183,39 @@ export declare interface ConfigError {
173
183
 
174
184
  declare type ConfigErrorCode = 'MISSING_CONFIG' | 'INVALID_CONFIG';
175
185
 
186
+ declare interface ContentArticleRaw {
187
+ title: string;
188
+ handle: string;
189
+ summary_text: string;
190
+ author: string;
191
+ tags: string[];
192
+ image: {
193
+ url: string;
194
+ width: number;
195
+ height: number;
196
+ } | null;
197
+ published_at: string;
198
+ }
199
+
200
+ declare interface ContentResult {
201
+ articles: unknown[];
202
+ query: string;
203
+ contentType: string | null;
204
+ totalResults: number;
205
+ page: number;
206
+ resultsPerPage: number;
207
+ }
208
+
209
+ declare interface ContentSearchResponse {
210
+ results: ContentArticleRaw[];
211
+ query: string;
212
+ contentType: string | null;
213
+ totalResults: number;
214
+ totalPages: number;
215
+ page: number;
216
+ resultsPerPage: number;
217
+ }
218
+
176
219
  export declare interface Controller<TData, TQuery = void> {
177
220
  readonly state: ReadonlySignal<QueryState<TData>>;
178
221
  execute(query?: TQuery): Promise<Result<TData, ClientError>>;
@@ -278,6 +321,8 @@ declare enum FilterOperator {
278
321
 
279
322
  declare type FilterValue = string | number | boolean;
280
323
 
324
+ declare type FormatPriceFn = (amount: number, currencyCode: string) => string;
325
+
281
326
  export declare function getClient(): Result<Client, ConfigError>;
282
327
 
283
328
  export declare function gt(property: string, value: number): FilterExpression;
@@ -382,21 +427,10 @@ declare interface MatchedVariant extends LayersVariant {
382
427
  rn?: number;
383
428
  }
384
429
 
385
- export declare interface Metafield extends MetafieldIdentifier {
386
- value: string;
387
- type: string;
388
- }
389
-
390
- declare interface MetafieldIdentifier {
391
- namespace: string;
392
- key: string;
393
- }
394
-
395
430
  export declare interface NetworkError {
396
431
  readonly _tag: 'NetworkError';
397
432
  readonly code: NetworkErrorCode;
398
433
  readonly message: string;
399
- readonly retryable: boolean;
400
434
  readonly retryAfterMs?: number;
401
435
  readonly cause?: unknown;
402
436
  }
@@ -465,7 +499,7 @@ export declare interface PriceRangeData {
465
499
  compareAtPriceRange: PriceRange | null;
466
500
  }
467
501
 
468
- export declare type Product<T extends Record<string, unknown> = Record<string, unknown>> = ProductBase & T;
502
+ export declare type Product = ProductBase & Record<string, unknown>;
469
503
 
470
504
  export declare interface ProductBase {
471
505
  id: CommerceBlocksID;
@@ -479,8 +513,8 @@ export declare interface ProductBase {
479
513
  tags: string[];
480
514
  images: Image_2[];
481
515
  featuredMedia: FeaturedMedia | null;
482
- priceRange: PriceRange;
483
- compareAtPriceRange: PriceRange | undefined;
516
+ price: Price;
517
+ compareAtPrice: Price | null;
484
518
  options: RichProductOption[];
485
519
  selectedOptions: ProductOption[];
486
520
  variants: ProductVariant[];
@@ -508,7 +542,7 @@ export declare interface ProductCardController {
508
542
  setSelectedOptions(options: ProductOption[]): void;
509
543
  setSelectedVariant(variantId: number): void;
510
544
  setCarouselPosition(position: number): void;
511
- subscribe(callback: (state: ProductCardState) => void): Unsubscribe_2;
545
+ subscribe(callback: (state: ProductCardState) => void): Unsubscribe;
512
546
  dispose(): void;
513
547
  }
514
548
 
@@ -582,17 +616,18 @@ declare interface ProductResult {
582
616
  featured_media: FeaturedMedia_2 | null;
583
617
  category: ProductCategory_2 | null;
584
618
  tags: string[];
585
- images: ProductImage[];
619
+ images?: ProductImage[];
586
620
  collection_titles: string[];
587
621
  metafields: Record<string, Record<string, unknown>>;
588
622
  calculated: Record<string, unknown>;
589
- price_range: {
623
+ options_v2: OptionV2[];
624
+ first_or_matched_variant: MatchedVariant;
625
+ price_range?: {
590
626
  from: number;
591
627
  to: number;
592
628
  compare_at_price: number;
593
629
  };
594
- options: Record<string, string[]>;
595
- options_v2: OptionV2[];
630
+ variants?: LayersVariant[];
596
631
  body_html?: string | null;
597
632
  is_gift_card?: boolean | null;
598
633
  has_variants_that_require_components?: boolean | null;
@@ -601,8 +636,6 @@ declare interface ProductResult {
601
636
  created_at?: string;
602
637
  updated_at?: string;
603
638
  published_at?: string;
604
- first_or_matched_variant?: MatchedVariant;
605
- variants: LayersVariant[];
606
639
  }
607
640
 
608
641
  export declare interface ProductVariant {
@@ -658,6 +691,7 @@ declare type Result<T, E = Error> = {
658
691
  };
659
692
 
660
693
  export declare interface RichProductOption extends ProductOption {
694
+ code: string;
661
695
  swatch?: Swatch;
662
696
  }
663
697
 
@@ -672,6 +706,33 @@ export declare interface SearchByImageQuery extends QueryParams {
672
706
  tuning?: SearchTuning;
673
707
  }
674
708
 
709
+ export declare interface SearchContentController {
710
+ readonly state: ReadonlySignal<QueryState<SearchContentResult>>;
711
+ execute(query?: SearchContentQuery): Promise<Result<SearchContentResult, ClientError>>;
712
+ subscribe(callback: (state: QueryState<SearchContentResult>) => void): Unsubscribe;
713
+ dispose(): void;
714
+ }
715
+
716
+ export declare interface SearchContentQuery {
717
+ query?: string;
718
+ contentType?: string;
719
+ page?: number;
720
+ limit?: number;
721
+ tuning?: SearchTuning;
722
+ signal?: AbortSignal;
723
+ transformRequest?: RequestTransformer;
724
+ temporary?: boolean;
725
+ }
726
+
727
+ export declare interface SearchContentResult {
728
+ articles: Article[];
729
+ query: string;
730
+ contentType: string | null;
731
+ totalResults: number;
732
+ page: number;
733
+ resultsPerPage: number;
734
+ }
735
+
675
736
  export declare interface SearchController {
676
737
  readonly state: ReadonlySignal<QueryState<SearchResult>>;
677
738
  prepare(query?: SearchQuery): Promise<Result<SearchPrepareResult, ClientError>>;
@@ -783,13 +844,12 @@ export declare interface Transforms {
783
844
  block?: (result: BlocksResult, raw: LayersResponse & {
784
845
  block?: BlocksInfo;
785
846
  }) => BlocksResult;
847
+ searchContent?: (result: SearchContentResult, raw: ContentSearchResponse) => SearchContentResult;
786
848
  filters?: (filters: unknown) => FilterGroup | undefined;
787
849
  }
788
850
 
789
851
  export declare type Unsubscribe = () => void;
790
852
 
791
- declare type Unsubscribe_2 = () => void;
792
-
793
853
  export declare interface UploadImageController {
794
854
  readonly state: ReadonlySignal<QueryState<UploadImageResult>>;
795
855
  subscribe(callback: (state: QueryState<UploadImageResult>) => void): Unsubscribe;