@doswiftly/storefront-sdk 22.3.0 → 22.5.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 CHANGED
@@ -1,5 +1,45 @@
1
1
  # Changelog
2
2
 
3
+ ## 22.5.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 3d5f1d3: Public reads now use the cacheable `GET` transport **by default** — you no longer opt in per query with `cacheLong()`. A non-mutation query that carries a persisted-document id and no signed-in identity is sent as a shared, credential-less `GET` so a CDN can cache it; a request carrying an `Authorization` bearer or a cart secret automatically stays on `POST` with credentials, so personalised reads are never shared. The server decides whether a result is actually cacheable via its `Cache-Control` response — the client only chooses the transport. Opt a specific persisted read out of the shared cache with `cachePrivate()` or `cacheNone()` (forces `POST`). Mutations and any request without a document id keep the existing `POST` behaviour, so current behaviour is unchanged until document ids are present.
8
+
9
+ ```ts
10
+ // Cacheable GET automatically (anonymous visitor) — no cacheLong() needed:
11
+ const products = await client.query(ProductsQuery, { first: 20 });
12
+
13
+ // Keep a persisted read per-user (forces POST):
14
+ const account = await client.query(MyAccountQuery, {}, cachePrivate());
15
+ ```
16
+
17
+ - 7664b81: Added a ready GraphQL codegen recipe for edge-cacheable reads. Import `createCodegenConfig` from `@doswiftly/storefront-operations/codegen` and `export default` it from your `codegen.ts` — it points the client preset at the bundled schema and emits persisted documents whose `documentId` matches the Storefront API's `sha256:<hex>` contract, so public reads resolve server-side and can be cached at the edge.
18
+
19
+ You write operations with the generated `gql(...)` tag. They compile to lightweight typed strings (no `graphql` package at runtime) that the SDK's `query` / `mutate` accept directly, custom scalars are mapped to precision-safe TypeScript types (money and 64-bit integers as strings), and fragment fields are read directly with no unmasking helper. If your project already imports a `gql` from another GraphQL client, override the tag name: `createCodegenConfig({ gqlTagName: 'graphql' })`. Requires `@graphql-codegen/cli`, `@graphql-codegen/client-preset@^4` and `graphql@^16` as devDependencies (the preset doesn't support `graphql` v17 yet — fragment operations fail to generate). See the README "Edge-cacheable reads" section. `@doswiftly/storefront-sdk` accepts these generated documents directly in `query` / `mutate`, and its cacheable `GET` transport degrades to a `POST` on any non-success response.
20
+
21
+ Author operations with the generated tag — codegen emits each as a typed document carrying its `documentId`:
22
+
23
+ ```ts
24
+ import { gql } from "./gql";
25
+
26
+ export const ProductsQuery = gql(`
27
+ query Products($first: Int) {
28
+ products(first: $first) {
29
+ nodes { id handle title }
30
+ }
31
+ }
32
+ `);
33
+ ```
34
+
35
+ ## 22.4.0
36
+
37
+ ### Minor Changes
38
+
39
+ - 848a3fe: Expose payment consents on `PaymentMethod`. The fragment now selects `acknowledgements` — consent statements a buyer can affirm before paying, each with linked documents — so storefronts can render the consent checkboxes and echo accepted codes back in `PaymentCreateInput.acknowledgements`.
40
+ - ecf919f: `<PriceDisplay>` now accepts an optional `locale` prop, forwarded to both the current price and the strikethrough compare-at price. Pass it (e.g. `locale="pl-PL"`) for deterministic, environment-independent formatting — bringing `<PriceDisplay>` in line with the `locale` prop already on `<Money>`. When omitted, behaviour is unchanged (the runtime default locale is used).
41
+ - be11ed6: Add a cacheable `GET` transport for public reads. When a document carries a persisted-document id (`TypedDocumentString.__meta__.hash`), a public cacheable query is sent as a `GET` with only the id — so a shared CDN can cache it — with the shop, currency and language in the URL and no credentials attached; an unrecognised id transparently replays as a `POST`. Mutations and any request without a document id keep the existing `POST` behaviour, so current behaviour is unchanged until document ids are present.
42
+
3
43
  ## 22.3.0
4
44
 
5
45
  ### Minor Changes
package/README.md CHANGED
@@ -718,11 +718,11 @@ your CSS approach. Available from `@doswiftly/storefront-sdk/react`:
718
718
 
719
719
  | Component | Purpose |
720
720
  |-----------|---------|
721
- | `<Money amount currency>` | Locale-formatted price string from minor units |
721
+ | `<Money amount currency locale?>` | Locale-formatted price string from minor units |
722
722
  | `<Image data sizes priority>` | `<img>` with thumbhash blur placeholder + sane defaults |
723
723
  | `<CartCount count label>` | Aria-live cart item count |
724
724
  | `<AddToCartButton variantId quantity>` | Button wired to `useCartManager().addItem` (loading state + a11y error surfacing) |
725
- | `<PriceDisplay price compareAtPrice currency>` | Price + optional strikethrough sale price |
725
+ | `<PriceDisplay price compareAtPrice currency locale?>` | Price + optional strikethrough sale price |
726
726
  | `<CartTotals subtotal tax shipping discount total currency>` | Cart financial breakdown `<dl>` |
727
727
  | `<PaymentInstrumentTile instrument>` | One selectable payment instrument (card brand, wallet, bank) |
728
728
  | `<PaymentInstrumentSection method>` | Instrument group for a payment method (renders tiles) |
@@ -730,8 +730,8 @@ your CSS approach. Available from `@doswiftly/storefront-sdk/react`:
730
730
  ```tsx
731
731
  import { Money, PriceDisplay, CartCount } from '@doswiftly/storefront-sdk/react';
732
732
 
733
- <Money amount={9990} currency="PLN" /> {/* "99,90 zł" */}
734
- <PriceDisplay price={7990} compareAtPrice={9990} currency="PLN" />
733
+ <Money amount={9990} currency="PLN" locale="pl-PL" /> {/* "99,90 zł" */}
734
+ <PriceDisplay price={7990} compareAtPrice={9990} currency="PLN" locale="pl-PL" />
735
735
  <CartCount count={3} label="items" />
736
736
  ```
737
737
 
@@ -1122,6 +1122,28 @@ cacheCustom({ maxAge: 300, swr: 600 }) // 5min + 10min swr
1122
1122
  const data = await client.query(ProductQuery, { handle }, cacheLong());
1123
1123
  ```
1124
1124
 
1125
+ ### Edge caching is the default for public reads
1126
+
1127
+ When an operation is generated with a persisted-document id (the recipe in
1128
+ `@doswiftly/storefront-operations` does this), a non-mutation read with no
1129
+ signed-in identity is sent as a **cacheable `GET`** automatically — you do not
1130
+ need to pass `cacheLong()`. A shared CDN can then serve it, and the server
1131
+ decides whether the result is actually cacheable via its `Cache-Control`
1132
+ response. Requests carrying an identity (an `Authorization` bearer or a cart
1133
+ secret) stay on `POST` with credentials, so personalised reads are never shared.
1134
+
1135
+ The cache strategies above are now **tuning / opt-out** rather than the on
1136
+ switch: pass `cachePrivate()` or `cacheNone()` to keep a persisted read per-user
1137
+ (forces `POST`), or `cacheLong({ tags })` to attach Next.js revalidation tags.
1138
+
1139
+ ```typescript
1140
+ // Cacheable GET automatically — no strategy argument needed:
1141
+ const products = await client.query(ProductsQuery, { first: 20 });
1142
+
1143
+ // Keep a persisted read per-user (forces POST):
1144
+ const account = await client.query(MyAccountQuery, {}, cachePrivate());
1145
+ ```
1146
+
1125
1147
  ## GraphQL schema for codegen
1126
1148
 
1127
1149
  The GraphQL SDL ships in the linked `@doswiftly/storefront-operations` package
@@ -0,0 +1,61 @@
1
+ /**
2
+ * Cacheable-public-read eligibility — the platform-default rule deciding whether
3
+ * a query may travel over the shared, credential-less cacheable GET transport
4
+ * instead of POST. No per-query `cacheLong()` opt-in is required: a non-mutation
5
+ * persisted read with no identity is eligible by default.
6
+ *
7
+ * Mirror of the backend storefront-cache header SSOT
8
+ * (`commerce/storefront-graphql/cache/cache-headers.ts`). The published SDK
9
+ * cannot import the private backend, so the contract is duplicated here
10
+ * intentionally — the same rationale as `LANGUAGE_HEADER_NAME`. A drift test
11
+ * (`cache-eligibility.drift.test.ts`, run against the backend source) keeps the
12
+ * two in sync:
13
+ * - {@link IDENTITY_HEADERS} must be a **superset** of the backend's (missing one
14
+ * → the SDK sends a credential-less GET that strips the identity → wrong data).
15
+ * - {@link VARIANCE_AXIS_HEADERS} must **equal** the backend's (a missing axis
16
+ * collapses two shops / currencies / languages onto one shared edge entry →
17
+ * cross-tenant / wrong-variant leak; an extra axis fragments the cache).
18
+ *
19
+ * The SDK does NOT classify operations as cacheable (PUBLIC_READ vs USER_SCOPED) —
20
+ * that stays the backend's sole authority. It resolves the documentId, classifies
21
+ * the operation fail-closed and emits `Cache-Control: public|private`; the SDK
22
+ * only chooses the *transport*. Do NOT add a PUBLIC_READ allow-list here — it
23
+ * would duplicate the backend registry and drift.
24
+ *
25
+ * Identity manifests as a header by the time a request reaches the transport:
26
+ * `authMiddleware` adds `Authorization` whenever a customer is signed in (the
27
+ * token lives in memory, seeded server-side or rehydrated client-side), and
28
+ * `cartSecretMiddleware` adds `x-cart-secret`. The backend's additional
29
+ * cookie / `@inContext` identity checks are upstream signals the SDK has already
30
+ * converted to one of these headers — so the header gate is sufficient.
31
+ */
32
+ import type { GraphQLRequest } from './types';
33
+ /** Shop-routing header the client always sends; also a cache variance axis. */
34
+ export declare const SHOP_SLUG_HEADER = "X-Shop-Slug";
35
+ /**
36
+ * Header-level identity signals. Their presence keeps a request on POST (with
37
+ * credentials) instead of the shared cacheable GET. Mirror of backend
38
+ * `IDENTITY_HEADERS`; compared case-insensitively.
39
+ */
40
+ export declare const IDENTITY_HEADERS: readonly ["authorization", "x-cart-secret"];
41
+ /**
42
+ * Cache variance axes as `[requestHeaderName, getUrlParam]`. The backend keys its
43
+ * L1 cache on the resolved `{tenant, currency, language}`; the SDK copies the same
44
+ * axes into the GET URL so a shared edge cache keys correctly (it keys on the URL,
45
+ * not on request headers). Consumed by `buildTrustedGetRequest`.
46
+ */
47
+ export declare const VARIANCE_AXIS_PARAMS: readonly [readonly ["X-Shop-Slug", "__shop"], readonly ["X-Preferred-Currency", "__currency"], readonly ["X-Language", "__language"]];
48
+ /**
49
+ * Lowercase variance header names — the contract compared against the backend
50
+ * `VARIANCE_HEADERS` SSOT by the drift test. Derived from {@link VARIANCE_AXIS_PARAMS}
51
+ * so there is one list in the SDK.
52
+ */
53
+ export declare const VARIANCE_AXIS_HEADERS: readonly string[];
54
+ /**
55
+ * Whether a request may use the cacheable GET transport (transport safety only —
56
+ * the backend decides actual cacheability via `Cache-Control`). Eligible when it
57
+ * is a non-mutation persisted read carrying no identity. The explicit cache
58
+ * opt-outs (`cachePrivate()` / `cacheNone()`) are applied by the caller.
59
+ */
60
+ export declare function isPublicReadEligible(request: GraphQLRequest): boolean;
61
+ //# sourceMappingURL=cache-eligibility.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"cache-eligibility.d.ts","sourceRoot":"","sources":["../../../src/core/client/cache-eligibility.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAI9C,+EAA+E;AAC/E,eAAO,MAAM,gBAAgB,gBAAgB,CAAC;AAE9C;;;;GAIG;AACH,eAAO,MAAM,gBAAgB,6CAA8C,CAAC;AAE5E;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,uIAIvB,CAAC;AAEX;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,EAAE,SAAS,MAAM,EAElD,CAAC;AAYF;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAAC,OAAO,EAAE,cAAc,GAAG,OAAO,CAIrE"}
@@ -0,0 +1,80 @@
1
+ /**
2
+ * Cacheable-public-read eligibility — the platform-default rule deciding whether
3
+ * a query may travel over the shared, credential-less cacheable GET transport
4
+ * instead of POST. No per-query `cacheLong()` opt-in is required: a non-mutation
5
+ * persisted read with no identity is eligible by default.
6
+ *
7
+ * Mirror of the backend storefront-cache header SSOT
8
+ * (`commerce/storefront-graphql/cache/cache-headers.ts`). The published SDK
9
+ * cannot import the private backend, so the contract is duplicated here
10
+ * intentionally — the same rationale as `LANGUAGE_HEADER_NAME`. A drift test
11
+ * (`cache-eligibility.drift.test.ts`, run against the backend source) keeps the
12
+ * two in sync:
13
+ * - {@link IDENTITY_HEADERS} must be a **superset** of the backend's (missing one
14
+ * → the SDK sends a credential-less GET that strips the identity → wrong data).
15
+ * - {@link VARIANCE_AXIS_HEADERS} must **equal** the backend's (a missing axis
16
+ * collapses two shops / currencies / languages onto one shared edge entry →
17
+ * cross-tenant / wrong-variant leak; an extra axis fragments the cache).
18
+ *
19
+ * The SDK does NOT classify operations as cacheable (PUBLIC_READ vs USER_SCOPED) —
20
+ * that stays the backend's sole authority. It resolves the documentId, classifies
21
+ * the operation fail-closed and emits `Cache-Control: public|private`; the SDK
22
+ * only chooses the *transport*. Do NOT add a PUBLIC_READ allow-list here — it
23
+ * would duplicate the backend registry and drift.
24
+ *
25
+ * Identity manifests as a header by the time a request reaches the transport:
26
+ * `authMiddleware` adds `Authorization` whenever a customer is signed in (the
27
+ * token lives in memory, seeded server-side or rehydrated client-side), and
28
+ * `cartSecretMiddleware` adds `x-cart-secret`. The backend's additional
29
+ * cookie / `@inContext` identity checks are upstream signals the SDK has already
30
+ * converted to one of these headers — so the header gate is sufficient.
31
+ */
32
+ import { CURRENCY_HEADER_NAME } from '../currency/cookie-config';
33
+ import { LANGUAGE_HEADER_NAME } from '../language/cookie-config';
34
+ /** Shop-routing header the client always sends; also a cache variance axis. */
35
+ export const SHOP_SLUG_HEADER = 'X-Shop-Slug';
36
+ /**
37
+ * Header-level identity signals. Their presence keeps a request on POST (with
38
+ * credentials) instead of the shared cacheable GET. Mirror of backend
39
+ * `IDENTITY_HEADERS`; compared case-insensitively.
40
+ */
41
+ export const IDENTITY_HEADERS = ['authorization', 'x-cart-secret'];
42
+ /**
43
+ * Cache variance axes as `[requestHeaderName, getUrlParam]`. The backend keys its
44
+ * L1 cache on the resolved `{tenant, currency, language}`; the SDK copies the same
45
+ * axes into the GET URL so a shared edge cache keys correctly (it keys on the URL,
46
+ * not on request headers). Consumed by `buildTrustedGetRequest`.
47
+ */
48
+ export const VARIANCE_AXIS_PARAMS = [
49
+ [SHOP_SLUG_HEADER, '__shop'],
50
+ [CURRENCY_HEADER_NAME, '__currency'],
51
+ [LANGUAGE_HEADER_NAME, '__language'],
52
+ ];
53
+ /**
54
+ * Lowercase variance header names — the contract compared against the backend
55
+ * `VARIANCE_HEADERS` SSOT by the drift test. Derived from {@link VARIANCE_AXIS_PARAMS}
56
+ * so there is one list in the SDK.
57
+ */
58
+ export const VARIANCE_AXIS_HEADERS = VARIANCE_AXIS_PARAMS.map(([header]) => header.toLowerCase());
59
+ const IDENTITY_HEADER_SET = new Set(IDENTITY_HEADERS);
60
+ /** True when any identity header is present (case-insensitive). */
61
+ function hasIdentityHeader(headers) {
62
+ for (const [name, value] of Object.entries(headers)) {
63
+ if (value && IDENTITY_HEADER_SET.has(name.toLowerCase()))
64
+ return true;
65
+ }
66
+ return false;
67
+ }
68
+ /**
69
+ * Whether a request may use the cacheable GET transport (transport safety only —
70
+ * the backend decides actual cacheability via `Cache-Control`). Eligible when it
71
+ * is a non-mutation persisted read carrying no identity. The explicit cache
72
+ * opt-outs (`cachePrivate()` / `cacheNone()`) are applied by the caller.
73
+ */
74
+ export function isPublicReadEligible(request) {
75
+ if (request.isMutation)
76
+ return false;
77
+ if (typeof request.documentId !== 'string' || request.documentId.length === 0)
78
+ return false;
79
+ return !hasIdentityHeader(request.headers);
80
+ }
@@ -1 +1 @@
1
- {"version":3,"file":"create-client.d.ts","sourceRoot":"","sources":["../../../src/core/client/create-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EACV,sBAAsB,EACtB,gBAAgB,EAKjB,MAAM,SAAS,CAAC;AAQjB,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,sBAAsB,GAAG,gBAAgB,CAgHvF"}
1
+ {"version":3,"file":"create-client.d.ts","sourceRoot":"","sources":["../../../src/core/client/create-client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,EACV,sBAAsB,EACtB,gBAAgB,EAKjB,MAAM,SAAS,CAAC;AAQjB,wBAAgB,sBAAsB,CAAC,MAAM,EAAE,sBAAsB,GAAG,gBAAgB,CAmIvF"}
@@ -47,16 +47,37 @@ export function createStorefrontClient(config) {
47
47
  return compiledPipeline;
48
48
  }
49
49
  /**
50
- * Resolve query string from TypedDocumentString or plain string.
50
+ * Resolve query string from a typed document or plain string.
51
51
  */
52
52
  function resolveQuery(document) {
53
53
  return typeof document === 'string' ? document : document.toString();
54
54
  }
55
+ /**
56
+ * Persisted-document id carried by codegen on `__meta__.hash`.
57
+ * Plain strings (and documents without a populated hash) have none → POST transport.
58
+ *
59
+ * `__meta__` is optional codegen metadata, not part of the public document
60
+ * contract (`TypedDocumentLike`), so it is read defensively via narrowing — no
61
+ * cast, no assumption that any given document carries it.
62
+ */
63
+ function resolveDocumentId(document) {
64
+ if (typeof document === 'string')
65
+ return undefined;
66
+ if (!('__meta__' in document))
67
+ return undefined;
68
+ const meta = document.__meta__;
69
+ if (typeof meta !== 'object' || meta === null)
70
+ return undefined;
71
+ if (!('hash' in meta) || typeof meta.hash !== 'string')
72
+ return undefined;
73
+ return meta.hash;
74
+ }
55
75
  /**
56
76
  * Core request execution — shared by query() and mutate().
57
77
  */
58
78
  async function request(document, variables, isMutation = false, cache) {
59
79
  const query = resolveQuery(document);
80
+ const documentId = resolveDocumentId(document);
60
81
  const operationName = getOperationName(query);
61
82
  const pipeline = getPipeline();
62
83
  const headers = {
@@ -73,6 +94,7 @@ export function createStorefrontClient(config) {
73
94
  headers,
74
95
  isMutation,
75
96
  cache,
97
+ documentId,
76
98
  };
77
99
  // Dedupe queries (not mutations) in the same tick
78
100
  const executeFn = () => pipeline(graphqlRequest);
@@ -1 +1 @@
1
- {"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../../src/core/client/execute.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,UAAU,EACV,YAAY,EAEZ,cAAc,EACd,eAAe,EAChB,MAAM,SAAS,CAAC;AAEjB,MAAM,WAAW,aAAa;IAC5B,2BAA2B;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,KAAK,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAC/B;;;;OAIG;IACH,KAAK,EAAE,oBAAoB,GAAG,IAAI,CAAC;CACpC;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,GAAG,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;CAClC;AAsCD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,YAAY,GAAG,SAAS,GACpD,oBAAoB,GAAG,IAAI,CAiB7B;AAwFD;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,IAGnB,SAAS,cAAc,KAAG,OAAO,CAAC,eAAe,CAAC,CAyEjF"}
1
+ {"version":3,"file":"execute.d.ts","sourceRoot":"","sources":["../../../src/core/client/execute.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EACV,UAAU,EACV,YAAY,EAEZ,cAAc,EACd,eAAe,EAChB,MAAM,SAAS,CAAC;AAGjB,MAAM,WAAW,aAAa;IAC5B,2BAA2B;IAC3B,QAAQ,EAAE,MAAM,CAAC;IACjB,kCAAkC;IAClC,KAAK,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAC/B;;;;OAIG;IACH,KAAK,EAAE,oBAAoB,GAAG,IAAI,CAAC;CACpC;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,OAAO,EAAE,OAAO,CAAC;IACjB,QAAQ,EAAE,OAAO,CAAC;IAClB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,OAAO,CAAC;IACpB,GAAG,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;CAClC;AAsCD;;;;;;;;GAQG;AACH,wBAAgB,mBAAmB,CACjC,KAAK,EAAE,OAAO,GAAG,SAAS,GAAG,YAAY,GAAG,SAAS,GACpD,oBAAoB,GAAG,IAAI,CAiB7B;AAqKD;;GAEG;AACH,wBAAgB,aAAa,CAAC,MAAM,EAAE,aAAa,IAGnB,SAAS,cAAc,KAAG,OAAO,CAAC,eAAe,CAAC,CAqFjF"}
@@ -4,6 +4,7 @@
4
4
  * Sends GraphQL POST request, parses response, returns typed GraphQLResponse.
5
5
  * Error normalization is handled by error middleware, NOT here.
6
6
  */
7
+ import { isPublicReadEligible, VARIANCE_AXIS_PARAMS } from './cache-eligibility';
7
8
  const DEFAULT_LOG = (event) => {
8
9
  // Header on a separate line so the data dump below renders untruncated in
9
10
  // both terminals and DevTools.
@@ -151,45 +152,135 @@ function extractUserErrors(data) {
151
152
  }
152
153
  return flat;
153
154
  }
155
+ /**
156
+ * Default POST transport — the full query travels in the body and cookies are sent.
157
+ * Every request that is not a cacheable public read uses this (unchanged contract).
158
+ */
159
+ function buildPostRequest(endpoint, request) {
160
+ const body = { query: request.query };
161
+ if (request.variables && Object.keys(request.variables).length > 0) {
162
+ body.variables = request.variables;
163
+ }
164
+ if (request.operationName) {
165
+ body.operationName = request.operationName;
166
+ }
167
+ return [
168
+ endpoint,
169
+ {
170
+ method: 'POST',
171
+ headers: {
172
+ 'Content-Type': 'application/json',
173
+ Accept: 'application/json',
174
+ ...request.headers,
175
+ },
176
+ body: JSON.stringify(body),
177
+ signal: request.signal,
178
+ // Include httpOnly cookies (e.g. customerAccessToken auth) on cross-origin
179
+ // requests. Required when the storefront runs on a domain different from the
180
+ // backend GraphQL endpoint — same-origin browsers send cookies regardless,
181
+ // but cross-origin needs `credentials: 'include'` paired with the backend's
182
+ // `Access-Control-Allow-Credentials: true` (already configured).
183
+ credentials: 'include',
184
+ },
185
+ ];
186
+ }
187
+ /**
188
+ * Cacheable GET transport for public reads with a known `documentId`. Only the id
189
+ * travels (the backend resolves it to the full query from its allowlist), the variance
190
+ * axes ride in the URL so a shared edge cache keys correctly, and no credentials are
191
+ * sent so the response stays shareable across visitors.
192
+ */
193
+ function buildTrustedGetRequest(endpoint, request) {
194
+ const url = new URL(endpoint);
195
+ url.searchParams.set('documentId', request.documentId);
196
+ if (request.operationName && request.operationName !== 'anonymous') {
197
+ url.searchParams.set('operationName', request.operationName);
198
+ }
199
+ if (request.variables && Object.keys(request.variables).length > 0) {
200
+ url.searchParams.set('variables', JSON.stringify(request.variables));
201
+ }
202
+ // Case-insensitive lookup for the variance axes: request header casing is not guaranteed
203
+ // (middleware/proxies may normalise), and the identity gate is already case-insensitive.
204
+ // A casing mismatch here would drop an axis from the URL — and the shared edge keys on the
205
+ // URL only, so two shops/currencies/languages would collapse onto one entry (cross-tenant /
206
+ // wrong-variant leak). Build one lowercased view, then look up each axis by lowercase name.
207
+ const lowerHeaders = {};
208
+ for (const [name, value] of Object.entries(request.headers))
209
+ lowerHeaders[name.toLowerCase()] = value;
210
+ for (const [headerName, param] of VARIANCE_AXIS_PARAMS) {
211
+ const value = lowerHeaders[headerName.toLowerCase()];
212
+ if (value)
213
+ url.searchParams.set(param, value);
214
+ }
215
+ const headers = { ...request.headers };
216
+ // Public read = customer-agnostic → the GET must carry NO credentials, so the cached
217
+ // response can be served to any visitor. `credentials: 'omit'` drops the browser cookie jar,
218
+ // but NOT an explicitly-set Cookie header (e.g. forwarded on SSR) — strip it (and the bearer
219
+ // token) by hand so the request is truly credential-less and can never personalise the body.
220
+ delete headers.Authorization;
221
+ delete headers.authorization;
222
+ delete headers.Cookie;
223
+ delete headers.cookie;
224
+ headers.Accept = 'application/json';
225
+ // Force a CORS preflight so the backend CSRF prevention admits the GET.
226
+ headers['apollo-require-preflight'] = 'true';
227
+ return [url.toString(), { method: 'GET', headers, signal: request.signal, credentials: 'omit' }];
228
+ }
154
229
  /**
155
230
  * Create the innermost execute function for the middleware pipeline.
156
231
  */
157
232
  export function createExecute(config) {
158
233
  const { endpoint, fetch: fetchFn, debug } = config;
159
234
  return async function execute(request) {
160
- const { query, variables, headers, signal, operationName } = request;
235
+ const { query, variables, headers, operationName, cache, documentId } = request;
161
236
  const startedAt = debug?.timing ? Date.now() : 0;
237
+ // Platform default: a non-mutation persisted read with no identity goes over the
238
+ // shared cacheable GET so an edge cache can serve it — no per-query `cacheLong()`
239
+ // opt-in needed. The caller can still opt OUT with `cachePrivate()` / `cacheNone()`
240
+ // for a persisted read it wants kept per-user. Whether the result is ACTUALLY
241
+ // cached is the backend's call (it classifies the operation and emits
242
+ // `Cache-Control`); the SDK never classifies operations here — adding a PUBLIC_READ
243
+ // list would duplicate the backend registry and drift.
244
+ const useTrustedGet = isPublicReadEligible(request) && cache?.mode !== 'private' && cache?.mode !== 'no-store';
162
245
  if (debug) {
163
- const requestData = { variables };
246
+ const requestData = { variables, method: useTrustedGet ? 'GET' : 'POST' };
247
+ if (useTrustedGet)
248
+ requestData.documentId = documentId;
164
249
  if (debug.request)
165
250
  requestData.query = query;
166
251
  if (debug.headers)
167
252
  requestData.headers = redactRequestHeaders(headers);
168
253
  debug.log({ phase: 'request', operationName, data: requestData });
169
254
  }
170
- const body = { query };
171
- if (variables && Object.keys(variables).length > 0) {
172
- body.variables = variables;
255
+ let response;
256
+ if (useTrustedGet) {
257
+ // The cacheable GET is only an optimization for a shared edge cache — it must NEVER
258
+ // break a read. Use it only on a clean 2xx; degrade to a full-query POST on ANY other
259
+ // outcome: the GET transport throwing (network / CORS / aborted), an opaque or blocked
260
+ // response (`status` 0, `ok` false), an unknown documentId surfaced as 400, a 5xx, etc.
261
+ // The full query travels in the POST body, so the backend always resolves it.
262
+ let getResponse = null;
263
+ try {
264
+ const [getUrl, getInit] = buildTrustedGetRequest(endpoint, request);
265
+ const candidate = await fetchFn(getUrl, getInit);
266
+ if (candidate.ok)
267
+ getResponse = candidate;
268
+ }
269
+ catch {
270
+ // network / CORS / aborted GET — fall through to the safe POST below
271
+ }
272
+ if (getResponse) {
273
+ response = getResponse;
274
+ }
275
+ else {
276
+ const [postUrl, postInit] = buildPostRequest(endpoint, request);
277
+ response = await fetchFn(postUrl, postInit);
278
+ }
173
279
  }
174
- if (operationName) {
175
- body.operationName = operationName;
280
+ else {
281
+ const [postUrl, postInit] = buildPostRequest(endpoint, request);
282
+ response = await fetchFn(postUrl, postInit);
176
283
  }
177
- const response = await fetchFn(endpoint, {
178
- method: 'POST',
179
- headers: {
180
- 'Content-Type': 'application/json',
181
- Accept: 'application/json',
182
- ...headers,
183
- },
184
- body: JSON.stringify(body),
185
- signal,
186
- // Include httpOnly cookies (e.g. customerAccessToken auth) on cross-origin
187
- // requests. Required when the storefront runs on a domain different from the
188
- // backend GraphQL endpoint — same-origin browsers send cookies regardless,
189
- // but cross-origin needs `credentials: 'include'` paired with the backend's
190
- // `Access-Control-Allow-Credentials: true` (already configured).
191
- credentials: 'include',
192
- });
193
284
  const json = await response.json();
194
285
  if (debug) {
195
286
  const responseData = {
@@ -3,13 +3,39 @@
3
3
  *
4
4
  * Framework-agnostic — no React, no Zustand, 0 runtime dependencies.
5
5
  */
6
+ /**
7
+ * Structural contract for a typed GraphQL document accepted by `query` / `mutate`.
8
+ *
9
+ * A document carries its result type (`TResult`) and variables type (`TVariables`)
10
+ * at the type level via the phantom `__apiType` brand (never called at runtime) and
11
+ * stringifies to the GraphQL operation text. This is the parameter type the client
12
+ * exposes — deliberately a *structural interface*, not a concrete class, so it is
13
+ * satisfied by:
14
+ * - the SDK's own {@link TypedDocumentString} class, and
15
+ * - the class graphql-codegen's client-preset emits in a consumer project
16
+ * (which declares a `private` field and would otherwise be nominally
17
+ * incompatible with a class-typed parameter — private members only block
18
+ * class-to-class assignment, never class-to-interface).
19
+ *
20
+ * Inference is preserved either way: `client.query(doc, vars)` infers both the
21
+ * result and the variables shape from the document.
22
+ */
23
+ export interface TypedDocumentLike<TResult = unknown, TVariables = unknown> {
24
+ /** Type-level brand carrying result & variable types — never called at runtime. */
25
+ __apiType?: (variables: TVariables) => TResult;
26
+ /** Stringifies to the GraphQL operation text. */
27
+ toString(): string;
28
+ }
6
29
  /**
7
30
  * A branded string carrying result & variable types.
8
31
  * Produced by graphql-codegen client-preset with `documentMode: 'string'`.
9
32
  *
10
33
  * SDK also accepts plain strings — TypedDocumentString is purely for DX.
34
+ *
35
+ * Structurally satisfies {@link TypedDocumentLike}, the type accepted by
36
+ * `query` / `mutate`.
11
37
  */
12
- export declare class TypedDocumentString<TResult = unknown, TVariables = unknown> extends String {
38
+ export declare class TypedDocumentString<TResult = unknown, TVariables = unknown> extends String implements TypedDocumentLike<TResult, TVariables> {
13
39
  __meta__?: {
14
40
  hash: string;
15
41
  } | undefined;
@@ -35,6 +61,12 @@ export interface GraphQLRequest {
35
61
  isMutation: boolean;
36
62
  /** Cache strategy override */
37
63
  cache?: CacheStrategy;
64
+ /**
65
+ * Persisted-document id (`sha256:<hex>`), carried by `TypedDocumentString.__meta__.hash`.
66
+ * When present on a cacheable public read, the transport sends only this id over GET
67
+ * (instead of the full query) so a shared edge cache can serve the response. Absent → POST.
68
+ */
69
+ documentId?: string;
38
70
  }
39
71
  export interface GraphQLResponse<T = unknown> {
40
72
  /** Parsed response data */
@@ -205,15 +237,16 @@ export interface StorefrontClient {
205
237
  /**
206
238
  * Execute a typed GraphQL query.
207
239
  *
208
- * Accepts TypedDocumentString (from codegen) or plain string.
240
+ * Accepts any typed document (the SDK's `TypedDocumentString` or a
241
+ * graphql-codegen client-preset document) or a plain string.
209
242
  */
210
- query<T = unknown, V = Record<string, unknown>>(document: TypedDocumentString<T, V> | string, variables?: V, cache?: CacheStrategy): Promise<T>;
243
+ query<T = unknown, V = Record<string, unknown>>(document: TypedDocumentLike<T, V> | string, variables?: V, cache?: CacheStrategy): Promise<T>;
211
244
  /**
212
245
  * Execute a typed GraphQL mutation.
213
246
  *
214
247
  * Mutations are never cached and never retried by retry middleware.
215
248
  */
216
- mutate<T = unknown, V = Record<string, unknown>>(document: TypedDocumentString<T, V> | string, variables?: V): Promise<T>;
249
+ mutate<T = unknown, V = Record<string, unknown>>(document: TypedDocumentLike<T, V> | string, variables?: V): Promise<T>;
217
250
  /**
218
251
  * Add middleware to the pipeline (imperative API).
219
252
  *
@@ -1 +1 @@
1
- {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/client/types.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/core/client/types.ts"],"names":[],"mappings":"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"}
@@ -3,14 +3,14 @@
3
3
  *
4
4
  * Framework-agnostic — no React, no Zustand, 0 runtime dependencies.
5
5
  */
6
- // ---------------------------------------------------------------------------
7
- // TypedDocumentString (graphql-codegen client-preset pattern)
8
- // ---------------------------------------------------------------------------
9
6
  /**
10
7
  * A branded string carrying result & variable types.
11
8
  * Produced by graphql-codegen client-preset with `documentMode: 'string'`.
12
9
  *
13
10
  * SDK also accepts plain strings — TypedDocumentString is purely for DX.
11
+ *
12
+ * Structurally satisfies {@link TypedDocumentLike}, the type accepted by
13
+ * `query` / `mutate`.
14
14
  */
15
15
  export class TypedDocumentString extends String {
16
16
  __meta__;
@@ -3940,12 +3940,18 @@ export type AvailablePaymentMethodsQuery = {
3940
3940
  instruments?: Maybe<Array<(Pick<PaymentInstrument, 'provider' | 'code' | 'type' | 'displayName' | 'displayHint' | 'enabled'> & {
3941
3941
  brandImage?: Maybe<Pick<Image, 'id' | 'url' | 'altText' | 'width' | 'height' | 'thumbhash'>>;
3942
3942
  })>>;
3943
+ acknowledgements: Array<(Pick<PaymentAcknowledgement, 'code' | 'enforcement' | 'statement'> & {
3944
+ documents: Array<Pick<PaymentAcknowledgementDocument, 'token' | 'kind' | 'url'>>;
3945
+ })>;
3943
3946
  })>;
3944
3947
  defaultMethod?: Maybe<(Pick<PaymentMethod, 'id' | 'name' | 'provider' | 'type' | 'description' | 'isDefault' | 'supportedCurrencies' | 'position' | 'providersAvailable' | 'preferredProvider' | 'available' | 'unavailableReason'> & {
3945
3948
  icon?: Maybe<Pick<Image, 'id' | 'url' | 'altText' | 'width' | 'height' | 'thumbhash'>>;
3946
3949
  instruments?: Maybe<Array<(Pick<PaymentInstrument, 'provider' | 'code' | 'type' | 'displayName' | 'displayHint' | 'enabled'> & {
3947
3950
  brandImage?: Maybe<Pick<Image, 'id' | 'url' | 'altText' | 'width' | 'height' | 'thumbhash'>>;
3948
3951
  })>>;
3952
+ acknowledgements: Array<(Pick<PaymentAcknowledgement, 'code' | 'enforcement' | 'statement'> & {
3953
+ documents: Array<Pick<PaymentAcknowledgementDocument, 'token' | 'kind' | 'url'>>;
3954
+ })>;
3949
3955
  })>;
3950
3956
  };
3951
3957
  };
@@ -5393,12 +5399,18 @@ export type AvailablePaymentMethodsFragment = {
5393
5399
  instruments?: Maybe<Array<(Pick<PaymentInstrument, 'provider' | 'code' | 'type' | 'displayName' | 'displayHint' | 'enabled'> & {
5394
5400
  brandImage?: Maybe<Pick<Image, 'id' | 'url' | 'altText' | 'width' | 'height' | 'thumbhash'>>;
5395
5401
  })>>;
5402
+ acknowledgements: Array<(Pick<PaymentAcknowledgement, 'code' | 'enforcement' | 'statement'> & {
5403
+ documents: Array<Pick<PaymentAcknowledgementDocument, 'token' | 'kind' | 'url'>>;
5404
+ })>;
5396
5405
  })>;
5397
5406
  defaultMethod?: Maybe<(Pick<PaymentMethod, 'id' | 'name' | 'provider' | 'type' | 'description' | 'isDefault' | 'supportedCurrencies' | 'position' | 'providersAvailable' | 'preferredProvider' | 'available' | 'unavailableReason'> & {
5398
5407
  icon?: Maybe<Pick<Image, 'id' | 'url' | 'altText' | 'width' | 'height' | 'thumbhash'>>;
5399
5408
  instruments?: Maybe<Array<(Pick<PaymentInstrument, 'provider' | 'code' | 'type' | 'displayName' | 'displayHint' | 'enabled'> & {
5400
5409
  brandImage?: Maybe<Pick<Image, 'id' | 'url' | 'altText' | 'width' | 'height' | 'thumbhash'>>;
5401
5410
  })>>;
5411
+ acknowledgements: Array<(Pick<PaymentAcknowledgement, 'code' | 'enforcement' | 'statement'> & {
5412
+ documents: Array<Pick<PaymentAcknowledgementDocument, 'token' | 'kind' | 'url'>>;
5413
+ })>;
5402
5414
  })>;
5403
5415
  };
5404
5416
  export type PaymentMethodFragment = (Pick<PaymentMethod, 'id' | 'name' | 'provider' | 'type' | 'description' | 'isDefault' | 'supportedCurrencies' | 'position' | 'providersAvailable' | 'preferredProvider' | 'available' | 'unavailableReason'> & {
@@ -5406,6 +5418,9 @@ export type PaymentMethodFragment = (Pick<PaymentMethod, 'id' | 'name' | 'provid
5406
5418
  instruments?: Maybe<Array<(Pick<PaymentInstrument, 'provider' | 'code' | 'type' | 'displayName' | 'displayHint' | 'enabled'> & {
5407
5419
  brandImage?: Maybe<Pick<Image, 'id' | 'url' | 'altText' | 'width' | 'height' | 'thumbhash'>>;
5408
5420
  })>>;
5421
+ acknowledgements: Array<(Pick<PaymentAcknowledgement, 'code' | 'enforcement' | 'statement'> & {
5422
+ documents: Array<Pick<PaymentAcknowledgementDocument, 'token' | 'kind' | 'url'>>;
5423
+ })>;
5409
5424
  });
5410
5425
  export type PaymentInstrumentFragment = (Pick<PaymentInstrument, 'provider' | 'code' | 'type' | 'displayName' | 'displayHint' | 'enabled'> & {
5411
5426
  brandImage?: Maybe<Pick<Image, 'id' | 'url' | 'altText' | 'width' | 'height' | 'thumbhash'>>;