@doswiftly/storefront-operations 13.0.0 → 13.1.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/AGENTS.md CHANGED
@@ -27,8 +27,8 @@ consumer's `codegen.ts` references this package's `.graphql` files as
27
27
  live in the consumer's repo.
28
28
 
29
29
  <!-- AUTOGEN:STATS:BEGIN — auto-regenerated, do not edit by hand -->
30
- - **Schema version**: 13.0.0
31
- - **Queries**: 49
30
+ - **Schema version**: 13.1.0
31
+ - **Queries**: 50
32
32
  - **Mutations**: 40
33
33
  - **Fragments**: 100
34
34
  <!-- AUTOGEN:STATS:END -->
package/CHANGELOG.md CHANGED
@@ -1,5 +1,68 @@
1
1
  # Changelog
2
2
 
3
+ ## 13.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 2e137ad: Added cart-aware shipping methods discovery: `Cart.requiresShipping`, `CartLine.requiresShipping`, and `cart.availableShippingMethods(address)`.
8
+
9
+ **What's new for the storefront**
10
+ - **`cart.requiresShipping: Boolean!`** — single signal whether the cart needs physical shipping at all. `false` for carts that contain only digital goods (downloads, gift cards, services, subscriptions). Use it to skip the shipping picker step in checkout entirely.
11
+ - **`cart.lines.nodes[].requiresShipping: Boolean!`** — per-line classification. Useful in cart drawer UI to flag physical vs digital lines independently.
12
+ - **`cart.availableShippingMethods(address: ShippingAddressInput!): AvailableShippingMethodsPayload!`** — field on `Cart` that returns shipping methods for the cart's contents at the given destination. The backend pulls subtotal and weight from the cart aggregate — you no longer need to compute them on the client. For digital-only carts the response is `{ methods: [], userErrors: [{ code: 'DIGITAL_ONLY_NO_SHIPPING' }] }`.
13
+ - **`CartAvailableShippingMethods` query** — new named operation ready to drop into your storefront codegen pipeline.
14
+
15
+ **Recommended checkout flow**
16
+
17
+ ```graphql
18
+ query CartAvailableShippingMethods(
19
+ $cartId: ID!
20
+ $address: ShippingAddressInput!
21
+ ) {
22
+ cart(id: $cartId) {
23
+ id
24
+ requiresShipping
25
+ availableShippingMethods(address: $address) {
26
+ methods {
27
+ id
28
+ name
29
+ price {
30
+ amount
31
+ currencyCode
32
+ }
33
+ }
34
+ userErrors {
35
+ code
36
+ message
37
+ field
38
+ }
39
+ }
40
+ }
41
+ }
42
+ ```
43
+
44
+ If `cart.requiresShipping === false`, skip rendering the shipping picker and proceed straight to payment. Calling `cartSelectShippingMethod` on a digital-only cart returns `userErrors[{ code: 'FORBIDDEN_SHIPPING_METHOD' }]`, matching the readiness check that `cartComplete` already enforces.
45
+
46
+ **Backward compatibility**
47
+ - The existing standalone `availableShippingMethods(address, cart: CartShippingInput)` query is **unchanged**. Keep using it for pre-cart shipping calculators on product detail pages (when the customer has not created a cart yet).
48
+ - The new field and new query are purely additive. No existing operations change shape.
49
+
50
+ **Free-shipping progress + delivery estimates are now localized**
51
+
52
+ The `freeShippingProgress.message` field and `estimatedDelivery.description` field used to be hardcoded in English. They now respect the `Accept-Language` header (defaults to Polish — `pl`). If your storefront previously parsed these strings, switch to the structured fields (`qualifies`, `progressPercent`, `remaining.amount`, `minDays`, `maxDays`) instead.
53
+
54
+ **Reason codes on shipping `userErrors`**
55
+
56
+ | Code | When | UI reaction |
57
+ | -------------------------- | -------------------------------- | -------------------------------------------- |
58
+ | `DIGITAL_ONLY_NO_SHIPPING` | Cart contains only digital items | Skip shipping picker, go straight to payment |
59
+ | `NO_SHIPPING_METHODS` | Address has no matching zone | Show "no delivery to your country" |
60
+ | `SHIPPING_ERROR` | Internal service error (rare) | Retry or fall back to pre-cart preview query |
61
+
62
+ **Why both packages bump together**
63
+
64
+ `@doswiftly/storefront-sdk` ships local copies of the cart fragments that this update extends with `requiresShipping`, so the SDK release pairs with the schema/operations release. `@doswiftly/commerce-policy` exports `NON_PHYSICAL_TYPES` for downstream tooling that wants to mirror the same classification on the client side.
65
+
3
66
  ## 13.0.0
4
67
 
5
68
  ### Major Changes
package/README.md CHANGED
@@ -241,7 +241,8 @@ full executable body of each operation.
241
241
 
242
242
  | Operation | Description |
243
243
  | --- | --- |
244
- | `AvailableShippingMethods` | Returns shipping methods for a given destination address and cart shape (subtotal, total weight, currency). The query computes everything from the inputs alone — no existing cart is required, so it can be used for "shipping cost preview" UIs before the customer adds anything to a cart. Each method includes price, free-shipping progress (`{ qualifies, currentAmount, threshold, remaining, progressPercent }`), estimated delivery, and carrier metadata. Sorted by the merchant's `sortOrder`, then by price. |
244
+ | `AvailableShippingMethods` | Returns shipping methods for a given destination address and cart shape (subtotal, total weight, currency). The query computes everything from the inputs alone — no existing cart is required, so it can be used for "shipping cost preview" UIs (e.g. product detail page shipping calculator) before the customer adds anything to a cart. Each method includes price, free-shipping progress (`{ qualifies, currentAmount, threshold, remaining, progressPercent }`), estimated delivery, and carrier metadata. Sorted by the merchant's `sortOrder`, then by price. For a cart-bound checkout flow (where the cart is already known and the storefront wants the resolver to skip non-physical items and surface a `DIGITAL_ONLY_NO_SHIPPING` user error for all-digital carts), use `CartAvailableShippingMethods` against `cart.availableShippingMethods(address)` instead. |
245
+ | `CartAvailableShippingMethods` | Cart-aware shipping methods discovery. Returns shipping methods available for the cart's contents at the given destination, with subtotal and physical-item weight pulled from the cart aggregate (no need to compute them client-side). When the cart contains only non-physical items (digital, gift card, service, subscription), the response is `methods: []` plus a `DIGITAL_ONLY_NO_SHIPPING` user error — use this as the signal to skip rendering the shipping picker step. Prefer this query over the standalone `AvailableShippingMethods` once a cart has been created (`cartCreate`). For pre-cart "shipping cost preview" UIs on product detail pages, the standalone query remains the right tool. |
245
246
 
246
247
  #### Attribute Filters
247
248
 
package/fragments.graphql CHANGED
@@ -407,6 +407,7 @@ fragment CartLine on CartLine {
407
407
  productTitle
408
408
  productHandle
409
409
  productType
410
+ requiresShipping
410
411
  }
411
412
 
412
413
  # Buyer identity associated with the cart. Note: only `customerId` is currently persisted server-side; `email`, `phone`, `countryCode` are accepted in the input but ignored.
@@ -488,6 +489,7 @@ fragment Cart on Cart {
488
489
  appliedGiftCards {
489
490
  ...CartAppliedGiftCard
490
491
  }
492
+ requiresShipping
491
493
  createdAt
492
494
  updatedAt
493
495
  }
package/llms-full.txt CHANGED
@@ -1,7 +1,7 @@
1
1
  # DoSwiftly Storefront Operations — Full Reference
2
2
 
3
- > Schema version: **13.0.0**
4
- > 49 queries · 40 mutations · 100 fragments
3
+ > Schema version: **13.1.0**
4
+ > 50 queries · 40 mutations · 100 fragments
5
5
 
6
6
  Auto-generated from `.graphql` source files. Do not edit by hand — this file is
7
7
  regenerated on every release to match the published schema.
@@ -676,7 +676,7 @@ query GiftCardValidate($code: String!, $amount: Float) {
676
676
 
677
677
  **Section**: Shipping Methods
678
678
 
679
- **Description**: Returns shipping methods for a given destination address and cart shape (subtotal, total weight, currency). The query computes everything from the inputs alone — no existing cart is required, so it can be used for "shipping cost preview" UIs before the customer adds anything to a cart. Each method includes price, free-shipping progress (`{ qualifies, currentAmount, threshold, remaining, progressPercent }`), estimated delivery, and carrier metadata. Sorted by the merchant's `sortOrder`, then by price.
679
+ **Description**: Returns shipping methods for a given destination address and cart shape (subtotal, total weight, currency). The query computes everything from the inputs alone — no existing cart is required, so it can be used for "shipping cost preview" UIs (e.g. product detail page shipping calculator) before the customer adds anything to a cart. Each method includes price, free-shipping progress (`{ qualifies, currentAmount, threshold, remaining, progressPercent }`), estimated delivery, and carrier metadata. Sorted by the merchant's `sortOrder`, then by price. For a cart-bound checkout flow (where the cart is already known and the storefront wants the resolver to skip non-physical items and surface a `DIGITAL_ONLY_NO_SHIPPING` user error for all-digital carts), use `CartAvailableShippingMethods` against `cart.availableShippingMethods(address)` instead.
680
680
 
681
681
  **Variables**:
682
682
  - `$address`: `ShippingAddressInput!`
@@ -701,6 +701,39 @@ query AvailableShippingMethods($address: ShippingAddressInput!, $cart: CartShipp
701
701
  }
702
702
  ```
703
703
 
704
+ ### Query: `CartAvailableShippingMethods`
705
+
706
+ **Section**: Shipping Methods
707
+
708
+ **Description**: Cart-aware shipping methods discovery. Returns shipping methods available for the cart's contents at the given destination, with subtotal and physical-item weight pulled from the cart aggregate (no need to compute them client-side). When the cart contains only non-physical items (digital, gift card, service, subscription), the response is `methods: []` plus a `DIGITAL_ONLY_NO_SHIPPING` user error — use this as the signal to skip rendering the shipping picker step. Prefer this query over the standalone `AvailableShippingMethods` once a cart has been created (`cartCreate`). For pre-cart "shipping cost preview" UIs on product detail pages, the standalone query remains the right tool.
709
+
710
+ **Variables**:
711
+ - `$cartId`: `ID!`
712
+ - `$address`: `ShippingAddressInput!`
713
+
714
+ **Fragments used**: `AvailableShippingMethod`, `FreeShippingProgress`, `UserError`
715
+
716
+ **GraphQL**:
717
+ ```graphql
718
+ query CartAvailableShippingMethods($cartId: ID!, $address: ShippingAddressInput!) {
719
+ cart(id: $cartId) {
720
+ id
721
+ requiresShipping
722
+ availableShippingMethods(address: $address) {
723
+ methods {
724
+ ...AvailableShippingMethod
725
+ }
726
+ freeShippingProgress {
727
+ ...FreeShippingProgress
728
+ }
729
+ userErrors {
730
+ ...UserError
731
+ }
732
+ }
733
+ }
734
+ }
735
+ ```
736
+
704
737
  ### Query: `ProductFilters`
705
738
 
706
739
  **Section**: Attribute Filters
@@ -2943,6 +2976,7 @@ fragment CartLine on CartLine {
2943
2976
  productTitle
2944
2977
  productHandle
2945
2978
  productType
2979
+ requiresShipping
2946
2980
  }
2947
2981
  ```
2948
2982
 
@@ -3060,6 +3094,7 @@ fragment Cart on Cart {
3060
3094
  appliedGiftCards {
3061
3095
  ...CartAppliedGiftCard
3062
3096
  }
3097
+ requiresShipping
3063
3098
  createdAt
3064
3099
  updatedAt
3065
3100
  }
package/operations.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "schemaVersion": "13.0.0",
2
+ "schemaVersion": "13.1.0",
3
3
  "queries": [
4
4
  {
5
5
  "name": "Shop",
@@ -504,7 +504,7 @@
504
504
  "name": "AvailableShippingMethods",
505
505
  "kind": "query",
506
506
  "section": "Shipping Methods",
507
- "description": "Returns shipping methods for a given destination address and cart shape (subtotal, total weight, currency). The query computes everything from the inputs alone — no existing cart is required, so it can be used for \"shipping cost preview\" UIs before the customer adds anything to a cart. Each method includes price, free-shipping progress (`{ qualifies, currentAmount, threshold, remaining, progressPercent }`), estimated delivery, and carrier metadata. Sorted by the merchant's `sortOrder`, then by price.",
507
+ "description": "Returns shipping methods for a given destination address and cart shape (subtotal, total weight, currency). The query computes everything from the inputs alone — no existing cart is required, so it can be used for \"shipping cost preview\" UIs (e.g. product detail page shipping calculator) before the customer adds anything to a cart. Each method includes price, free-shipping progress (`{ qualifies, currentAmount, threshold, remaining, progressPercent }`), estimated delivery, and carrier metadata. Sorted by the merchant's `sortOrder`, then by price. For a cart-bound checkout flow (where the cart is already known and the storefront wants the resolver to skip non-physical items and surface a `DIGITAL_ONLY_NO_SHIPPING` user error for all-digital carts), use `CartAvailableShippingMethods` against `cart.availableShippingMethods(address)` instead.",
508
508
  "variables": [
509
509
  {
510
510
  "name": "address",
@@ -524,6 +524,30 @@
524
524
  ],
525
525
  "body": "query AvailableShippingMethods($address: ShippingAddressInput!, $cart: CartShippingInput) {\n availableShippingMethods(address: $address, cart: $cart) {\n methods {\n ...AvailableShippingMethod\n }\n freeShippingProgress {\n ...FreeShippingProgress\n }\n userErrors {\n ...UserError\n }\n }\n}"
526
526
  },
527
+ {
528
+ "name": "CartAvailableShippingMethods",
529
+ "kind": "query",
530
+ "section": "Shipping Methods",
531
+ "description": "Cart-aware shipping methods discovery. Returns shipping methods available for the cart's contents at the given destination, with subtotal and physical-item weight pulled from the cart aggregate (no need to compute them client-side). When the cart contains only non-physical items (digital, gift card, service, subscription), the response is `methods: []` plus a `DIGITAL_ONLY_NO_SHIPPING` user error — use this as the signal to skip rendering the shipping picker step. Prefer this query over the standalone `AvailableShippingMethods` once a cart has been created (`cartCreate`). For pre-cart \"shipping cost preview\" UIs on product detail pages, the standalone query remains the right tool.",
532
+ "variables": [
533
+ {
534
+ "name": "cartId",
535
+ "type": "ID!",
536
+ "defaultValue": null
537
+ },
538
+ {
539
+ "name": "address",
540
+ "type": "ShippingAddressInput!",
541
+ "defaultValue": null
542
+ }
543
+ ],
544
+ "fragmentRefs": [
545
+ "AvailableShippingMethod",
546
+ "FreeShippingProgress",
547
+ "UserError"
548
+ ],
549
+ "body": "query CartAvailableShippingMethods($cartId: ID!, $address: ShippingAddressInput!) {\n cart(id: $cartId) {\n id\n requiresShipping\n availableShippingMethods(address: $address) {\n methods {\n ...AvailableShippingMethod\n }\n freeShippingProgress {\n ...FreeShippingProgress\n }\n userErrors {\n ...UserError\n }\n }\n }\n}"
550
+ },
527
551
  {
528
552
  "name": "ProductFilters",
529
553
  "kind": "query",
@@ -2065,7 +2089,7 @@
2065
2089
  "CartLineCost",
2066
2090
  "ProductVariant"
2067
2091
  ],
2068
- "body": "fragment CartLine on CartLine {\n id\n quantity\n variant {\n ...ProductVariant\n }\n cost {\n ...CartLineCost\n }\n attributes {\n key\n value\n }\n attributeSelections {\n ...AttributeSelection\n }\n productId\n productTitle\n productHandle\n productType\n}",
2092
+ "body": "fragment CartLine on CartLine {\n id\n quantity\n variant {\n ...ProductVariant\n }\n cost {\n ...CartLineCost\n }\n attributes {\n key\n value\n }\n attributeSelections {\n ...AttributeSelection\n }\n productId\n productTitle\n productHandle\n productType\n requiresShipping\n}",
2069
2093
  "onType": "CartLine"
2070
2094
  },
2071
2095
  {
@@ -2118,7 +2142,7 @@
2118
2142
  "MailingAddress",
2119
2143
  "PageInfo"
2120
2144
  ],
2121
- "body": "fragment Cart on Cart {\n id\n checkoutUrl\n totalQuantity\n cost {\n ...CartCost\n }\n lines(first: 100) {\n edges {\n cursor\n node {\n ... on CartLine {\n ...CartLine\n }\n }\n }\n nodes {\n ... on CartLine {\n ...CartLine\n }\n }\n pageInfo {\n ...PageInfo\n }\n totalCount\n }\n buyerIdentity {\n ...CartBuyerIdentity\n }\n discountCodes {\n ...CartDiscountCode\n }\n discountAllocations {\n ...CartDiscountAllocation\n }\n note\n attributes {\n key\n value\n }\n email\n phone\n shippingAddress {\n ...MailingAddress\n }\n billingAddress {\n ...MailingAddress\n }\n selectedShippingMethod {\n ...CartShippingMethod\n }\n selectedPaymentMethod {\n ...CartSelectedPaymentMethod\n }\n appliedGiftCards {\n ...CartAppliedGiftCard\n }\n createdAt\n updatedAt\n}",
2145
+ "body": "fragment Cart on Cart {\n id\n checkoutUrl\n totalQuantity\n cost {\n ...CartCost\n }\n lines(first: 100) {\n edges {\n cursor\n node {\n ... on CartLine {\n ...CartLine\n }\n }\n }\n nodes {\n ... on CartLine {\n ...CartLine\n }\n }\n pageInfo {\n ...PageInfo\n }\n totalCount\n }\n buyerIdentity {\n ...CartBuyerIdentity\n }\n discountCodes {\n ...CartDiscountCode\n }\n discountAllocations {\n ...CartDiscountAllocation\n }\n note\n attributes {\n key\n value\n }\n email\n phone\n shippingAddress {\n ...MailingAddress\n }\n billingAddress {\n ...MailingAddress\n }\n selectedShippingMethod {\n ...CartShippingMethod\n }\n selectedPaymentMethod {\n ...CartSelectedPaymentMethod\n }\n appliedGiftCards {\n ...CartAppliedGiftCard\n }\n requiresShipping\n createdAt\n updatedAt\n}",
2122
2146
  "onType": "Cart"
2123
2147
  },
2124
2148
  {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doswiftly/storefront-operations",
3
- "version": "13.0.0",
3
+ "version": "13.1.0",
4
4
  "description": "GraphQL operations for DoSwiftly Storefront - SSOT from backend",
5
5
  "homepage": "https://doswiftly.pl",
6
6
  "publishConfig": {
package/queries.graphql CHANGED
@@ -368,7 +368,7 @@ query GiftCardValidate($code: String!, $amount: Float) {
368
368
  # Shipping Methods
369
369
  # ============================================
370
370
 
371
- # Returns shipping methods for a given destination address and cart shape (subtotal, total weight, currency). The query computes everything from the inputs alone — no existing cart is required, so it can be used for "shipping cost preview" UIs before the customer adds anything to a cart. Each method includes price, free-shipping progress (`{ qualifies, currentAmount, threshold, remaining, progressPercent }`), estimated delivery, and carrier metadata. Sorted by the merchant's `sortOrder`, then by price.
371
+ # Returns shipping methods for a given destination address and cart shape (subtotal, total weight, currency). The query computes everything from the inputs alone — no existing cart is required, so it can be used for "shipping cost preview" UIs (e.g. product detail page shipping calculator) before the customer adds anything to a cart. Each method includes price, free-shipping progress (`{ qualifies, currentAmount, threshold, remaining, progressPercent }`), estimated delivery, and carrier metadata. Sorted by the merchant's `sortOrder`, then by price. For a cart-bound checkout flow (where the cart is already known and the storefront wants the resolver to skip non-physical items and surface a `DIGITAL_ONLY_NO_SHIPPING` user error for all-digital carts), use `CartAvailableShippingMethods` against `cart.availableShippingMethods(address)` instead.
372
372
  query AvailableShippingMethods($address: ShippingAddressInput!, $cart: CartShippingInput) {
373
373
  availableShippingMethods(address: $address, cart: $cart) {
374
374
  methods {
@@ -383,6 +383,25 @@ query AvailableShippingMethods($address: ShippingAddressInput!, $cart: CartShipp
383
383
  }
384
384
  }
385
385
 
386
+ # Cart-aware shipping methods discovery. Returns shipping methods available for the cart's contents at the given destination, with subtotal and physical-item weight pulled from the cart aggregate (no need to compute them client-side). When the cart contains only non-physical items (digital, gift card, service, subscription), the response is `methods: []` plus a `DIGITAL_ONLY_NO_SHIPPING` user error — use this as the signal to skip rendering the shipping picker step. Prefer this query over the standalone `AvailableShippingMethods` once a cart has been created (`cartCreate`). For pre-cart "shipping cost preview" UIs on product detail pages, the standalone query remains the right tool.
387
+ query CartAvailableShippingMethods($cartId: ID!, $address: ShippingAddressInput!) {
388
+ cart(id: $cartId) {
389
+ id
390
+ requiresShipping
391
+ availableShippingMethods(address: $address) {
392
+ methods {
393
+ ...AvailableShippingMethod
394
+ }
395
+ freeShippingProgress {
396
+ ...FreeShippingProgress
397
+ }
398
+ userErrors {
399
+ ...UserError
400
+ }
401
+ }
402
+ }
403
+ }
404
+
386
405
  # ============================================
387
406
  # Attribute Filters
388
407
  # ============================================
package/schema.graphql CHANGED
@@ -776,6 +776,11 @@ type Cart implements Node {
776
776
  """Available payment methods dla tego cart (shop-configured providers)"""
777
777
  availablePaymentMethods: [PaymentMethod!]!
778
778
 
779
+ """
780
+ Cart-aware shipping methods discovery. Returns empty methods + DIGITAL_ONLY_NO_SHIPPING user error when the cart contains only non-physical items (digital, gift card, service, subscription). Physical / mixed carts get the same method computation as the standalone `availableShippingMethods` query but with subtotal and weight pulled from the cart aggregate.
781
+ """
782
+ availableShippingMethods(address: ShippingAddressInput!): AvailableShippingMethodsPayload!
783
+
779
784
  """
780
785
  Billing address ustawiony przez cartSetBillingAddress (jeśli różny od shipping)
781
786
  """
@@ -821,6 +826,11 @@ type Cart implements Node {
821
826
  """Product recommendations based on cart contents"""
822
827
  recommendations(first: Int = 4): CartRecommendations
823
828
 
829
+ """
830
+ True if any line in the cart requires physical shipping. False when every line is non-physical (digital, gift card, service, subscription). Use as the single signal to render or skip the shipping picker in checkout.
831
+ """
832
+ requiresShipping: Boolean!
833
+
824
834
  """Selected payment method (PayU/Stripe/COD/etc.)"""
825
835
  selectedPaymentMethod: PaymentMethod
826
836
 
@@ -1139,6 +1149,11 @@ type CartLine {
1139
1149
  """Quantity of this item"""
1140
1150
  quantity: Int!
1141
1151
 
1152
+ """
1153
+ True if this line item requires physical shipping (productType=PHYSICAL). False for digital, gift card, service, and subscription items. Use to skip the shipping step in checkout when every line is non-physical.
1154
+ """
1155
+ requiresShipping: Boolean!
1156
+
1142
1157
  """Product variant being purchased"""
1143
1158
  variant: ProductVariant!
1144
1159
  }