local-storefront 0.3.0-rc.3 → 0.3.0-rc.5
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/CHANGELOG.md +43 -0
- package/docs/admin.md +15 -7
- package/docs/checkout.md +24 -8
- package/package.json +7 -7
package/CHANGELOG.md
CHANGED
|
@@ -3,6 +3,49 @@
|
|
|
3
3
|
What changed in each version of `lsf`, for someone who uses it. The section of a version is
|
|
4
4
|
what its [GitHub release](https://github.com/0xtlt/local-storefront/releases) says.
|
|
5
5
|
|
|
6
|
+
## 0.3.0-rc.5 - 2026-11-13
|
|
7
|
+
|
|
8
|
+
Fifth release candidate of 0.3.0. The errors of `/cart/change.js` read as a store's.
|
|
9
|
+
|
|
10
|
+
### Breaking changes
|
|
11
|
+
|
|
12
|
+
What to change when you upgrade from 0.3.0-rc.4.
|
|
13
|
+
|
|
14
|
+
- **The `message` of a `/cart/change.js` error is its `description`**, no longer `Cart Error`.
|
|
15
|
+
Theme tests that expect `Cart Error` from `change.js` need the description instead. `add.js`
|
|
16
|
+
and `update.js` keep `Cart Error`.
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
#### Themes and the storefront
|
|
21
|
+
|
|
22
|
+
- **`/cart/change.js` capped at the stock counts what the line holds.** A line of 1 asked for 5
|
|
23
|
+
with 2 in stock answers "Only 2 items were added to your cart due to availability.", as a
|
|
24
|
+
store does, no longer "Only 1 item". `add.js` still counts the units it added.
|
|
25
|
+
|
|
26
|
+
## 0.3.0-rc.4 - 2026-11-12
|
|
27
|
+
|
|
28
|
+
Fourth release candidate of 0.3.0. It fixes the code discounts of 0.3.0-rc.3: their uses, their
|
|
29
|
+
minimums and their one use per customer now follow what Shopify does.
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
#### The checkout of an app
|
|
34
|
+
|
|
35
|
+
- **A code discount's `usageLimit` and `appliesOncePerCustomer` hold when checkouts complete at
|
|
36
|
+
once.** The code is checked again when the order is recorded; when the buyer can no longer use
|
|
37
|
+
it, the completion answers `422` with the checkout without it and "`<code>` discount code isn't
|
|
38
|
+
available to you right now".
|
|
39
|
+
- **A free shipping code that takes nothing off is not on the order.** Over its
|
|
40
|
+
`maximumShippingPrice` for the selected rate, on a cart with nothing to ship, or beaten by a
|
|
41
|
+
Function's shipping discount, it is no longer in `discount_codes` of the order and its
|
|
42
|
+
webhooks, and it is not counted as a use.
|
|
43
|
+
- **Minimums count the discounts before them**, as Shopify's help center describes: a free
|
|
44
|
+
shipping minimum the cart after the product and order discounts, an amount off minimum the
|
|
45
|
+
items it applies to after the product discounts.
|
|
46
|
+
- **Renaming or deleting a redeemed code no longer resets `appliesOncePerCustomer`.** Uses are
|
|
47
|
+
matched by discount, not by its codes of the moment.
|
|
48
|
+
|
|
6
49
|
## 0.3.0-rc.3 - 2026-11-12
|
|
7
50
|
|
|
8
51
|
Third release candidate of 0.3.0. The local checkout applies the code discounts a backend makes
|
package/docs/admin.md
CHANGED
|
@@ -1631,13 +1631,21 @@ What the backend changes through the Admin API is what the storefront and the
|
|
|
1631
1631
|
checkout takes the code while the discount is `ACTIVE` by its dates, its `usageLimit` is not
|
|
1632
1632
|
reached, the buyer is one of its `context` (all buyers, the customers it lists, the members of
|
|
1633
1633
|
the segments it lists: a guest is the customer whose email they gave), has not used it when it
|
|
1634
|
-
`appliesOncePerCustomer`, and the cart meets its `minimumRequirement` (the
|
|
1635
|
-
|
|
1636
|
-
|
|
1637
|
-
|
|
1638
|
-
|
|
1639
|
-
|
|
1640
|
-
|
|
1634
|
+
`appliesOncePerCustomer`, and the cart meets its `minimumRequirement` (the quantity of the
|
|
1635
|
+
items it applies to; a subtotal at the prices the discounts before it left, as on Shopify:
|
|
1636
|
+
the items it applies to after the product discounts for an amount off, the cart after the
|
|
1637
|
+
product and order discounts for free shipping); free shipping makes free the rates up to its
|
|
1638
|
+
`maximumShippingPrice`, to the countries of its `destination`. Otherwise the page says "Enter
|
|
1639
|
+
a valid discount code", and the state's `discountCodes` says why (`reason`). The order carries
|
|
1640
|
+
the code it took something off with (`discountCodes`, a `DiscountCodeApplication`, the
|
|
1641
|
+
allocations), so do the `discount_codes` and `discount_applications` of `orders/create` and
|
|
1642
|
+
`orders/paid` (`type` `shipping` for free shipping), and the discount's `asyncUsageCount` (and
|
|
1643
|
+
its code's) goes up by one at once. A free shipping code that took nothing off (a rate over
|
|
1644
|
+
its maximum, nothing to ship) is neither on the order nor used. The usage limit and the one
|
|
1645
|
+
use per customer are checked again as the order is recorded, so that two checkouts priced at
|
|
1646
|
+
once do not both take the last use: the second completion is refused. A use is the
|
|
1647
|
+
discount's: renaming its code or deleting it (`discountCodeRedeemCodeBulkDelete`) does not
|
|
1648
|
+
give a buyer who used it another one. One code of these types
|
|
1641
1649
|
applies per checkout (lsf does not read `combinesWith`), and it stacks with the discounts of
|
|
1642
1650
|
the Functions; of the shipping discounts, the best one applies. Not read: a `context` of
|
|
1643
1651
|
markets (every buyer), `appliesOnOneTimePurchase` and `appliesOnSubscription`. Buy X get Y
|
package/docs/checkout.md
CHANGED
|
@@ -1124,15 +1124,30 @@ amount off the order is an order discount, one off listed products, variants or
|
|
|
1124
1124
|
off each item) a product discount, free shipping a shipping discount, and each takes its place
|
|
1125
1125
|
among the discounts of the Functions by the rules above. The code is checked when the
|
|
1126
1126
|
checkout is computed: the discount's dates and usage limit, its buyers, one use per customer,
|
|
1127
|
-
its minimum
|
|
1128
|
-
|
|
1129
|
-
discount
|
|
1130
|
-
(
|
|
1131
|
-
|
|
1132
|
-
|
|
1127
|
+
its minimum, and for free shipping the country of the address (a code is taken before there is
|
|
1128
|
+
one). A minimum subtotal counts the prices the discounts before it left, as Shopify's do
|
|
1129
|
+
([amount off](https://help.shopify.com/en/manual/discounts/discount-types/percentage-fixed-amount),
|
|
1130
|
+
[free shipping](https://help.shopify.com/en/manual/discounts/discount-types/free-shipping),
|
|
1131
|
+
"Understanding minimum purchase requirements"): for an amount off, the items it applies to
|
|
1132
|
+
after the product discounts of the Functions; for free shipping, the whole cart after the
|
|
1133
|
+
product and order discounts. A refused code shows "Enter a valid discount code", and its entry
|
|
1134
|
+
of `discountCodes` in the state has a `reason` (`"its minimum purchase is 50.00 USD, and the
|
|
1135
|
+
items it applies to come to 32.00 USD after the product discounts"`). One code of these types
|
|
1136
|
+
applies per checkout, the first one entered that qualifies: a second is refused, as on Shopify
|
|
1137
|
+
when a discount combines with no other (lsf does not read `combinesWith`). Its
|
|
1133
1138
|
`DiscountApplication` is `ACROSS` (`EACH` off each item), `ALL` for the order and `ENTITLED`
|
|
1134
1139
|
for listed items; free shipping is 100% off the shipping line, `EACH`.
|
|
1135
1140
|
|
|
1141
|
+
A free shipping code that takes nothing off (the selected rate costs more than its
|
|
1142
|
+
`maximumShippingPrice`, nothing in the cart ships, or a shipping discount of a Function beat it)
|
|
1143
|
+
stays applicable on the page, as entered, but it is not the order's: the order does not carry it
|
|
1144
|
+
and it is not counted as a use. The codes are checked again as the order is recorded: when
|
|
1145
|
+
another order took the last use of the discount (`usageLimit`) or the buyer's one
|
|
1146
|
+
(`appliesOncePerCustomer`) after the checkout was priced, the order is not placed. The
|
|
1147
|
+
completion answers 422 with the checkout as it now is (the code refused, with its `reason`, the
|
|
1148
|
+
price without it) and the error "`<code>` discount code isn't available to you right now" on
|
|
1149
|
+
`$.cart.discountCodes`; the buyer pays again for what they now see.
|
|
1150
|
+
|
|
1136
1151
|
An amount is shared between the lines in proportion to what they cost, to the cent, and the
|
|
1137
1152
|
allocations of the lines (`discountAllocations`, in the page and for the Functions that run
|
|
1138
1153
|
later) add up to the discount.
|
|
@@ -1645,8 +1660,9 @@ store before a release:
|
|
|
1645
1660
|
- **Discount combinations and conditions**: lsf combines every product and order discount
|
|
1646
1661
|
([how discounts stack](#how-discounts-stack)).
|
|
1647
1662
|
- **Codes of Shopify's own discount types**: one applies per checkout whatever its
|
|
1648
|
-
`combinesWith`,
|
|
1649
|
-
refused code says lsf's reason, where
|
|
1663
|
+
`combinesWith`, a minimum subtotal counts the prices after every discount before it (Shopify
|
|
1664
|
+
counts only those that combine with it), and a refused code says lsf's reason, where
|
|
1665
|
+
Shopify's checkout has messages of its own.
|
|
1650
1666
|
- **Currencies and price lists**: a buyer pays in the currency of the country of the shipping
|
|
1651
1667
|
address (else of the one selected on the storefront), at its market's `rate` or fixed `prices`
|
|
1652
1668
|
([localization](data-format.md#localization)); the Functions read that rate as
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "local-storefront",
|
|
3
|
-
"version": "0.3.0-rc.
|
|
3
|
+
"version": "0.3.0-rc.5",
|
|
4
4
|
"description": "Run Shopify themes and apps locally, without Shopify: the storefront, a local checkout for Functions and checkout UI extensions, and a local GraphQL Admin API with webhooks, from JSON store data. Made for tests in CI.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"shopify",
|
|
@@ -38,11 +38,11 @@
|
|
|
38
38
|
"node": ">=18"
|
|
39
39
|
},
|
|
40
40
|
"optionalDependencies": {
|
|
41
|
-
"local-storefront-darwin-arm64": "0.3.0-rc.
|
|
42
|
-
"local-storefront-darwin-x64": "0.3.0-rc.
|
|
43
|
-
"local-storefront-linux-arm64": "0.3.0-rc.
|
|
44
|
-
"local-storefront-linux-x64": "0.3.0-rc.
|
|
45
|
-
"local-storefront-win32-arm64": "0.3.0-rc.
|
|
46
|
-
"local-storefront-win32-x64": "0.3.0-rc.
|
|
41
|
+
"local-storefront-darwin-arm64": "0.3.0-rc.5",
|
|
42
|
+
"local-storefront-darwin-x64": "0.3.0-rc.5",
|
|
43
|
+
"local-storefront-linux-arm64": "0.3.0-rc.5",
|
|
44
|
+
"local-storefront-linux-x64": "0.3.0-rc.5",
|
|
45
|
+
"local-storefront-win32-arm64": "0.3.0-rc.5",
|
|
46
|
+
"local-storefront-win32-x64": "0.3.0-rc.5"
|
|
47
47
|
}
|
|
48
48
|
}
|