@doswiftly/storefront-sdk 22.9.0 → 22.10.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,25 @@
1
1
  # Changelog
2
2
 
3
+ ## 22.10.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 3961ca4: Attach the cart access secret (`x-cart-secret`) only to cart operations.
8
+
9
+ Previously the header was sent on every request whenever a cart cookie was
10
+ present, which kept public catalog reads (products, search, recommendations,
11
+ payment methods) off the shared response cache for any visitor who had a cart.
12
+ The secret is now attached only to cart-scoped operations, so those public
13
+ reads become cacheable again — faster responses, with no change needed in your
14
+ code.
15
+
16
+ A new `isCartScopedOperation` helper is exported, and `cartSecretMiddleware` /
17
+ `serverCartSecretMiddleware` accept an optional classifier as their second
18
+ argument. Cart operations are recognised by a `Cart` operation-name prefix. If
19
+ you issue custom cart queries through the SDK, name them with a `Cart` prefix,
20
+ or pass your own classifier:
21
+ `cartSecretMiddleware(getSecret, (operationName) => ...)`.
22
+
3
23
  ## 22.9.0
4
24
 
5
25
  ### Minor Changes
package/README.md CHANGED
@@ -197,7 +197,7 @@ const client = createStorefrontClient({
197
197
  apiUrl: 'https://api.doswiftly.pl',
198
198
  shopSlug: 'my-shop',
199
199
  middleware: [
200
- cartSecretMiddleware(() => cartSecret), // lazy getter picks up rotation
200
+ cartSecretMiddleware(() => cartSecret), // lazy getter; secret rides cart ops only
201
201
  retryMiddleware({ maxRetries: 2 }),
202
202
  timeoutMiddleware({ timeout: 5000 }),
203
203
  errorMiddleware(), // ALWAYS LAST
@@ -425,17 +425,24 @@ Cart access is authorized by **possession of a secret**, not by the customer
425
425
  session. The `cart-id` cookie stores a composite value `"<cartId>.<secret>"`
426
426
  (30 days, SSR/edge-visible, not httpOnly — the cart carries no payment data).
427
427
  `CartClient.create()` and `recoveryRedeem()` reveal the secret **once**; the SDK
428
- persists it into the cookie for you. Every request then carries the secret in
429
- the `x-cart-secret` header via middleware:
428
+ persists it into the cookie for you. **Cart operations** then carry the secret in
429
+ the `x-cart-secret` header via middleware — public reads (product listings,
430
+ search, recommendations, payment methods) do **not**, so they stay eligible for
431
+ the shared cache even when the visitor has a cart:
430
432
 
431
433
  - **Browser**: `StorefrontProvider` wires `cartSecretMiddleware` automatically —
432
- the secret is read lazily from the cookie on every request, so a rotated
433
- secret is picked up without rebuilding the client.
434
+ the secret is read lazily from the cookie and attached only to cart
435
+ operations, so a rotated secret is picked up without rebuilding the client.
434
436
  - **Server (SSR/edge)**: prepend `serverCartSecretMiddleware(await readCartCredentials())`
435
437
  to your server client — see [Server-side](#server-side-reactserver).
436
438
  - **Custom runtimes**: `cartSecretMiddleware(() => secret)` + the
437
439
  `parseCartCookieValue` / `formatCartCookieValue` helpers.
438
440
 
441
+ Cart operations are recognised by a `Cart` operation-name prefix
442
+ (`isCartScopedOperation`). If you issue custom cart queries, name them with a
443
+ `Cart` prefix, or pass your own classifier as the second argument:
444
+ `cartSecretMiddleware(getSecret, (operationName) => ...)`.
445
+
439
446
  A cookie without the secret half (or a stale capability) makes the cart
440
447
  unreachable — mutations reject with `CART_NOT_FOUND` and the standard recovery
441
448
  flow recreates a fresh cart.
@@ -806,7 +813,7 @@ replay; a 401 on a mutation fires the `session-expired` signal instead.
806
813
  ```typescript
807
814
  import {
808
815
  authMiddleware, // Authorization: Bearer <token> (lazy getter)
809
- cartSecretMiddleware, // x-cart-secret header (lazy getter)
816
+ cartSecretMiddleware, // x-cart-secret header (cart operations only)
810
817
  currencyMiddleware, // X-Preferred-Currency header
811
818
  languageMiddleware, // X-Language header (skipped when null — intentional)
812
819
  botProtectionMiddleware, // challenge token for protected mutations only
@@ -39,7 +39,7 @@ export type { StorefrontClient, StorefrontClientConfig, Middleware, ExecuteFn, G
39
39
  export { createRemoteDebugTransport } from './client/remote-debug-transport';
40
40
  export type { RemoteDebugTransport, RemoteDebugTransportConfig } from './client/remote-debug-transport';
41
41
  export { authMiddleware } from './middleware/auth';
42
- export { cartSecretMiddleware, serverCartSecretMiddleware, CART_SECRET_HEADER, } from './middleware/cart-secret';
42
+ export { cartSecretMiddleware, serverCartSecretMiddleware, isCartScopedOperation, CART_SECRET_HEADER, } from './middleware/cart-secret';
43
43
  export { currencyMiddleware } from './middleware/currency';
44
44
  export { languageMiddleware } from './middleware/language';
45
45
  export { forwardedIpMiddleware, forwardedIpSignedMessage, FORWARDED_IP_HEADER, FORWARDED_IP_TS_HEADER, FORWARDED_IP_SIG_HEADER, type ForwardedIpMiddlewareOptions, } from './middleware/forwarded-ip';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"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"}
@@ -42,7 +42,7 @@ export { isPublicReadEligible, IDENTITY_HEADERS, VARIANCE_AXIS_HEADERS, VARIANCE
42
42
  export { createRemoteDebugTransport } from './client/remote-debug-transport';
43
43
  // Middleware
44
44
  export { authMiddleware } from './middleware/auth';
45
- export { cartSecretMiddleware, serverCartSecretMiddleware, CART_SECRET_HEADER, } from './middleware/cart-secret';
45
+ export { cartSecretMiddleware, serverCartSecretMiddleware, isCartScopedOperation, CART_SECRET_HEADER, } from './middleware/cart-secret';
46
46
  export { currencyMiddleware } from './middleware/currency';
47
47
  export { languageMiddleware } from './middleware/language';
48
48
  export { forwardedIpMiddleware, forwardedIpSignedMessage, FORWARDED_IP_HEADER, FORWARDED_IP_TS_HEADER, FORWARDED_IP_SIG_HEADER, } from './middleware/forwarded-ip';
@@ -3,6 +3,13 @@
3
3
  * cart access secret. Possession of the secret is what authorizes cart
4
4
  * operations; the cart id alone is not enough.
5
5
  *
6
+ * The header is attached **only to cart operations** (classified by
7
+ * {@link isCartScopedOperation} — operation names with a `Cart` prefix). Public
8
+ * reads such as product listings, search and recommendations never carry the
9
+ * secret, so they stay eligible for the shared response cache even when the
10
+ * visitor already has a cart. Pass a custom classifier as the second argument
11
+ * to override which operations are treated as cart-scoped.
12
+ *
6
13
  * Two variants share one header contract:
7
14
  *
8
15
  * - **Client** (`cartSecretMiddleware`) resolves the secret lazily on every
@@ -27,17 +34,38 @@ import type { CartCredentials } from '../cart/cookie-config';
27
34
  /** Request header carrying the cart access secret. */
28
35
  export declare const CART_SECRET_HEADER = "x-cart-secret";
29
36
  /**
30
- * Client cart-secret middleware. `getSecret` is read on every request so a
31
- * rotated secret is picked up without rebuilding the pipeline. No header is
32
- * sent when the secret is absent (legacy plain-id cookie) the backend then
33
- * treats the cart as unreachable and the SDK recreates one.
37
+ * Default classifier deciding whether an operation should carry the cart access
38
+ * secret. Cart operations are named with a `Cart` prefix (`Cart`, `CartAddLines`,
39
+ * `CartComplete`, `CartAvailableShippingMethods`, ). Three operations are
40
+ * deliberately excluded because none of them is bound to a specific cart, so the
41
+ * secret would be meaningless on them:
42
+ * - `AvailablePaymentMethods` — a shop-level read (cacheable);
43
+ * - `OrderByToken` — a guest order lookup authorized by an opaque order token
44
+ * (not cacheable — served `no-store`);
45
+ * - `PaymentCreate` — a mutation operating on an order.
46
+ *
47
+ * Withholding the secret keeps the cacheable shop-level read in the shared cache
48
+ * (carrying the secret would mark it identity-bearing and push it off); for the
49
+ * order operations it simply avoids sending a credential they never read.
50
+ */
51
+ export declare function isCartScopedOperation(operationName: string | undefined): boolean;
52
+ /**
53
+ * Client cart-secret middleware. The header is sent only for cart-scoped
54
+ * operations (`isCartScoped`, default {@link isCartScopedOperation}); public
55
+ * reads stay cacheable and never touch the cookie. For a cart operation,
56
+ * `getSecret` is read fresh on each request so a rotated secret (recovery
57
+ * redeem) is picked up without rebuilding the pipeline. No header is sent when
58
+ * the secret is absent (legacy plain-id cookie) — the backend then treats the
59
+ * cart as unreachable and the SDK recreates one.
34
60
  */
35
- export declare function cartSecretMiddleware(getSecret: () => string | null | undefined): Middleware;
61
+ export declare function cartSecretMiddleware(getSecret: () => string | null | undefined, isCartScoped?: (operationName: string | undefined) => boolean): Middleware;
36
62
  /**
37
63
  * Server cart-secret middleware. Takes the credentials already read from the
38
64
  * request cookies (`readCartCredentials`) — prepend it to a server client so
39
- * SSR / edge cart reads carry the secret. No header is sent when credentials
40
- * are null or carry no secret.
65
+ * SSR / edge cart reads carry the secret. As with the client variant, the
66
+ * header is sent only for cart-scoped operations (`isCartScoped`, default
67
+ * {@link isCartScopedOperation}). No header is sent when credentials are null or
68
+ * carry no secret.
41
69
  */
42
- export declare function serverCartSecretMiddleware(credentials: CartCredentials | null): Middleware;
70
+ export declare function serverCartSecretMiddleware(credentials: CartCredentials | null, isCartScoped?: (operationName: string | undefined) => boolean): Middleware;
43
71
  //# sourceMappingURL=cart-secret.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"cart-secret.d.ts","sourceRoot":"","sources":["../../../src/core/middleware/cart-secret.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;GAuBG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,sDAAsD;AACtD,eAAO,MAAM,kBAAkB,kBAAkB,CAAC;AAElD;;;;;GAKG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,GACzC,UAAU,CAQZ;AAED;;;;;GAKG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,eAAe,GAAG,IAAI,GAClC,UAAU,CAOZ"}
1
+ {"version":3,"file":"cart-secret.d.ts","sourceRoot":"","sources":["../../../src/core/middleware/cart-secret.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA8BG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAClD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AAE7D,sDAAsD;AACtD,eAAO,MAAM,kBAAkB,kBAAkB,CAAC;AAElD;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,qBAAqB,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAEhF;AAED;;;;;;;;GAQG;AACH,wBAAgB,oBAAoB,CAClC,SAAS,EAAE,MAAM,MAAM,GAAG,IAAI,GAAG,SAAS,EAC1C,YAAY,GAAE,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,KAAK,OAA+B,GACnF,UAAU,CAYZ;AAED;;;;;;;GAOG;AACH,wBAAgB,0BAA0B,CACxC,WAAW,EAAE,eAAe,GAAG,IAAI,EACnC,YAAY,GAAE,CAAC,aAAa,EAAE,MAAM,GAAG,SAAS,KAAK,OAA+B,GACnF,UAAU,CAOZ"}
@@ -3,6 +3,13 @@
3
3
  * cart access secret. Possession of the secret is what authorizes cart
4
4
  * operations; the cart id alone is not enough.
5
5
  *
6
+ * The header is attached **only to cart operations** (classified by
7
+ * {@link isCartScopedOperation} — operation names with a `Cart` prefix). Public
8
+ * reads such as product listings, search and recommendations never carry the
9
+ * secret, so they stay eligible for the shared response cache even when the
10
+ * visitor already has a cart. Pass a custom classifier as the second argument
11
+ * to override which operations are treated as cart-scoped.
12
+ *
6
13
  * Two variants share one header contract:
7
14
  *
8
15
  * - **Client** (`cartSecretMiddleware`) resolves the secret lazily on every
@@ -25,16 +32,41 @@
25
32
  /** Request header carrying the cart access secret. */
26
33
  export const CART_SECRET_HEADER = 'x-cart-secret';
27
34
  /**
28
- * Client cart-secret middleware. `getSecret` is read on every request so a
29
- * rotated secret is picked up without rebuilding the pipeline. No header is
30
- * sent when the secret is absent (legacy plain-id cookie) the backend then
31
- * treats the cart as unreachable and the SDK recreates one.
35
+ * Default classifier deciding whether an operation should carry the cart access
36
+ * secret. Cart operations are named with a `Cart` prefix (`Cart`, `CartAddLines`,
37
+ * `CartComplete`, `CartAvailableShippingMethods`, ). Three operations are
38
+ * deliberately excluded because none of them is bound to a specific cart, so the
39
+ * secret would be meaningless on them:
40
+ * - `AvailablePaymentMethods` — a shop-level read (cacheable);
41
+ * - `OrderByToken` — a guest order lookup authorized by an opaque order token
42
+ * (not cacheable — served `no-store`);
43
+ * - `PaymentCreate` — a mutation operating on an order.
44
+ *
45
+ * Withholding the secret keeps the cacheable shop-level read in the shared cache
46
+ * (carrying the secret would mark it identity-bearing and push it off); for the
47
+ * order operations it simply avoids sending a credential they never read.
48
+ */
49
+ export function isCartScopedOperation(operationName) {
50
+ return typeof operationName === 'string' && operationName.startsWith('Cart');
51
+ }
52
+ /**
53
+ * Client cart-secret middleware. The header is sent only for cart-scoped
54
+ * operations (`isCartScoped`, default {@link isCartScopedOperation}); public
55
+ * reads stay cacheable and never touch the cookie. For a cart operation,
56
+ * `getSecret` is read fresh on each request so a rotated secret (recovery
57
+ * redeem) is picked up without rebuilding the pipeline. No header is sent when
58
+ * the secret is absent (legacy plain-id cookie) — the backend then treats the
59
+ * cart as unreachable and the SDK recreates one.
32
60
  */
33
- export function cartSecretMiddleware(getSecret) {
61
+ export function cartSecretMiddleware(getSecret, isCartScoped = isCartScopedOperation) {
34
62
  return (request, next) => {
35
- const secret = getSecret();
36
- if (secret) {
37
- request.headers[CART_SECRET_HEADER] = secret;
63
+ // Gate on the cheap operation-name check first so public reads (the
64
+ // high-volume path) skip the secret getter (a cookie read) entirely.
65
+ if (isCartScoped(request.operationName)) {
66
+ const secret = getSecret();
67
+ if (secret) {
68
+ request.headers[CART_SECRET_HEADER] = secret;
69
+ }
38
70
  }
39
71
  return next(request);
40
72
  };
@@ -42,12 +74,14 @@ export function cartSecretMiddleware(getSecret) {
42
74
  /**
43
75
  * Server cart-secret middleware. Takes the credentials already read from the
44
76
  * request cookies (`readCartCredentials`) — prepend it to a server client so
45
- * SSR / edge cart reads carry the secret. No header is sent when credentials
46
- * are null or carry no secret.
77
+ * SSR / edge cart reads carry the secret. As with the client variant, the
78
+ * header is sent only for cart-scoped operations (`isCartScoped`, default
79
+ * {@link isCartScopedOperation}). No header is sent when credentials are null or
80
+ * carry no secret.
47
81
  */
48
- export function serverCartSecretMiddleware(credentials) {
82
+ export function serverCartSecretMiddleware(credentials, isCartScoped = isCartScopedOperation) {
49
83
  return (request, next) => {
50
- if (credentials?.cartSecret) {
84
+ if (credentials?.cartSecret && isCartScoped(request.operationName)) {
51
85
  request.headers[CART_SECRET_HEADER] = credentials.cartSecret;
52
86
  }
53
87
  return next(request);
@@ -1 +1 @@
1
- {"version":3,"file":"storefront-client-provider.d.ts","sourceRoot":"","sources":["../../../src/react/providers/storefront-client-provider.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAA6C,MAAM,OAAO,CAAC;AAElE,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAOzD,OAAO,EAA2B,KAAK,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AAKhH,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAEpG,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAE5E,MAAM,WAAW,4BAA4B;IAC3C,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,EAAE,UAAU,CAAC;IACvB,UAAU,EAAE,UAAU,CAAC;CACxB;AAID,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,EAAE,sBAAsB,CAAC;IAC/B;;;OAGG;IACH,UAAU,CAAC,EAAE,UAAU,EAAE,CAAC;IAC1B,oEAAoE;IACpE,aAAa,CAAC,EAAE,0BAA0B,GAAG,IAAI,CAAC;IAClD,+DAA+D;IAC/D,uBAAuB,CAAC,EAAE,MAAM,EAAE,CAAC;IACnC;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,qBAAqB,CAAC;IAC9C,4HAA4H;IAC5H,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,wBAAwB,CAAC,EACvC,QAAQ,EACR,MAAM,EACN,UAAU,EAAE,gBAAqB,EACjC,aAAa,EACb,uBAAuB,EACvB,qBAAqB,EACrB,YAAY,GACb,EAAE,6BAA6B,2CA8E/B;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,IAAI,4BAA4B,CAMzE"}
1
+ {"version":3,"file":"storefront-client-provider.d.ts","sourceRoot":"","sources":["../../../src/react/providers/storefront-client-provider.tsx"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAIH,OAAO,KAA6C,MAAM,OAAO,CAAC;AAElE,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AACzD,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAC;AAOzD,OAAO,EAA2B,KAAK,0BAA0B,EAAE,MAAM,sCAAsC,CAAC;AAKhH,OAAO,KAAK,EAAE,gBAAgB,EAAE,sBAAsB,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAEpG,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,gCAAgC,CAAC;AAE5E,MAAM,WAAW,4BAA4B;IAC3C,MAAM,EAAE,gBAAgB,CAAC;IACzB,UAAU,EAAE,UAAU,CAAC;IACvB,UAAU,EAAE,UAAU,CAAC;CACxB;AAID,MAAM,WAAW,6BAA6B;IAC5C,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B,MAAM,EAAE,sBAAsB,CAAC;IAC/B;;;OAGG;IACH,UAAU,CAAC,EAAE,UAAU,EAAE,CAAC;IAC1B,oEAAoE;IACpE,aAAa,CAAC,EAAE,0BAA0B,GAAG,IAAI,CAAC;IAClD,+DAA+D;IAC/D,uBAAuB,CAAC,EAAE,MAAM,EAAE,CAAC;IACnC;;;;OAIG;IACH,qBAAqB,CAAC,EAAE,qBAAqB,CAAC;IAC9C,4HAA4H;IAC5H,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB;AAED,wBAAgB,wBAAwB,CAAC,EACvC,QAAQ,EACR,MAAM,EACN,UAAU,EAAE,gBAAqB,EACjC,aAAa,EACb,uBAAuB,EACvB,qBAAqB,EACrB,YAAY,GACb,EAAE,6BAA6B,2CAiF/B;AAED;;;GAGG;AACH,wBAAgB,0BAA0B,IAAI,4BAA4B,CAMzE"}
@@ -13,7 +13,7 @@ import { createStorefrontClient } from '../../core/client/create-client';
13
13
  import { CartClient } from '../../core/cart/cart-client';
14
14
  import { AuthClient } from '../../core/auth/auth-client';
15
15
  import { authMiddleware } from '../../core/middleware/auth';
16
- import { cartSecretMiddleware } from '../../core/middleware/cart-secret';
16
+ import { cartSecretMiddleware, isCartScopedOperation } from '../../core/middleware/cart-secret';
17
17
  import { CART_COOKIE_NAME, parseCartCookieValue } from '../../core/cart/cookie-config';
18
18
  import { getCookie } from '../cookies';
19
19
  import { currencyMiddleware } from '../../core/middleware/currency';
@@ -67,7 +67,9 @@ export function StorefrontClientProvider({ children, config, middleware: customM
67
67
  authMiddleware(() => authStore.getState().accessToken),
68
68
  // Cart access secret — read lazily from the composite cart-id cookie so
69
69
  // a rotated secret (recovery redeem) is picked up without rebuilding.
70
- cartSecretMiddleware(() => parseCartCookieValue(getCookie(CART_COOKIE_NAME))?.cartSecret ?? null),
70
+ // Attached only to cart operations (passed explicitly for clarity; it is
71
+ // the default) so public reads stay cacheable when a cart exists.
72
+ cartSecretMiddleware(() => parseCartCookieValue(getCookie(CART_COOKIE_NAME))?.cartSecret ?? null, isCartScopedOperation),
71
73
  currencyMiddleware(() => currencyStore.getState().currency),
72
74
  languageMiddleware(() => languageStore.getState().language),
73
75
  // Bot protection (if configured)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@doswiftly/storefront-sdk",
3
- "version": "22.9.0",
3
+ "version": "22.10.0",
4
4
  "description": "Storefront runtime SDK for DoSwiftly Commerce — layered transport, middleware pipeline, React providers, Zustand stores, cache strategies. 0 runtime dependencies in core.",
5
5
  "type": "module",
6
6
  "sideEffects": false,