@doswiftly/storefront-sdk 18.1.0 → 19.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/CHANGELOG.md +129 -0
- package/dist/core/client/create-client.d.ts.map +1 -1
- package/dist/core/client/create-client.js +8 -1
- package/dist/core/client/remote-debug-transport.d.ts +48 -0
- package/dist/core/client/remote-debug-transport.d.ts.map +1 -0
- package/dist/core/client/remote-debug-transport.js +198 -0
- package/dist/core/client/types.d.ts +51 -1
- package/dist/core/client/types.d.ts.map +1 -1
- package/dist/core/index.d.ts +3 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +3 -0
- package/dist/react/cookies.d.ts +16 -23
- package/dist/react/cookies.d.ts.map +1 -1
- package/dist/react/cookies.js +16 -51
- package/dist/react/hooks/use-cart-manager.d.ts +7 -0
- package/dist/react/hooks/use-cart-manager.d.ts.map +1 -1
- package/dist/react/hooks/use-cart-manager.js +5 -2
- package/dist/react/index.d.ts +1 -1
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +3 -2
- package/dist/react/providers/cart-manager-provider.d.ts +8 -1
- package/dist/react/providers/cart-manager-provider.d.ts.map +1 -1
- package/dist/react/providers/cart-manager-provider.js +2 -1
- package/dist/react/providers/storefront-provider.d.ts +12 -1
- package/dist/react/providers/storefront-provider.d.ts.map +1 -1
- package/dist/react/providers/storefront-provider.js +4 -3
- package/dist/react/server/cookie-readers.d.ts +29 -0
- package/dist/react/server/cookie-readers.d.ts.map +1 -0
- package/dist/react/server/cookie-readers.js +52 -0
- package/dist/react/server/index.d.ts +1 -0
- package/dist/react/server/index.d.ts.map +1 -1
- package/dist/react/server/index.js +2 -0
- package/dist/react/stores/currency.store.d.ts +6 -1
- package/dist/react/stores/currency.store.d.ts.map +1 -1
- package/dist/react/stores/currency.store.js +49 -41
- package/dist/react/stores/language.store.d.ts +6 -1
- package/dist/react/stores/language.store.d.ts.map +1 -1
- package/dist/react/stores/language.store.js +46 -38
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,134 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 19.1.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 868894e: Add remote debug shipping for both GraphQL **and** cookie-lifecycle events, so you can trace a real shopper session end-to-end instead of reproducing it with DevTools open.
|
|
8
|
+
|
|
9
|
+
**Why**: the most useful checkout signal is often "when was the `cart-id` / currency / language cookie set or cleared?" (e.g. `cart-id` cleared by `complete()`), but debug output previously covered only GraphQL request/response. Now both flow through one channel keyed by a single `sessionId`, so one filter in your logs gives the full interleaved timeline.
|
|
10
|
+
|
|
11
|
+
**Additive (backward-compatible)** — everything is opt-in and off by default:
|
|
12
|
+
1. `DebugOptions.remote?: boolean | RemoteDebugOptions | RemoteDebugSink` — ship debug events to a backend ingest endpoint. Pass a pre-built transport (a `RemoteDebugSink`) to share **one** channel/`sessionId` across the client and the cookie stores.
|
|
13
|
+
2. New `createRemoteDebugTransport(...)` factory (+ `RemoteDebugTransport` / `RemoteDebugSink` / `RemoteDebugOptions` types).
|
|
14
|
+
3. Cookie stores emit a new `DebugEvent` with `phase: 'cookie'` (`{ name, action: 'set' | 'clear', value?, maxAge? }`) when wired with a sink:
|
|
15
|
+
- `createBrowserCartCookieStore({ onDebug })` — `cart-id` (when you own the cookie store).
|
|
16
|
+
- `useCartManager({ cookieDebug })` / `<CartManagerProvider cookieDebug={...}>` — `cart-id` when the cart manager owns the cookie store.
|
|
17
|
+
- `createCurrencyStore({ onDebug })` / `createLanguageStore(initial, { onDebug })` — currency / language.
|
|
18
|
+
- `<StorefrontProvider cookieDebug={...}>` forwards the sink to the currency + language stores.
|
|
19
|
+
4. `DebugOptions`, `DebugEvent`, `RemoteDebugOptions`, `RemoteDebugSink` are exported for typing.
|
|
20
|
+
|
|
21
|
+
**Usage example** (one shared channel across GraphQL + cookies):
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import {
|
|
25
|
+
createStorefrontClient,
|
|
26
|
+
createRemoteDebugTransport,
|
|
27
|
+
createBrowserCartCookieStore,
|
|
28
|
+
} from "@doswiftly/storefront-sdk";
|
|
29
|
+
|
|
30
|
+
const debugOn = process.env.NEXT_PUBLIC_SDK_DEBUG_REMOTE === "true";
|
|
31
|
+
const transport = debugOn
|
|
32
|
+
? createRemoteDebugTransport({
|
|
33
|
+
endpoint: `${apiUrl}/storefront/debug-logs`,
|
|
34
|
+
shopSlug,
|
|
35
|
+
fetch: globalThis.fetch,
|
|
36
|
+
})
|
|
37
|
+
: null;
|
|
38
|
+
|
|
39
|
+
const client = createStorefrontClient({
|
|
40
|
+
apiUrl,
|
|
41
|
+
shopSlug,
|
|
42
|
+
debug: transport
|
|
43
|
+
? { userErrors: true, response: true, timing: true, remote: transport }
|
|
44
|
+
: false,
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// cart-id set/clear on the same channel:
|
|
48
|
+
const cartCookieStore = createBrowserCartCookieStore(
|
|
49
|
+
transport ? { onDebug: (e) => transport.capture(e) } : undefined,
|
|
50
|
+
);
|
|
51
|
+
|
|
52
|
+
// currency + language via the provider:
|
|
53
|
+
// <StorefrontProvider cookieDebug={transport ? (e) => transport.capture(e) : undefined} … />
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Behaviour:
|
|
57
|
+
- **Batched** (default 10 events / 5s); `keepalive` is used on page-hide/unload flushes so trailing events survive a navigation or payment-gateway redirect.
|
|
58
|
+
- **Fire-and-forget**: a failed send is swallowed — telemetry can never break the request that produced it.
|
|
59
|
+
- Cookies are **not** sent (`credentials: 'omit'`); the shop is identified by the `X-Shop-Slug` header.
|
|
60
|
+
- **Redaction**: the SDK masks `Authorization` / `customerAccessToken` in _headers_. It does **not** redact request `variables` or response bodies — the backend ingest endpoint masks emails + credential-named fields. Enable `response` / `headers` only for operations whose payload you're comfortable sending to your backend.
|
|
61
|
+
- Default endpoint is `${apiUrl}/storefront/debug-logs`; override via `RemoteDebugOptions.endpoint`.
|
|
62
|
+
|
|
63
|
+
**Migration checklist for existing storefronts**:
|
|
64
|
+
- [ ] No action required — all of the above is opt-in and defaults to off.
|
|
65
|
+
- [ ] To adopt: build one transport (gated behind an env flag), pass it to `createStorefrontClient`, the cart cookie store, and `<StorefrontProvider cookieDebug>`, then confirm requests to `/storefront/debug-logs`.
|
|
66
|
+
|
|
67
|
+
## 19.0.0
|
|
68
|
+
|
|
69
|
+
### Major Changes
|
|
70
|
+
|
|
71
|
+
- 7ee241f: Move the server-side cookie readers to the server entry and rename them for intent.
|
|
72
|
+
|
|
73
|
+
The two async cookie readers now live in `@doswiftly/storefront-sdk/react/server` (next to
|
|
74
|
+
`getInitialAuth`) and are renamed to describe what they read instead of how:
|
|
75
|
+
|
|
76
|
+
| Removed (from `@doswiftly/storefront-sdk/react`) | New (in `@doswiftly/storefront-sdk/react/server`) |
|
|
77
|
+
| ------------------------------------------------ | ------------------------------------------------- |
|
|
78
|
+
| `getCurrencyFromCookieAsync()` | `readCurrencyCookie()` |
|
|
79
|
+
| `getCartIdFromCookieAsync()` | `readCartIdCookie()` |
|
|
80
|
+
|
|
81
|
+
Both keep the same return type, `Promise<string | null>`, and read the same cookies
|
|
82
|
+
(`preferred-currency`, `cart-id`). They are still server-first with a `document.cookie` fallback,
|
|
83
|
+
so they remain safe to call from either side — these cookies are readable (not httpOnly).
|
|
84
|
+
|
|
85
|
+
`getCookie`, `setCookie`, `deleteCookie`, and `createBrowserCartCookieStore` are unchanged and
|
|
86
|
+
still exported from `@doswiftly/storefront-sdk/react`.
|
|
87
|
+
|
|
88
|
+
**Why**: the readers depend on `next/headers`, a server-only API, but were exported from the
|
|
89
|
+
client React entry — so the entry's own description ("client-side; for server use `next/headers`")
|
|
90
|
+
contradicted two of its functions. Moving them next to `getInitialAuth` keeps `next/headers` out of
|
|
91
|
+
the client entry and gives both cold-start cookie reads (auth, and now cart-id / currency) one home.
|
|
92
|
+
|
|
93
|
+
**Migration** — update the import path and the names:
|
|
94
|
+
|
|
95
|
+
```ts
|
|
96
|
+
// Before — client React entry
|
|
97
|
+
import {
|
|
98
|
+
getCurrencyFromCookieAsync,
|
|
99
|
+
getCartIdFromCookieAsync,
|
|
100
|
+
} from "@doswiftly/storefront-sdk/react";
|
|
101
|
+
const currency = await getCurrencyFromCookieAsync();
|
|
102
|
+
const cartId = await getCartIdFromCookieAsync();
|
|
103
|
+
|
|
104
|
+
// After — server entry (use in a Server Component)
|
|
105
|
+
import {
|
|
106
|
+
readCurrencyCookie,
|
|
107
|
+
readCartIdCookie,
|
|
108
|
+
} from "@doswiftly/storefront-sdk/react/server";
|
|
109
|
+
const currency = await readCurrencyCookie();
|
|
110
|
+
const cartId = await readCartIdCookie();
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
Reading the cart-id server-side is the supported way to resolve a returning buyer's cart on the
|
|
114
|
+
first render — including when a separate checkout deployment shares the storefront domain:
|
|
115
|
+
|
|
116
|
+
```ts
|
|
117
|
+
// app/checkout/page.tsx (Server Component)
|
|
118
|
+
import { readCartIdCookie } from "@doswiftly/storefront-sdk/react/server";
|
|
119
|
+
|
|
120
|
+
const cartId = await readCartIdCookie();
|
|
121
|
+
const cart = cartId ? await fetchCart(cartId) : null;
|
|
122
|
+
```
|
|
123
|
+
|
|
124
|
+
**Migration checklist**:
|
|
125
|
+
- [ ] Replace `getCurrencyFromCookieAsync` / `getCartIdFromCookieAsync` imports from
|
|
126
|
+
`@doswiftly/storefront-sdk/react` with `readCurrencyCookie` / `readCartIdCookie` from
|
|
127
|
+
`@doswiftly/storefront-sdk/react/server`.
|
|
128
|
+
- [ ] For purely client-side reads, use `getCookie('preferred-currency')` /
|
|
129
|
+
`getCookie('cart-id')` (or `createBrowserCartCookieStore()` for the cart id) from
|
|
130
|
+
`@doswiftly/storefront-sdk/react` instead.
|
|
131
|
+
|
|
3
132
|
## 18.1.0
|
|
4
133
|
|
|
5
134
|
### Minor Changes
|
|
@@ -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;
|
|
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"}
|
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
* 0 runtime dependencies.
|
|
12
12
|
*/
|
|
13
13
|
import { createExecute, resolveDebugOptions } from './execute';
|
|
14
|
+
import { maybeAttachRemoteTransport } from './remote-debug-transport';
|
|
14
15
|
import { compose } from './compose';
|
|
15
16
|
import { dedupe } from './dedupe';
|
|
16
17
|
import { hashQuery } from './hash';
|
|
@@ -24,7 +25,13 @@ export function createStorefrontClient(config) {
|
|
|
24
25
|
// Normalise the public `boolean | 'verbose' | DebugOptions` union into the
|
|
25
26
|
// canonical ResolvedDebugOptions shape (or null = no logging). Env var
|
|
26
27
|
// fallback (`DOSWIFTLY_SDK_DEBUG`) applies only when `debug` is undefined.
|
|
27
|
-
|
|
28
|
+
// When `debug.remote` is set, the resolved sink is wrapped so every event is
|
|
29
|
+
// also shipped to the backend ingest endpoint (default `${apiUrl}/storefront/debug-logs`).
|
|
30
|
+
const resolvedDebug = maybeAttachRemoteTransport(resolveDebugOptions(debug), debug, {
|
|
31
|
+
apiUrl,
|
|
32
|
+
shopSlug,
|
|
33
|
+
fetch: customFetch,
|
|
34
|
+
});
|
|
28
35
|
// Create the innermost execute function (native fetch)
|
|
29
36
|
const innerExecute = createExecute({ endpoint, fetch: customFetch, debug: resolvedDebug });
|
|
30
37
|
/**
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote debug transport — batches debug events and ships them to a backend ingest
|
|
3
|
+
* endpoint over `fetch`. 0 runtime dependencies (only `fetch`, `Date`, `globalThis.crypto`).
|
|
4
|
+
*
|
|
5
|
+
* Fire-and-forget by contract: a failed flush is swallowed so telemetry can never break the
|
|
6
|
+
* storefront request that produced the event. The buffer flushes when it reaches `batchSize`,
|
|
7
|
+
* after `flushIntervalMs`, or when the page is hidden / unloading (browser only) — the last one
|
|
8
|
+
* matters for redirect-heavy flows (e.g. payment gateway hand-off) where trailing events would
|
|
9
|
+
* otherwise be lost. `fetch(..., { keepalive: true })` lets that final send complete during unload.
|
|
10
|
+
*
|
|
11
|
+
* Credentials are never sent (`credentials: 'omit'`) — this is telemetry, not an authenticated
|
|
12
|
+
* call. The shop is identified by the `X-Shop-Slug` header; the backend validates it.
|
|
13
|
+
*/
|
|
14
|
+
import type { DebugEvent, DebugOptions } from './types';
|
|
15
|
+
import type { ResolvedDebugOptions } from './execute';
|
|
16
|
+
export interface RemoteDebugTransportConfig {
|
|
17
|
+
/** Fully-resolved ingest endpoint URL. */
|
|
18
|
+
endpoint: string;
|
|
19
|
+
/** Shop slug sent as `X-Shop-Slug` so the backend can resolve the tenant. */
|
|
20
|
+
shopSlug: string;
|
|
21
|
+
/** Fetch implementation (the same one the client transport uses). */
|
|
22
|
+
fetch: typeof globalThis.fetch;
|
|
23
|
+
batchSize?: number;
|
|
24
|
+
flushIntervalMs?: number;
|
|
25
|
+
sessionId?: string;
|
|
26
|
+
sdkVersion?: string;
|
|
27
|
+
}
|
|
28
|
+
export interface RemoteDebugTransport {
|
|
29
|
+
/** Buffer one debug event; flushes automatically per the batching policy. */
|
|
30
|
+
capture(event: DebugEvent): void;
|
|
31
|
+
}
|
|
32
|
+
export declare function createRemoteDebugTransport(config: RemoteDebugTransportConfig): RemoteDebugTransport;
|
|
33
|
+
/**
|
|
34
|
+
* Build a `phase: 'cookie'` {@link DebugEvent} for a cookie set/clear. Single construction point so
|
|
35
|
+
* the cart / currency / language stores emit an identical shape (`{ name, action, value?, maxAge? }`).
|
|
36
|
+
*/
|
|
37
|
+
export declare function cookieDebugEvent(name: string, action: 'set' | 'clear', value?: string, maxAge?: number): DebugEvent;
|
|
38
|
+
/**
|
|
39
|
+
* If `debug.remote` is enabled, wrap the resolved debug sink so every event is also handed to a
|
|
40
|
+
* remote transport. Returns the input untouched when remote is off (or debug is disabled), so the
|
|
41
|
+
* local `log` behaviour is preserved exactly.
|
|
42
|
+
*/
|
|
43
|
+
export declare function maybeAttachRemoteTransport(resolved: ResolvedDebugOptions | null, debugConfig: boolean | 'verbose' | DebugOptions | undefined, context: {
|
|
44
|
+
apiUrl: string;
|
|
45
|
+
shopSlug: string;
|
|
46
|
+
fetch: typeof globalThis.fetch;
|
|
47
|
+
}): ResolvedDebugOptions | null;
|
|
48
|
+
//# sourceMappingURL=remote-debug-transport.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"remote-debug-transport.d.ts","sourceRoot":"","sources":["../../../src/core/client/remote-debug-transport.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,OAAO,KAAK,EAAE,UAAU,EAAE,YAAY,EAAuC,MAAM,SAAS,CAAC;AAC7F,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,WAAW,CAAC;AAMtD,MAAM,WAAW,0BAA0B;IACzC,0CAA0C;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,6EAA6E;IAC7E,QAAQ,EAAE,MAAM,CAAC;IACjB,qEAAqE;IACrE,KAAK,EAAE,OAAO,UAAU,CAAC,KAAK,CAAC;IAC/B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,oBAAoB;IACnC,6EAA6E;IAC7E,OAAO,CAAC,KAAK,EAAE,UAAU,GAAG,IAAI,CAAC;CAClC;AAuDD,wBAAgB,0BAA0B,CAAC,MAAM,EAAE,0BAA0B,GAAG,oBAAoB,CAmFnG;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,CAC9B,IAAI,EAAE,MAAM,EACZ,MAAM,EAAE,KAAK,GAAG,OAAO,EACvB,KAAK,CAAC,EAAE,MAAM,EACd,MAAM,CAAC,EAAE,MAAM,GACd,UAAU,CAKZ;AAOD;;;;GAIG;AACH,wBAAgB,0BAA0B,CACxC,QAAQ,EAAE,oBAAoB,GAAG,IAAI,EACrC,WAAW,EAAE,OAAO,GAAG,SAAS,GAAG,YAAY,GAAG,SAAS,EAC3D,OAAO,EAAE;IAAE,MAAM,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,OAAO,UAAU,CAAC,KAAK,CAAA;CAAE,GAC5E,oBAAoB,GAAG,IAAI,CA0C7B"}
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Remote debug transport — batches debug events and ships them to a backend ingest
|
|
3
|
+
* endpoint over `fetch`. 0 runtime dependencies (only `fetch`, `Date`, `globalThis.crypto`).
|
|
4
|
+
*
|
|
5
|
+
* Fire-and-forget by contract: a failed flush is swallowed so telemetry can never break the
|
|
6
|
+
* storefront request that produced the event. The buffer flushes when it reaches `batchSize`,
|
|
7
|
+
* after `flushIntervalMs`, or when the page is hidden / unloading (browser only) — the last one
|
|
8
|
+
* matters for redirect-heavy flows (e.g. payment gateway hand-off) where trailing events would
|
|
9
|
+
* otherwise be lost. `fetch(..., { keepalive: true })` lets that final send complete during unload.
|
|
10
|
+
*
|
|
11
|
+
* Credentials are never sent (`credentials: 'omit'`) — this is telemetry, not an authenticated
|
|
12
|
+
* call. The shop is identified by the `X-Shop-Slug` header; the backend validates it.
|
|
13
|
+
*/
|
|
14
|
+
import { CART_COOKIE_NAME } from '../cart/cookie-config';
|
|
15
|
+
const DEFAULT_BATCH_SIZE = 10;
|
|
16
|
+
const DEFAULT_FLUSH_INTERVAL_MS = 5000;
|
|
17
|
+
/**
|
|
18
|
+
* Pull identifying ids out of operation variables so the backend log is filterable by cart /
|
|
19
|
+
* order without parsing the whole payload. `cartId` comes from an explicit `cartId` variable, or
|
|
20
|
+
* from the `id` variable on cart operations (cart mutations pass the cart as `id: cartId`). The
|
|
21
|
+
* operation-name check is anchored (`^cart`) so only cart operations (`CartAddLines`, …) qualify —
|
|
22
|
+
* an unrelated op that merely contains "cart" (e.g. `ProductInCartBadge`) won't mislabel its `id`.
|
|
23
|
+
*/
|
|
24
|
+
function extractIdentifiers(event) {
|
|
25
|
+
// Cookie events carry the value directly — surface the cart-id so its set/clear correlates with
|
|
26
|
+
// the cart's GraphQL operations under one `cartId` filter.
|
|
27
|
+
if (event.phase === 'cookie') {
|
|
28
|
+
const cookie = event.data;
|
|
29
|
+
return cookie.name === CART_COOKIE_NAME && typeof cookie.value === 'string'
|
|
30
|
+
? { cartId: cookie.value }
|
|
31
|
+
: {};
|
|
32
|
+
}
|
|
33
|
+
const variables = event.data.variables;
|
|
34
|
+
if (!variables || typeof variables !== 'object')
|
|
35
|
+
return {};
|
|
36
|
+
const v = variables;
|
|
37
|
+
const op = event.operationName ?? '';
|
|
38
|
+
const out = {};
|
|
39
|
+
const cartId = typeof v.cartId === 'string'
|
|
40
|
+
? v.cartId
|
|
41
|
+
: /^cart/i.test(op) && typeof v.id === 'string'
|
|
42
|
+
? v.id
|
|
43
|
+
: undefined;
|
|
44
|
+
if (cartId)
|
|
45
|
+
out.cartId = cartId;
|
|
46
|
+
if (typeof v.orderId === 'string')
|
|
47
|
+
out.orderId = v.orderId;
|
|
48
|
+
return out;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Generate a session correlation id. Prefers `crypto.randomUUID`; falls back to a
|
|
52
|
+
* timestamp + random suffix when unavailable (uniqueness within one client is all that is
|
|
53
|
+
* required here — there is no security property to preserve).
|
|
54
|
+
*/
|
|
55
|
+
function generateSessionId() {
|
|
56
|
+
const c = globalThis.crypto;
|
|
57
|
+
if (c && typeof c.randomUUID === 'function')
|
|
58
|
+
return c.randomUUID();
|
|
59
|
+
return `sdk-${Date.now().toString(36)}-${Math.random().toString(36).slice(2, 10)}`;
|
|
60
|
+
}
|
|
61
|
+
export function createRemoteDebugTransport(config) {
|
|
62
|
+
const { endpoint, shopSlug, fetch: fetchFn, batchSize = DEFAULT_BATCH_SIZE, flushIntervalMs = DEFAULT_FLUSH_INTERVAL_MS, sdkVersion, } = config;
|
|
63
|
+
const sessionId = config.sessionId ?? generateSessionId();
|
|
64
|
+
let buffer = [];
|
|
65
|
+
let timer = null;
|
|
66
|
+
function clearFlushTimer() {
|
|
67
|
+
if (timer !== null) {
|
|
68
|
+
clearTimeout(timer);
|
|
69
|
+
timer = null;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
function flush(useKeepalive = false) {
|
|
73
|
+
if (buffer.length === 0)
|
|
74
|
+
return;
|
|
75
|
+
const events = buffer;
|
|
76
|
+
buffer = [];
|
|
77
|
+
clearFlushTimer();
|
|
78
|
+
// Fire-and-forget — a transport failure must never surface to the caller.
|
|
79
|
+
void Promise.resolve()
|
|
80
|
+
.then(() => fetchFn(endpoint, {
|
|
81
|
+
method: 'POST',
|
|
82
|
+
headers: {
|
|
83
|
+
'Content-Type': 'application/json',
|
|
84
|
+
'X-Shop-Slug': shopSlug,
|
|
85
|
+
},
|
|
86
|
+
body: JSON.stringify({ sessionId, sdkVersion, events }),
|
|
87
|
+
// `keepalive` only for unload flushes — on a live page a large batch can exceed the
|
|
88
|
+
// browser's keepalive body quota (~64KB) and be rejected (then silently swallowed),
|
|
89
|
+
// whereas an ordinary fetch has no such cap. Unload flushes need it to survive navigation.
|
|
90
|
+
keepalive: useKeepalive,
|
|
91
|
+
credentials: 'omit',
|
|
92
|
+
}))
|
|
93
|
+
.catch(() => undefined);
|
|
94
|
+
}
|
|
95
|
+
// Flush on page hide / unload so trailing events survive a navigation or gateway redirect.
|
|
96
|
+
// Browser-only — guarded so the core stays usable in Node / Edge / Deno. These listeners live for
|
|
97
|
+
// the transport's lifetime (no teardown): the storefront client is expected to be created once per
|
|
98
|
+
// app (the SDK's singleton/Context convention), so they are registered once. Do not build a new
|
|
99
|
+
// client with `debug.remote` on every render — that would accumulate listeners.
|
|
100
|
+
const g = globalThis;
|
|
101
|
+
if (typeof g.addEventListener === 'function') {
|
|
102
|
+
g.addEventListener('pagehide', () => flush(true));
|
|
103
|
+
g.addEventListener('visibilitychange', () => {
|
|
104
|
+
if (g.document?.visibilityState === 'hidden')
|
|
105
|
+
flush(true);
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
return {
|
|
109
|
+
capture(event) {
|
|
110
|
+
buffer.push({
|
|
111
|
+
timestamp: new Date().toISOString(),
|
|
112
|
+
phase: event.phase,
|
|
113
|
+
operationName: event.operationName ?? null,
|
|
114
|
+
...extractIdentifiers(event),
|
|
115
|
+
data: event.data,
|
|
116
|
+
});
|
|
117
|
+
if (buffer.length >= batchSize) {
|
|
118
|
+
flush();
|
|
119
|
+
}
|
|
120
|
+
else if (timer === null) {
|
|
121
|
+
timer = setTimeout(() => flush(), flushIntervalMs);
|
|
122
|
+
// Don't keep a Node/Edge event loop alive for a pending flush (browser timers are numbers
|
|
123
|
+
// with no `unref`; Node/Edge timers are objects that have it).
|
|
124
|
+
if (timer && typeof timer === 'object' && 'unref' in timer) {
|
|
125
|
+
timer.unref();
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
/**
|
|
132
|
+
* Build a `phase: 'cookie'` {@link DebugEvent} for a cookie set/clear. Single construction point so
|
|
133
|
+
* the cart / currency / language stores emit an identical shape (`{ name, action, value?, maxAge? }`).
|
|
134
|
+
*/
|
|
135
|
+
export function cookieDebugEvent(name, action, value, maxAge) {
|
|
136
|
+
const data = { name, action };
|
|
137
|
+
if (value !== undefined)
|
|
138
|
+
data.value = value;
|
|
139
|
+
if (maxAge !== undefined)
|
|
140
|
+
data.maxAge = maxAge;
|
|
141
|
+
return { phase: 'cookie', operationName: undefined, data };
|
|
142
|
+
}
|
|
143
|
+
/** True when `remote` is a pre-built sink (has a callable `capture`) rather than options/boolean. */
|
|
144
|
+
function isRemoteSink(remote) {
|
|
145
|
+
return typeof remote === 'object' && typeof remote.capture === 'function';
|
|
146
|
+
}
|
|
147
|
+
/**
|
|
148
|
+
* If `debug.remote` is enabled, wrap the resolved debug sink so every event is also handed to a
|
|
149
|
+
* remote transport. Returns the input untouched when remote is off (or debug is disabled), so the
|
|
150
|
+
* local `log` behaviour is preserved exactly.
|
|
151
|
+
*/
|
|
152
|
+
export function maybeAttachRemoteTransport(resolved, debugConfig, context) {
|
|
153
|
+
if (!resolved)
|
|
154
|
+
return resolved;
|
|
155
|
+
// `remote` only exists on the object form of `debug` (`DebugOptions`).
|
|
156
|
+
if (typeof debugConfig !== 'object' || !debugConfig.remote) {
|
|
157
|
+
return resolved;
|
|
158
|
+
}
|
|
159
|
+
const remote = debugConfig.remote;
|
|
160
|
+
// A pre-built sink (e.g. a transport shared with the cookie stores) is used as-is, so the whole
|
|
161
|
+
// app reports through one channel + sessionId. Otherwise build a transport from the options.
|
|
162
|
+
let transport;
|
|
163
|
+
if (isRemoteSink(remote)) {
|
|
164
|
+
transport = remote;
|
|
165
|
+
}
|
|
166
|
+
else {
|
|
167
|
+
const remoteOpts = remote === true ? {} : remote;
|
|
168
|
+
transport = createRemoteDebugTransport({
|
|
169
|
+
endpoint: remoteOpts.endpoint ?? `${context.apiUrl.replace(/\/$/, '')}/storefront/debug-logs`,
|
|
170
|
+
shopSlug: context.shopSlug,
|
|
171
|
+
fetch: context.fetch,
|
|
172
|
+
batchSize: remoteOpts.batchSize,
|
|
173
|
+
flushIntervalMs: remoteOpts.flushIntervalMs,
|
|
174
|
+
sessionId: remoteOpts.sessionId,
|
|
175
|
+
sdkVersion: remoteOpts.sdkVersion,
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
const originalLog = resolved.log;
|
|
179
|
+
return {
|
|
180
|
+
...resolved,
|
|
181
|
+
// Telemetry must never break the request: `execute` calls `debug.log` synchronously outside any
|
|
182
|
+
// try/catch, so a throwing local sink or remote `capture` is swallowed here.
|
|
183
|
+
log: (event) => {
|
|
184
|
+
try {
|
|
185
|
+
originalLog(event);
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
/* local sink threw — ignore */
|
|
189
|
+
}
|
|
190
|
+
try {
|
|
191
|
+
transport.capture(event);
|
|
192
|
+
}
|
|
193
|
+
catch {
|
|
194
|
+
/* remote sink threw — ignore */
|
|
195
|
+
}
|
|
196
|
+
},
|
|
197
|
+
};
|
|
198
|
+
}
|
|
@@ -110,14 +110,64 @@ export interface DebugOptions {
|
|
|
110
110
|
userErrors?: boolean;
|
|
111
111
|
/** Custom logger sink. Default `console.log('[StorefrontSDK]', event.phase, event.operationName, event.data)`. Use for routing into `pino` / `winston`. */
|
|
112
112
|
log?: (event: DebugEvent) => void;
|
|
113
|
+
/**
|
|
114
|
+
* Ship debug events to a remote ingest endpoint, in addition to the local `log` sink.
|
|
115
|
+
*
|
|
116
|
+
* Pass `true` to use defaults, or a `RemoteDebugOptions` object to customise the endpoint and
|
|
117
|
+
* batching. Events are buffered and sent with `fetch` (with `keepalive` on page-unload flushes so
|
|
118
|
+
* trailing events survive a navigation); a failed send is swallowed so telemetry can never break
|
|
119
|
+
* the request that produced it. Cookies are not sent (`credentials: 'omit'`). Default `false`.
|
|
120
|
+
*
|
|
121
|
+
* Redaction note: the SDK masks `Authorization` / `customerAccessToken` in *headers*. It does NOT
|
|
122
|
+
* redact the request `variables` or response body it ships — the backend ingest endpoint is the
|
|
123
|
+
* safety net there (it masks emails and credential-named fields). Enable `response` / `headers`
|
|
124
|
+
* only for operations whose payload you are comfortable sending to your backend.
|
|
125
|
+
*
|
|
126
|
+
* Which events are shipped is governed by the other flags above — `remote: true` alone ships the
|
|
127
|
+
* minimal set (request variables + response status + `userErrors`); enable `response` / `headers`
|
|
128
|
+
* / `timing` to ship more.
|
|
129
|
+
*
|
|
130
|
+
* Pass a pre-built transport (a `RemoteDebugSink`, e.g. from `createRemoteDebugTransport`) to
|
|
131
|
+
* share ONE channel + sessionId across the GraphQL client and the cookie stores, so a single
|
|
132
|
+
* `sessionId` in your logs gives the full interleaved timeline (GraphQL + cookie set/clear).
|
|
133
|
+
*/
|
|
134
|
+
remote?: boolean | RemoteDebugOptions | RemoteDebugSink;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Configuration for the remote debug transport (`DebugOptions.remote`). All fields optional.
|
|
138
|
+
*/
|
|
139
|
+
export interface RemoteDebugOptions {
|
|
140
|
+
/** Ingest endpoint URL. Default `${apiUrl}/storefront/debug-logs`. */
|
|
141
|
+
endpoint?: string;
|
|
142
|
+
/** Flush the buffer once this many events accumulate. Default `10`. */
|
|
143
|
+
batchSize?: number;
|
|
144
|
+
/** Flush the buffer after this many milliseconds even if `batchSize` is not reached. Default `5000`. */
|
|
145
|
+
flushIntervalMs?: number;
|
|
146
|
+
/** Correlation id grouping every event from one client instance. Default a generated id. */
|
|
147
|
+
sessionId?: string;
|
|
148
|
+
/** Optional client/build version label attached to every batch. */
|
|
149
|
+
sdkVersion?: string;
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Minimal surface of a debug transport — accepts already-built `DebugEvent`s. A `RemoteDebugSink`
|
|
153
|
+
* created once (via `createRemoteDebugTransport`) and shared between the GraphQL client and the
|
|
154
|
+
* cookie stores forms a single debug channel with one `sessionId`.
|
|
155
|
+
*/
|
|
156
|
+
export interface RemoteDebugSink {
|
|
157
|
+
capture(event: DebugEvent): void;
|
|
113
158
|
}
|
|
114
159
|
/**
|
|
115
160
|
* Structured event handed to a custom `DebugOptions.log` sink. Stable contract:
|
|
116
161
|
* `phase` and `operationName` are always present; `data` carries the merged
|
|
117
162
|
* payload selected by `DebugOptions` flags.
|
|
163
|
+
*
|
|
164
|
+
* `phase: 'cookie'` events come from the SDK cookie layer (cart / currency / language) rather than
|
|
165
|
+
* the GraphQL transport — `operationName` is `undefined` and `data` is
|
|
166
|
+
* `{ name, action: 'set' | 'clear', value?, maxAge? }`. They let you see exactly when a cookie was
|
|
167
|
+
* written or removed (e.g. `cart-id` cleared by `complete()`) alongside the GraphQL timeline.
|
|
118
168
|
*/
|
|
119
169
|
export interface DebugEvent {
|
|
120
|
-
phase: 'request' | 'response';
|
|
170
|
+
phase: 'request' | 'response' | 'cookie';
|
|
121
171
|
operationName: string | undefined;
|
|
122
172
|
data: Record<string, unknown>;
|
|
123
173
|
}
|
|
@@ -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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"}
|
package/dist/core/index.d.ts
CHANGED
|
@@ -34,7 +34,9 @@
|
|
|
34
34
|
* ```
|
|
35
35
|
*/
|
|
36
36
|
export { createStorefrontClient } from './client/create-client';
|
|
37
|
-
export type { StorefrontClient, StorefrontClientConfig, Middleware, ExecuteFn, GraphQLRequest, GraphQLResponse, GraphQLErrorInfo, UserError, CacheStrategy, CacheOptions, TypedDocumentString, } from './client/types';
|
|
37
|
+
export type { StorefrontClient, StorefrontClientConfig, Middleware, ExecuteFn, GraphQLRequest, GraphQLResponse, GraphQLErrorInfo, UserError, CacheStrategy, CacheOptions, TypedDocumentString, DebugOptions, DebugEvent, RemoteDebugOptions, RemoteDebugSink, } from './client/types';
|
|
38
|
+
export { createRemoteDebugTransport } from './client/remote-debug-transport';
|
|
39
|
+
export type { RemoteDebugTransport, RemoteDebugTransportConfig } from './client/remote-debug-transport';
|
|
38
40
|
export { authMiddleware } from './middleware/auth';
|
|
39
41
|
export { currencyMiddleware } from './middleware/currency';
|
|
40
42
|
export { languageMiddleware } from './middleware/language';
|
package/dist/core/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAkCG;AAGH,OAAO,EAAE,sBAAsB,EAAE,MAAM,wBAAwB,CAAC;AAGhE,YAAY,EACV,gBAAgB,EAChB,sBAAsB,EACtB,UAAU,EACV,SAAS,EACT,cAAc,EACd,eAAe,EACf,gBAAgB,EAChB,SAAS,EACT,aAAa,EACb,YAAY,EACZ,mBAAmB,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/core/index.ts"],"names":[],"mappings":"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"}
|
package/dist/core/index.js
CHANGED
|
@@ -35,6 +35,9 @@
|
|
|
35
35
|
*/
|
|
36
36
|
// Client factory
|
|
37
37
|
export { createStorefrontClient } from './client/create-client';
|
|
38
|
+
// Remote debug transport — build one and share it across the client + cookie stores
|
|
39
|
+
// (`debug: { remote: transport }`) for a single debug channel / sessionId.
|
|
40
|
+
export { createRemoteDebugTransport } from './client/remote-debug-transport';
|
|
38
41
|
// Middleware
|
|
39
42
|
export { authMiddleware } from './middleware/auth';
|
|
40
43
|
export { currencyMiddleware } from './middleware/currency';
|
package/dist/react/cookies.d.ts
CHANGED
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
2
|
+
* Client-side cookie utilities for SDK consumers.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Synchronous read/write over `document.cookie`, SSR-safe via a `typeof document`
|
|
5
|
+
* guard (return null / no-op when there is no document). To read these cookies in
|
|
6
|
+
* a Next.js Server Component use the `@doswiftly/storefront-sdk/react/server`
|
|
7
|
+
* entry (`readCartIdCookie`, `readCurrencyCookie`).
|
|
6
8
|
*/
|
|
7
9
|
/**
|
|
8
10
|
* Get cookie value by name (client-side only).
|
|
@@ -24,6 +26,16 @@ export declare function setCookie(name: string, value: string, options?: {
|
|
|
24
26
|
*/
|
|
25
27
|
export declare function deleteCookie(name: string, path?: string): void;
|
|
26
28
|
import type { CartCookieStore } from '../core/cart/cart-recovery';
|
|
29
|
+
import type { DebugEvent } from '../core/client/types';
|
|
30
|
+
export interface BrowserCartCookieStoreOptions {
|
|
31
|
+
/**
|
|
32
|
+
* Optional debug sink. When provided, every `set` / `clear` of the `cart-id` cookie emits a
|
|
33
|
+
* `phase: 'cookie'` {@link DebugEvent}. Wire it to the same transport the GraphQL client uses
|
|
34
|
+
* (one shared sessionId) to see exactly when the cart cookie is written or removed — e.g. the
|
|
35
|
+
* `clear()` that `complete()` performs after checkout.
|
|
36
|
+
*/
|
|
37
|
+
onDebug?: (event: DebugEvent) => void;
|
|
38
|
+
}
|
|
27
39
|
/**
|
|
28
40
|
* Build a browser-side `CartCookieStore` backed by `document.cookie`.
|
|
29
41
|
*
|
|
@@ -43,24 +55,5 @@ import type { CartCookieStore } from '../core/cart/cart-recovery';
|
|
|
43
55
|
* });
|
|
44
56
|
* ```
|
|
45
57
|
*/
|
|
46
|
-
export declare function createBrowserCartCookieStore(): CartCookieStore;
|
|
47
|
-
/**
|
|
48
|
-
* Get preferred currency from cookie (async — works with Next.js cookies()).
|
|
49
|
-
* Falls back to document.cookie on client.
|
|
50
|
-
*/
|
|
51
|
-
export declare function getCurrencyFromCookieAsync(): Promise<string | null>;
|
|
52
|
-
/**
|
|
53
|
-
* Get cart ID from cookie (async — works with Next.js cookies()).
|
|
54
|
-
* Falls back to document.cookie on client.
|
|
55
|
-
*
|
|
56
|
-
* Use in Server Components for SSR cart badge:
|
|
57
|
-
* ```typescript
|
|
58
|
-
* const cartId = await getCartIdFromCookieAsync();
|
|
59
|
-
* if (cartId) {
|
|
60
|
-
* const cart = await fetchCart(cartId);
|
|
61
|
-
* // Render cart badge with real totalQuantity — no skeleton needed
|
|
62
|
-
* }
|
|
63
|
-
* ```
|
|
64
|
-
*/
|
|
65
|
-
export declare function getCartIdFromCookieAsync(): Promise<string | null>;
|
|
58
|
+
export declare function createBrowserCartCookieStore(options?: BrowserCartCookieStoreOptions): CartCookieStore;
|
|
66
59
|
//# sourceMappingURL=cookies.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../src/react/cookies.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"cookies.d.ts","sourceRoot":"","sources":["../../src/react/cookies.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH;;GAEG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAIrD;AAED;;;;GAIG;AACH,wBAAgB,SAAS,CACvB,IAAI,EAAE,MAAM,EACZ,KAAK,EAAE,MAAM,EACb,OAAO,GAAE;IAAE,MAAM,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAAC,MAAM,CAAC,EAAE,OAAO,CAAA;CAAO,GACpF,IAAI,CAON;AAED;;GAEG;AACH,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,EAAE,IAAI,SAAM,GAAG,IAAI,CAG3D;AAGD,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,4BAA4B,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAGvD,MAAM,WAAW,6BAA6B;IAC5C;;;;;OAKG;IACH,OAAO,CAAC,EAAE,CAAC,KAAK,EAAE,UAAU,KAAK,IAAI,CAAC;CACvC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,4BAA4B,CAC1C,OAAO,CAAC,EAAE,6BAA6B,GACtC,eAAe,CAcjB"}
|