@commerce-blocks/sdk 1.3.0 → 2.0.0-alpha.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.
Files changed (4) hide show
  1. package/README.md +103 -64
  2. package/dist/index.d.ts +176 -109
  3. package/dist/index.js +1300 -1146
  4. package/package.json +8 -3
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # @commerce-blocks/sdk
2
2
 
3
- ES module SDK for Shopify storefronts with Layers API integration.
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 Shopify Storefront API for richer product data
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 | Description |
91
- | ------------------ | ------------------------------- | ---------------------------------- |
92
- | `extendProduct` | `({ base, raw, shopify }) => 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 |
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, shopify }) => ({
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 | Shopify Product GIDs |
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 |
@@ -502,8 +502,9 @@ interface CollectionResult {
502
502
  resultsPerPage?: number
503
503
  facets: Record<string, Record<string, number>>
504
504
  facetRanges?: Record<string, { min: number; max: number }>
505
+ priceRange?: PriceRange // Formatted min/max prices from result set
505
506
  attributionToken: string
506
- collection?: ShopifyCollection
507
+ collection?: StorefrontCollection
507
508
  }
508
509
 
509
510
  interface SearchResult {
@@ -514,6 +515,7 @@ interface SearchResult {
514
515
  resultsPerPage?: number
515
516
  facets: Record<string, Record<string, number>>
516
517
  facetRanges?: Record<string, { min: number; max: number }>
518
+ priceRange?: PriceRange // Formatted min/max prices from result set
517
519
  attributionToken: string
518
520
  }
519
521
 
@@ -525,14 +527,26 @@ interface BlocksResult {
525
527
  resultsPerPage?: number
526
528
  facets: Record<string, Record<string, number>>
527
529
  facetRanges?: Record<string, { min: number; max: number }>
530
+ priceRange?: PriceRange // Formatted min/max prices from result set
528
531
  attributionToken: string
529
532
  block?: BlocksInfo // { id, title, anchor_type, strategy_type, strategy_key }
530
533
  }
531
534
 
535
+ interface PriceRange {
536
+ min: Price
537
+ max: Price
538
+ }
539
+
540
+ interface Price {
541
+ amount: number
542
+ currencyCode: string
543
+ formatted: string
544
+ }
545
+
532
546
  interface StorefrontResult {
533
547
  products: Product[]
534
- collection?: ShopifyCollection
535
- page?: ShopifyPage
548
+ collection?: StorefrontCollection
549
+ page?: StorefrontPage
536
550
  }
537
551
  ```
538
552
 
@@ -551,7 +565,7 @@ if (result.error) {
551
565
  break
552
566
  case 'ApiError':
553
567
  // Server errors, rate limits
554
- console.log(result.error.source) // 'layers' | 'shopify'
568
+ console.log(result.error.source) // 'layers' | 'storefront'
555
569
  console.log(result.error.status) // HTTP status code
556
570
  break
557
571
  case 'ValidationError':
@@ -585,7 +599,7 @@ if (result.error) {
585
599
  | Field | Type | Description |
586
600
  | -------------- | -------------- | -------------------------------------------------- |
587
601
  | `code` | `ApiErrorCode` | `NOT_FOUND`, `RATE_LIMITED`, `GRAPHQL_ERROR`, etc. |
588
- | `source` | `ApiSource` | `'layers'` or `'shopify'` |
602
+ | `source` | `ApiSource` | `'layers'` or `'storefront'` |
589
603
  | `status` | `number?` | HTTP status code |
590
604
  | `retryable` | `boolean` | Whether the request can be retried |
591
605
  | `retryAfterMs` | `number?` | Suggested retry delay |
@@ -606,6 +620,8 @@ if (result.error) {
606
620
 
607
621
  ### Error Helpers
608
622
 
623
+ Always check `isRetryable()` before calling `getRetryDelay()`. `getRetryDelay()` returns `undefined` for non-retryable errors — it's not meant to be used standalone.
624
+
609
625
  ```typescript
610
626
  import { isRetryable, getRetryDelay } from '@commerce-blocks/sdk'
611
627
 
@@ -636,7 +652,7 @@ const priceFilter = filter(and(gte('price', 50), lte('price', 200)))
636
652
 
637
653
  // Use in query
638
654
  await collection.execute({
639
- filters: multiFilter.filter_group,
655
+ filters: multiFilter,
640
656
  })
641
657
  ```
642
658
 
@@ -675,57 +691,80 @@ await collection.execute({
675
691
  })
676
692
  ```
677
693
 
678
- ## Hooks
694
+ ## Product Card
679
695
 
680
- ### `useProductCard()` - Product Card Helper
696
+ ### `createProductCard()` - Reactive Product Card Controller
681
697
 
682
- Utility for building product cards with variant selection and availability logic.
698
+ 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
699
 
684
700
  ```typescript
685
- import { useProductCard } from '@commerce-blocks/sdk'
701
+ import { createProductCard, effect } from '@commerce-blocks/sdk'
686
702
 
687
- const card = useProductCard({
703
+ const card = createProductCard({
688
704
  product,
689
- selectedOptions: [{ name: 'Size', value: 'M' }],
690
- breakAwayOptions: [{ name: 'Color', value: 'Red' }],
705
+ selectedOptions: [{ name: 'Size', value: '7' }],
706
+ breakoutOptions: [{ name: 'Stone', value: 'Ruby' }],
707
+ })
708
+
709
+ // Subscribe to reactive state
710
+ effect(() => {
711
+ console.log('Selected variant:', card.selectedVariant.value)
712
+ console.log('Available options:', card.options.value)
691
713
  })
692
714
 
693
- // Access computed data
694
- card.variants // Variants filtered by breakAwayOptions
695
- card.selectedVariant // Currently selected variant
696
- card.options // Available options (excludes breakaway option names)
697
- card.images // Variant image or product images
698
- card.carouselIndex // Image index for carousel
715
+ // Access reactive signals
716
+ card.variants.value // Variants filtered by breakoutOptions
717
+ card.selectedVariant.value // Currently selected variant
718
+ card.options.value // Available options (excludes breakout option names)
719
+ card.images.value // Variant image or product images
720
+ card.carouselIndex.value // Image index for carousel
699
721
 
700
- // Helper methods
722
+ // Mutate state via actions
723
+ card.selectOption('Size', 'L') // Select a single option
724
+ card.setSelectedOptions([{ name: 'Size', value: 'L' }]) // Merge options by name
725
+ card.setBreakoutOptions([{ name: 'Stone', value: 'Emerald' }]) // Change breakout filter
726
+
727
+ // Query methods (pure)
701
728
  card.getOptionValues('Size') // ['S', 'M', 'L']
702
729
  card.getSwatches('Color') // Swatch definitions
703
730
  card.isOptionAvailable('Size', 'L') // Check if selecting 'L' results in available variant
704
731
  card.getVariantByOptions([{ name: 'Size', value: 'L' }])
732
+
733
+ // Cleanup
734
+ card.dispose()
705
735
  ```
706
736
 
707
737
  **Parameters:**
708
738
 
709
- | Parameter | Type | Required | Description |
710
- | ------------------ | ----------------- | -------- | ------------------------------------------------------------- |
711
- | `product` | `Product` | Yes | Product to display |
712
- | `selectedOptions` | `ProductOption[]` | No | Currently selected options |
713
- | `breakAwayOptions` | `ProductOption[]` | No | Options to filter visible variants (e.g., pre-selected color) |
714
-
715
- **Returns:**
716
-
717
- | Property | Type | Description |
718
- | ----------------- | ------------------------ | --------------------------------------- |
719
- | `product` | `Product` | Original product |
720
- | `variants` | `ProductVariant[]` | Variants matching breakAwayOptions |
721
- | `selectedVariant` | `ProductVariant \| null` | Variant matching all selected options |
722
- | `selectedOptions` | `ProductOption[]` | Combined breakaway + selected options |
723
- | `options` | `RichProductOption[]` | Available options from visible variants |
724
- | `optionNames` | `string[]` | Option names (excludes breakaway) |
725
- | `images` | `Image[]` | Variant image or product images |
726
- | `carouselIndex` | `number` | Index of selected variant's image |
727
-
728
- **Helper Methods:**
739
+ | Parameter | Type | Required | Description |
740
+ | ----------------- | ----------------- | -------- | -------------------------------------------------------- |
741
+ | `product` | `Product` | Yes | Product to display |
742
+ | `selectedOptions` | `ProductOption[]` | No | Initially selected options |
743
+ | `breakoutOptions` | `ProductOption[]` | No | Options to filter variants (auto-set from variant tiles) |
744
+
745
+ **Reactive State (ReadonlySignal):**
746
+
747
+ | Property | Type | Description |
748
+ | ----------------- | ---------------------------------------- | --------------------------------------- |
749
+ | `product` | `Product` | Original product (static) |
750
+ | `variants` | `ReadonlySignal<ProductVariant[]>` | Variants matching breakoutOptions |
751
+ | `selectedVariant` | `ReadonlySignal<ProductVariant \| null>` | Variant matching all selected options |
752
+ | `selectedOptions` | `ReadonlySignal<ProductOption[]>` | Combined breakout + selected options |
753
+ | `options` | `ReadonlySignal<RichProductOption[]>` | Available options from visible variants |
754
+ | `optionNames` | `ReadonlySignal<string[]>` | Option names (excludes breakout) |
755
+ | `images` | `ReadonlySignal<Image[]>` | Variant image or product images |
756
+ | `carouselIndex` | `ReadonlySignal<number>` | Index of selected variant's image |
757
+
758
+ **Actions:**
759
+
760
+ | Method | Description |
761
+ | ----------------------------- | ------------------------------- |
762
+ | `selectOption(name, value)` | Select a single option by name |
763
+ | `setSelectedOptions(options)` | Merge options by name |
764
+ | `setBreakoutOptions(options)` | Replace breakout filter options |
765
+ | `dispose()` | Cleanup controller |
766
+
767
+ **Query Methods:**
729
768
 
730
769
  | Method | Returns | Description |
731
770
  | -------------------------------- | ------------------------ | ------------------------------------------------------ |