@asstio-dev/storefront 0.19.0 → 0.21.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/README.md +22 -0
- package/dist/analytics/events.d.ts +20 -0
- package/dist/analytics/events.js +23 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/dist/schema.d.ts +165 -2
- package/package.json +2 -2
- package/src/schema.d.ts +165 -2
package/README.md
CHANGED
|
@@ -243,6 +243,28 @@ config. Holes in the data never throw: a missing image, description or price onl
|
|
|
243
243
|
|
|
244
244
|
hreflang sets are only as reciprocal as the API's `alternateUrls`; the SDK renders what the API gives.
|
|
245
245
|
|
|
246
|
+
### Search events
|
|
247
|
+
|
|
248
|
+
`trackSearchEvents(client, events, { consent })` posts search exposures, product clicks and add-to-carts to
|
|
249
|
+
`POST /v1/events`, for measuring search quality. It sends **nothing** unless `consent === true`: the storefront stores
|
|
250
|
+
no PII and cannot verify consent, so the frontend decides, and passes `sessionId` (opaque, frontend-generated) only
|
|
251
|
+
under the same consent.
|
|
252
|
+
|
|
253
|
+
```ts
|
|
254
|
+
const exposure = { type: "search_exposure", occurredAt: new Date().toISOString(), resultId: listing.search!.resultId, query, page: 1, productIds };
|
|
255
|
+
await trackSearchEvents(client, [exposure], { consent: shopperAcceptedMeasurement });
|
|
256
|
+
await trackSearchEvents(client, [{ type: "product_click", occurredAt: new Date().toISOString(), exposureId: exposure.eventId, productId, position: 3 }], { consent });
|
|
257
|
+
```
|
|
258
|
+
|
|
259
|
+
- An event without `eventId` gets a UUID **written into the object**. Retry the same objects after an uncertain
|
|
260
|
+
answer (a timeout, a dropped connection) and the ids are reused, so the server counts the repeats as `duplicates`.
|
|
261
|
+
A second click from the same exposure is a new event: build a new object.
|
|
262
|
+
- `occurredAt` is UTC ISO-8601 (`new Date().toISOString()`), within 7 days back and 5 minutes ahead.
|
|
263
|
+
- At most 50 events per call (a larger batch throws `RangeError`), 64 KB per body. The answer lists `rejected`
|
|
264
|
+
events with their `index` and `member`; the others are kept. Any non-2xx answer throws: a malformed envelope (a
|
|
265
|
+
`400` problem) and a bare `5xx` alike, so a failed send is never mistaken for "no consent" (`null`).
|
|
266
|
+
- Exposures come only from the client; a catalog GET is never one.
|
|
267
|
+
|
|
246
268
|
### Product facts and GA4
|
|
247
269
|
|
|
248
270
|
Cards and cart lines carry the product facts the product page has: `brand`, `categories`, `shortDescription` and
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { Client } from "openapi-fetch";
|
|
2
|
+
import type { components } from "../schema";
|
|
3
|
+
import type { StorefrontPaths } from "../index.js";
|
|
4
|
+
type Schemas = components["schemas"];
|
|
5
|
+
/** One search event. `eventId` may be left out: `trackSearchEvents` writes a UUID into the object. */
|
|
6
|
+
export type SearchEvent = Omit<Schemas["SearchEventDto"], "eventId"> & {
|
|
7
|
+
eventId?: string | null;
|
|
8
|
+
};
|
|
9
|
+
export type SearchEventsResponse = Schemas["SearchEventsResponse"];
|
|
10
|
+
/**
|
|
11
|
+
* `POST /v1/events` (spec 2026-10-13 §9.2). A no-op unless `consent` is exactly `true`: the storefront cannot verify
|
|
12
|
+
* consent, so the caller decides (and sends `sessionId` only under the same consent). An event without `eventId` gets
|
|
13
|
+
* a UUID written into the object, so retrying the same objects after an uncertain response reuses the ids and the
|
|
14
|
+
* server counts them as duplicates. Throws a `RangeError` for more than 50 events (split the batch) and an `Error` on any
|
|
15
|
+
* non-2xx answer (a problem body or none). Build `occurredAt` with `new Date().toISOString()`: the server wants UTC.
|
|
16
|
+
*/
|
|
17
|
+
export declare function trackSearchEvents(client: Client<StorefrontPaths>, events: SearchEvent[], options: {
|
|
18
|
+
consent: boolean;
|
|
19
|
+
}): Promise<SearchEventsResponse | null>;
|
|
20
|
+
export {};
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** The server's per-envelope cap (spec 2026-10-13 §9.2); a larger batch is a `400` that stores nothing. */
|
|
2
|
+
const MAX_EVENTS_PER_REQUEST = 50;
|
|
3
|
+
/**
|
|
4
|
+
* `POST /v1/events` (spec 2026-10-13 §9.2). A no-op unless `consent` is exactly `true`: the storefront cannot verify
|
|
5
|
+
* consent, so the caller decides (and sends `sessionId` only under the same consent). An event without `eventId` gets
|
|
6
|
+
* a UUID written into the object, so retrying the same objects after an uncertain response reuses the ids and the
|
|
7
|
+
* server counts them as duplicates. Throws a `RangeError` for more than 50 events (split the batch) and an `Error` on any
|
|
8
|
+
* non-2xx answer (a problem body or none). Build `occurredAt` with `new Date().toISOString()`: the server wants UTC.
|
|
9
|
+
*/
|
|
10
|
+
export async function trackSearchEvents(client, events, options) {
|
|
11
|
+
if (options?.consent !== true || events.length === 0)
|
|
12
|
+
return null;
|
|
13
|
+
if (events.length > MAX_EVENTS_PER_REQUEST)
|
|
14
|
+
throw new RangeError(`trackSearchEvents takes at most ${MAX_EVENTS_PER_REQUEST} events per call, got ${events.length}`);
|
|
15
|
+
for (const e of events)
|
|
16
|
+
if (!e.eventId)
|
|
17
|
+
e.eventId = crypto.randomUUID();
|
|
18
|
+
const { data, error, response } = await client.POST("/v1/events", { body: { events: events } });
|
|
19
|
+
// `error` is undefined for an empty-body 5xx (openapi-fetch parses nothing), so the status decides: a non-2xx always throws.
|
|
20
|
+
if (!response.ok)
|
|
21
|
+
throw new Error(`trackSearchEvents failed: ${response.status} ${error === undefined ? "" : JSON.stringify(error)}`.trimEnd());
|
|
22
|
+
return data ?? null;
|
|
23
|
+
}
|
package/dist/index.d.ts
CHANGED
|
@@ -58,5 +58,6 @@ export { productJsonLd } from "./seo/product.js";
|
|
|
58
58
|
export { jsonLdScript } from "./seo/serialize.js";
|
|
59
59
|
export { productFacts } from "./analytics/facts.js";
|
|
60
60
|
export { toGa4Ecommerce, toGa4Item } from "./analytics/ga4.js";
|
|
61
|
+
export { trackSearchEvents, type SearchEvent, type SearchEventsResponse } from "./analytics/events.js";
|
|
61
62
|
export { AnalyticsError, type Ga4Ecommerce, type Ga4Item, type Ga4ItemOptions, type Ga4ItemSource, type Ga4Purchase, type Ga4SharedOptions, type ProductFacts, type SelectedVariant, } from "./analytics/types.js";
|
|
62
63
|
export { SeoError, type BreadcrumbLink, type CollectionHeadOptions, type HeadOptions, type JsonLd, type NextMetadataLike, type ProductJsonLdOptions, type SchemaProperty, type SeoContext, type SeoHead, type SeoMeta, type VariantAvailability, } from "./seo/types.js";
|
package/dist/index.js
CHANGED
|
@@ -47,5 +47,6 @@ export { productJsonLd } from "./seo/product.js";
|
|
|
47
47
|
export { jsonLdScript } from "./seo/serialize.js";
|
|
48
48
|
export { productFacts } from "./analytics/facts.js";
|
|
49
49
|
export { toGa4Ecommerce, toGa4Item } from "./analytics/ga4.js";
|
|
50
|
+
export { trackSearchEvents } from "./analytics/events.js";
|
|
50
51
|
export { AnalyticsError, } from "./analytics/types.js";
|
|
51
52
|
export { SeoError, } from "./seo/types.js";
|
package/dist/schema.d.ts
CHANGED
|
@@ -249,6 +249,23 @@ export interface paths {
|
|
|
249
249
|
patch?: never;
|
|
250
250
|
trace?: never;
|
|
251
251
|
};
|
|
252
|
+
"/v1/events": {
|
|
253
|
+
parameters: {
|
|
254
|
+
query?: never;
|
|
255
|
+
header?: never;
|
|
256
|
+
path?: never;
|
|
257
|
+
cookie?: never;
|
|
258
|
+
};
|
|
259
|
+
get?: never;
|
|
260
|
+
put?: never;
|
|
261
|
+
/** Search exposures, clicks and add-to-carts for measurement (spec 2026-10-13 §9.2). Send only with the shopper's consent. Each event is validated on its own: a bad one is listed in rejected and the others are kept; an eventId the site already has is a duplicate and changes nothing. */
|
|
262
|
+
post: operations["TrackSearchEvents"];
|
|
263
|
+
delete?: never;
|
|
264
|
+
options?: never;
|
|
265
|
+
head?: never;
|
|
266
|
+
patch?: never;
|
|
267
|
+
trace?: never;
|
|
268
|
+
};
|
|
252
269
|
"/v1/orders/{orderId}": {
|
|
253
270
|
parameters: {
|
|
254
271
|
query?: never;
|
|
@@ -422,6 +439,8 @@ export interface components {
|
|
|
422
439
|
AppliedFilterDto: {
|
|
423
440
|
facet: string;
|
|
424
441
|
label: string;
|
|
442
|
+
/** @default filter */
|
|
443
|
+
source: string;
|
|
425
444
|
value: string;
|
|
426
445
|
};
|
|
427
446
|
AttributeDto: {
|
|
@@ -752,6 +771,7 @@ export interface components {
|
|
|
752
771
|
pageSize: number | string;
|
|
753
772
|
queryCorrection: null | string;
|
|
754
773
|
redirect: null | string;
|
|
774
|
+
search?: null | components["schemas"]["SearchInfoDto"];
|
|
755
775
|
sort: components["schemas"]["SortDto"];
|
|
756
776
|
/** Format: int64 */
|
|
757
777
|
total: number | string;
|
|
@@ -1020,6 +1040,60 @@ export interface components {
|
|
|
1020
1040
|
importer: null | components["schemas"]["PartyDto"];
|
|
1021
1041
|
manufacturer: null | components["schemas"]["PartyDto"];
|
|
1022
1042
|
};
|
|
1043
|
+
SearchEventDto: {
|
|
1044
|
+
eventId: null | string;
|
|
1045
|
+
exposureId?: null | string;
|
|
1046
|
+
/** Format: date-time */
|
|
1047
|
+
occurredAt: null | string;
|
|
1048
|
+
/** Format: int32 */
|
|
1049
|
+
page?: number | null;
|
|
1050
|
+
/** Format: int32 */
|
|
1051
|
+
position?: number | null;
|
|
1052
|
+
productId?: null | string;
|
|
1053
|
+
productIds?: string[] | null;
|
|
1054
|
+
/** Format: int32 */
|
|
1055
|
+
quantity?: number | null;
|
|
1056
|
+
query?: null | string;
|
|
1057
|
+
resultId?: null | string;
|
|
1058
|
+
sessionId?: null | string;
|
|
1059
|
+
type: null | string;
|
|
1060
|
+
variantId?: null | string;
|
|
1061
|
+
};
|
|
1062
|
+
SearchEventRejection: {
|
|
1063
|
+
code: string;
|
|
1064
|
+
/** Format: int32 */
|
|
1065
|
+
index: number | string;
|
|
1066
|
+
member: null | string;
|
|
1067
|
+
};
|
|
1068
|
+
SearchEventsRequest: {
|
|
1069
|
+
events: components["schemas"]["SearchEventDto"][] | null;
|
|
1070
|
+
};
|
|
1071
|
+
SearchEventsResponse: {
|
|
1072
|
+
/** Format: int32 */
|
|
1073
|
+
accepted: number | string;
|
|
1074
|
+
/** Format: int32 */
|
|
1075
|
+
duplicates: number | string;
|
|
1076
|
+
rejected: components["schemas"]["SearchEventRejection"][];
|
|
1077
|
+
/** Format: int32 */
|
|
1078
|
+
stored: number | string;
|
|
1079
|
+
};
|
|
1080
|
+
SearchInfoDto: {
|
|
1081
|
+
codeMatch: boolean;
|
|
1082
|
+
intents: components["schemas"]["SearchIntentDto"][];
|
|
1083
|
+
rescue: null | components["schemas"]["SearchRescueDto"];
|
|
1084
|
+
resultId: string;
|
|
1085
|
+
tier: string;
|
|
1086
|
+
};
|
|
1087
|
+
SearchIntentDto: {
|
|
1088
|
+
facet: string;
|
|
1089
|
+
queryWithout: string;
|
|
1090
|
+
values: string[];
|
|
1091
|
+
};
|
|
1092
|
+
SearchRescueDto: {
|
|
1093
|
+
collectionId: string;
|
|
1094
|
+
name: string;
|
|
1095
|
+
slug: string;
|
|
1096
|
+
};
|
|
1023
1097
|
SellabilityDto: {
|
|
1024
1098
|
mode: components["schemas"]["SiteSellabilityMode"];
|
|
1025
1099
|
};
|
|
@@ -2612,7 +2686,7 @@ export interface operations {
|
|
|
2612
2686
|
GetCollection: {
|
|
2613
2687
|
parameters: {
|
|
2614
2688
|
query?: {
|
|
2615
|
-
/** @description Free-text search. Omit to browse rather than search. */
|
|
2689
|
+
/** @description Free-text search. Omit to browse rather than search; a q of separators only (such as !!!) browses too. A search answers with search (how it was answered), and may set queryCorrection and redirect. */
|
|
2616
2690
|
q?: string;
|
|
2617
2691
|
/** @description 1-based page. page × pageSize must not exceed 10000 (default pageSize 24, max 100). */
|
|
2618
2692
|
page?: number;
|
|
@@ -2624,6 +2698,8 @@ export interface operations {
|
|
|
2624
2698
|
filter?: {
|
|
2625
2699
|
[key: string]: string;
|
|
2626
2700
|
};
|
|
2701
|
+
/** @description Where a search came from, for measurement only: typed, suggestion, recent or other. It never changes the answer. Anything else is ignored with an ORIGIN_UNKNOWN warning. */
|
|
2702
|
+
origin?: "other" | "recent" | "suggestion" | "typed";
|
|
2627
2703
|
};
|
|
2628
2704
|
header: {
|
|
2629
2705
|
/** @description Publishable, preview or secret site key. Required on every request; secret keys are server-side only. */
|
|
@@ -2788,6 +2864,91 @@ export interface operations {
|
|
|
2788
2864
|
};
|
|
2789
2865
|
};
|
|
2790
2866
|
};
|
|
2867
|
+
TrackSearchEvents: {
|
|
2868
|
+
parameters: {
|
|
2869
|
+
query?: never;
|
|
2870
|
+
header: {
|
|
2871
|
+
/** @description Publishable, preview or secret site key. Required on every request; secret keys are server-side only. */
|
|
2872
|
+
"X-Site-Key": string;
|
|
2873
|
+
/** @description Market code (e.g. "se"). Omit for the site's default market. */
|
|
2874
|
+
"X-Market"?: string;
|
|
2875
|
+
/** @description BCP-47 language tag. Omit to use the market's language. */
|
|
2876
|
+
"Accept-Language"?: string;
|
|
2877
|
+
};
|
|
2878
|
+
path?: never;
|
|
2879
|
+
cookie?: never;
|
|
2880
|
+
};
|
|
2881
|
+
requestBody: {
|
|
2882
|
+
content: {
|
|
2883
|
+
"application/json": components["schemas"]["SearchEventsRequest"];
|
|
2884
|
+
};
|
|
2885
|
+
};
|
|
2886
|
+
responses: {
|
|
2887
|
+
/** @description Accepted */
|
|
2888
|
+
202: {
|
|
2889
|
+
headers: {
|
|
2890
|
+
[name: string]: unknown;
|
|
2891
|
+
};
|
|
2892
|
+
content: {
|
|
2893
|
+
"application/json": components["schemas"]["SearchEventsResponse"];
|
|
2894
|
+
};
|
|
2895
|
+
};
|
|
2896
|
+
/** @description problem+json. EVENTS_INVALID (not JSON, not { "events": [ … ] }, no event or more than 50, or a body over 64 KB; nothing is stored), or MARKET_UNKNOWN. One bad event is not a 400: it is listed in the 202's rejected. */
|
|
2897
|
+
400: {
|
|
2898
|
+
headers: {
|
|
2899
|
+
[name: string]: unknown;
|
|
2900
|
+
};
|
|
2901
|
+
content: {
|
|
2902
|
+
"application/problem+json": components["schemas"]["StorefrontProblem"];
|
|
2903
|
+
};
|
|
2904
|
+
};
|
|
2905
|
+
/** @description problem+json. SITE_KEY_MISSING or SITE_KEY_INVALID. */
|
|
2906
|
+
401: {
|
|
2907
|
+
headers: {
|
|
2908
|
+
[name: string]: unknown;
|
|
2909
|
+
};
|
|
2910
|
+
content: {
|
|
2911
|
+
"application/problem+json": components["schemas"]["StorefrontProblem"];
|
|
2912
|
+
};
|
|
2913
|
+
};
|
|
2914
|
+
/** @description problem+json. SECRET_KEY_FROM_BROWSER: a secret key was sent from a browser. */
|
|
2915
|
+
403: {
|
|
2916
|
+
headers: {
|
|
2917
|
+
[name: string]: unknown;
|
|
2918
|
+
};
|
|
2919
|
+
content: {
|
|
2920
|
+
"application/problem+json": components["schemas"]["StorefrontProblem"];
|
|
2921
|
+
};
|
|
2922
|
+
};
|
|
2923
|
+
/** @description problem+json. SITE_INACTIVE, or NOT_FOUND for an unrouted path. */
|
|
2924
|
+
404: {
|
|
2925
|
+
headers: {
|
|
2926
|
+
[name: string]: unknown;
|
|
2927
|
+
};
|
|
2928
|
+
content: {
|
|
2929
|
+
"application/problem+json": components["schemas"]["StorefrontProblem"];
|
|
2930
|
+
};
|
|
2931
|
+
};
|
|
2932
|
+
/** @description problem+json. RATE_LIMITED; retryAfterSeconds and the Retry-After header say how long to wait. */
|
|
2933
|
+
429: {
|
|
2934
|
+
headers: {
|
|
2935
|
+
[name: string]: unknown;
|
|
2936
|
+
};
|
|
2937
|
+
content: {
|
|
2938
|
+
"application/problem+json": components["schemas"]["StorefrontProblem"];
|
|
2939
|
+
};
|
|
2940
|
+
};
|
|
2941
|
+
/** @description problem+json. SITE_REGISTRY_WARMING or INDEX_UNAVAILABLE — retry, the request was not wrong. */
|
|
2942
|
+
503: {
|
|
2943
|
+
headers: {
|
|
2944
|
+
[name: string]: unknown;
|
|
2945
|
+
};
|
|
2946
|
+
content: {
|
|
2947
|
+
"application/problem+json": components["schemas"]["StorefrontProblem"];
|
|
2948
|
+
};
|
|
2949
|
+
};
|
|
2950
|
+
};
|
|
2951
|
+
};
|
|
2791
2952
|
GetOrder: {
|
|
2792
2953
|
parameters: {
|
|
2793
2954
|
query?: never;
|
|
@@ -2878,7 +3039,7 @@ export interface operations {
|
|
|
2878
3039
|
ListProducts: {
|
|
2879
3040
|
parameters: {
|
|
2880
3041
|
query?: {
|
|
2881
|
-
/** @description Free-text search. Omit to browse rather than search. */
|
|
3042
|
+
/** @description Free-text search. Omit to browse rather than search; a q of separators only (such as !!!) browses too. A search answers with search (how it was answered), and may set queryCorrection and redirect. */
|
|
2882
3043
|
q?: string;
|
|
2883
3044
|
/** @description 1-based page. page × pageSize must not exceed 10000 (default pageSize 24, max 100). */
|
|
2884
3045
|
page?: number;
|
|
@@ -2890,6 +3051,8 @@ export interface operations {
|
|
|
2890
3051
|
filter?: {
|
|
2891
3052
|
[key: string]: string;
|
|
2892
3053
|
};
|
|
3054
|
+
/** @description Where a search came from, for measurement only: typed, suggestion, recent or other. It never changes the answer. Anything else is ignored with an ORIGIN_UNKNOWN warning. */
|
|
3055
|
+
origin?: "other" | "recent" | "suggestion" | "typed";
|
|
2893
3056
|
/** @description Narrow to a collection by slug path or id. An unknown one is answered with a COLLECTION_UNKNOWN warning, not a 404. */
|
|
2894
3057
|
collection?: string;
|
|
2895
3058
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@asstio-dev/storefront",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.21.0",
|
|
4
4
|
"description": "Typed client for the Asstio Storefront API (generated from OpenAPI).",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
"test": "vitest run",
|
|
22
22
|
"test:browser": "playwright test -c browser/playwright.config.ts",
|
|
23
23
|
"check": "npm run generate && npm run typecheck-examples && npm test && git diff --exit-code -- src/schema.d.ts && test -z \"$(git status --porcelain -- src/schema.d.ts)\"",
|
|
24
|
-
"smoke": "node --input-type=module -e \"import(\\\"./dist/index.js\\\").then(m => { for (const f of [\\\"createStorefrontClient\\\", \\\"filterQuery\\\", \\\"encodeFilterValues\\\", \\\"decodeFilterValues\\\", \\\"pictureSources\\\", \\\"mediaUrl\\\", \\\"formatImage\\\", \\\"styleToCss\\\", \\\"startCheckout\\\", \\\"syncCheckout\\\", \\\"completeCheckout\\\", \\\"getCheckoutStatus\\\", \\\"liveShippingFor\\\", \\\"getOrder\\\", \\\"awaitCompletion\\\", \\\"mountCheckout\\\", \\\"productJsonLd\\\", \\\"gtinJsonLd\\\", \\\"breadcrumbJsonLd\\\", \\\"productHead\\\", \\\"collectionHead\\\", \\\"brandHead\\\", \\\"toNextMetadata\\\", \\\"jsonLdScript\\\", \\\"SeoError\\\", \\\"productFacts\\\", \\\"toGa4Item\\\", \\\"toGa4Ecommerce\\\", \\\"AnalyticsError\\\"]) if (typeof m[f] !== \\\"function\\\") throw new Error(\\\"missing export \\\" + f) })\""
|
|
24
|
+
"smoke": "node --input-type=module -e \"import(\\\"./dist/index.js\\\").then(m => { for (const f of [\\\"createStorefrontClient\\\", \\\"filterQuery\\\", \\\"encodeFilterValues\\\", \\\"decodeFilterValues\\\", \\\"pictureSources\\\", \\\"mediaUrl\\\", \\\"formatImage\\\", \\\"styleToCss\\\", \\\"startCheckout\\\", \\\"syncCheckout\\\", \\\"completeCheckout\\\", \\\"getCheckoutStatus\\\", \\\"liveShippingFor\\\", \\\"getOrder\\\", \\\"awaitCompletion\\\", \\\"mountCheckout\\\", \\\"productJsonLd\\\", \\\"gtinJsonLd\\\", \\\"breadcrumbJsonLd\\\", \\\"productHead\\\", \\\"collectionHead\\\", \\\"brandHead\\\", \\\"toNextMetadata\\\", \\\"jsonLdScript\\\", \\\"SeoError\\\", \\\"productFacts\\\", \\\"toGa4Item\\\", \\\"toGa4Ecommerce\\\", \\\"AnalyticsError\\\", \\\"trackSearchEvents\\\"]) if (typeof m[f] !== \\\"function\\\") throw new Error(\\\"missing export \\\" + f) })\""
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"openapi-fetch": "0.17.0"
|
package/src/schema.d.ts
CHANGED
|
@@ -249,6 +249,23 @@ export interface paths {
|
|
|
249
249
|
patch?: never;
|
|
250
250
|
trace?: never;
|
|
251
251
|
};
|
|
252
|
+
"/v1/events": {
|
|
253
|
+
parameters: {
|
|
254
|
+
query?: never;
|
|
255
|
+
header?: never;
|
|
256
|
+
path?: never;
|
|
257
|
+
cookie?: never;
|
|
258
|
+
};
|
|
259
|
+
get?: never;
|
|
260
|
+
put?: never;
|
|
261
|
+
/** Search exposures, clicks and add-to-carts for measurement (spec 2026-10-13 §9.2). Send only with the shopper's consent. Each event is validated on its own: a bad one is listed in rejected and the others are kept; an eventId the site already has is a duplicate and changes nothing. */
|
|
262
|
+
post: operations["TrackSearchEvents"];
|
|
263
|
+
delete?: never;
|
|
264
|
+
options?: never;
|
|
265
|
+
head?: never;
|
|
266
|
+
patch?: never;
|
|
267
|
+
trace?: never;
|
|
268
|
+
};
|
|
252
269
|
"/v1/orders/{orderId}": {
|
|
253
270
|
parameters: {
|
|
254
271
|
query?: never;
|
|
@@ -422,6 +439,8 @@ export interface components {
|
|
|
422
439
|
AppliedFilterDto: {
|
|
423
440
|
facet: string;
|
|
424
441
|
label: string;
|
|
442
|
+
/** @default filter */
|
|
443
|
+
source: string;
|
|
425
444
|
value: string;
|
|
426
445
|
};
|
|
427
446
|
AttributeDto: {
|
|
@@ -752,6 +771,7 @@ export interface components {
|
|
|
752
771
|
pageSize: number | string;
|
|
753
772
|
queryCorrection: null | string;
|
|
754
773
|
redirect: null | string;
|
|
774
|
+
search?: null | components["schemas"]["SearchInfoDto"];
|
|
755
775
|
sort: components["schemas"]["SortDto"];
|
|
756
776
|
/** Format: int64 */
|
|
757
777
|
total: number | string;
|
|
@@ -1020,6 +1040,60 @@ export interface components {
|
|
|
1020
1040
|
importer: null | components["schemas"]["PartyDto"];
|
|
1021
1041
|
manufacturer: null | components["schemas"]["PartyDto"];
|
|
1022
1042
|
};
|
|
1043
|
+
SearchEventDto: {
|
|
1044
|
+
eventId: null | string;
|
|
1045
|
+
exposureId?: null | string;
|
|
1046
|
+
/** Format: date-time */
|
|
1047
|
+
occurredAt: null | string;
|
|
1048
|
+
/** Format: int32 */
|
|
1049
|
+
page?: number | null;
|
|
1050
|
+
/** Format: int32 */
|
|
1051
|
+
position?: number | null;
|
|
1052
|
+
productId?: null | string;
|
|
1053
|
+
productIds?: string[] | null;
|
|
1054
|
+
/** Format: int32 */
|
|
1055
|
+
quantity?: number | null;
|
|
1056
|
+
query?: null | string;
|
|
1057
|
+
resultId?: null | string;
|
|
1058
|
+
sessionId?: null | string;
|
|
1059
|
+
type: null | string;
|
|
1060
|
+
variantId?: null | string;
|
|
1061
|
+
};
|
|
1062
|
+
SearchEventRejection: {
|
|
1063
|
+
code: string;
|
|
1064
|
+
/** Format: int32 */
|
|
1065
|
+
index: number | string;
|
|
1066
|
+
member: null | string;
|
|
1067
|
+
};
|
|
1068
|
+
SearchEventsRequest: {
|
|
1069
|
+
events: components["schemas"]["SearchEventDto"][] | null;
|
|
1070
|
+
};
|
|
1071
|
+
SearchEventsResponse: {
|
|
1072
|
+
/** Format: int32 */
|
|
1073
|
+
accepted: number | string;
|
|
1074
|
+
/** Format: int32 */
|
|
1075
|
+
duplicates: number | string;
|
|
1076
|
+
rejected: components["schemas"]["SearchEventRejection"][];
|
|
1077
|
+
/** Format: int32 */
|
|
1078
|
+
stored: number | string;
|
|
1079
|
+
};
|
|
1080
|
+
SearchInfoDto: {
|
|
1081
|
+
codeMatch: boolean;
|
|
1082
|
+
intents: components["schemas"]["SearchIntentDto"][];
|
|
1083
|
+
rescue: null | components["schemas"]["SearchRescueDto"];
|
|
1084
|
+
resultId: string;
|
|
1085
|
+
tier: string;
|
|
1086
|
+
};
|
|
1087
|
+
SearchIntentDto: {
|
|
1088
|
+
facet: string;
|
|
1089
|
+
queryWithout: string;
|
|
1090
|
+
values: string[];
|
|
1091
|
+
};
|
|
1092
|
+
SearchRescueDto: {
|
|
1093
|
+
collectionId: string;
|
|
1094
|
+
name: string;
|
|
1095
|
+
slug: string;
|
|
1096
|
+
};
|
|
1023
1097
|
SellabilityDto: {
|
|
1024
1098
|
mode: components["schemas"]["SiteSellabilityMode"];
|
|
1025
1099
|
};
|
|
@@ -2612,7 +2686,7 @@ export interface operations {
|
|
|
2612
2686
|
GetCollection: {
|
|
2613
2687
|
parameters: {
|
|
2614
2688
|
query?: {
|
|
2615
|
-
/** @description Free-text search. Omit to browse rather than search. */
|
|
2689
|
+
/** @description Free-text search. Omit to browse rather than search; a q of separators only (such as !!!) browses too. A search answers with search (how it was answered), and may set queryCorrection and redirect. */
|
|
2616
2690
|
q?: string;
|
|
2617
2691
|
/** @description 1-based page. page × pageSize must not exceed 10000 (default pageSize 24, max 100). */
|
|
2618
2692
|
page?: number;
|
|
@@ -2624,6 +2698,8 @@ export interface operations {
|
|
|
2624
2698
|
filter?: {
|
|
2625
2699
|
[key: string]: string;
|
|
2626
2700
|
};
|
|
2701
|
+
/** @description Where a search came from, for measurement only: typed, suggestion, recent or other. It never changes the answer. Anything else is ignored with an ORIGIN_UNKNOWN warning. */
|
|
2702
|
+
origin?: "other" | "recent" | "suggestion" | "typed";
|
|
2627
2703
|
};
|
|
2628
2704
|
header: {
|
|
2629
2705
|
/** @description Publishable, preview or secret site key. Required on every request; secret keys are server-side only. */
|
|
@@ -2788,6 +2864,91 @@ export interface operations {
|
|
|
2788
2864
|
};
|
|
2789
2865
|
};
|
|
2790
2866
|
};
|
|
2867
|
+
TrackSearchEvents: {
|
|
2868
|
+
parameters: {
|
|
2869
|
+
query?: never;
|
|
2870
|
+
header: {
|
|
2871
|
+
/** @description Publishable, preview or secret site key. Required on every request; secret keys are server-side only. */
|
|
2872
|
+
"X-Site-Key": string;
|
|
2873
|
+
/** @description Market code (e.g. "se"). Omit for the site's default market. */
|
|
2874
|
+
"X-Market"?: string;
|
|
2875
|
+
/** @description BCP-47 language tag. Omit to use the market's language. */
|
|
2876
|
+
"Accept-Language"?: string;
|
|
2877
|
+
};
|
|
2878
|
+
path?: never;
|
|
2879
|
+
cookie?: never;
|
|
2880
|
+
};
|
|
2881
|
+
requestBody: {
|
|
2882
|
+
content: {
|
|
2883
|
+
"application/json": components["schemas"]["SearchEventsRequest"];
|
|
2884
|
+
};
|
|
2885
|
+
};
|
|
2886
|
+
responses: {
|
|
2887
|
+
/** @description Accepted */
|
|
2888
|
+
202: {
|
|
2889
|
+
headers: {
|
|
2890
|
+
[name: string]: unknown;
|
|
2891
|
+
};
|
|
2892
|
+
content: {
|
|
2893
|
+
"application/json": components["schemas"]["SearchEventsResponse"];
|
|
2894
|
+
};
|
|
2895
|
+
};
|
|
2896
|
+
/** @description problem+json. EVENTS_INVALID (not JSON, not { "events": [ … ] }, no event or more than 50, or a body over 64 KB; nothing is stored), or MARKET_UNKNOWN. One bad event is not a 400: it is listed in the 202's rejected. */
|
|
2897
|
+
400: {
|
|
2898
|
+
headers: {
|
|
2899
|
+
[name: string]: unknown;
|
|
2900
|
+
};
|
|
2901
|
+
content: {
|
|
2902
|
+
"application/problem+json": components["schemas"]["StorefrontProblem"];
|
|
2903
|
+
};
|
|
2904
|
+
};
|
|
2905
|
+
/** @description problem+json. SITE_KEY_MISSING or SITE_KEY_INVALID. */
|
|
2906
|
+
401: {
|
|
2907
|
+
headers: {
|
|
2908
|
+
[name: string]: unknown;
|
|
2909
|
+
};
|
|
2910
|
+
content: {
|
|
2911
|
+
"application/problem+json": components["schemas"]["StorefrontProblem"];
|
|
2912
|
+
};
|
|
2913
|
+
};
|
|
2914
|
+
/** @description problem+json. SECRET_KEY_FROM_BROWSER: a secret key was sent from a browser. */
|
|
2915
|
+
403: {
|
|
2916
|
+
headers: {
|
|
2917
|
+
[name: string]: unknown;
|
|
2918
|
+
};
|
|
2919
|
+
content: {
|
|
2920
|
+
"application/problem+json": components["schemas"]["StorefrontProblem"];
|
|
2921
|
+
};
|
|
2922
|
+
};
|
|
2923
|
+
/** @description problem+json. SITE_INACTIVE, or NOT_FOUND for an unrouted path. */
|
|
2924
|
+
404: {
|
|
2925
|
+
headers: {
|
|
2926
|
+
[name: string]: unknown;
|
|
2927
|
+
};
|
|
2928
|
+
content: {
|
|
2929
|
+
"application/problem+json": components["schemas"]["StorefrontProblem"];
|
|
2930
|
+
};
|
|
2931
|
+
};
|
|
2932
|
+
/** @description problem+json. RATE_LIMITED; retryAfterSeconds and the Retry-After header say how long to wait. */
|
|
2933
|
+
429: {
|
|
2934
|
+
headers: {
|
|
2935
|
+
[name: string]: unknown;
|
|
2936
|
+
};
|
|
2937
|
+
content: {
|
|
2938
|
+
"application/problem+json": components["schemas"]["StorefrontProblem"];
|
|
2939
|
+
};
|
|
2940
|
+
};
|
|
2941
|
+
/** @description problem+json. SITE_REGISTRY_WARMING or INDEX_UNAVAILABLE — retry, the request was not wrong. */
|
|
2942
|
+
503: {
|
|
2943
|
+
headers: {
|
|
2944
|
+
[name: string]: unknown;
|
|
2945
|
+
};
|
|
2946
|
+
content: {
|
|
2947
|
+
"application/problem+json": components["schemas"]["StorefrontProblem"];
|
|
2948
|
+
};
|
|
2949
|
+
};
|
|
2950
|
+
};
|
|
2951
|
+
};
|
|
2791
2952
|
GetOrder: {
|
|
2792
2953
|
parameters: {
|
|
2793
2954
|
query?: never;
|
|
@@ -2878,7 +3039,7 @@ export interface operations {
|
|
|
2878
3039
|
ListProducts: {
|
|
2879
3040
|
parameters: {
|
|
2880
3041
|
query?: {
|
|
2881
|
-
/** @description Free-text search. Omit to browse rather than search. */
|
|
3042
|
+
/** @description Free-text search. Omit to browse rather than search; a q of separators only (such as !!!) browses too. A search answers with search (how it was answered), and may set queryCorrection and redirect. */
|
|
2882
3043
|
q?: string;
|
|
2883
3044
|
/** @description 1-based page. page × pageSize must not exceed 10000 (default pageSize 24, max 100). */
|
|
2884
3045
|
page?: number;
|
|
@@ -2890,6 +3051,8 @@ export interface operations {
|
|
|
2890
3051
|
filter?: {
|
|
2891
3052
|
[key: string]: string;
|
|
2892
3053
|
};
|
|
3054
|
+
/** @description Where a search came from, for measurement only: typed, suggestion, recent or other. It never changes the answer. Anything else is ignored with an ORIGIN_UNKNOWN warning. */
|
|
3055
|
+
origin?: "other" | "recent" | "suggestion" | "typed";
|
|
2893
3056
|
/** @description Narrow to a collection by slug path or id. An unknown one is answered with a COLLECTION_UNKNOWN warning, not a 404. */
|
|
2894
3057
|
collection?: string;
|
|
2895
3058
|
};
|