@commerce-blocks/sdk 1.3.0 → 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 +89 -64
- package/dist/index.d.ts +168 -101
- package/dist/index.js +1246 -1111
- 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,18 +38,6 @@ if (result.error) {
|
|
|
38
38
|
|
|
39
39
|
## Configuration
|
|
40
40
|
|
|
41
|
-
### Shopify (optional)
|
|
42
|
-
|
|
43
|
-
Storefront API is opt-in. Enable it for richer product data (metafields, full variant info, collection/page metadata).
|
|
44
|
-
|
|
45
|
-
| Option | Type | Required | Description |
|
|
46
|
-
| ----------------------- | ------------- | ----------------------------- | ---------------------------------------------------- |
|
|
47
|
-
| `enableStorefront` | `boolean` | No | Enable Storefront API hydration (default: `false`) |
|
|
48
|
-
| `shop` | `string` | When `enableStorefront: true` | Store domain |
|
|
49
|
-
| `storefrontPublicToken` | `string` | When `enableStorefront: true` | Storefront API public access token |
|
|
50
|
-
| `storefrontApiVersion` | `string` | No | API version (default: `2025-01`) |
|
|
51
|
-
| `fetch` | `CustomFetch` | No | Custom fetch implementation (SSR, testing, proxying) |
|
|
52
|
-
|
|
53
41
|
### Layers
|
|
54
42
|
|
|
55
43
|
| Option | Type | Required | Description |
|
|
@@ -61,6 +49,18 @@ Storefront API is opt-in. Enable it for richer product data (metafields, full va
|
|
|
61
49
|
| `layersBaseUrl` | `string` | No | Custom API URL |
|
|
62
50
|
| `fetch` | `CustomFetch` | No | Custom fetch implementation (SSR, testing, proxying) |
|
|
63
51
|
|
|
52
|
+
### Storefront (optional)
|
|
53
|
+
|
|
54
|
+
Storefront API is opt-in. Enable it for collection/page metadata and Shopify-specific fields (metafields, variant detail).
|
|
55
|
+
|
|
56
|
+
| Option | Type | Required | Description |
|
|
57
|
+
| ----------------------- | ------------- | ----------------------------- | ---------------------------------------------------- |
|
|
58
|
+
| `enableStorefront` | `boolean` | No | Enable Storefront API hydration (default: `false`) |
|
|
59
|
+
| `shop` | `string` | When `enableStorefront: true` | Store domain |
|
|
60
|
+
| `storefrontPublicToken` | `string` | When `enableStorefront: true` | Storefront API public access token |
|
|
61
|
+
| `storefrontApiVersion` | `string` | No | API version (default: `2025-01`) |
|
|
62
|
+
| `fetch` | `CustomFetch` | No | Custom fetch implementation (SSR, testing, proxying) |
|
|
63
|
+
|
|
64
64
|
Layers API supports identity tracking for personalization. Pass identity fields via request context:
|
|
65
65
|
|
|
66
66
|
```typescript
|
|
@@ -87,14 +87,14 @@ interface LayersIdentity {
|
|
|
87
87
|
|
|
88
88
|
### Extensibility
|
|
89
89
|
|
|
90
|
-
| Option | Type
|
|
91
|
-
| ------------------ |
|
|
92
|
-
| `extendProduct` | `({ base, raw,
|
|
93
|
-
| `extendCollection` | `(result, raw) => result`
|
|
94
|
-
| `extendSearch` | `(result, raw) => result`
|
|
95
|
-
| `extendBlock` | `(result, raw) => result`
|
|
96
|
-
| `transformFilters` | `(filters) => FilterGroup`
|
|
97
|
-
| `filterMap` | `FilterMap`
|
|
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 |
|
|
98
98
|
|
|
99
99
|
Once configured, extenders and transformers are applied automatically. You pass simple inputs and receive transformed outputs—no manual transformation needed on each call.
|
|
100
100
|
|
|
@@ -102,7 +102,7 @@ Once configured, extenders and transformers are applied automatically. You pass
|
|
|
102
102
|
// Configure once at initialization
|
|
103
103
|
const sdk = createSdk({
|
|
104
104
|
// ... base config
|
|
105
|
-
extendProduct: ({ base, raw,
|
|
105
|
+
extendProduct: ({ base, raw, storefront }) => ({
|
|
106
106
|
...base,
|
|
107
107
|
isNew: raw.tags?.includes('new') ?? false,
|
|
108
108
|
rating: raw.calculated?.average_rating,
|
|
@@ -457,7 +457,7 @@ effect(() => {
|
|
|
457
457
|
|
|
458
458
|
| Parameter | Type | Required | Description |
|
|
459
459
|
| --------------------- | ------------- | -------- | ---------------------------------------- |
|
|
460
|
-
| `ids` | `string[]` | Yes |
|
|
460
|
+
| `ids` | `string[]` | Yes | Product GIDs |
|
|
461
461
|
| `meta.collection` | `string` | No | Collection handle to fetch metadata |
|
|
462
462
|
| `meta.page` | `string` | No | Page handle to fetch metadata |
|
|
463
463
|
| `meta.includeFilters` | `boolean` | No | Include available filters for collection |
|
|
@@ -503,7 +503,7 @@ interface CollectionResult {
|
|
|
503
503
|
facets: Record<string, Record<string, number>>
|
|
504
504
|
facetRanges?: Record<string, { min: number; max: number }>
|
|
505
505
|
attributionToken: string
|
|
506
|
-
collection?:
|
|
506
|
+
collection?: StorefrontCollection
|
|
507
507
|
}
|
|
508
508
|
|
|
509
509
|
interface SearchResult {
|
|
@@ -531,8 +531,8 @@ interface BlocksResult {
|
|
|
531
531
|
|
|
532
532
|
interface StorefrontResult {
|
|
533
533
|
products: Product[]
|
|
534
|
-
collection?:
|
|
535
|
-
page?:
|
|
534
|
+
collection?: StorefrontCollection
|
|
535
|
+
page?: StorefrontPage
|
|
536
536
|
}
|
|
537
537
|
```
|
|
538
538
|
|
|
@@ -551,7 +551,7 @@ if (result.error) {
|
|
|
551
551
|
break
|
|
552
552
|
case 'ApiError':
|
|
553
553
|
// Server errors, rate limits
|
|
554
|
-
console.log(result.error.source) // 'layers' | '
|
|
554
|
+
console.log(result.error.source) // 'layers' | 'storefront'
|
|
555
555
|
console.log(result.error.status) // HTTP status code
|
|
556
556
|
break
|
|
557
557
|
case 'ValidationError':
|
|
@@ -585,7 +585,7 @@ if (result.error) {
|
|
|
585
585
|
| Field | Type | Description |
|
|
586
586
|
| -------------- | -------------- | -------------------------------------------------- |
|
|
587
587
|
| `code` | `ApiErrorCode` | `NOT_FOUND`, `RATE_LIMITED`, `GRAPHQL_ERROR`, etc. |
|
|
588
|
-
| `source` | `ApiSource` | `'layers'` or `'
|
|
588
|
+
| `source` | `ApiSource` | `'layers'` or `'storefront'` |
|
|
589
589
|
| `status` | `number?` | HTTP status code |
|
|
590
590
|
| `retryable` | `boolean` | Whether the request can be retried |
|
|
591
591
|
| `retryAfterMs` | `number?` | Suggested retry delay |
|
|
@@ -606,6 +606,8 @@ if (result.error) {
|
|
|
606
606
|
|
|
607
607
|
### Error Helpers
|
|
608
608
|
|
|
609
|
+
Always check `isRetryable()` before calling `getRetryDelay()`. `getRetryDelay()` returns `undefined` for non-retryable errors — it's not meant to be used standalone.
|
|
610
|
+
|
|
609
611
|
```typescript
|
|
610
612
|
import { isRetryable, getRetryDelay } from '@commerce-blocks/sdk'
|
|
611
613
|
|
|
@@ -636,7 +638,7 @@ const priceFilter = filter(and(gte('price', 50), lte('price', 200)))
|
|
|
636
638
|
|
|
637
639
|
// Use in query
|
|
638
640
|
await collection.execute({
|
|
639
|
-
filters: multiFilter
|
|
641
|
+
filters: multiFilter,
|
|
640
642
|
})
|
|
641
643
|
```
|
|
642
644
|
|
|
@@ -675,57 +677,80 @@ await collection.execute({
|
|
|
675
677
|
})
|
|
676
678
|
```
|
|
677
679
|
|
|
678
|
-
##
|
|
680
|
+
## Product Card
|
|
679
681
|
|
|
680
|
-
### `
|
|
682
|
+
### `createProductCard()` - Reactive Product Card Controller
|
|
681
683
|
|
|
682
|
-
|
|
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.
|
|
683
685
|
|
|
684
686
|
```typescript
|
|
685
|
-
import {
|
|
687
|
+
import { createProductCard, effect } from '@commerce-blocks/sdk'
|
|
686
688
|
|
|
687
|
-
const card =
|
|
689
|
+
const card = createProductCard({
|
|
688
690
|
product,
|
|
689
|
-
selectedOptions: [{ name: 'Size', value: '
|
|
690
|
-
|
|
691
|
+
selectedOptions: [{ name: 'Size', value: '7' }],
|
|
692
|
+
breakoutOptions: [{ name: 'Stone', value: 'Ruby' }],
|
|
691
693
|
})
|
|
692
694
|
|
|
693
|
-
//
|
|
694
|
-
|
|
695
|
-
card.selectedVariant
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
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
|
|
699
707
|
|
|
700
|
-
//
|
|
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
|
|
712
|
+
|
|
713
|
+
// Query methods (pure)
|
|
701
714
|
card.getOptionValues('Size') // ['S', 'M', 'L']
|
|
702
715
|
card.getSwatches('Color') // Swatch definitions
|
|
703
716
|
card.isOptionAvailable('Size', 'L') // Check if selecting 'L' results in available variant
|
|
704
717
|
card.getVariantByOptions([{ name: 'Size', value: 'L' }])
|
|
718
|
+
|
|
719
|
+
// Cleanup
|
|
720
|
+
card.dispose()
|
|
705
721
|
```
|
|
706
722
|
|
|
707
723
|
**Parameters:**
|
|
708
724
|
|
|
709
|
-
| Parameter
|
|
710
|
-
|
|
|
711
|
-
| `product`
|
|
712
|
-
| `selectedOptions`
|
|
713
|
-
| `
|
|
714
|
-
|
|
715
|
-
**
|
|
716
|
-
|
|
717
|
-
| Property | Type
|
|
718
|
-
| ----------------- |
|
|
719
|
-
| `product` | `Product`
|
|
720
|
-
| `variants` | `ProductVariant[]
|
|
721
|
-
| `selectedVariant` | `ProductVariant \| null
|
|
722
|
-
| `selectedOptions` | `ProductOption[]
|
|
723
|
-
| `options` | `RichProductOption[]
|
|
724
|
-
| `optionNames` | `string[]
|
|
725
|
-
| `images` | `Image[]
|
|
726
|
-
| `carouselIndex` | `number
|
|
727
|
-
|
|
728
|
-
**
|
|
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 |
|
|
743
|
+
|
|
744
|
+
**Actions:**
|
|
745
|
+
|
|
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 |
|
|
752
|
+
|
|
753
|
+
**Query Methods:**
|
|
729
754
|
|
|
730
755
|
| Method | Returns | Description |
|
|
731
756
|
| -------------------------------- | ------------------------ | ------------------------------------------------------ |
|