@decocms/apps-vtex 8.1.0-next.0 → 8.1.0-next.2
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/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -0
- package/dist/utils/constants.d.ts +28 -0
- package/dist/utils/constants.d.ts.map +1 -0
- package/dist/utils/constants.js +17 -0
- package/dist/utils/constants.js.map +1 -0
- package/dist/utils/cookieSanitizer.d.ts +87 -0
- package/dist/utils/cookieSanitizer.d.ts.map +1 -0
- package/dist/utils/cookieSanitizer.js +136 -0
- package/dist/utils/cookieSanitizer.js.map +1 -0
- package/dist/utils/operationRouter.d.ts +25 -0
- package/dist/utils/operationRouter.d.ts.map +1 -0
- package/dist/utils/operationRouter.js +120 -0
- package/dist/utils/operationRouter.js.map +1 -0
- package/dist/utils/types.d.ts +944 -0
- package/dist/utils/types.d.ts.map +1 -0
- package/dist/utils/types.js +25 -0
- package/dist/utils/types.js.map +1 -0
- package/dist/vtexClient.d.ts +173 -0
- package/dist/vtexClient.d.ts.map +1 -0
- package/dist/vtexClient.js +242 -0
- package/dist/vtexClient.js.map +1 -0
- package/package.json +16 -57
- package/src/index.ts +11 -29
- package/src/utils/constants.ts +2 -56
- package/src/utils/cookieSanitizer.ts +59 -59
- package/src/utils/operationRouter.ts +75 -65
- package/src/utils/types.ts +709 -1557
- package/src/vtexClient.ts +358 -347
- package/src/README.md +0 -6
- package/src/__conformance__/upstream.test.ts +0 -158
- package/src/__tests__/client-segment-cookie.test.ts +0 -255
- package/src/__tests__/client-set-cookie-forward.test.ts +0 -257
- package/src/__tests__/schemas.test.ts +0 -71
- package/src/actions/address.ts +0 -250
- package/src/actions/analytics/sendEvent.ts +0 -86
- package/src/actions/auth.ts +0 -333
- package/src/actions/checkout.ts +0 -659
- package/src/actions/index.ts +0 -11
- package/src/actions/masterData.ts +0 -168
- package/src/actions/misc.ts +0 -188
- package/src/actions/newsletter.ts +0 -105
- package/src/actions/orders.ts +0 -34
- package/src/actions/profile.ts +0 -201
- package/src/actions/session.ts +0 -85
- package/src/actions/trigger.ts +0 -43
- package/src/actions/wishlist.ts +0 -114
- package/src/client.ts +0 -724
- package/src/commerceLoaders.ts +0 -267
- package/src/hooks/__tests__/createCart.render.test.ts +0 -114
- package/src/hooks/__tests__/createCart.test.ts +0 -92
- package/src/hooks/__tests__/createUseCart.test.ts +0 -184
- package/src/hooks/__tests__/createUseUser.test.ts +0 -48
- package/src/hooks/__tests__/createUseWishlist.test.ts +0 -87
- package/src/hooks/cartQuery.ts +0 -204
- package/src/hooks/createCart.ts +0 -427
- package/src/hooks/createUseCart.ts +0 -360
- package/src/hooks/createUseUser.ts +0 -153
- package/src/hooks/createUseWishlist.ts +0 -242
- package/src/hooks/index.ts +0 -25
- package/src/hooks/useAutocomplete.ts +0 -83
- package/src/hooks/useCart.ts +0 -243
- package/src/hooks/useUser.ts +0 -78
- package/src/hooks/useWishlist.ts +0 -119
- package/src/invoke.ts +0 -235
- package/src/loaders/ProductDetailsPage.ts +0 -1
- package/src/loaders/ProductList.ts +0 -1
- package/src/loaders/ProductListingPage.ts +0 -1
- package/src/loaders/__tests__/legacyProductList.test.ts +0 -157
- package/src/loaders/address.ts +0 -115
- package/src/loaders/autocomplete.ts +0 -56
- package/src/loaders/brands.ts +0 -44
- package/src/loaders/cart/attachments.ts +0 -53
- package/src/loaders/cart/full.ts +0 -67
- package/src/loaders/cart/gifts.ts +0 -43
- package/src/loaders/cart/orderFormId.ts +0 -31
- package/src/loaders/cart/shipping.test.ts +0 -125
- package/src/loaders/cart/shipping.ts +0 -136
- package/src/loaders/cart/summary.ts +0 -36
- package/src/loaders/cart.ts +0 -52
- package/src/loaders/catalog.ts +0 -163
- package/src/loaders/collections.ts +0 -55
- package/src/loaders/index.ts +0 -20
- package/src/loaders/intelligentSearch/__tests__/productListingPage.test.ts +0 -51
- package/src/loaders/intelligentSearch/productDetailsPage.ts +0 -111
- package/src/loaders/intelligentSearch/productList.ts +0 -170
- package/src/loaders/intelligentSearch/productListingPage.ts +0 -525
- package/src/loaders/intelligentSearch/suggestions.ts +0 -44
- package/src/loaders/legacy/productDetailsPage.ts +0 -1
- package/src/loaders/legacy/productList.ts +0 -1
- package/src/loaders/legacy/relatedProductsLoader.ts +0 -79
- package/src/loaders/legacy.ts +0 -710
- package/src/loaders/logistics.ts +0 -111
- package/src/loaders/minicart.ts +0 -78
- package/src/loaders/navbar.ts +0 -26
- package/src/loaders/orders.ts +0 -92
- package/src/loaders/pageType.ts +0 -68
- package/src/loaders/paths/PDPDefaultPath.ts +0 -51
- package/src/loaders/payment.ts +0 -97
- package/src/loaders/productListFull.ts +0 -157
- package/src/loaders/profile.ts +0 -138
- package/src/loaders/promotion.ts +0 -30
- package/src/loaders/search.ts +0 -124
- package/src/loaders/session.ts +0 -83
- package/src/loaders/user.ts +0 -87
- package/src/loaders/wishlist.ts +0 -89
- package/src/loaders/wishlistProducts.ts +0 -69
- package/src/loaders/workflow/products.ts +0 -62
- package/src/loaders/workflow.ts +0 -316
- package/src/logo.png +0 -0
- package/src/manifest.gen.ts +0 -89
- package/src/middleware.cacheHeaders.test.ts +0 -44
- package/src/middleware.ts +0 -240
- package/src/mod.ts +0 -173
- package/src/registry.ts +0 -9
- package/src/schemas.gen.ts +0 -3191
- package/src/schemas.ts +0 -30
- package/src/types.ts +0 -248
- package/src/utils/__tests__/cartProjection.test.ts +0 -118
- package/src/utils/__tests__/cookieSanitizer.test.ts +0 -162
- package/src/utils/__tests__/fetch.test.ts +0 -80
- package/src/utils/__tests__/fetchCache.test.ts +0 -155
- package/src/utils/__tests__/instrumentedFetch.test.ts +0 -158
- package/src/utils/__tests__/intelligentSearch.test.ts +0 -88
- package/src/utils/__tests__/minicart.test.ts +0 -184
- package/src/utils/__tests__/operationRouter.test.ts +0 -227
- package/src/utils/__tests__/resilience.test.ts +0 -205
- package/src/utils/__tests__/resourceRange.test.ts +0 -32
- package/src/utils/__tests__/simulationCache.test.ts +0 -40
- package/src/utils/__tests__/sitemap.test.ts +0 -185
- package/src/utils/__tests__/slugify.test.ts +0 -31
- package/src/utils/__tests__/storefrontBaseUrl.test.ts +0 -61
- package/src/utils/__tests__/transform.test.ts +0 -1111
- package/src/utils/accountLoaders.ts +0 -203
- package/src/utils/authHelpers.ts +0 -85
- package/src/utils/batch.ts +0 -18
- package/src/utils/buildOfferShelf.test.ts +0 -59
- package/src/utils/cartProjection.ts +0 -96
- package/src/utils/cookies.ts +0 -167
- package/src/utils/enrichment.ts +0 -556
- package/src/utils/fetch.ts +0 -111
- package/src/utils/fetchCache.ts +0 -55
- package/src/utils/index.ts +0 -19
- package/src/utils/instrumentedFetch.ts +0 -98
- package/src/utils/intelligentSearch.ts +0 -96
- package/src/utils/legacy.ts +0 -139
- package/src/utils/minicart.ts +0 -139
- package/src/utils/pickAndOmit.ts +0 -25
- package/src/utils/proxy.ts +0 -495
- package/src/utils/resilience.ts +0 -352
- package/src/utils/resourceRange.ts +0 -10
- package/src/utils/segment.ts +0 -152
- package/src/utils/similars.ts +0 -35
- package/src/utils/simulationCache.ts +0 -73
- package/src/utils/sitemap.ts +0 -283
- package/src/utils/slugCache.ts +0 -28
- package/src/utils/slugify.ts +0 -13
- package/src/utils/transform.ts +0 -1691
- package/src/utils/vtexId.ts +0 -151
- package/src/vtexClient.test.ts +0 -204
- package/tsconfig.json +0 -7
package/src/vtexClient.ts
CHANGED
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
* per-shopper input (cookies, region) as arguments, and never reads the
|
|
19
19
|
* incoming request, caches, or converts. Converters, hooks, cart/session/sign-in
|
|
20
20
|
* flows and loaders live in the VTEX platform template and site code; upstream
|
|
21
|
-
* caching lives in the
|
|
21
|
+
* caching lives in the site (/next/caching#upstream-data), keyed
|
|
22
22
|
* by everything a response depends on, such as `regionId`.
|
|
23
23
|
*
|
|
24
24
|
* Retries and a circuit breaker are ON by default for VTEX (and only for
|
|
@@ -26,124 +26,124 @@
|
|
|
26
26
|
* never repeated. Pass `retry: false` / `circuitBreaker: false` to turn them off.
|
|
27
27
|
*/
|
|
28
28
|
import { createInstrumentedFetch } from "@decocms/blocks/fetch";
|
|
29
|
-
import { DEFAULT_RESILIENCE_CONFIG } from "./utils/constants";
|
|
30
|
-
import { sanitizeOutboundCookieHeader } from "./utils/cookieSanitizer";
|
|
31
|
-
import { vtexOperationRouter } from "./utils/operationRouter";
|
|
29
|
+
import { DEFAULT_RESILIENCE_CONFIG } from "./utils/constants.ts";
|
|
30
|
+
import { sanitizeOutboundCookieHeader } from "./utils/cookieSanitizer.ts";
|
|
31
|
+
import { vtexOperationRouter } from "./utils/operationRouter.ts";
|
|
32
32
|
import type {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
} from "./utils/types";
|
|
33
|
+
Category,
|
|
34
|
+
CrossSellingType,
|
|
35
|
+
FacetSearchResult,
|
|
36
|
+
Fuzzy,
|
|
37
|
+
LegacyProduct,
|
|
38
|
+
LegacySort,
|
|
39
|
+
OrderForm,
|
|
40
|
+
OrderFormItemInput,
|
|
41
|
+
PageType,
|
|
42
|
+
ProductSearchResult,
|
|
43
|
+
SelectedFacet,
|
|
44
|
+
Session,
|
|
45
|
+
SimulationBehavior,
|
|
46
|
+
SimulationOrderForm,
|
|
47
|
+
Sort,
|
|
48
|
+
Suggestion,
|
|
49
|
+
} from "./utils/types.ts";
|
|
50
50
|
|
|
51
51
|
export interface VtexClientConfig {
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
52
|
+
/** The VTEX account name, e.g. `mystore`. */
|
|
53
|
+
account: string;
|
|
54
|
+
/** Sent as `X-VTEX-API-AppKey` when set together with `appToken`. */
|
|
55
|
+
appKey?: string;
|
|
56
|
+
/** Sent as `X-VTEX-API-AppToken` when set together with `appKey`. */
|
|
57
|
+
appToken?: string;
|
|
58
|
+
/** @default "vtexcommercestable" */
|
|
59
|
+
environment?: string;
|
|
60
|
+
/** @default "com.br" */
|
|
61
|
+
domain?: string;
|
|
62
|
+
/** Default sales channel (`sc`), overridable per call where VTEX accepts one. */
|
|
63
|
+
salesChannel?: string;
|
|
64
|
+
/** Default Intelligent Search locale, e.g. `pt-BR`. */
|
|
65
|
+
locale?: string;
|
|
66
|
+
/** The fetch underneath; for tests. Defaults to `globalThis.fetch`. */
|
|
67
|
+
fetch?: typeof globalThis.fetch;
|
|
68
|
+
/** On by default (2 retries, 150ms backoff); idempotent requests only. `false` turns it off. */
|
|
69
|
+
retry?: { attempts: number; backoffMs?: number } | false;
|
|
70
|
+
/** On by default (opens after 5 failures, for 5s). `false` turns it off. */
|
|
71
|
+
circuitBreaker?: { failures: number; cooldownMs: number } | false;
|
|
72
72
|
}
|
|
73
73
|
|
|
74
74
|
const VTEX_DEFAULT_RETRY = {
|
|
75
|
-
|
|
76
|
-
|
|
75
|
+
attempts: DEFAULT_RESILIENCE_CONFIG.maxRetries,
|
|
76
|
+
backoffMs: DEFAULT_RESILIENCE_CONFIG.backoffBaseMs,
|
|
77
77
|
} as const;
|
|
78
78
|
|
|
79
79
|
const VTEX_DEFAULT_CIRCUIT_BREAKER = {
|
|
80
|
-
|
|
81
|
-
|
|
80
|
+
failures: DEFAULT_RESILIENCE_CONFIG.breakerConsecutiveFailures,
|
|
81
|
+
cooldownMs: DEFAULT_RESILIENCE_CONFIG.breakerOpenCooldownMs,
|
|
82
82
|
} as const;
|
|
83
83
|
|
|
84
84
|
/** Per-call inputs. Nothing is read from the incoming request. */
|
|
85
85
|
export interface VtexRequestOptions {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
86
|
+
/** The shopper's `Cookie` header to forward (non-ASCII and malformed pairs are dropped). */
|
|
87
|
+
cookie?: string;
|
|
88
|
+
signal?: AbortSignal;
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
/** A shopper-scoped response: the body plus VTEX's `Set-Cookie` values, unmodified, for the caller to forward. */
|
|
92
92
|
export interface VtexResponse<T> {
|
|
93
|
-
|
|
94
|
-
|
|
93
|
+
data: T;
|
|
94
|
+
setCookies: string[];
|
|
95
95
|
}
|
|
96
96
|
|
|
97
97
|
/** A failed VTEX call. Carries the operation and the status, never a body, URL, token or cookie. */
|
|
98
98
|
export class VtexError extends Error {
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
99
|
+
constructor(
|
|
100
|
+
readonly operation: string,
|
|
101
|
+
readonly status: number,
|
|
102
|
+
) {
|
|
103
|
+
super(`vtex ${operation} failed with HTTP ${status}`);
|
|
104
|
+
this.name = "VtexError";
|
|
105
|
+
}
|
|
106
106
|
}
|
|
107
107
|
|
|
108
108
|
/** Intelligent Search arguments, mirroring its query parameters. */
|
|
109
109
|
export interface VtexSearchArgs extends VtexRequestOptions {
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
110
|
+
query?: string;
|
|
111
|
+
/** Selected facets, sent as the path (`category-1/shirts/brand/acme`). */
|
|
112
|
+
facets?: SelectedFacet[];
|
|
113
|
+
/** Intelligent Search's 1-based page. */
|
|
114
|
+
page?: number;
|
|
115
|
+
count?: number;
|
|
116
|
+
sort?: Sort;
|
|
117
|
+
fuzzy?: Fuzzy;
|
|
118
|
+
locale?: string;
|
|
119
|
+
hideUnavailableItems?: boolean;
|
|
120
|
+
simulationBehavior?: SimulationBehavior;
|
|
121
|
+
/** The shopper's region (from the `vtex_segment` cookie); part of the cache key. */
|
|
122
|
+
regionId?: string;
|
|
123
|
+
salesChannel?: string;
|
|
124
124
|
}
|
|
125
125
|
|
|
126
126
|
/** Legacy Catalog search arguments (`/api/catalog_system/pub/products/search`). */
|
|
127
127
|
export interface VtexCatalogSearchArgs extends VtexRequestOptions {
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
128
|
+
/** Path term, e.g. a category path. */
|
|
129
|
+
term?: string;
|
|
130
|
+
/** Full-text query (`ft`). */
|
|
131
|
+
ft?: string;
|
|
132
|
+
/** Filter queries (`fq`), e.g. `productId:123`. */
|
|
133
|
+
fq?: string[];
|
|
134
|
+
/** `_from`, 0-based inclusive. */
|
|
135
|
+
from?: number;
|
|
136
|
+
/** `_to`, 0-based inclusive. */
|
|
137
|
+
to?: number;
|
|
138
|
+
/** `O`. */
|
|
139
|
+
sort?: LegacySort;
|
|
140
|
+
salesChannel?: string;
|
|
141
141
|
}
|
|
142
142
|
|
|
143
143
|
interface CallInit extends VtexRequestOptions {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
144
|
+
method?: string;
|
|
145
|
+
body?: unknown;
|
|
146
|
+
headers?: Record<string, string>;
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
/**
|
|
@@ -152,282 +152,293 @@ interface CallInit extends VtexRequestOptions {
|
|
|
152
152
|
* query (`?`, `#`) while the app credentials are attached.
|
|
153
153
|
*/
|
|
154
154
|
function pathSegments(path: string): string {
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
155
|
+
return path
|
|
156
|
+
.split("/")
|
|
157
|
+
.filter((segment) => segment !== "")
|
|
158
|
+
.map((segment) => {
|
|
159
|
+
if (/^(\.|%2e){1,2}$/i.test(segment)) throw new Error("vtex: invalid path segment");
|
|
160
|
+
return encodeURIComponent(segment);
|
|
161
|
+
})
|
|
162
|
+
.join("/");
|
|
163
163
|
}
|
|
164
164
|
|
|
165
165
|
export function createVtexClient(config: VtexClientConfig) {
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
166
|
+
const host = `${config.account}.${config.environment ?? "vtexcommercestable"}.${config.domain ?? "com.br"}`;
|
|
167
|
+
const base = `https://${host}`;
|
|
168
|
+
const request = createInstrumentedFetch({
|
|
169
|
+
provider: "vtex",
|
|
170
|
+
fetch: config.fetch,
|
|
171
|
+
retry: config.retry === false ? undefined : (config.retry ?? VTEX_DEFAULT_RETRY),
|
|
172
|
+
circuitBreaker:
|
|
173
|
+
config.circuitBreaker === false
|
|
174
|
+
? undefined
|
|
175
|
+
: (config.circuitBreaker ?? VTEX_DEFAULT_CIRCUIT_BREAKER),
|
|
176
|
+
});
|
|
177
177
|
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
178
|
+
async function call(url: string | URL, init: CallInit = {}): Promise<Response> {
|
|
179
|
+
const method = (init.method ?? "GET").toUpperCase();
|
|
180
|
+
const operation = vtexOperationRouter(String(url), method) ?? "unknown";
|
|
181
|
+
const headers = new Headers({ accept: "application/json", ...init.headers });
|
|
182
|
+
if (init.body !== undefined) headers.set("content-type", "application/json");
|
|
183
|
+
// Shopper-scoped calls go out as the shopper only: with app credentials
|
|
184
|
+
// VTEX would answer an orderForm with the profile data unmasked.
|
|
185
|
+
if (config.appKey && config.appToken && !init.cookie) {
|
|
186
|
+
headers.set("x-vtex-api-appkey", config.appKey);
|
|
187
|
+
headers.set("x-vtex-api-apptoken", config.appToken);
|
|
188
|
+
}
|
|
189
|
+
if (init.cookie) {
|
|
190
|
+
const { cookies } = sanitizeOutboundCookieHeader(init.cookie);
|
|
191
|
+
if (cookies) headers.set("cookie", cookies);
|
|
192
|
+
}
|
|
193
|
+
const response = await request(url, {
|
|
194
|
+
method,
|
|
195
|
+
headers,
|
|
196
|
+
body: init.body === undefined ? undefined : JSON.stringify(init.body),
|
|
197
|
+
signal: init.signal,
|
|
198
|
+
operation,
|
|
199
|
+
});
|
|
200
|
+
if (!response.ok) {
|
|
201
|
+
void response.body?.cancel().catch(() => {});
|
|
202
|
+
throw new VtexError(operation, response.status);
|
|
203
|
+
}
|
|
204
|
+
return response;
|
|
205
|
+
}
|
|
206
206
|
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
207
|
+
/** Only the per-call request options, never the operation's other arguments. */
|
|
208
|
+
const pick = (options: VtexRequestOptions = {}): VtexRequestOptions => ({
|
|
209
|
+
cookie: options.cookie,
|
|
210
|
+
signal: options.signal,
|
|
211
|
+
});
|
|
212
212
|
|
|
213
|
-
|
|
214
|
-
|
|
213
|
+
const json = async <T>(url: string | URL, init?: CallInit): Promise<T> =>
|
|
214
|
+
(await call(url, init)).json() as Promise<T>;
|
|
215
215
|
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
216
|
+
const withCookies = async <T>(url: string | URL, init?: CallInit): Promise<VtexResponse<T>> => {
|
|
217
|
+
const response = await call(url, init);
|
|
218
|
+
const setCookies =
|
|
219
|
+
typeof response.headers.getSetCookie === "function" ? response.headers.getSetCookie() : [];
|
|
220
|
+
return { data: (await response.json()) as T, setCookies };
|
|
221
|
+
};
|
|
222
222
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
223
|
+
const url = (
|
|
224
|
+
path: string,
|
|
225
|
+
params: Record<string, string | number | boolean | undefined> = {},
|
|
226
|
+
) => {
|
|
227
|
+
const result = new URL(path, base);
|
|
228
|
+
for (const [key, value] of Object.entries(params)) {
|
|
229
|
+
if (value !== undefined && value !== "") result.searchParams.set(key, String(value));
|
|
230
|
+
}
|
|
231
|
+
return result;
|
|
232
|
+
};
|
|
230
233
|
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
234
|
+
const isUrl = (endpoint: string, args: VtexSearchArgs) => {
|
|
235
|
+
const facetPath = (args.facets ?? [])
|
|
236
|
+
.map(({ key, value }) =>
|
|
237
|
+
key ? `${encodeURIComponent(key)}/${encodeURIComponent(value)}` : encodeURIComponent(value),
|
|
238
|
+
)
|
|
239
|
+
.join("/");
|
|
240
|
+
return url(`/api/io/_v/api/intelligent-search/${endpoint}/${facetPath}`, {
|
|
241
|
+
query: args.query,
|
|
242
|
+
page: args.page,
|
|
243
|
+
count: args.count,
|
|
244
|
+
sort: args.sort,
|
|
245
|
+
fuzzy: args.fuzzy,
|
|
246
|
+
locale: args.locale ?? config.locale,
|
|
247
|
+
hideUnavailableItems: args.hideUnavailableItems,
|
|
248
|
+
simulationBehavior: args.simulationBehavior,
|
|
249
|
+
regionId: args.regionId,
|
|
250
|
+
sc: args.salesChannel ?? config.salesChannel,
|
|
251
|
+
});
|
|
252
|
+
};
|
|
250
253
|
|
|
251
|
-
|
|
252
|
-
|
|
254
|
+
const orderFormPath = (orderFormId: string, rest = "") =>
|
|
255
|
+
`/api/checkout/pub/orderForm/${encodeURIComponent(orderFormId)}${rest}`;
|
|
253
256
|
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
257
|
+
return {
|
|
258
|
+
/** Intelligent Search. */
|
|
259
|
+
search: {
|
|
260
|
+
products: (args: VtexSearchArgs = {}) =>
|
|
261
|
+
json<ProductSearchResult>(isUrl("product_search", args), pick(args)),
|
|
262
|
+
facets: (args: VtexSearchArgs = {}) =>
|
|
263
|
+
json<FacetSearchResult>(isUrl("facets", args), pick(args)),
|
|
264
|
+
suggestions: (args: VtexSearchArgs) =>
|
|
265
|
+
json<Suggestion>(isUrl("search_suggestions", { ...args, facets: [] }), pick(args)),
|
|
266
|
+
autocomplete: (args: VtexSearchArgs) =>
|
|
267
|
+
json<Suggestion>(isUrl("autocomplete_suggestions", { ...args, facets: [] }), pick(args)),
|
|
268
|
+
topSearches: (args: VtexSearchArgs = {}) =>
|
|
269
|
+
json<Suggestion>(isUrl("top_searches", { ...args, facets: [] }), pick(args)),
|
|
270
|
+
},
|
|
267
271
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
272
|
+
/** Legacy Catalog API. */
|
|
273
|
+
catalog: {
|
|
274
|
+
pageType: async (path: string, options?: VtexRequestOptions) =>
|
|
275
|
+
json<PageType>(
|
|
276
|
+
url(`/api/catalog_system/pub/portal/pagetype/${pathSegments(path)}`),
|
|
277
|
+
options,
|
|
278
|
+
),
|
|
279
|
+
products: async (args: VtexCatalogSearchArgs = {}) => {
|
|
280
|
+
const target = url(
|
|
281
|
+
`/api/catalog_system/pub/products/search/${pathSegments(args.term ?? "")}`,
|
|
282
|
+
{
|
|
283
|
+
ft: args.ft,
|
|
284
|
+
_from: args.from,
|
|
285
|
+
_to: args.to,
|
|
286
|
+
O: args.sort,
|
|
287
|
+
sc: args.salesChannel ?? config.salesChannel,
|
|
288
|
+
},
|
|
289
|
+
);
|
|
290
|
+
for (const fq of args.fq ?? []) target.searchParams.append("fq", fq);
|
|
291
|
+
return json<LegacyProduct[]>(target, pick(args));
|
|
292
|
+
},
|
|
293
|
+
crossSelling: (type: CrossSellingType, productId: string, options?: VtexRequestOptions) =>
|
|
294
|
+
json<LegacyProduct[]>(
|
|
295
|
+
url(
|
|
296
|
+
`/api/catalog_system/pub/products/crossselling/${type}/${encodeURIComponent(productId)}`,
|
|
297
|
+
),
|
|
298
|
+
options,
|
|
299
|
+
),
|
|
300
|
+
categoryTree: (levels = 3, options?: VtexRequestOptions) =>
|
|
301
|
+
json<Category[]>(url(`/api/catalog_system/pub/category/tree/${levels}`), options),
|
|
302
|
+
},
|
|
296
303
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
304
|
+
/** Checkout API. Cart calls are shopper-scoped and return VTEX's `Set-Cookie` values. */
|
|
305
|
+
checkout: {
|
|
306
|
+
/**
|
|
307
|
+
* Gets the shopper's cart from their `checkout.vtex.com` cookie, or creates one.
|
|
308
|
+
* Every section by default; `sections` narrows it (`expectedOrderFormSections`).
|
|
309
|
+
*/
|
|
310
|
+
orderForm: (options: VtexRequestOptions & { sections?: string[] } = {}) =>
|
|
311
|
+
withCookies<OrderForm>(url("/api/checkout/pub/orderForm"), {
|
|
312
|
+
...pick(options),
|
|
313
|
+
method: "POST",
|
|
314
|
+
body: options.sections ? { expectedOrderFormSections: options.sections } : {},
|
|
315
|
+
}),
|
|
316
|
+
addItems: (
|
|
317
|
+
orderFormId: string,
|
|
318
|
+
orderItems: OrderFormItemInput[],
|
|
319
|
+
options?: VtexRequestOptions,
|
|
320
|
+
) =>
|
|
321
|
+
withCookies<OrderForm>(url(orderFormPath(orderFormId, "/items")), {
|
|
322
|
+
...options,
|
|
323
|
+
method: "POST",
|
|
324
|
+
body: { orderItems },
|
|
325
|
+
}),
|
|
326
|
+
updateItems: (
|
|
327
|
+
orderFormId: string,
|
|
328
|
+
orderItems: { index: number; quantity: number }[],
|
|
329
|
+
options?: VtexRequestOptions,
|
|
330
|
+
) =>
|
|
331
|
+
withCookies<OrderForm>(url(orderFormPath(orderFormId, "/items/update")), {
|
|
332
|
+
...options,
|
|
333
|
+
method: "POST",
|
|
334
|
+
body: { orderItems },
|
|
335
|
+
}),
|
|
336
|
+
addCoupon: (orderFormId: string, text: string, options?: VtexRequestOptions) =>
|
|
337
|
+
withCookies<OrderForm>(url(orderFormPath(orderFormId, "/coupons")), {
|
|
338
|
+
...options,
|
|
339
|
+
method: "POST",
|
|
340
|
+
body: { text },
|
|
341
|
+
}),
|
|
342
|
+
attachment: (
|
|
343
|
+
orderFormId: string,
|
|
344
|
+
name: string,
|
|
345
|
+
body: Record<string, unknown>,
|
|
346
|
+
options?: VtexRequestOptions,
|
|
347
|
+
) =>
|
|
348
|
+
withCookies<OrderForm>(
|
|
349
|
+
url(orderFormPath(orderFormId, `/attachments/${encodeURIComponent(name)}`)),
|
|
350
|
+
{ ...options, method: "POST", body },
|
|
351
|
+
),
|
|
352
|
+
simulation: (
|
|
353
|
+
body: {
|
|
354
|
+
items: { id: string; quantity: number; seller: string }[];
|
|
355
|
+
postalCode?: string;
|
|
356
|
+
country?: string;
|
|
357
|
+
},
|
|
358
|
+
options: VtexRequestOptions & { salesChannel?: string } = {},
|
|
359
|
+
) =>
|
|
360
|
+
json<SimulationOrderForm>(
|
|
361
|
+
url("/api/checkout/pub/orderForms/simulation", {
|
|
362
|
+
sc: options.salesChannel ?? config.salesChannel,
|
|
363
|
+
}),
|
|
364
|
+
{ ...pick(options), method: "POST", body },
|
|
365
|
+
),
|
|
366
|
+
/** The region (and its sellers) serving a postal code; its `id` is the `regionId`. */
|
|
367
|
+
regions: (
|
|
368
|
+
args: { postalCode: string; country: string; salesChannel?: string },
|
|
369
|
+
options?: VtexRequestOptions,
|
|
370
|
+
) =>
|
|
371
|
+
json<{ id: string; sellers: { id: string; name: string }[] }[]>(
|
|
372
|
+
url("/api/checkout/pub/regions", {
|
|
373
|
+
postalCode: args.postalCode,
|
|
374
|
+
country: args.country,
|
|
375
|
+
sc: args.salesChannel ?? config.salesChannel,
|
|
376
|
+
}),
|
|
377
|
+
options,
|
|
378
|
+
),
|
|
379
|
+
},
|
|
369
380
|
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
381
|
+
/** Session API. Shopper-scoped; returns VTEX's `Set-Cookie` values. */
|
|
382
|
+
sessions: {
|
|
383
|
+
get: (options: VtexRequestOptions & { items?: string[] } = {}) =>
|
|
384
|
+
withCookies<Session>(
|
|
385
|
+
url("/api/sessions", { items: options.items?.join(",") }),
|
|
386
|
+
pick(options),
|
|
387
|
+
),
|
|
388
|
+
update: (publicProps: Record<string, { value: string }>, options?: VtexRequestOptions) =>
|
|
389
|
+
withCookies<{ id: string; sessionToken?: string }>(url("/api/sessions"), {
|
|
390
|
+
...options,
|
|
391
|
+
method: "POST",
|
|
392
|
+
body: { public: publicProps },
|
|
393
|
+
}),
|
|
394
|
+
},
|
|
384
395
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
396
|
+
/** VTEX IO's private GraphQL endpoint (`{account}.myvtex.com`). */
|
|
397
|
+
io: {
|
|
398
|
+
graphql: async <T>(
|
|
399
|
+
body: { query: string; variables?: Record<string, unknown>; operationName?: string },
|
|
400
|
+
options?: VtexRequestOptions,
|
|
401
|
+
): Promise<T> => {
|
|
402
|
+
const result = await json<{ data: T; errors?: unknown[] }>(
|
|
403
|
+
`https://${config.account}.myvtex.com/_v/private/graphql/v1`,
|
|
404
|
+
{ ...options, method: "POST", body },
|
|
405
|
+
);
|
|
406
|
+
// GraphQL errors arrive with HTTP 200; their messages can echo inputs, so they stay out.
|
|
407
|
+
if (result.errors?.length) throw new VtexError("io.graphql", 200);
|
|
408
|
+
return result.data;
|
|
409
|
+
},
|
|
410
|
+
},
|
|
400
411
|
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
412
|
+
/** Master Data v1. */
|
|
413
|
+
masterdata: {
|
|
414
|
+
search: <T>(
|
|
415
|
+
entity: string,
|
|
416
|
+
args: VtexRequestOptions & {
|
|
417
|
+
where?: string;
|
|
418
|
+
fields?: string[];
|
|
419
|
+
sort?: string;
|
|
420
|
+
from?: number;
|
|
421
|
+
to?: number;
|
|
422
|
+
} = {},
|
|
423
|
+
) =>
|
|
424
|
+
json<T[]>(
|
|
425
|
+
url(`/api/dataentities/${encodeURIComponent(entity)}/search`, {
|
|
426
|
+
_where: args.where,
|
|
427
|
+
_fields: args.fields?.join(","),
|
|
428
|
+
_sort: args.sort,
|
|
429
|
+
}),
|
|
430
|
+
{
|
|
431
|
+
...pick(args),
|
|
432
|
+
headers: { "rest-range": `resources=${args.from ?? 0}-${args.to ?? 10}` },
|
|
433
|
+
},
|
|
434
|
+
),
|
|
435
|
+
create: (entity: string, document: Record<string, unknown>, options?: VtexRequestOptions) =>
|
|
436
|
+
json<{ Id: string; Href: string; DocumentId: string }>(
|
|
437
|
+
url(`/api/dataentities/${encodeURIComponent(entity)}/documents`),
|
|
438
|
+
{ ...options, method: "POST", body: document },
|
|
439
|
+
),
|
|
440
|
+
},
|
|
441
|
+
};
|
|
431
442
|
}
|
|
432
443
|
|
|
433
444
|
export type VtexClient = ReturnType<typeof createVtexClient>;
|