@commerce-blocks/sdk 1.2.1 → 2.0.0-alpha.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 +226 -93
- package/dist/index.d.ts +231 -119
- package/dist/index.js +1224 -1038
- package/package.json +7 -2
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# @commerce-blocks/sdk
|
|
2
2
|
|
|
3
|
-
ES module SDK
|
|
3
|
+
ES module SDK powered by Layers API with optional Shopify Storefront enrichment.
|
|
4
4
|
|
|
5
5
|
## Installation
|
|
6
6
|
|
|
@@ -22,7 +22,7 @@ const result = createSdk({
|
|
|
22
22
|
],
|
|
23
23
|
facets: ['options.color', 'options.size', 'vendor'],
|
|
24
24
|
|
|
25
|
-
// Opt in to
|
|
25
|
+
// Opt in to Storefront API for additional product data
|
|
26
26
|
// enableStorefront: true,
|
|
27
27
|
// shop: 'your-store.myshopify.com',
|
|
28
28
|
// storefrontPublicToken: 'your-storefront-token',
|
|
@@ -38,9 +38,20 @@ if (result.error) {
|
|
|
38
38
|
|
|
39
39
|
## Configuration
|
|
40
40
|
|
|
41
|
-
###
|
|
41
|
+
### Layers
|
|
42
42
|
|
|
43
|
-
|
|
43
|
+
| Option | Type | Required | Description |
|
|
44
|
+
| ------------------- | ------------- | -------- | ---------------------------------------------------- |
|
|
45
|
+
| `layersPublicToken` | `string` | Yes | Layers API public token |
|
|
46
|
+
| `sorts` | `Sort[]` | Yes | Sort options (`{ label, code }`) |
|
|
47
|
+
| `facets` | `string[]` | Yes | Facet fields for filtering |
|
|
48
|
+
| `attributes` | `string[]` | No | Product attributes to fetch |
|
|
49
|
+
| `layersBaseUrl` | `string` | No | Custom API URL |
|
|
50
|
+
| `fetch` | `CustomFetch` | No | Custom fetch implementation (SSR, testing, proxying) |
|
|
51
|
+
|
|
52
|
+
### Storefront (optional)
|
|
53
|
+
|
|
54
|
+
Storefront API is opt-in. Enable it for collection/page metadata and Shopify-specific fields (metafields, variant detail).
|
|
44
55
|
|
|
45
56
|
| Option | Type | Required | Description |
|
|
46
57
|
| ----------------------- | ------------- | ----------------------------- | ---------------------------------------------------- |
|
|
@@ -50,37 +61,40 @@ Storefront API is opt-in. Enable it for richer product data (metafields, full va
|
|
|
50
61
|
| `storefrontApiVersion` | `string` | No | API version (default: `2025-01`) |
|
|
51
62
|
| `fetch` | `CustomFetch` | No | Custom fetch implementation (SSR, testing, proxying) |
|
|
52
63
|
|
|
53
|
-
|
|
64
|
+
Layers API supports identity tracking for personalization. Pass identity fields via request context:
|
|
54
65
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
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
|
+
```
|
|
63
74
|
|
|
64
75
|
### Product
|
|
65
76
|
|
|
66
|
-
| Option
|
|
67
|
-
|
|
|
68
|
-
| `currencyCode`
|
|
69
|
-
| `formatPrice`
|
|
70
|
-
| `swatches`
|
|
71
|
-
| `options`
|
|
72
|
-
| `productMetafields`
|
|
73
|
-
| `variantMetafields`
|
|
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
|
|
|
77
|
-
| Option | Type
|
|
78
|
-
| ------------------ |
|
|
79
|
-
| `extendProduct` | `({ base, raw,
|
|
80
|
-
| `extendCollection` | `(result, raw) => result`
|
|
81
|
-
| `extendSearch` | `(result, raw) => result`
|
|
82
|
-
| `
|
|
83
|
-
| `
|
|
90
|
+
| Option | Type | Description |
|
|
91
|
+
| ------------------ | ---------------------------------- | ---------------------------------- |
|
|
92
|
+
| `extendProduct` | `({ base, raw, storefront }) => P` | Transform products after hydration |
|
|
93
|
+
| `extendCollection` | `(result, raw) => result` | Transform collection results |
|
|
94
|
+
| `extendSearch` | `(result, raw) => result` | Transform search results |
|
|
95
|
+
| `extendBlock` | `(result, raw) => result` | Transform blocks results |
|
|
96
|
+
| `transformFilters` | `(filters) => FilterGroup` | Custom filter transformation |
|
|
97
|
+
| `filterMap` | `FilterMap` | URL-friendly filter key mapping |
|
|
84
98
|
|
|
85
99
|
Once configured, extenders and transformers are applied automatically. You pass simple inputs and receive transformed outputs—no manual transformation needed on each call.
|
|
86
100
|
|
|
@@ -88,7 +102,7 @@ Once configured, extenders and transformers are applied automatically. You pass
|
|
|
88
102
|
// Configure once at initialization
|
|
89
103
|
const sdk = createSdk({
|
|
90
104
|
// ... base config
|
|
91
|
-
extendProduct: ({ base, raw,
|
|
105
|
+
extendProduct: ({ base, raw, storefront }) => ({
|
|
92
106
|
...base,
|
|
93
107
|
isNew: raw.tags?.includes('new') ?? false,
|
|
94
108
|
rating: raw.calculated?.average_rating,
|
|
@@ -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
|
|
166
|
-
|
|
|
167
|
-
| `page`
|
|
168
|
-
| `limit`
|
|
169
|
-
| `sortOrderCode`
|
|
170
|
-
| `filters`
|
|
171
|
-
| `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
|
|
224
|
-
| ---------------------- |
|
|
225
|
-
| `page` | `number`
|
|
226
|
-
| `limit` | `number`
|
|
227
|
-
| `filters` | `unknown`
|
|
228
|
-
| `signal` | `AbortSignal`
|
|
229
|
-
| `discountEntitlements` | `DiscountEntitlement[]`
|
|
230
|
-
| `context` | `BlocksContext`
|
|
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
|
|
320
|
-
|
|
|
321
|
-
| `query`
|
|
322
|
-
| `page`
|
|
323
|
-
| `limit`
|
|
324
|
-
| `filters`
|
|
325
|
-
| `tuning`
|
|
326
|
-
| `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
|
|
|
@@ -399,7 +457,7 @@ effect(() => {
|
|
|
399
457
|
|
|
400
458
|
| Parameter | Type | Required | Description |
|
|
401
459
|
| --------------------- | ------------- | -------- | ---------------------------------------- |
|
|
402
|
-
| `ids` | `string[]` | Yes |
|
|
460
|
+
| `ids` | `string[]` | Yes | Product GIDs |
|
|
403
461
|
| `meta.collection` | `string` | No | Collection handle to fetch metadata |
|
|
404
462
|
| `meta.page` | `string` | No | Page handle to fetch metadata |
|
|
405
463
|
| `meta.includeFilters` | `boolean` | No | Include available filters for collection |
|
|
@@ -441,8 +499,11 @@ 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>>
|
|
445
|
-
|
|
504
|
+
facetRanges?: Record<string, { min: number; max: number }>
|
|
505
|
+
attributionToken: string
|
|
506
|
+
collection?: StorefrontCollection
|
|
446
507
|
}
|
|
447
508
|
|
|
448
509
|
interface SearchResult {
|
|
@@ -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,14 +522,17 @@ 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
|
|
|
465
532
|
interface StorefrontResult {
|
|
466
533
|
products: Product[]
|
|
467
|
-
collection?:
|
|
468
|
-
page?:
|
|
534
|
+
collection?: StorefrontCollection
|
|
535
|
+
page?: StorefrontPage
|
|
469
536
|
}
|
|
470
537
|
```
|
|
471
538
|
|
|
@@ -484,7 +551,7 @@ if (result.error) {
|
|
|
484
551
|
break
|
|
485
552
|
case 'ApiError':
|
|
486
553
|
// Server errors, rate limits
|
|
487
|
-
console.log(result.error.source) // 'layers' | '
|
|
554
|
+
console.log(result.error.source) // 'layers' | 'storefront'
|
|
488
555
|
console.log(result.error.status) // HTTP status code
|
|
489
556
|
break
|
|
490
557
|
case 'ValidationError':
|
|
@@ -518,7 +585,7 @@ if (result.error) {
|
|
|
518
585
|
| Field | Type | Description |
|
|
519
586
|
| -------------- | -------------- | -------------------------------------------------- |
|
|
520
587
|
| `code` | `ApiErrorCode` | `NOT_FOUND`, `RATE_LIMITED`, `GRAPHQL_ERROR`, etc. |
|
|
521
|
-
| `source` | `ApiSource` | `'layers'` or `'
|
|
588
|
+
| `source` | `ApiSource` | `'layers'` or `'storefront'` |
|
|
522
589
|
| `status` | `number?` | HTTP status code |
|
|
523
590
|
| `retryable` | `boolean` | Whether the request can be retried |
|
|
524
591
|
| `retryAfterMs` | `number?` | Suggested retry delay |
|
|
@@ -539,6 +606,8 @@ if (result.error) {
|
|
|
539
606
|
|
|
540
607
|
### Error Helpers
|
|
541
608
|
|
|
609
|
+
Always check `isRetryable()` before calling `getRetryDelay()`. `getRetryDelay()` returns `undefined` for non-retryable errors — it's not meant to be used standalone.
|
|
610
|
+
|
|
542
611
|
```typescript
|
|
543
612
|
import { isRetryable, getRetryDelay } from '@commerce-blocks/sdk'
|
|
544
613
|
|
|
@@ -569,7 +638,7 @@ const priceFilter = filter(and(gte('price', 50), lte('price', 200)))
|
|
|
569
638
|
|
|
570
639
|
// Use in query
|
|
571
640
|
await collection.execute({
|
|
572
|
-
filters: multiFilter
|
|
641
|
+
filters: multiFilter,
|
|
573
642
|
})
|
|
574
643
|
```
|
|
575
644
|
|
|
@@ -608,57 +677,80 @@ await collection.execute({
|
|
|
608
677
|
})
|
|
609
678
|
```
|
|
610
679
|
|
|
611
|
-
##
|
|
680
|
+
## Product Card
|
|
612
681
|
|
|
613
|
-
### `
|
|
682
|
+
### `createProductCard()` - Reactive Product Card Controller
|
|
614
683
|
|
|
615
|
-
|
|
684
|
+
Creates a reactive controller for product cards with variant selection and availability logic. All derived values are computed signals that auto-update when inputs change.
|
|
616
685
|
|
|
617
686
|
```typescript
|
|
618
|
-
import {
|
|
687
|
+
import { createProductCard, effect } from '@commerce-blocks/sdk'
|
|
619
688
|
|
|
620
|
-
const card =
|
|
689
|
+
const card = createProductCard({
|
|
621
690
|
product,
|
|
622
|
-
selectedOptions: [{ name: 'Size', value: '
|
|
623
|
-
|
|
691
|
+
selectedOptions: [{ name: 'Size', value: '7' }],
|
|
692
|
+
breakoutOptions: [{ name: 'Stone', value: 'Ruby' }],
|
|
624
693
|
})
|
|
625
694
|
|
|
626
|
-
//
|
|
627
|
-
|
|
628
|
-
card.selectedVariant
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
695
|
+
// Subscribe to reactive state
|
|
696
|
+
effect(() => {
|
|
697
|
+
console.log('Selected variant:', card.selectedVariant.value)
|
|
698
|
+
console.log('Available options:', card.options.value)
|
|
699
|
+
})
|
|
700
|
+
|
|
701
|
+
// Access reactive signals
|
|
702
|
+
card.variants.value // Variants filtered by breakoutOptions
|
|
703
|
+
card.selectedVariant.value // Currently selected variant
|
|
704
|
+
card.options.value // Available options (excludes breakout option names)
|
|
705
|
+
card.images.value // Variant image or product images
|
|
706
|
+
card.carouselIndex.value // Image index for carousel
|
|
707
|
+
|
|
708
|
+
// Mutate state via actions
|
|
709
|
+
card.selectOption('Size', 'L') // Select a single option
|
|
710
|
+
card.setSelectedOptions([{ name: 'Size', value: 'L' }]) // Merge options by name
|
|
711
|
+
card.setBreakoutOptions([{ name: 'Stone', value: 'Emerald' }]) // Change breakout filter
|
|
632
712
|
|
|
633
|
-
//
|
|
713
|
+
// Query methods (pure)
|
|
634
714
|
card.getOptionValues('Size') // ['S', 'M', 'L']
|
|
635
715
|
card.getSwatches('Color') // Swatch definitions
|
|
636
716
|
card.isOptionAvailable('Size', 'L') // Check if selecting 'L' results in available variant
|
|
637
717
|
card.getVariantByOptions([{ name: 'Size', value: 'L' }])
|
|
718
|
+
|
|
719
|
+
// Cleanup
|
|
720
|
+
card.dispose()
|
|
638
721
|
```
|
|
639
722
|
|
|
640
723
|
**Parameters:**
|
|
641
724
|
|
|
642
|
-
| Parameter
|
|
643
|
-
|
|
|
644
|
-
| `product`
|
|
645
|
-
| `selectedOptions`
|
|
646
|
-
| `
|
|
725
|
+
| Parameter | Type | Required | Description |
|
|
726
|
+
| ----------------- | ----------------- | -------- | -------------------------------------------------------- |
|
|
727
|
+
| `product` | `Product` | Yes | Product to display |
|
|
728
|
+
| `selectedOptions` | `ProductOption[]` | No | Initially selected options |
|
|
729
|
+
| `breakoutOptions` | `ProductOption[]` | No | Options to filter variants (auto-set from variant tiles) |
|
|
730
|
+
|
|
731
|
+
**Reactive State (ReadonlySignal):**
|
|
732
|
+
|
|
733
|
+
| Property | Type | Description |
|
|
734
|
+
| ----------------- | ---------------------------------------- | --------------------------------------- |
|
|
735
|
+
| `product` | `Product` | Original product (static) |
|
|
736
|
+
| `variants` | `ReadonlySignal<ProductVariant[]>` | Variants matching breakoutOptions |
|
|
737
|
+
| `selectedVariant` | `ReadonlySignal<ProductVariant \| null>` | Variant matching all selected options |
|
|
738
|
+
| `selectedOptions` | `ReadonlySignal<ProductOption[]>` | Combined breakout + selected options |
|
|
739
|
+
| `options` | `ReadonlySignal<RichProductOption[]>` | Available options from visible variants |
|
|
740
|
+
| `optionNames` | `ReadonlySignal<string[]>` | Option names (excludes breakout) |
|
|
741
|
+
| `images` | `ReadonlySignal<Image[]>` | Variant image or product images |
|
|
742
|
+
| `carouselIndex` | `ReadonlySignal<number>` | Index of selected variant's image |
|
|
647
743
|
|
|
648
|
-
**
|
|
744
|
+
**Actions:**
|
|
649
745
|
|
|
650
|
-
|
|
|
651
|
-
|
|
|
652
|
-
| `
|
|
653
|
-
| `
|
|
654
|
-
| `
|
|
655
|
-
| `
|
|
656
|
-
| `options` | `RichProductOption[]` | Available options from visible variants |
|
|
657
|
-
| `optionNames` | `string[]` | Option names (excludes breakaway) |
|
|
658
|
-
| `images` | `Image[]` | Variant image or product images |
|
|
659
|
-
| `carouselIndex` | `number` | Index of selected variant's image |
|
|
746
|
+
| Method | Description |
|
|
747
|
+
| ----------------------------- | ------------------------------- |
|
|
748
|
+
| `selectOption(name, value)` | Select a single option by name |
|
|
749
|
+
| `setSelectedOptions(options)` | Merge options by name |
|
|
750
|
+
| `setBreakoutOptions(options)` | Replace breakout filter options |
|
|
751
|
+
| `dispose()` | Cleanup controller |
|
|
660
752
|
|
|
661
|
-
**
|
|
753
|
+
**Query Methods:**
|
|
662
754
|
|
|
663
755
|
| Method | Returns | Description |
|
|
664
756
|
| -------------------------------- | ------------------------ | ------------------------------------------------------ |
|
|
@@ -703,13 +795,54 @@ store.queries.invalidate('browse:*') // invalidate by pattern
|
|
|
703
795
|
// Collection metadata
|
|
704
796
|
const meta = store.collections.get('shirts')
|
|
705
797
|
|
|
798
|
+
// Page metadata
|
|
799
|
+
const page = store.pages.get('about')
|
|
800
|
+
store.pages.set('about', { title: 'About Us', body: '...' })
|
|
801
|
+
store.pages.delete('about')
|
|
802
|
+
|
|
706
803
|
// Persistence
|
|
707
|
-
store.persist() // save to
|
|
708
|
-
store.restore() // restore from
|
|
804
|
+
store.persist() // save to storage
|
|
805
|
+
store.restore() // restore from storage
|
|
709
806
|
store.clear() // clear all caches
|
|
710
807
|
|
|
711
808
|
// Stats
|
|
712
|
-
console.log(store.stats) // { products, queries, collections }
|
|
809
|
+
console.log(store.stats) // { products, queries, collections, pages }
|
|
810
|
+
```
|
|
811
|
+
|
|
812
|
+
## Storage Adapters
|
|
813
|
+
|
|
814
|
+
Configure how the SDK persists cache data. By default, the SDK uses `localStorage` in browsers.
|
|
815
|
+
|
|
816
|
+
### Built-in Adapters
|
|
817
|
+
|
|
818
|
+
```typescript
|
|
819
|
+
import { localStorageAdapter, jsonFileAdapter } from '@commerce-blocks/sdk'
|
|
820
|
+
|
|
821
|
+
// Browser: localStorage (returns null if unavailable)
|
|
822
|
+
const browserAdapter = localStorageAdapter('my-cache-key')
|
|
823
|
+
|
|
824
|
+
// Node.js: JSON file
|
|
825
|
+
import fs from 'fs'
|
|
826
|
+
const nodeAdapter = jsonFileAdapter('./cache.json', fs)
|
|
827
|
+
```
|
|
828
|
+
|
|
829
|
+
### Custom Adapter
|
|
830
|
+
|
|
831
|
+
Implement the `StorageAdapter` interface:
|
|
832
|
+
|
|
833
|
+
```typescript
|
|
834
|
+
interface StorageAdapter {
|
|
835
|
+
read(): string | null
|
|
836
|
+
write(data: string): void
|
|
837
|
+
remove(): void
|
|
838
|
+
}
|
|
839
|
+
|
|
840
|
+
// Example: sessionStorage adapter
|
|
841
|
+
const sessionAdapter: StorageAdapter = {
|
|
842
|
+
read: () => sessionStorage.getItem('my-key'),
|
|
843
|
+
write: (data) => sessionStorage.setItem('my-key', data),
|
|
844
|
+
remove: () => sessionStorage.removeItem('my-key'),
|
|
845
|
+
}
|
|
713
846
|
```
|
|
714
847
|
|
|
715
848
|
## Standalone Utilities
|