@doswiftly/storefront-operations 6.1.0 → 7.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 ADDED
@@ -0,0 +1,183 @@
1
+ # @doswiftly/storefront-operations — AI Agent Guide
2
+
3
+ > Pre-built GraphQL operations and schema for the **DoSwiftly Storefront API**.
4
+ > Use this when building e-commerce storefronts that consume DoSwiftly platform.
5
+
6
+ This file is the entry point for AI coding agents (Cursor, Claude Code, GitHub
7
+ Copilot, Codex, Aider, etc.). It uses **progressive disclosure** — read the
8
+ sections you need, then fetch deeper references on demand.
9
+
10
+ ## Intended use — this is a reference catalog, not a runtime library
11
+
12
+ This package is installed as a `devDependency` and ships only `.graphql` source
13
+ files plus Markdown / JSON / text reference documents. There is no JavaScript
14
+ to import — nothing here ends up in the storefront's runtime bundle.
15
+
16
+ **Your job when generating code**: read the catalog (this file + `llms-full.txt`
17
+ + `queries.graphql` / `mutations.graphql` in the same directory), then write
18
+ operations directly into the consumer's `src/graphql/` directory (or wherever
19
+ their codegen `documents:` glob points). The consumer's storefront codegen will
20
+ produce `TypedDocumentNode`s from your written operations.
21
+
22
+ Do **not** suggest importing operations from `@doswiftly/storefront-operations`
23
+ in runtime code — the package has no such exports. Do **not** assume the
24
+ consumer's `codegen.ts` references this package's `.graphql` files as
25
+ `documents:` (that's an advanced, discouraged pattern — see the package README
26
+ "Advanced" section). The default expectation is: schema lives here, operations
27
+ live in the consumer's repo.
28
+
29
+ <!-- AUTOGEN:STATS:BEGIN — auto-regenerated, do not edit by hand -->
30
+ - **Schema version**: 7.1.0
31
+ - **Queries**: 48
32
+ - **Mutations**: 44
33
+ - **Fragments**: 108
34
+ <!-- AUTOGEN:STATS:END -->
35
+
36
+ ## Loading order
37
+
38
+ When you need to build a storefront feature, load resources in this order:
39
+
40
+ 1. **`schema.graphql`** — full GraphQL schema. Authoritative source for types,
41
+ fields, enums, input types, and directives. Read this first when you need
42
+ to know what's available.
43
+ 2. **`AGENTS.md`** (this file) — critical conventions and anti-hallucination
44
+ notes. Read once per session.
45
+ 3. **`llms-full.txt`** — full operation reference with descriptions, typed
46
+ variables, and ready-to-execute GraphQL bodies. Search this file when you
47
+ need to know which named operation to use for a task.
48
+ 4. **`operations.json`** — same operations as structured JSON, useful when
49
+ you're calling tools programmatically (MCP servers, codegen).
50
+ 5. **`queries.graphql`, `mutations.graphql`, `fragments.graphql`** — raw
51
+ `.graphql` source. Use these directly with `@graphql-codegen/cli` or any
52
+ GraphQL client tooling.
53
+
54
+ ## Critical conventions — DO NOT hallucinate
55
+
56
+ These are conventions where LLMs **frequently hallucinate from training data**.
57
+ Verify against this list before generating queries.
58
+
59
+ ### Cart mutation names
60
+
61
+ The DoSwiftly API uses `cart<Verb><Object>` naming. **Do not** generate
62
+ `cart<Object><Verb>` aliases — they do not exist in this API.
63
+
64
+ | ✅ Use | ❌ Do NOT use (hallucination) |
65
+ | ------------------------------- | ------------------------------------ |
66
+ | `cartCreate` | (same) |
67
+ | `cartAddLines` | `cartLinesAdd` |
68
+ | `cartUpdateLines` | `cartLinesUpdate` |
69
+ | `cartRemoveLines` | `cartLinesRemove` |
70
+ | `cartApplyDiscountCodes` | `cartDiscountCodesUpdate` |
71
+ | `cartUpdateBuyerIdentity` | `cartBuyerIdentityUpdate` |
72
+ | `cartUpdateNote` | `cartNoteUpdate` |
73
+ | `cartUpdateAttributes` | `cartAttributesUpdate` |
74
+
75
+ ### `userErrors[]` is the global error envelope
76
+
77
+ Every mutation returns a `userErrors: [UserError!]!` field. The shape is:
78
+
79
+ ```graphql
80
+ type UserError {
81
+ message: String!
82
+ code: String # NOT a typed enum at the global level
83
+ field: [String!]
84
+ }
85
+ ```
86
+
87
+ **Always check `userErrors` BEFORE consuming the happy-path payload**:
88
+
89
+ ```graphql
90
+ mutation {
91
+ cartAddLines(id: $id, lines: $lines) {
92
+ cart { id }
93
+ userErrors { code field message } # ← check this first
94
+ }
95
+ }
96
+ ```
97
+
98
+ `userErrors[].code` is `String`, not an enum — handle it as a string. The
99
+ codes are listed in `llms-full.txt` and `operations.json` per-mutation.
100
+
101
+ **Domain-specific typed enums** exist for warnings (not for errors):
102
+ `CartWarningCode`, `DiscountErrorCode`, `GiftCardErrorCode`. These are used
103
+ in dedicated `warnings[]` fields on cart/discount/gift-card payloads — they
104
+ are **separate** from the generic `userErrors[]`.
105
+
106
+ ### Authentication — pick ONE, never both
107
+
108
+ Two auth modes are supported:
109
+
110
+ - **Browser storefronts**: rely on the `customerAccessToken` httpOnly cookie
111
+ (set by the SDK after `customerLogin` mutation). The cookie is sent
112
+ automatically — no header needed.
113
+ - **Server-to-server / mobile**: send `Authorization: Bearer <token>` header,
114
+ where `<token>` is the `accessToken` field returned by `customerLogin`.
115
+
116
+ **Never send both.** Do **not** invent `X-Customer-Token`, `X-Auth-Token`, or
117
+ similar — they are ignored.
118
+
119
+ ### `@inContext` directive
120
+
121
+ Pass storefront context (country, language, B2B buyer) via the `@inContext`
122
+ directive on operations:
123
+
124
+ ```graphql
125
+ query Products @inContext(country: PL, language: pl, preferredLocationId: "...") {
126
+ products(first: 12) { ... }
127
+ }
128
+ ```
129
+
130
+ Available args: `country`, `language`, `preferredLocationId`,
131
+ `buyer { customerAccessToken, companyLocationId }`. The `buyer` arg is for
132
+ **B2B server-to-server** flows only — browser storefronts use cookie auth and
133
+ should not send `buyer.customerAccessToken` in the directive.
134
+
135
+ ### `Money.amount` is a string, not a number
136
+
137
+ Money values use `Money { amount: String!, currencyCode: CurrencyCode! }`.
138
+ The `amount` is a **string** to preserve decimal precision. Never `parseFloat`
139
+ totals — use string-aware money math (e.g., `Decimal.js`) or pass the string
140
+ verbatim to display.
141
+
142
+ ### Pagination is Relay-style on every list
143
+
144
+ Every list query (products, collections, orders, etc.) uses Relay Connection
145
+ shape: `edges { cursor, node { ... } }`, `nodes { ... }`, `pageInfo { ... }`,
146
+ `totalCount`. Variables: `first`, `after`, `last`, `before`, `sortKey`,
147
+ `reverse`. The `cursor` is opaque — pass it back as `after` for the next page.
148
+
149
+ The `query` argument (when present) is a **text-search filter**, not a
150
+ GraphQL operation. Don't confuse with operation name.
151
+
152
+ ### `product(id, handle)` — both optional, one required
153
+
154
+ The `product` query accepts EITHER `id: ID` OR `handle: String`. Pass exactly
155
+ one. Do **not** invent `slug`, `productHandle`, `productId`, or `productSlug`
156
+ arg names.
157
+
158
+ ### Customer order — singular, not plural
159
+
160
+ To fetch customer orders:
161
+
162
+ - `query Customer { customer { orders { ... } } }` — list of orders.
163
+ - `query CustomerOrder($orderId: ID!) { ... }` — single order by ID.
164
+
165
+ **Hallucinations to avoid**: `OrderById`, `Order(id)`, `CustomerOrders` (plural),
166
+ `CustomerAddresses` (use `CustomerProfile` for profile data),
167
+ `CustomerMetaPropertiesSet`/`CustomerMetaPropertyDelete` — these do not exist.
168
+
169
+ ### Mutations are NOT auto-retried
170
+
171
+ `@doswiftly/storefront-sdk` retries **queries** on transient network errors
172
+ but does **NOT** retry mutations (that's a deliberate platform contract — at-most-once
173
+ semantics). Idempotency is the caller's responsibility. If you need retries
174
+ on a mutation, wrap with your own backoff logic and check `userErrors` to
175
+ decide whether to retry.
176
+
177
+ ## When in doubt
178
+
179
+ - **Schema question** ("what fields are on `X`?") → grep `schema.graphql`
180
+ - **Operation question** ("which mutation does Y?") → grep `llms-full.txt` for the verb in `**Description**:` lines
181
+ - **Programmatic enumeration** → load `operations.json`
182
+
183
+ For codegen setup and runtime transport, see `README.md` and [`@doswiftly/storefront-sdk`](https://www.npmjs.com/package/@doswiftly/storefront-sdk).
package/CHANGELOG.md CHANGED
@@ -1,5 +1,235 @@
1
1
  # Changelog
2
2
 
3
+ ## 7.1.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 0399ef8: Auto-generated, drift-proof docs and AI-agent context shipped inside the package.
8
+
9
+ The package now ships three new files alongside the `.graphql` sources, all
10
+ generated automatically and kept in sync with the schema:
11
+ - **`AGENTS.md`** — entry point for AI coding agents (Cursor, Claude Code,
12
+ GitHub Copilot, Codex, Aider, Gemini CLI, …). Critical conventions and
13
+ anti-hallucination notes — load this once per session.
14
+ - **`llms-full.txt`** — full operation reference: descriptions, typed variables,
15
+ ready-to-execute GraphQL bodies, fragment cross-references.
16
+ - **`operations.json`** — same operations as structured JSON for MCP servers
17
+ and programmatic tools.
18
+
19
+ The `Available Operations` section in `README.md` is now also auto-generated,
20
+ so it can never drift from the actual schema again.
21
+
22
+ **Anti-hallucination conventions** documented in `AGENTS.md`:
23
+ - Cart mutations are `cartAddLines` / `cartUpdateLines` / `cartRemoveLines`
24
+ (NOT the `cart<Object><Verb>` aliases — they do not exist in this API).
25
+ - `userErrors[].code` is a `String`, not an enum. Domain-typed enums exist
26
+ separately for warnings (`CartWarningCode`, `DiscountErrorCode`, etc.).
27
+ - Authentication uses **either** the `customerAccessToken` cookie **or** the
28
+ `Authorization: Bearer` header — never both.
29
+ - `Money.amount` is a string (decimal precision), not a number.
30
+
31
+ No code changes for existing consumers — `schema.graphql`, `queries.graphql`,
32
+ `mutations.graphql`, and `fragments.graphql` are unchanged in surface (only
33
+ non-functional `#` description comments were added in the operation files).
34
+ Codegen continues to produce identical typed documents.
35
+
36
+ ## 7.0.0
37
+
38
+ ### Major Changes
39
+
40
+ - 227629d: # Storefront GraphQL Operations 7.0 — Breaking changes + nowe rozszerzenia API
41
+
42
+ Major bump wprowadza zmiany breaking w schemie storefront GraphQL plus 3 znaczące nowe funkcjonalności: per-address tax info dla B2B, generic Meta Properties (extensible custom fields), oraz strukturalne userErrors[] z typed codes dla wszystkich mutacji.
43
+
44
+ ## Breaking changes
45
+
46
+ ### 1. `Product.category` (free-text) → `Product.categories` + `Product.primaryCategory` (structured)
47
+
48
+ Wcześniej `Product.category: String` zwracał free-text label — tekst nie był powiązany z entity Category. Klienci nie mogli pobrać slug/name kategorii ani zbudować breadcrumb. Po 7.0:
49
+
50
+ ```graphql
51
+ # PRZED (6.x):
52
+ product { category } # → "Pop Vinyl" (free-text string, brak slug/parent)
53
+
54
+ # PO (7.0):
55
+ product {
56
+ categories { id slug name parent { slug } } # M2M lista (junction table)
57
+ primaryCategory { slug name } # categories[0] dla breadcrumb
58
+ }
59
+ ```
60
+
61
+ **Migration**:
62
+ - Klient renderujący breadcrumb po category name: zamień `product.category` na `product.primaryCategory.name`.
63
+ - Klient filtrujący kategorie z `Product.category` jako string: użyj `product.categories[]` array.
64
+ - Filter `productCategory` w `ProductFilter` usunięty — używaj `category: { id }` (junction lookup, działało wcześniej).
65
+
66
+ ### 2. `categories` query — `CategoryTree` → `CategoryConnection` (Relay)
67
+
68
+ Wcześniej `categories` zwracał outlier shape `{ roots, totalCount }` vs reszta schemy używała Relay Connection. Klienci próbujący `categories { nodes }` dostawali GraphQL error. Po 7.0:
69
+
70
+ ```graphql
71
+ # PRZED (6.x):
72
+ categories(first: 20, rootsOnly: true) {
73
+ roots { id name children { id name } }
74
+ totalCount
75
+ }
76
+
77
+ # PO (7.0):
78
+ categories(first: 20, rootsOnly: true) {
79
+ nodes { id name children { id name } }
80
+ edges { cursor node { id } }
81
+ pageInfo { hasNextPage hasPreviousPage startCursor endCursor }
82
+ totalCount
83
+ }
84
+ ```
85
+
86
+ **Migration**:
87
+ - Zamień `categories.roots` na `categories.nodes`.
88
+ - Tree shape rebuild po stronie klienta — używaj `Category.parent` / `Category.children` field resolvers (DataLoader N+1 safe). Filtr `rootsOnly: true` zwraca tylko top-level (parent IS NULL).
89
+ - Pełen Relay args: `first/after/last/before` zamiast tylko `first/after`.
90
+
91
+ ### 3. `Customer.defaultAddress` — czytelny stan (poprzednio zawsze null)
92
+
93
+ Wcześniej `Customer.defaultAddress` zwracał `null` mimo że adres miał `isDefault: true` w `addresses`. Niespójność łamała storefront breadcrumb i powodowała duplikaty w formularzu dostawy. Po 7.0 zwraca poprawnie wartość — single source of truth z `addresses[].isDefaultShipping` flag.
94
+
95
+ **Migration**: brak — klient po prostu zacznie dostawać poprawne wartości.
96
+
97
+ ### 4. Apollo response — bez `extensions.stacktrace` w produkcji
98
+
99
+ Wcześniej w środowisku produkcyjnym GraphQL response zawierał stacktrace z absolutnymi ścieżkami systemu plików backendu. Po 7.0 production response strippuje wszystko poza `code`, `message`, `locations`, `path` + banner `extensions.environment`. Stacktrace dostępny **tylko** w dev/staging dla DX.
100
+
101
+ **Migration**: brak — klient produkcyjny nie powinien był polegać na stacktrace.
102
+
103
+ ### 5. Validation errors → `userErrors[]` z typed codes (tech debt resolution)
104
+
105
+ Wcześniej walidacja DTO leciała jako `body.errors[].extensions.code = "INTERNAL_SERVER_ERROR"` envelope (wyglądało jak crash). Po 7.0 mutations zwracają strukturalne `userErrors[]` z explicit codes per validation rule:
106
+
107
+ ```graphql
108
+ # Invalid NIP, password too short, malformed email — wszystkie zwracają payload
109
+ mutation {
110
+ customerUpdate(customer: { taxId: "1234567890" }) {
111
+ customer {
112
+ id
113
+ }
114
+ userErrors {
115
+ field
116
+ code
117
+ message
118
+ }
119
+ # → [{ field: ["taxId"], code: "INVALID_TAX_ID_CHECKSUM", message: "..." }]
120
+ }
121
+ }
122
+ ```
123
+
124
+ **Typed codes** dla 25 walidatorów: `INVALID_TAX_ID_CHECKSUM`, `INVALID_VAT_NUMBER`, `INVALID_REGON`, `INVALID_EMAIL_FORMAT`, `INVALID_FORMAT`, `INVALID_PHONE_FORMAT`, `INVALID_UUID`, `INVALID_URL`, `INVALID_DATE`, `INVALID_ENUM_VALUE`, `INVALID_TYPE`, `INVALID_COUNTRY_CODE`, `INVALID_POSTAL_CODE`, `TOO_SHORT`, `TOO_LONG`, `TOO_MANY`, `TOO_FEW`, `OUT_OF_RANGE`, `REQUIRED`, `INVALID_NESTED`, `VALIDATION_ERROR`, etc.
125
+
126
+ **Migration**: klient widzący poprzednio `body.errors[].extensions.code = "INTERNAL_SERVER_ERROR"` — sprawdź `data.<mutation>.userErrors[]` zamiast envelope.
127
+
128
+ ## New features
129
+
130
+ ### 6. `MailingAddress.taxId` + `vatNumber` — per-address B2B tax info
131
+
132
+ Adres ma teraz opcjonalne `taxId` (polski NIP, 10 cyfr z checksumą) + `vatNumber` (VAT UE, np. PL5260250274). Use case: B2B z różnymi danymi firmowymi per adres dostawy/billing (faktura na firmę matkę, dostawa na oddział z osobnym NIP-em).
133
+
134
+ ```graphql
135
+ mutation {
136
+ customerAddAddress(
137
+ address: {
138
+ streetLine1: "ul. Marszałkowska 100"
139
+ city: "Warszawa"
140
+ postalCode: "00-001"
141
+ country: PL
142
+ company: "GameGoods Sp. z o.o."
143
+ taxId: "5260250274"
144
+ vatNumber: "PL5260250274"
145
+ }
146
+ ) {
147
+ address {
148
+ taxId
149
+ vatNumber
150
+ }
151
+ userErrors {
152
+ code
153
+ }
154
+ }
155
+ }
156
+ ```
157
+
158
+ Walidacja format/checksum aktywna — invalid NIP zwraca `userErrors` z `INVALID_TAX_ID_CHECKSUM`.
159
+
160
+ ### 7. Meta Properties — extensible custom fields per encji
161
+
162
+ Generic mechanizm rozszerzania encji (Customer/Product/ProductVariant/Order) o storefront-specific dane bez wymogu zmian schematu po stronie platformy. Per-customer birthday, per-product warranty_years, per-order gift_message — wszystko jako typed key-value entries w polymorphic table.
163
+
164
+ ```graphql
165
+ # Customer self-edit (Bearer token klienta)
166
+ mutation {
167
+ customerMetaPropertiesSet(
168
+ properties: [
169
+ {
170
+ namespace: "storefront"
171
+ key: "birthday"
172
+ value: "1990-05-15"
173
+ type: DATE
174
+ }
175
+ {
176
+ namespace: "storefront"
177
+ key: "favorite_color"
178
+ value: "#FF6600"
179
+ type: COLOR
180
+ }
181
+ ]
182
+ ) {
183
+ metaProperties {
184
+ id
185
+ namespace
186
+ key
187
+ value
188
+ type
189
+ }
190
+ userErrors {
191
+ code
192
+ message
193
+ }
194
+ }
195
+ }
196
+
197
+ # Read na każdej encji
198
+ query {
199
+ customer {
200
+ metaProperty(namespace: "storefront", key: "birthday") {
201
+ value
202
+ }
203
+ }
204
+ product(id: "...") {
205
+ metaProperties(first: 10, namespace: "warranty") {
206
+ nodes {
207
+ key
208
+ value
209
+ }
210
+ }
211
+ }
212
+ }
213
+ ```
214
+
215
+ 10 typed values: `STRING/TEXT/INTEGER/DECIMAL/BOOLEAN/JSON/DATE/DATE_TIME/URL/COLOR`. Visibility flag `isPrivate` (storefront API filtruje `isPrivate=true` na public encjach jak Product/ProductVariant). Reserved namespace prefix `doswiftly:` dla platform fields.
216
+
217
+ **Faza 1 MVP scope**: Customer self-edit only (`customerMetaPropertiesSet` / `customerMetaPropertyDelete` z auth Bearer token klienta). Cross-resource writes (Product/Order/Variant) wymagają Admin layer (Faza 2).
218
+
219
+ ### 8. NIP/REGON sanity guard — odrzucone all-zeros / repeated-digit patterns
220
+
221
+ Walidator NIP wcześniej akceptował `'0000000000'` (suma kontrolna 0=0). Po 7.0 odrzucamy wszystkie repeated-digit patterns (`'0000000000'`, `'1111111111'`, ..., `'9999999999'`) — to oczywiste fake test data / NULL placeholders, żaden urząd ich nie wystawia. Analogicznie REGON 9-digit i 14-digit.
222
+
223
+ ### 9. Empty string clear-value semantyka (wszystkie nullable input fields)
224
+
225
+ Customer kasujący wartość w UI inputie (np. `<input value="" />`) wysyła `phone: ""` zamiast `null`. Po 7.0 backend traktuje empty string jako "wyczyść pole" (DB → null) — natywne form library behavior, klient nie wymaga specjalnej logiki rozróżniającej `null` vs `""`.
226
+
227
+ Pokrycie: wszystkie nullable string/enum fields w `CustomerCreateInput`, `CustomerUpdateInput`, `MailingAddressInput`.
228
+
229
+ ## Notes
230
+ - 7.0 jest cumulative — kumulacja zmian z 6.x patches (B2B fields w `customerUpdate`, marketing consent flow) + breaking changes opisane wyżej.
231
+ - Wszystkie zmiany pokryte test coverage — backend integration tests gwarantują regression-free contract.
232
+
3
233
  ## 6.1.0
4
234
 
5
235
  ### Minor Changes