@doswiftly/cli 0.2.5 → 1.0.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/CHANGELOG.md +899 -0
- package/dist/lib/proxy-server.d.ts.map +1 -1
- package/dist/lib/proxy-server.js +20 -7
- package/dist/lib/proxy-server.js.map +1 -1
- package/package.json +4 -2
- package/templates/storefront-nextjs-shadcn/app/[locale]/account/addresses/page.tsx +10 -10
- package/templates/storefront-nextjs-shadcn/app/[locale]/account/orders/[id]/page.tsx +9 -9
- package/templates/storefront-nextjs-shadcn/app/[locale]/account/orders/[id]/tracking/page.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/app/[locale]/account/orders/page.tsx +2 -2
- package/templates/storefront-nextjs-shadcn/app/[locale]/account/settings/page.tsx +3 -3
- package/templates/storefront-nextjs-shadcn/app/[locale]/checkout/page.tsx +41 -41
- package/templates/storefront-nextjs-shadcn/app/[locale]/products/[slug]/product-client.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/app/api/auth/whoami/route.ts +6 -0
- package/templates/storefront-nextjs-shadcn/components/account/address-form.tsx +43 -43
- package/templates/storefront-nextjs-shadcn/components/account/address-list.tsx +9 -9
- package/templates/storefront-nextjs-shadcn/components/account/customer-info.fragment.graphql +5 -5
- package/templates/storefront-nextjs-shadcn/components/account/order-details.tsx +8 -8
- package/templates/storefront-nextjs-shadcn/components/account/order-history.tsx +2 -2
- package/templates/storefront-nextjs-shadcn/components/account/order-summary.fragment.graphql +15 -13
- package/templates/storefront-nextjs-shadcn/components/cart/cart-line.fragment.graphql +6 -6
- package/templates/storefront-nextjs-shadcn/components/discount/discount-code-input.tsx +10 -10
- package/templates/storefront-nextjs-shadcn/components/gift-card/gift-card-input.tsx +9 -9
- package/templates/storefront-nextjs-shadcn/components/order/delivery-estimate.tsx +2 -2
- package/templates/storefront-nextjs-shadcn/components/product/product-card.tsx +1 -1
- package/templates/storefront-nextjs-shadcn/components/seo/product-json-ld.ts +1 -1
- package/templates/storefront-nextjs-shadcn/hooks/use-auth-sync.ts +43 -25
- package/templates/storefront-nextjs-shadcn/hooks/use-cart-actions.ts +1 -1
- package/templates/storefront-nextjs-shadcn/hooks/use-cart-sync.ts +14 -14
- package/templates/storefront-nextjs-shadcn/lib/graphql/hooks.ts +24 -35
- package/templates/storefront-nextjs-shadcn/lib/graphql/server.ts +21 -4
- package/templates/storefront-nextjs-shadcn/messages/en.json +6 -6
- package/templates/storefront-nextjs-shadcn/messages/pl.json +6 -6
- package/templates/storefront-nextjs-shadcn/stores/checkout-store.ts +8 -8
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,899 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## 1.0.0
|
|
4
|
+
|
|
5
|
+
### Major Changes
|
|
6
|
+
|
|
7
|
+
- d51eb03: Storefront API: cookie-first authentication + comprehensive typing fixes.
|
|
8
|
+
|
|
9
|
+
**⚠️ Breaking changes — migration required**
|
|
10
|
+
|
|
11
|
+
### Authentication: cookie-first, drop `customerAccessToken` argument
|
|
12
|
+
|
|
13
|
+
The storefront API no longer accepts `customerAccessToken` as a query/mutation argument.
|
|
14
|
+
Authentication context is now resolved per-request from (in priority order):
|
|
15
|
+
1. **httpOnly cookie `customerAccessToken`** — set automatically by the API after `customerAccessTokenCreate` / `customerCreate` / `customerAccessTokenRenew` / `customerActivateByUrl` / `customerResetByUrl` / `customerReset`. Browser-based storefronts gain XSS-safe auth out of the box.
|
|
16
|
+
2. **`Authorization: Bearer <token>` header** — for non-browser clients (mobile native, server-to-server, OAuth integrations per RFC 6750).
|
|
17
|
+
|
|
18
|
+
`customerAccessTokenDelete` (logout) clears the cookie via `Set-Cookie: customerAccessToken=; Max-Age=0`.
|
|
19
|
+
|
|
20
|
+
**Affected operations** (drop `customerAccessToken: String!` arg + value pass-through):
|
|
21
|
+
- Queries: `customer`, `customerOrder`, `shipment`, `return`, `returnsByOrder`
|
|
22
|
+
- Mutations: `customerAccessTokenDelete`, `customerAccessTokenRenew`, `customerUpdate`, `customerAddressCreate`, `customerAddressUpdate`, `customerAddressDelete`, `customerDefaultAddressUpdate`, `returnCreate`, `returnCancel`
|
|
23
|
+
|
|
24
|
+
**Migration**:
|
|
25
|
+
|
|
26
|
+
```graphql
|
|
27
|
+
# Before (4.x)
|
|
28
|
+
query Customer($customerAccessToken: String!) {
|
|
29
|
+
customer(customerAccessToken: $customerAccessToken) { ... }
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
# After (5.x)
|
|
33
|
+
query Customer {
|
|
34
|
+
customer { ... }
|
|
35
|
+
}
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
For a browser, send the request with `credentials: 'include'` so the httpOnly cookie travels along. For non-browser clients, set `Authorization: Bearer <jwt>` on each request. The `accessToken` field in mutation responses (`customerAccessTokenCreate.customerAccessToken.accessToken`) is still present for non-browser clients that manage their own token storage.
|
|
39
|
+
|
|
40
|
+
### `LANGUAGE_HEADER_NAME` constant renamed `X-Lang` → `X-Language`
|
|
41
|
+
|
|
42
|
+
Storefronts importing this constant from `@doswiftly/storefront-sdk` will pick up the new value at rebuild time. The previous value did not match the API reader and silently fell through to `Accept-Language` auto-detection — explicit per-request language switching now works as documented.
|
|
43
|
+
|
|
44
|
+
### `@inContext` directive accepts new arguments
|
|
45
|
+
|
|
46
|
+
`@inContext` previously exposed only `preferredLocationId`. It now also accepts `country: String`, `language: String`, `currency: String`, and `buyer: BuyerInput { customerAccessToken, companyLocationId }`. Resolution priority: `directive > request header > cookie > Accept-Language auto-detect > shop default`. All five layers are independent — pick whichever matches your routing/SSR architecture.
|
|
47
|
+
|
|
48
|
+
### `Shop.primaryDomain` is now a `Domain` object
|
|
49
|
+
|
|
50
|
+
```graphql
|
|
51
|
+
# Before
|
|
52
|
+
type Shop {
|
|
53
|
+
primaryDomain: String # "https://example.com"
|
|
54
|
+
primaryDomainObject: Domain! # { host, url, sslEnabled }
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
# After
|
|
58
|
+
type Shop {
|
|
59
|
+
primaryDomain: Domain! # { host, url, sslEnabled }
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Update the `Shop` fragment to query the structured fields:
|
|
64
|
+
|
|
65
|
+
```graphql
|
|
66
|
+
fragment Shop on Shop {
|
|
67
|
+
primaryDomain { host url sslEnabled }
|
|
68
|
+
...
|
|
69
|
+
}
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
### Removed redundant fields
|
|
73
|
+
- `Menu.itemsCount` — drop. Use `items.length` in the client (the count was always equal to the array length).
|
|
74
|
+
- `GiftCard.lastCharacters` — drop. Compute from `maskedCode.slice(-4)` in the client.
|
|
75
|
+
|
|
76
|
+
### Stronger types — `Date`, enums, structured errors
|
|
77
|
+
|
|
78
|
+
Several fields previously returned `String` (ISO 8601) or untyped error arrays — now properly typed:
|
|
79
|
+
- **DateTime**: `Order.processedAt`, `BlogPost.{publishedAt,createdAt,updatedAt}`, `ShopPage.{publishedAt,createdAt,updatedAt}`, `LoyaltyMember.{lastActivityAt,enrolledAt}`, `LoyaltyTransaction.{createdAt,expiresAt}`, `LoyaltyPointsSummary.nextExpiryDate`, `CustomerAccessToken.expiresAt`. Codegen now emits `Date | string` (or your codegen's date type).
|
|
80
|
+
- **Enums**: `Customer.emailMarketingState` is `EmailMarketingState!`, `ProductAttributeDefinition.fillingMode/billingMode` are typed enums, `ProductAttributeOption.surchargeType` is `AttributeOptionSurchargeType`, `Shop.currencyCode` / `paymentCurrencies` / `supportedCurrencies` use `CurrencyCode`, `Shop.defaultLanguage` / `supportedLanguages` use `LanguageCode`, `Currency.code` is `CurrencyCode`, `Currency.symbolPosition` is the new `CurrencySymbolPosition` enum (`BEFORE` / `AFTER`).
|
|
81
|
+
- **Structured errors**: `WishlistPayload.userErrors`, `RedeemRewardPayload.userErrors`, `GenerateReferralCodePayload.userErrors` are now `[UserError!]!` (`{ message, code, field }`) instead of `[String!]!`. Replace `userErrors[0]` with `userErrors[0].message` in your error handling.
|
|
82
|
+
|
|
83
|
+
### New: `ProductReview.unhelpfulCount`
|
|
84
|
+
|
|
85
|
+
Storefronts can now render upvote/downvote counts side-by-side without computing one from the other.
|
|
86
|
+
|
|
87
|
+
### Cross-domain cookie auth — `credentials: 'include'`
|
|
88
|
+
|
|
89
|
+
The SDK GraphQL transport now sends `credentials: 'include'` on every request. If your storefront runs on a domain different from the API and uses cookie auth, the API CORS configuration must allow your origin (with `Access-Control-Allow-Credentials: true`). Same-origin deployments are unaffected.
|
|
90
|
+
|
|
91
|
+
### Auth store no longer persists `accessToken` to localStorage (XSS hardening)
|
|
92
|
+
|
|
93
|
+
`useAuthStore` only persists `customer` and `isAuthenticated` to localStorage. The token lives in the httpOnly cookie (browser auto-sent) plus in-memory store state (set by login flow). After a page refresh, browser-based storefronts call `GET /api/auth/whoami` (new BFF route shipped with the template) to rehydrate `customer` info — the cookie continues to authenticate every subsequent GraphQL request transparently.
|
|
94
|
+
|
|
95
|
+
The `auth-storage` localStorage version bumps from `v2` to `v3`; the migrate handler clears legacy persisted tokens on first load.
|
|
96
|
+
|
|
97
|
+
### New: `createWhoamiHandler()` factory
|
|
98
|
+
|
|
99
|
+
Exports from `@doswiftly/storefront-sdk`. Pair with the `app/api/auth/whoami/route.ts` example shipped with the CLI template:
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
// app/api/auth/whoami/route.ts
|
|
103
|
+
import { createWhoamiHandler } from "@doswiftly/storefront-sdk";
|
|
104
|
+
export const GET = createWhoamiHandler({
|
|
105
|
+
apiUrl: process.env.NEXT_PUBLIC_API_URL,
|
|
106
|
+
shopSlug: process.env.NEXT_PUBLIC_SHOP_SLUG,
|
|
107
|
+
});
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
### `AuthClient` (low-level core API) — drop token argument
|
|
111
|
+
|
|
112
|
+
```typescript
|
|
113
|
+
// Before
|
|
114
|
+
authClient.logout(token);
|
|
115
|
+
authClient.renewToken(token);
|
|
116
|
+
authClient.getCustomer(token);
|
|
117
|
+
|
|
118
|
+
// After
|
|
119
|
+
authClient.logout(); // auth via cookie/Bearer in middleware
|
|
120
|
+
authClient.renewToken();
|
|
121
|
+
authClient.getCustomer();
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
### Template (`@doswiftly/cli` scaffolded storefront)
|
|
125
|
+
|
|
126
|
+
The Next.js template now uses cookie-first auth end-to-end:
|
|
127
|
+
- `lib/graphql/hooks.ts` — query/mutation hooks no longer pass `customerAccessToken`
|
|
128
|
+
- `lib/graphql/server.ts` — server-side helper reads `customerAccessToken` cookie via `next/headers` and forwards as `Authorization: Bearer`
|
|
129
|
+
- `app/api/auth/whoami/route.ts` (new) — BFF route for `customer` rehydration after page refresh
|
|
130
|
+
- `hooks/use-auth-sync.ts` — rewritten to use the whoami endpoint instead of token desync detection
|
|
131
|
+
- Auth-gated React components/pages now check `isAuthenticated` instead of `accessToken`
|
|
132
|
+
|
|
133
|
+
### Wishlist pagination + GiftCard read surface
|
|
134
|
+
- **`wishlists`** is now a paginated **`WishlistConnection`** (Relay pattern). Replace direct array selection with `nodes` (shortcut) or `edges { cursor node { ... } }`. Supports `first: Int = 20` and `after: String` cursor arguments.
|
|
135
|
+
|
|
136
|
+
```graphql
|
|
137
|
+
# Before (4.x)
|
|
138
|
+
query Wishlists {
|
|
139
|
+
wishlists {
|
|
140
|
+
id
|
|
141
|
+
name
|
|
142
|
+
itemCount
|
|
143
|
+
items {
|
|
144
|
+
id
|
|
145
|
+
productId
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
# After (5.x)
|
|
151
|
+
query Wishlists($first: Int = 20, $after: String) {
|
|
152
|
+
wishlists(first: $first, after: $after) {
|
|
153
|
+
nodes {
|
|
154
|
+
id
|
|
155
|
+
name
|
|
156
|
+
itemCount
|
|
157
|
+
items {
|
|
158
|
+
id
|
|
159
|
+
productId
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
pageInfo {
|
|
163
|
+
hasNextPage
|
|
164
|
+
hasPreviousPage
|
|
165
|
+
startCursor
|
|
166
|
+
endCursor
|
|
167
|
+
}
|
|
168
|
+
totalCount
|
|
169
|
+
}
|
|
170
|
+
}
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
- **`giftCard(code)`** read query no longer wraps result in `GiftCardPayload`. Returns nullable `GiftCard` directly (`null` = not found). The wrapper pattern `{ giftCard, userErrors }` is reserved for mutations only.
|
|
174
|
+
|
|
175
|
+
```graphql
|
|
176
|
+
# Before (4.x)
|
|
177
|
+
query GiftCard($code: String!) {
|
|
178
|
+
giftCard(code: $code) {
|
|
179
|
+
giftCard {
|
|
180
|
+
id
|
|
181
|
+
maskedCode
|
|
182
|
+
balance {
|
|
183
|
+
amount
|
|
184
|
+
currencyCode
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
userErrors {
|
|
188
|
+
message
|
|
189
|
+
code
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
# After (5.x)
|
|
195
|
+
query GiftCard($code: String!) {
|
|
196
|
+
giftCard(code: $code) {
|
|
197
|
+
id
|
|
198
|
+
maskedCode
|
|
199
|
+
balance {
|
|
200
|
+
amount
|
|
201
|
+
currencyCode
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
- **`GiftCard.transactions`** field removed from storefront API entirely. The field always returned `[]` (PII safety: transactions contain `orderId` / `customerId` not safe to expose at anonymous code-lookup endpoint). Schema now rejects the selection at validation time. Storefronts that selected this field will get a clear schema error instead of an always-empty array.
|
|
208
|
+
|
|
209
|
+
### Filter scalars + structured Weight type
|
|
210
|
+
- **`Filter.id` and `FilterValue.id`** are now `ID!` (previously `String!`). Codegen treats `ID` identically to `String` in TypeScript, but downstream client tooling (Relay-style caches, normalized stores) gains proper identity semantics for entity deduplication.
|
|
211
|
+
- **`FilterValue.input`** changed from `String!` (JSON-stringified payload) to **`JSON!`** (new opaque structured scalar). Storefronts no longer need to `JSON.parse(filterValue.input)` before passing it to the `filters` argument — the value arrives as a structured object ready to use:
|
|
212
|
+
|
|
213
|
+
```graphql
|
|
214
|
+
# Before (4.x) — client-side parsing required
|
|
215
|
+
filterValue.input # "{\"variantOption\":{\"name\":\"Color\",\"value\":\"Red\"}}"
|
|
216
|
+
JSON.parse(filterValue.input)
|
|
217
|
+
|
|
218
|
+
# After (5.x) — directly passable as filters arg element
|
|
219
|
+
filterValue.input # { variantOption: { name: "Color", value: "Red" } }
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
- **`ProductVariant.weight`** changed from `Float` (raw grams, implicit unit per docs) to **`Weight { value: Float!, unit: WeightUnit! }`** structured type with the new `WeightUnit` enum (`GRAMS | KILOGRAMS | OUNCES | POUNDS`). The API always returns `unit: GRAMS` (canonical storage unit). Storefronts can convert to the preferred unit for shipping or checkout UI without hardcoding "grams" anywhere in the codebase:
|
|
223
|
+
|
|
224
|
+
```graphql
|
|
225
|
+
# Before (4.x)
|
|
226
|
+
variant { weight } # 250 (Float, "grams" implicit per docs)
|
|
227
|
+
|
|
228
|
+
# After (5.x)
|
|
229
|
+
variant { weight { value unit } } # { value: 250, unit: GRAMS }
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### Removed: legacy `x-customer-access-token` header
|
|
233
|
+
|
|
234
|
+
The legacy `x-customer-access-token` header reader (which never had a matching SDK middleware) is removed from the API. If a custom integration was relying on it, switch to `Authorization: Bearer <token>`.
|
|
235
|
+
|
|
236
|
+
### Field & mutation naming sweep
|
|
237
|
+
|
|
238
|
+
Several renames have landed in this release. Update queries and variables accordingly:
|
|
239
|
+
- **Cart mutations**: `cartLinesAdd` → `cartAddLines`, `cartLinesUpdate` → `cartUpdateLines`, `cartLinesRemove` → `cartRemoveLines`, `cartNoteUpdate` → `cartUpdateNote`, `cartAttributesUpdate` → `cartUpdateAttributes`, `cartBuyerIdentityUpdate` → `cartUpdateBuyerIdentity`, `cartDiscountCodesUpdate` → `cartApplyDiscountCodes`. Resource-id arg renamed `cartId` → `id` on mutations (the foreign-key `CheckoutCreateInput.cartId` keeps its name).
|
|
240
|
+
- **Checkout mutations**: `checkoutId` → `id`. `checkoutSelectShippingRate` arg `shippingRateHandle` → `rateId`.
|
|
241
|
+
- **Wishlist mutations**: `wishlistId` → `id`.
|
|
242
|
+
- **Field renames on output types**:
|
|
243
|
+
- `Product.productType` → `Product.category` (free-text classification field; the `Product.type: ProductTypeEnum` enum field is unchanged)
|
|
244
|
+
- `ProductVariant.quantityAvailable` → `availableStock` (disambiguates effective availability from raw inventory)
|
|
245
|
+
- `ProductOption.position` / `ProductOptionValue.position` → `sortOrder`
|
|
246
|
+
- `Customer.numberOfOrders` → `orderCount`
|
|
247
|
+
- `WishlistItem.priceAtAdd` → `priceWhenAdded`
|
|
248
|
+
- `Shop.primaryDomain.sslEnabled` → `isSslEnabled` (boolean prefix convention)
|
|
249
|
+
- `Shipment.estimatedDeliveryDate` → `estimatedDeliveryAt`
|
|
250
|
+
- `StoreAvailability.pickUpTime` → `pickupTime`
|
|
251
|
+
- `Location.pickupEnabled` → `supportsPickup`
|
|
252
|
+
- `ShopPage.bodySummary` → `excerpt`
|
|
253
|
+
- **New field**: `ProductReview.unhelpfulCount` — render upvote/downvote counts side-by-side without computing one from the other.
|
|
254
|
+
- **`UserError`/payload selection set** is now required on `WishlistPayload.userErrors` (and other payloads switched to typed `[UserError!]!`). Bare `userErrors` selection without a sub-selection is rejected by schema validation.
|
|
255
|
+
- **Error code stability**: typed error codes (e.g. `CART_NOT_FOUND`, `CUSTOMER_TOKEN_INVALID`) are now locale-agnostic — they remain stable across language responses (in earlier versions the mapping silently regressed when a translated message lost a specific English keyword).
|
|
256
|
+
|
|
257
|
+
### Unified `UserError` across all mutation payloads
|
|
258
|
+
|
|
259
|
+
Per-domain user-error types (`CartUserError`, `CheckoutUserError`, `CustomerUserError`) have been replaced by the single generic `UserError` type used everywhere. Error codes remain unique per domain (e.g. `CART_NOT_FOUND` vs `CUSTOMER_NOT_FOUND` vs `CHECKOUT_PAYMENT_FAILED`) — string comparison on the `code` field gives the same branching power without per-domain type proliferation.
|
|
260
|
+
|
|
261
|
+
Customer mutation payloads also rename the error field from `customerUserErrors` to `userErrors` for consistency with cart, checkout, wishlist, and other payloads:
|
|
262
|
+
|
|
263
|
+
```graphql
|
|
264
|
+
# Before (4.x)
|
|
265
|
+
mutation CartCreate {
|
|
266
|
+
cartCreate {
|
|
267
|
+
userErrors {
|
|
268
|
+
...CartUserError
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
mutation CustomerLogin {
|
|
273
|
+
customerLogin {
|
|
274
|
+
customerUserErrors {
|
|
275
|
+
...CustomerUserError
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
mutation CheckoutCreate {
|
|
280
|
+
checkoutCreate {
|
|
281
|
+
userErrors {
|
|
282
|
+
...CheckoutUserError
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
# After (5.x)
|
|
288
|
+
mutation CartCreate {
|
|
289
|
+
cartCreate {
|
|
290
|
+
userErrors {
|
|
291
|
+
...UserError
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
mutation CustomerLogin {
|
|
296
|
+
customerLogin {
|
|
297
|
+
userErrors {
|
|
298
|
+
...UserError
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
mutation CheckoutCreate {
|
|
303
|
+
checkoutCreate {
|
|
304
|
+
userErrors {
|
|
305
|
+
...UserError
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
Update the shared `UserError` fragment as the only error fragment you need:
|
|
312
|
+
|
|
313
|
+
```graphql
|
|
314
|
+
fragment UserError on UserError {
|
|
315
|
+
message
|
|
316
|
+
code # namespaced string — e.g. "CART_NOT_FOUND", "CUSTOMER_TOKEN_INVALID"
|
|
317
|
+
field # path to the input field that caused the error
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
Client TypeScript change after codegen: `userErrors[i].code` is now `string | null` instead of a strongly-typed enum (`CartErrorCode` / `CheckoutErrorCode` / `CustomerErrorCode`). Switch from `code === CartErrorCode.CART_NOT_FOUND` to `code === 'CART_NOT_FOUND'`. The full list of namespaced codes per domain is documented in the schema descriptions on each payload's `userErrors` field.
|
|
322
|
+
|
|
323
|
+
### Address `country` field — `CountryCode` enum on inputs
|
|
324
|
+
|
|
325
|
+
Address input types (`MailingAddressInput`, `CheckoutAddressInput`, `ShippingAddressInput`) now use the `CountryCode` enum for the `country` field instead of `String` with regex validation. This matches the pattern already used by output types (`MailingAddress.countryCode: CountryCode`) and gives client codegen a strongly-typed enum value instead of a free-form string:
|
|
326
|
+
|
|
327
|
+
```graphql
|
|
328
|
+
# Before (4.x)
|
|
329
|
+
input CheckoutAddressInput {
|
|
330
|
+
country: String! # validated server-side via @Matches(/^[A-Z]{2}$/)
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
# After (5.x)
|
|
334
|
+
input CheckoutAddressInput {
|
|
335
|
+
country: CountryCode! # GraphQL enum — invalid values rejected at validation
|
|
336
|
+
}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
Output types keep two distinct fields where applicable: `country` (display name, e.g. "Polska") + `countryCode` (typed enum, e.g. `PL`).
|
|
340
|
+
|
|
341
|
+
### Address fields — international naming
|
|
342
|
+
|
|
343
|
+
`MailingAddress`, `LocationAddress`, `ShopAddress`, and the address inputs (`MailingAddressInput`, `CheckoutAddressInput`) now use international neutral terminology instead of US/Canadian-centric field names. The new names match common international postal conventions and the underlying database column names:
|
|
344
|
+
|
|
345
|
+
| Before (4.x) | After (5.x) |
|
|
346
|
+
| -------------- | ------------- |
|
|
347
|
+
| `address1` | `streetLine1` |
|
|
348
|
+
| `address2` | `streetLine2` |
|
|
349
|
+
| `province` | `state` |
|
|
350
|
+
| `provinceCode` | `stateCode` |
|
|
351
|
+
| `zip` | `postalCode` |
|
|
352
|
+
|
|
353
|
+
```graphql
|
|
354
|
+
# Before (4.x)
|
|
355
|
+
fragment MailingAddress on MailingAddress {
|
|
356
|
+
address1
|
|
357
|
+
address2
|
|
358
|
+
province
|
|
359
|
+
zip
|
|
360
|
+
}
|
|
361
|
+
|
|
362
|
+
# After (5.x)
|
|
363
|
+
fragment MailingAddress on MailingAddress {
|
|
364
|
+
streetLine1
|
|
365
|
+
streetLine2
|
|
366
|
+
state
|
|
367
|
+
postalCode
|
|
368
|
+
}
|
|
369
|
+
|
|
370
|
+
# Mutation input
|
|
371
|
+
mutation AddAddress($input: MailingAddressInput!) {
|
|
372
|
+
customerAddAddress(input: $input) { ... }
|
|
373
|
+
}
|
|
374
|
+
# variables (4.x): { address1: "...", province: "...", zip: "..." }
|
|
375
|
+
# variables (5.x): { streetLine1: "...", state: "...", postalCode: "..." }
|
|
376
|
+
```
|
|
377
|
+
|
|
378
|
+
**Backward-compat for existing orders**: `Order.shippingAddress` and `Order.billingAddress` are stored as JSONB snapshots at order placement time. Snapshots created under the previous API contain `address1` / `province` / `zip`; new snapshots use `streetLine1` / `state` / `postalCode`. The `MailingAddress` resolver reads new field names first and falls back to legacy names — historical orders continue to render correctly. Frontend codegen will emit the new field names; templates rendering historical orders gain the new schema without data migration. Backward-compat fallback is scheduled for removal once historical snapshots reach end-of-life (~2-3 years).
|
|
379
|
+
|
|
380
|
+
**`MailingAddress.formatted`** (country-aware ordered lines) continues to work — internal helper updated to use new field names. Storefronts rendering addresses via `formatted` need no changes.
|
|
381
|
+
|
|
382
|
+
### Costs and totals — nested wrappers everywhere
|
|
383
|
+
|
|
384
|
+
Cart, Checkout, and Order now expose monetary breakdowns via dedicated wrapper types instead of flat fields on the parent. This adds a single hop in selections but makes adding new cost dimensions (duty, fees, surcharges) non-breaking, and brings symmetry across the three checkout-flow stages:
|
|
385
|
+
|
|
386
|
+
```graphql
|
|
387
|
+
# CartCost — drop "Amount" suffix from field names (Money type implies amount)
|
|
388
|
+
fragment CartCost on CartCost {
|
|
389
|
+
subtotal {
|
|
390
|
+
...Money
|
|
391
|
+
} # was: subtotalAmount
|
|
392
|
+
total {
|
|
393
|
+
...Money
|
|
394
|
+
} # was: totalAmount
|
|
395
|
+
totalTax {
|
|
396
|
+
...Money
|
|
397
|
+
} # was: totalTaxAmount
|
|
398
|
+
totalDuty {
|
|
399
|
+
...Money
|
|
400
|
+
} # was: totalDutyAmount
|
|
401
|
+
checkoutCharge {
|
|
402
|
+
...Money
|
|
403
|
+
} # was: checkoutChargeAmount
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
# CartLineCost — drop "Amount" suffix + "amountPerQuantity" → "pricePerUnit"
|
|
407
|
+
fragment CartLineCost on CartLineCost {
|
|
408
|
+
pricePerUnit {
|
|
409
|
+
...Money
|
|
410
|
+
} # was: amountPerQuantity
|
|
411
|
+
subtotal {
|
|
412
|
+
...Money
|
|
413
|
+
} # was: subtotalAmount
|
|
414
|
+
total {
|
|
415
|
+
...Money
|
|
416
|
+
} # was: totalAmount
|
|
417
|
+
compareAtPricePerUnit {
|
|
418
|
+
...Money
|
|
419
|
+
} # was: compareAtAmountPerQuantity
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
# CheckoutCost — NEW wrapper, replaces flat checkout fields
|
|
423
|
+
query Checkout {
|
|
424
|
+
checkout {
|
|
425
|
+
cost {
|
|
426
|
+
subtotal {
|
|
427
|
+
...Money
|
|
428
|
+
} # was: Checkout.subtotalPrice
|
|
429
|
+
total {
|
|
430
|
+
...Money
|
|
431
|
+
} # was: Checkout.totalPrice
|
|
432
|
+
totalTax {
|
|
433
|
+
...Money
|
|
434
|
+
} # was: Checkout.totalTax (now nested)
|
|
435
|
+
totalShipping {
|
|
436
|
+
...Money
|
|
437
|
+
} # was: Checkout.totalShippingPrice
|
|
438
|
+
totalDiscounts {
|
|
439
|
+
...Money
|
|
440
|
+
} # was: Checkout.totalDiscounts (now nested)
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
445
|
+
# OrderTotals — NEW wrapper, replaces flat order fields
|
|
446
|
+
query CustomerOrder {
|
|
447
|
+
customerOrder {
|
|
448
|
+
totals {
|
|
449
|
+
subtotal {
|
|
450
|
+
...Money
|
|
451
|
+
} # was: Order.subtotalPrice
|
|
452
|
+
total {
|
|
453
|
+
...Money
|
|
454
|
+
} # was: Order.totalPrice
|
|
455
|
+
totalTax {
|
|
456
|
+
...Money
|
|
457
|
+
} # was: Order.totalTax (now nested)
|
|
458
|
+
totalShipping {
|
|
459
|
+
...Money
|
|
460
|
+
} # was: Order.totalShipping (now nested)
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
465
|
+
# CheckoutLineItem — line price field renames
|
|
466
|
+
fragment CheckoutLineItem on CheckoutLineItem {
|
|
467
|
+
pricePerUnit {
|
|
468
|
+
...Money
|
|
469
|
+
} # was: unitPrice
|
|
470
|
+
total {
|
|
471
|
+
...Money
|
|
472
|
+
} # was: totalPrice
|
|
473
|
+
}
|
|
474
|
+
```
|
|
475
|
+
|
|
476
|
+
Conversion-transparency opt-in fields (`*WithConversion`) are renamed in lockstep:
|
|
477
|
+
- `subtotalAmountWithConversion` → `subtotalWithConversion`
|
|
478
|
+
- `totalAmountWithConversion` → `totalWithConversion`
|
|
479
|
+
- `amountPerQuantityWithConversion` → `pricePerUnitWithConversion`
|
|
480
|
+
- ...and so on.
|
|
481
|
+
|
|
482
|
+
### Cart line: simplified shape — `variant` replaces `merchandise` union
|
|
483
|
+
|
|
484
|
+
`CartLine.merchandise: Merchandise!` (a union currently with only `ProductVariant` member) and the `BaseCartLine` interface have been removed. `CartLine` is now a concrete type with a direct `variant: ProductVariant!` field — no union wrapper, no inline fragments needed:
|
|
485
|
+
|
|
486
|
+
```graphql
|
|
487
|
+
# Before (4.x)
|
|
488
|
+
fragment CartLine on CartLine {
|
|
489
|
+
id
|
|
490
|
+
quantity
|
|
491
|
+
merchandise {
|
|
492
|
+
... on ProductVariant {
|
|
493
|
+
id
|
|
494
|
+
title
|
|
495
|
+
price { amount currencyCode }
|
|
496
|
+
}
|
|
497
|
+
}
|
|
498
|
+
cost { ... }
|
|
499
|
+
}
|
|
500
|
+
|
|
501
|
+
# After (5.x) — direct field, no union/inline fragment
|
|
502
|
+
fragment CartLine on CartLine {
|
|
503
|
+
id
|
|
504
|
+
quantity
|
|
505
|
+
variant {
|
|
506
|
+
id
|
|
507
|
+
title
|
|
508
|
+
price { amount currencyCode }
|
|
509
|
+
}
|
|
510
|
+
cost { ... }
|
|
511
|
+
}
|
|
512
|
+
```
|
|
513
|
+
|
|
514
|
+
Cart connection types renamed for the same reason: `BaseCartLineConnection` / `BaseCartLineEdge` → `CartLineConnection` / `CartLineEdge`. Update fragment selections accordingly:
|
|
515
|
+
|
|
516
|
+
```graphql
|
|
517
|
+
# Before (4.x)
|
|
518
|
+
query Cart($id: ID!) {
|
|
519
|
+
cart(id: $id) {
|
|
520
|
+
lines(first: 10) {
|
|
521
|
+
edges {
|
|
522
|
+
node {
|
|
523
|
+
... on CartLine {
|
|
524
|
+
id
|
|
525
|
+
}
|
|
526
|
+
}
|
|
527
|
+
}
|
|
528
|
+
nodes {
|
|
529
|
+
... on CartLine {
|
|
530
|
+
id
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
|
|
537
|
+
# After (5.x)
|
|
538
|
+
query Cart($id: ID!) {
|
|
539
|
+
cart(id: $id) {
|
|
540
|
+
lines(first: 10) {
|
|
541
|
+
edges {
|
|
542
|
+
node {
|
|
543
|
+
id
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
nodes {
|
|
547
|
+
id
|
|
548
|
+
}
|
|
549
|
+
}
|
|
550
|
+
}
|
|
551
|
+
}
|
|
552
|
+
```
|
|
553
|
+
|
|
554
|
+
Cart line input renamed `merchandiseId` → `variantId` (consistent with the output field):
|
|
555
|
+
|
|
556
|
+
```graphql
|
|
557
|
+
# Before (4.x)
|
|
558
|
+
mutation CartAddLines($id: ID!, $lines: [CartLineInput!]!) {
|
|
559
|
+
cartAddLines(id: $id, lines: $lines) { ... }
|
|
560
|
+
}
|
|
561
|
+
# variables: { lines: [{ merchandiseId: "uuid", quantity: 1 }] }
|
|
562
|
+
|
|
563
|
+
# After (5.x)
|
|
564
|
+
mutation CartAddLines($id: ID!, $lines: [CartLineInput!]!) {
|
|
565
|
+
cartAddLines(id: $id, lines: $lines) { ... }
|
|
566
|
+
}
|
|
567
|
+
# variables: { lines: [{ variantId: "uuid", quantity: 1 }] }
|
|
568
|
+
```
|
|
569
|
+
|
|
570
|
+
The `variantId` name also matches the `userErrors[].field` path returned for input-related errors (e.g. `field: ["lines", "variantId"]`).
|
|
571
|
+
|
|
572
|
+
### Recommendations: top-level queries replaced by field resolvers
|
|
573
|
+
|
|
574
|
+
Two top-level queries were removed. Use the field resolvers on the parent entity instead — they return identical payloads with automatic context inheritance, cleaner cache invalidation, and a clearer parent-not-found vs empty-recommendations distinction:
|
|
575
|
+
|
|
576
|
+
```graphql
|
|
577
|
+
# Before (4.x)
|
|
578
|
+
query SimilarProducts($productId: String!, $first: Int) {
|
|
579
|
+
similarProducts(productId: $productId, first: $first) {
|
|
580
|
+
items {
|
|
581
|
+
product {
|
|
582
|
+
id
|
|
583
|
+
title
|
|
584
|
+
}
|
|
585
|
+
type
|
|
586
|
+
score
|
|
587
|
+
}
|
|
588
|
+
totalCount
|
|
589
|
+
}
|
|
590
|
+
}
|
|
591
|
+
query CartRecommendations($cartId: String!, $first: Int) {
|
|
592
|
+
cartRecommendations(cartId: $cartId, first: $first) {
|
|
593
|
+
frequentlyBoughtTogether {
|
|
594
|
+
product {
|
|
595
|
+
id
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
youMayAlsoLike {
|
|
599
|
+
product {
|
|
600
|
+
id
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
# After (5.x) — same payloads, parent-scoped
|
|
607
|
+
query Product($id: ID!, $first: Int) {
|
|
608
|
+
product(id: $id) {
|
|
609
|
+
recommendations(first: $first) {
|
|
610
|
+
items {
|
|
611
|
+
product {
|
|
612
|
+
id
|
|
613
|
+
title
|
|
614
|
+
}
|
|
615
|
+
type
|
|
616
|
+
score
|
|
617
|
+
}
|
|
618
|
+
totalCount
|
|
619
|
+
}
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
query Cart($id: ID!, $first: Int) {
|
|
623
|
+
cart(id: $id) {
|
|
624
|
+
recommendations(first: $first) {
|
|
625
|
+
frequentlyBoughtTogether {
|
|
626
|
+
product {
|
|
627
|
+
id
|
|
628
|
+
}
|
|
629
|
+
}
|
|
630
|
+
youMayAlsoLike {
|
|
631
|
+
product {
|
|
632
|
+
id
|
|
633
|
+
}
|
|
634
|
+
}
|
|
635
|
+
}
|
|
636
|
+
}
|
|
637
|
+
}
|
|
638
|
+
```
|
|
639
|
+
|
|
640
|
+
For broader product-scoped recommendations driven by intent (`SIMILAR | COMPLEMENTARY | UPSELL`), use the existing top-level `productRecommendations(productId, intent, limit)` query, which returns a flat `[Product!]!` payload — separate surface from the rich `Product.recommendations` field (which carries metadata `type`, `score`, `reason` per item).
|
|
641
|
+
|
|
642
|
+
## 0.2.6
|
|
643
|
+
|
|
644
|
+
### Patch Changes
|
|
645
|
+
|
|
646
|
+
- 7846bdb: Fix `doswiftly dev` port pre-flight on Windows (and any host where another
|
|
647
|
+
process binds the port on IPv6 `::` only): the probe now checks IPv4 and
|
|
648
|
+
IPv6 in parallel and requires BOTH free before marking a port as available.
|
|
649
|
+
Previously an IPv6-only conflict (common on Windows, where Next.js binds
|
|
650
|
+
`::` by default) slipped past the IPv4-only probe — the banner advertised
|
|
651
|
+
`http://localhost:3000` and the framework immediately crashed with
|
|
652
|
+
`EADDRINUSE :::3000`. With this fix `doswiftly dev` falls back to the next
|
|
653
|
+
free port (3001, 3002, …) as documented.
|
|
654
|
+
|
|
655
|
+
Also ship `CHANGELOG.md` inside the npm tarball. Previous releases packed
|
|
656
|
+
only `dist`/`bin`/`templates` (and `schema.graphql`/operations for
|
|
657
|
+
`storefront-operations`, `dist` for `storefront-sdk`), so consumers who
|
|
658
|
+
`npm install @doswiftly/cli` got a version number with no user-visible
|
|
659
|
+
release notes.
|
|
660
|
+
|
|
661
|
+
## 0.2.5
|
|
662
|
+
|
|
663
|
+
### Patch Changes
|
|
664
|
+
|
|
665
|
+
- e3174bb: Fix `doswiftly dev` to handle two common papercuts out of the box.
|
|
666
|
+
|
|
667
|
+
**Port pre-flight + auto-fallback.** `doswiftly dev` now probes the requested port
|
|
668
|
+
(default `3000`) before spawning the framework and auto-falls back to the next free
|
|
669
|
+
port up to `+10` (e.g. `3000 → 3001 → 3002`) — no more `EADDRINUSE` crashes when
|
|
670
|
+
another process already owns the default port. The banner is printed after the port
|
|
671
|
+
is reserved, so the advertised URL always matches what the framework actually binds.
|
|
672
|
+
Pass `--strict-port` (or set `dev.strictPort: true` in `doswiftly.config.ts`) to
|
|
673
|
+
fail fast instead of falling back.
|
|
674
|
+
|
|
675
|
+
**Active profile now wins over `api.url`.** `doswiftly env use <profile>` used to be
|
|
676
|
+
silently ignored for `doswiftly dev`, `inspect`, and `proxy` when `doswiftly.config.ts`
|
|
677
|
+
contained an `api.url` (which every scaffold sets). The precedence is now:
|
|
678
|
+
`DOSWIFTLY_API_URL` / `NEXT_PUBLIC_API_URL` shell env > active profile >
|
|
679
|
+
`doswiftly.config.ts` `api.url` > `.env.local` > default. `doswiftly deploy`
|
|
680
|
+
keeps its existing behaviour (project config wins unless `DOSWIFTLY_API_URL` is set).
|
|
681
|
+
|
|
682
|
+
Extras shipped alongside the fixes:
|
|
683
|
+
- `--strict-port` flag on `doswiftly dev`.
|
|
684
|
+
- The dev banner reports where the API URL came from
|
|
685
|
+
(e.g. `API: http://localhost:8000 (from profile "dev")`).
|
|
686
|
+
- `doswiftly env use` prints a one-line hint when the active profile overrides
|
|
687
|
+
`api.url` from `doswiftly.config.ts`.
|
|
688
|
+
- A warning when your `package.json` dev script hardcodes a port
|
|
689
|
+
(e.g. `"dev": "next dev -p 3000"`) that may override the CLI-supplied port.
|
|
690
|
+
|
|
691
|
+
All notable changes to `@doswiftly/cli` will be documented in this file.
|
|
692
|
+
|
|
693
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
|
|
694
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
695
|
+
|
|
696
|
+
## [0.2.4] - 2026-04-20
|
|
697
|
+
|
|
698
|
+
### Fixed
|
|
699
|
+
|
|
700
|
+
- **`doswiftly dev` no longer crashes with `EADDRINUSE` when the dev port is in use.**
|
|
701
|
+
The CLI now probes the requested port (default `3000`) before starting the framework and
|
|
702
|
+
auto-falls back to the next free port up to `+10` (so `3000 → 3001 → 3002`). The banner is
|
|
703
|
+
printed after the port is bound, so `Storefront: http://localhost:3001` always matches what
|
|
704
|
+
actually starts. Opt out with `--strict-port` on the command or `dev.strictPort: true` in
|
|
705
|
+
`doswiftly.config.ts` to fail fast instead.
|
|
706
|
+
- **`doswiftly env use <profile>` now takes precedence over `api.url` in `doswiftly.config.ts`
|
|
707
|
+
for `doswiftly dev`, `inspect`, and `proxy`.** Previously, the project config silently
|
|
708
|
+
overrode the active profile, so switching to a `dev` profile with `API: http://localhost:8000`
|
|
709
|
+
still hit the production URL configured at scaffold time. Shell env vars (`DOSWIFTLY_API_URL`,
|
|
710
|
+
`NEXT_PUBLIC_API_URL`) stay on top for CI/CD and ad-hoc overrides. `doswiftly deploy`
|
|
711
|
+
behaviour is unchanged.
|
|
712
|
+
|
|
713
|
+
### Added
|
|
714
|
+
|
|
715
|
+
- **`--strict-port` flag and `dev.strictPort` config field** for `doswiftly dev`. When set,
|
|
716
|
+
the command fails with an actionable error (including the blocking port and the hint to
|
|
717
|
+
pass `--port <N>`) instead of auto-falling back.
|
|
718
|
+
- **Banner now reports where the API URL came from** (e.g. `API: http://localhost:8000
|
|
719
|
+
(from profile "dev")`), making it easier to diagnose unexpected target URLs.
|
|
720
|
+
- **`doswiftly env use` surfaces a one-line notice** when the project's `doswiftly.config.ts`
|
|
721
|
+
hardcodes a different `api.url` than the active profile, so you know the profile is
|
|
722
|
+
intentionally taking over.
|
|
723
|
+
- **Warning for hardcoded port in your `dev` script.** When `package.json` has
|
|
724
|
+
`"dev": "next dev -p 3000"` (or similar), the CLI warns in CI logs that the hardcoded port
|
|
725
|
+
may override `--port` / `PORT` env.
|
|
726
|
+
|
|
727
|
+
## [0.2.3] - 2026-04-17
|
|
728
|
+
|
|
729
|
+
### Security
|
|
730
|
+
|
|
731
|
+
- Bump `axios` to `^1.15.0` to resolve GHSA-3p68-rc4w-qgx5 (NO_PROXY normalization bypass → SSRF) and GHSA-fvcv-3m26-pcqx (cloud metadata exfiltration via header injection).
|
|
732
|
+
|
|
733
|
+
## [0.2.2] - 2026-04-12
|
|
734
|
+
|
|
735
|
+
### Changed
|
|
736
|
+
|
|
737
|
+
- **Default `@doswiftly/storefront-operations` dependency bumped `^1.0.0` → `^5.0.0`.** New storefronts
|
|
738
|
+
scaffolded via `doswiftly init` / `doswiftly template` now pull the per-location
|
|
739
|
+
`storeAvailability` surface out of the box. Existing storefronts on older operations keep working —
|
|
740
|
+
bump the dependency manually and re-run `pnpm install` when you're ready to adopt it.
|
|
741
|
+
- **Storefront template (`storefront-nextjs-shadcn`): BOPIS example in `custom.example.graphql`
|
|
742
|
+
rewritten to the new API.** The `storeAvailability(first, near, locationType)` field on
|
|
743
|
+
`ProductVariant` replaces the previous per-location lookup, and the example now demonstrates the
|
|
744
|
+
`@inContext(preferredLocationId)` operation directive plus a `GetPickupLocations` store picker
|
|
745
|
+
query.
|
|
746
|
+
|
|
747
|
+
## [0.2.1] - 2026-04-10
|
|
748
|
+
|
|
749
|
+
### Fixed
|
|
750
|
+
|
|
751
|
+
- **Dev proxy: strip Referer header alongside Origin.** The CSRF origin middleware on the
|
|
752
|
+
production backend validates both `Origin` and `Referer` headers. The proxy was only stripping
|
|
753
|
+
`Origin`, so the browser's `Referer: http://localhost:3000` was forwarded and rejected as
|
|
754
|
+
an unknown origin. Now both are stripped, making proxied requests appear as server-to-server.
|
|
755
|
+
|
|
756
|
+
## [0.2.0] - 2026-04-10
|
|
757
|
+
|
|
758
|
+
### Added
|
|
759
|
+
|
|
760
|
+
- **Init: adopt flow for existing projects.** Running `doswiftly init` in a non-empty directory
|
|
761
|
+
now detects the existing project and offers to connect it to DoSwiftly without copying template
|
|
762
|
+
files. Creates `doswiftly.config.ts`, merges `.env.local`, and optionally installs SDK packages.
|
|
763
|
+
- **Dev: automatic API proxy for CORS-free development.** `doswiftly dev` now auto-starts a local
|
|
764
|
+
proxy when the API URL points to a remote server, eliminating CORS errors during local development.
|
|
765
|
+
The proxy forwards all headers and runs transparently — no code changes needed in the storefront.
|
|
766
|
+
|
|
767
|
+
### Changed
|
|
768
|
+
|
|
769
|
+
- **Renamed "Commerce SDK" to "Storefront SDK"** across all CLI output, command descriptions,
|
|
770
|
+
and help text to match the actual package name (`@doswiftly/storefront-sdk`).
|
|
771
|
+
- **Doctor: accepts `storefront-sdk` OR `storefront-operations`** as valid DoSwiftly dependencies
|
|
772
|
+
(matching deploy command logic).
|
|
773
|
+
- **Dev: framework-agnostic dev command.** `doswiftly dev` now reads `scripts.dev` from
|
|
774
|
+
`package.json` instead of hardcoding `next dev`.
|
|
775
|
+
- **Proxy: full header forwarding.** The proxy now forwards all request/response headers
|
|
776
|
+
(except hop-by-hop), including SDK headers, cookies, and Set-Cookie.
|
|
777
|
+
|
|
778
|
+
### Removed
|
|
779
|
+
|
|
780
|
+
- **Deprecated templates `storefront-nextjs` and `storefront-minimal`** removed from the package.
|
|
781
|
+
Only `storefront-nextjs-shadcn` (Next.js 16 + shadcn/ui) is shipped. These templates had
|
|
782
|
+
broken dependencies and outdated SDK references.
|
|
783
|
+
- **Dead code:** removed duplicate `sdkVersionCommand` in `types.ts`, removed unused
|
|
784
|
+
`COMMERCE_SDK_VERSION` placeholder from init and template commands.
|
|
785
|
+
|
|
786
|
+
### Fixed
|
|
787
|
+
|
|
788
|
+
- **Verify/Check: fixed `env:generate` → `env generate`** in help text (commands use spaces, not colons).
|
|
789
|
+
|
|
790
|
+
## [0.1.24] - 2026-04-10
|
|
791
|
+
|
|
792
|
+
### Fixed
|
|
793
|
+
|
|
794
|
+
- **Deploy: fix esbuild version detection using `pnpm ls` instead of binary/require().**
|
|
795
|
+
Both `pnpm exec esbuild --version` and `require('esbuild/package.json')` fail silently
|
|
796
|
+
in pnpm strict mode for transitive deps. Now uses `pnpm ls esbuild --parseable` which
|
|
797
|
+
queries pnpm's own dependency graph — works regardless of hoisting or strict mode config.
|
|
798
|
+
- **Deploy: suggest highest installed esbuild version in fix instructions.** Instead of
|
|
799
|
+
a hardcoded `>=0.17.0`, the error message now suggests the highest esbuild version
|
|
800
|
+
already in the dependency tree (e.g. `0.25.4` from `@opennextjs/cloudflare`).
|
|
801
|
+
|
|
802
|
+
## [0.1.23] - 2026-04-10
|
|
803
|
+
|
|
804
|
+
### Added
|
|
805
|
+
|
|
806
|
+
- **Deploy: early detection of incompatible esbuild versions.** If a transitive dependency
|
|
807
|
+
(e.g. `react-email`) pulls in esbuild <0.16.10 (lacks subpath alias support), CLI now
|
|
808
|
+
detects this before the build and exits with an actionable error message explaining how
|
|
809
|
+
to add an override. Uses the same `pnpm exec` resolution pattern as wrangler.
|
|
810
|
+
|
|
811
|
+
### Removed
|
|
812
|
+
|
|
813
|
+
- Removed auto-override of merchant's `package.json` (0.1.20-0.1.22). The approach was
|
|
814
|
+
unreliable across pnpm configurations and overstepped CLI's responsibility — dependency
|
|
815
|
+
management belongs to the project owner, not the deploy tool.
|
|
816
|
+
|
|
817
|
+
## [0.1.19] - 2026-04-08
|
|
818
|
+
|
|
819
|
+
### Fixed
|
|
820
|
+
|
|
821
|
+
- Deploy: improved Cloudflare WAF/Bot protection error diagnostics with CF-Ray ID and hints.
|
|
822
|
+
- Deploy: better error formatting for HTML error pages from Cloudflare.
|
|
823
|
+
|
|
824
|
+
## [0.1.18] - 2026-04-05
|
|
825
|
+
|
|
826
|
+
### Added
|
|
827
|
+
|
|
828
|
+
- Deploy: `[observability]` section in generated `wrangler.toml` — logs persisted in Cloudflare Dashboard.
|
|
829
|
+
- Deploy: `[images]` binding for Next.js `/_next/image` optimization via Cloudflare Images.
|
|
830
|
+
|
|
831
|
+
## [0.1.17] - 2026-03-28
|
|
832
|
+
|
|
833
|
+
### Fixed
|
|
834
|
+
|
|
835
|
+
- Deploy: added Wrangler re-bundle step (`wrangler deploy --dry-run --outdir`) to convert
|
|
836
|
+
`require()` calls to ESM imports. Without this, CF Workers throw
|
|
837
|
+
`Dynamic require of "node:crypto" is not supported`.
|
|
838
|
+
- Deploy: CLI now auto-installs `wrangler` if not present in the project.
|
|
839
|
+
- Deploy: only `.js`, `.mjs`, `.wasm` files are copied from Wrangler output (skips `.map`, `README`).
|
|
840
|
+
|
|
841
|
+
### Changed
|
|
842
|
+
|
|
843
|
+
- Deploy: CLI generates `wrangler.toml` automatically, overwriting merchant's version to ensure
|
|
844
|
+
correct worker name, compatibility flags, and ASSETS binding.
|
|
845
|
+
- Deploy: CLI generates `open-next.config.ts` (required by `@opennextjs/cloudflare` 1.17+).
|
|
846
|
+
|
|
847
|
+
## [0.1.16] - 2026-03-20
|
|
848
|
+
|
|
849
|
+
### Added
|
|
850
|
+
|
|
851
|
+
- `doswiftly upgrade` — user-friendly wrapper for template migration commands.
|
|
852
|
+
- `doswiftly migrate check/apply/diff` — template update management.
|
|
853
|
+
|
|
854
|
+
## [0.1.15] - 2026-03-15
|
|
855
|
+
|
|
856
|
+
### Added
|
|
857
|
+
|
|
858
|
+
- `doswiftly auth github` — GitHub account linking via device flow.
|
|
859
|
+
- `doswiftly auth token` — deploy token generation for CI/CD pipelines.
|
|
860
|
+
- `doswiftly template create/register/publish/validate` — SaaS developer template management.
|
|
861
|
+
|
|
862
|
+
## [0.1.14] - 2026-03-10
|
|
863
|
+
|
|
864
|
+
### Added
|
|
865
|
+
|
|
866
|
+
- `doswiftly preview create/list/stop/logs/open` — preview environment management.
|
|
867
|
+
- `doswiftly config domain add/remove/list` — custom domain management.
|
|
868
|
+
|
|
869
|
+
## [0.1.13] - 2026-03-05
|
|
870
|
+
|
|
871
|
+
### Added
|
|
872
|
+
|
|
873
|
+
- `doswiftly deploy run/status/rollback/logs` — Track 2 deployment pipeline.
|
|
874
|
+
- Content-hash manifest for incremental uploads (only changed assets uploaded).
|
|
875
|
+
- Idempotency keys for safe retries.
|
|
876
|
+
|
|
877
|
+
## [0.1.12] - 2026-02-28
|
|
878
|
+
|
|
879
|
+
### Added
|
|
880
|
+
|
|
881
|
+
- `doswiftly env add/use/list/generate/delete/set` — environment profile management.
|
|
882
|
+
- `doswiftly doctor` — project health diagnostics.
|
|
883
|
+
- `doswiftly check` — dependency validation.
|
|
884
|
+
|
|
885
|
+
## [0.1.11] - 2026-02-20
|
|
886
|
+
|
|
887
|
+
### Added
|
|
888
|
+
|
|
889
|
+
- `doswiftly dev` — development server with automatic env injection from API.
|
|
890
|
+
- `doswiftly config pull/show` — configuration management.
|
|
891
|
+
|
|
892
|
+
## [0.1.10] - 2026-02-15
|
|
893
|
+
|
|
894
|
+
### Added
|
|
895
|
+
|
|
896
|
+
- `doswiftly init` — interactive project scaffolding with template selection.
|
|
897
|
+
- `doswiftly auth login/logout` — OAuth authentication.
|
|
898
|
+
- `doswiftly whoami` — current context display.
|
|
899
|
+
- `doswiftly verify` — project verification.
|