@decocms/apps-vtex 8.0.0 → 8.1.0-next.1
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/package.json +7 -52
- package/src/index.ts +12 -15
- package/src/utils/constants.ts +2 -56
- package/src/utils/cookieSanitizer.ts +59 -59
- package/src/utils/operationRouter.ts +76 -72
- package/src/utils/types.ts +709 -1557
- package/src/vtexClient.ts +444 -0
- package/src/README.md +0 -6
- 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 -667
- 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/address.ts +0 -115
- package/src/loaders/autocomplete.ts +0 -58
- 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 -113
- package/src/loaders/intelligentSearch/productList.ts +0 -154
- package/src/loaders/intelligentSearch/productListingPage.ts +0 -526
- package/src/loaders/intelligentSearch/suggestions.ts +0 -46
- package/src/loaders/legacy/productDetailsPage.ts +0 -1
- package/src/loaders/legacy/productList.ts +0 -1
- package/src/loaders/legacy/relatedProductsLoader.ts +0 -81
- package/src/loaders/legacy.ts +0 -635
- 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 -159
- 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 -64
- package/src/loaders/workflow.ts +0 -316
- package/src/logo.png +0 -0
- package/src/manifest.gen.ts +0 -89
- package/src/middleware.ts +0 -240
- package/src/mod.ts +0 -157
- 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__/transform.test.ts +0 -769
- package/src/utils/accountLoaders.ts +0 -203
- package/src/utils/authHelpers.ts +0 -85
- package/src/utils/batch.ts +0 -18
- package/src/utils/cartProjection.ts +0 -96
- package/src/utils/cookies.ts +0 -167
- package/src/utils/enrichment.ts +0 -560
- package/src/utils/fetch.ts +0 -107
- package/src/utils/fetchCache.ts +0 -199
- package/src/utils/index.ts +0 -19
- package/src/utils/instrumentedFetch.ts +0 -97
- 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 -435
- 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 -37
- package/src/utils/simulationCache.ts +0 -73
- package/src/utils/sitemap.ts +0 -282
- package/src/utils/slugCache.ts +0 -28
- package/src/utils/slugify.ts +0 -13
- package/src/utils/transform.ts +0 -1522
- package/src/utils/vtexId.ts +0 -138
- package/tsconfig.json +0 -7
package/package.json
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@decocms/apps-vtex",
|
|
3
|
-
"version": "8.0.
|
|
3
|
+
"version": "8.1.0-next.1",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"description": "
|
|
5
|
+
"description": "Thin VTEX API client for Deco sites",
|
|
6
6
|
"repository": {
|
|
7
7
|
"type": "git",
|
|
8
8
|
"url": "https://github.com/decocms/blocks.git",
|
|
@@ -10,67 +10,22 @@
|
|
|
10
10
|
},
|
|
11
11
|
"files": [
|
|
12
12
|
"src",
|
|
13
|
-
"
|
|
13
|
+
"!src/**/*.test.ts",
|
|
14
|
+
"!src/**/__tests__"
|
|
14
15
|
],
|
|
15
16
|
"main": "./src/index.ts",
|
|
16
17
|
"exports": {
|
|
17
|
-
".": "./src/index.ts"
|
|
18
|
-
"./commerceLoaders": "./src/commerceLoaders.ts",
|
|
19
|
-
"./mod": "./src/mod.ts",
|
|
20
|
-
"./schemas": "./src/schemas.ts",
|
|
21
|
-
"./client": "./src/client.ts",
|
|
22
|
-
"./types": "./src/types.ts",
|
|
23
|
-
"./actions": "./src/actions/index.ts",
|
|
24
|
-
"./actions/*": "./src/actions/*.ts",
|
|
25
|
-
"./actions/analytics/*": "./src/actions/analytics/*.ts",
|
|
26
|
-
"./loaders": "./src/loaders/index.ts",
|
|
27
|
-
"./loaders/*": "./src/loaders/*.ts",
|
|
28
|
-
"./utils": "./src/utils/index.ts",
|
|
29
|
-
"./utils/*": "./src/utils/*.ts",
|
|
30
|
-
"./loaders/intelligentSearch/*": "./src/loaders/intelligentSearch/*.ts",
|
|
31
|
-
"./loaders/legacy/*": "./src/loaders/legacy/*.ts",
|
|
32
|
-
"./loaders/workflow/*": "./src/loaders/workflow/*.ts",
|
|
33
|
-
"./inline-loaders/productDetailsPage": "./src/loaders/intelligentSearch/productDetailsPage.ts",
|
|
34
|
-
"./inline-loaders/productListingPage": "./src/loaders/intelligentSearch/productListingPage.ts",
|
|
35
|
-
"./inline-loaders/productList": "./src/loaders/productListFull.ts",
|
|
36
|
-
"./inline-loaders/productListShelf": "./src/loaders/intelligentSearch/productList.ts",
|
|
37
|
-
"./inline-loaders/relatedProducts": "./src/loaders/legacy/relatedProductsLoader.ts",
|
|
38
|
-
"./inline-loaders/suggestions": "./src/loaders/intelligentSearch/suggestions.ts",
|
|
39
|
-
"./inline-loaders/minicart": "./src/loaders/minicart.ts",
|
|
40
|
-
"./inline-loaders/workflowProducts": "./src/loaders/workflow/products.ts",
|
|
41
|
-
"./hooks": "./src/hooks/index.ts",
|
|
42
|
-
"./hooks/*": "./src/hooks/*.ts",
|
|
43
|
-
"./middleware": "./src/middleware.ts",
|
|
44
|
-
"./registry": "./src/registry.ts"
|
|
18
|
+
".": "./src/index.ts"
|
|
45
19
|
},
|
|
46
20
|
"scripts": {
|
|
47
21
|
"build": "tsc",
|
|
48
|
-
"generate:schemas": "bun ../blocks-cli/scripts/generate-app-schemas.ts",
|
|
49
22
|
"test": "vitest run --root ../.. packages/apps-vtex/",
|
|
50
|
-
"typecheck": "tsc --noEmit"
|
|
51
|
-
"lint:unused": "knip"
|
|
23
|
+
"typecheck": "tsc --noEmit"
|
|
52
24
|
},
|
|
53
25
|
"dependencies": {
|
|
54
|
-
"@decocms/blocks": "8.0.
|
|
55
|
-
"@decocms/apps-commerce": "8.0.0",
|
|
56
|
-
"@decocms/apps-website": "8.0.0",
|
|
57
|
-
"@decocms/tanstack": "8.0.0"
|
|
58
|
-
},
|
|
59
|
-
"peerDependencies": {
|
|
60
|
-
"@tanstack/react-query": ">=5.0.0",
|
|
61
|
-
"react": "^19.0.0",
|
|
62
|
-
"react-dom": "^19.0.0"
|
|
63
|
-
},
|
|
64
|
-
"peerDependenciesMeta": {
|
|
65
|
-
"@tanstack/react-query": {
|
|
66
|
-
"optional": true
|
|
67
|
-
}
|
|
26
|
+
"@decocms/blocks": "8.1.0-next.1"
|
|
68
27
|
},
|
|
69
28
|
"devDependencies": {
|
|
70
|
-
"@tanstack/react-query": "^5.96.0",
|
|
71
|
-
"@types/react": "^19.0.0",
|
|
72
|
-
"@types/react-dom": "^19.0.0",
|
|
73
|
-
"knip": "^5.86.0",
|
|
74
29
|
"typescript": "^5.9.0"
|
|
75
30
|
},
|
|
76
31
|
"publishConfig": {
|
package/src/index.ts
CHANGED
|
@@ -1,17 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
|
-
*
|
|
3
|
-
*
|
|
4
|
-
*
|
|
5
|
-
* For actions/loaders/utils, use sub-path imports:
|
|
6
|
-
* import { addItemsToCart } from "@decocms/apps/vtex/actions/checkout"
|
|
7
|
-
* import { searchProducts } from "@decocms/apps/vtex/loaders/catalog"
|
|
8
|
-
* import { slugify } from "@decocms/apps/vtex/utils/slugify"
|
|
9
|
-
*
|
|
10
|
-
* Or barrel imports:
|
|
11
|
-
* import { addItemsToCart } from "@decocms/apps/vtex/actions"
|
|
12
|
-
* import { searchProducts } from "@decocms/apps/vtex/loaders"
|
|
2
|
+
* `@decocms/apps-vtex`: a thin, instrumented VTEX client (`createVtexClient`).
|
|
3
|
+
* See ./vtexClient.ts and /next/upstream-clients.
|
|
13
4
|
*/
|
|
14
|
-
export
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
5
|
+
export {
|
|
6
|
+
createVtexClient,
|
|
7
|
+
type VtexCatalogSearchArgs,
|
|
8
|
+
type VtexClient,
|
|
9
|
+
type VtexClientConfig,
|
|
10
|
+
VtexError,
|
|
11
|
+
type VtexRequestOptions,
|
|
12
|
+
type VtexResponse,
|
|
13
|
+
type VtexSearchArgs,
|
|
14
|
+
} from "./vtexClient";
|
package/src/utils/constants.ts
CHANGED
|
@@ -1,62 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Tuning knobs for
|
|
3
|
-
*
|
|
4
|
-
* Every timeout, limit, and threshold that governs how `resilience.ts`
|
|
5
|
-
* (abort/timeout, circuit breaker, retry budget) and `fetchCache.ts` (SWR +
|
|
6
|
-
* stale-if-error) behave under VTEX slowness/outages lives HERE, in one file,
|
|
7
|
-
* with the reasoning for each number next to it. Auditing or tuning the
|
|
8
|
-
* resilience posture means reading one file, not hunting across two.
|
|
2
|
+
* Tuning knobs for VTEX request resilience (timeouts, retries, circuit
|
|
3
|
+
* breaker), with the reasoning for each number next to it.
|
|
9
4
|
*/
|
|
10
5
|
|
|
11
|
-
// ---------------------------------------------------------------------------
|
|
12
|
-
// fetchCache.ts — SWR + stale-if-error cache for VTEX GET responses
|
|
13
|
-
// ---------------------------------------------------------------------------
|
|
14
|
-
|
|
15
|
-
/** Max distinct cache keys kept in memory; oldest `createdAt` evicted first. */
|
|
16
|
-
export const FETCH_CACHE_MAX_ENTRIES = 500;
|
|
17
|
-
|
|
18
|
-
/**
|
|
19
|
-
* How long a cached response is considered FRESH, keyed by status class.
|
|
20
|
-
* Past this the entry is stale — served via SWR (2xx) while a background
|
|
21
|
-
* refresh runs, or treated as expired (404/5xx). See `ttlForStatus` in
|
|
22
|
-
* `fetchCache.ts`.
|
|
23
|
-
*/
|
|
24
|
-
export const FETCH_CACHE_FRESH_TTL_MS = {
|
|
25
|
-
/** 2xx — 3 min. Catalog/price data doesn't need to be second-fresh. */
|
|
26
|
-
success: 180_000,
|
|
27
|
-
/** 404 — 10s. Short on purpose: a just-published SKU shouldn't 404 long. */
|
|
28
|
-
notFound: 10_000,
|
|
29
|
-
/** 5xx — never "fresh". A server error is never treated as a good cache hit. */
|
|
30
|
-
serverError: 0,
|
|
31
|
-
} as const;
|
|
32
|
-
|
|
33
|
-
/**
|
|
34
|
-
* Stale-if-error window: how long PAST the freshness TTL a last-good 2xx entry
|
|
35
|
-
* may still be served when the origin is failing (5xx / timeout / circuit
|
|
36
|
-
* open). This is the "availability first" knob — a warm key survives a full
|
|
37
|
-
* VTEX outage for up to this long with zero upstream pressure; once an entry
|
|
38
|
-
* is older than `freshTtl + this`, it's considered too stale to serve and the
|
|
39
|
-
* error surfaces (→ the degraded / branded-error path takes over). Mirrors the
|
|
40
|
-
* edge `sie` window sites configure for the product/listing cache profiles.
|
|
41
|
-
*/
|
|
42
|
-
export const FETCH_CACHE_STALE_IF_ERROR_MS = 86_400_000; // 24h
|
|
43
|
-
|
|
44
|
-
/**
|
|
45
|
-
* Inflight-slot backstop timeout. Bounds how long a single hung `fetch()` can
|
|
46
|
-
* hold a dedup entry alive. Without this, a VTEX subrequest that never
|
|
47
|
-
* settles leaks the inflight Map slot forever and every subsequent request
|
|
48
|
-
* for the same cache key joins the zombie Promise, pinning memory until
|
|
49
|
-
* `exceededMemory` (observed in prod: 514 hard crashes / 24h on a PLP route).
|
|
50
|
-
*
|
|
51
|
-
* MUST stay ABOVE `RESILIENCE_CONFIG.totalTimeoutMs` (see below) — the
|
|
52
|
-
* resilient fetch already owns real per-call abort/timeout, so this is only a
|
|
53
|
-
* last-resort backstop. If it fired first it would kill a slow-but-recoverable
|
|
54
|
-
* response the resilience layer was about to deliver, discard the good body
|
|
55
|
-
* (it never reaches the cache), and free the dedup slot early so a concurrent
|
|
56
|
-
* request launches a duplicate upstream fetch.
|
|
57
|
-
*/
|
|
58
|
-
export const FETCH_CACHE_INFLIGHT_BACKSTOP_MS = 15_000;
|
|
59
|
-
|
|
60
6
|
// ---------------------------------------------------------------------------
|
|
61
7
|
// resilience.ts — abort/timeout, retry budget, circuit breaker
|
|
62
8
|
// ---------------------------------------------------------------------------
|
|
@@ -29,36 +29,36 @@
|
|
|
29
29
|
* `*.vtexcommercestable.com.br` / `*.myvtex.com` / `secure.<storefront>` APIs.
|
|
30
30
|
*/
|
|
31
31
|
export const VTEX_COOKIE_PREFIXES: readonly string[] = [
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
32
|
+
"VtexIdclientAutCookie",
|
|
33
|
+
"checkout.vtex.com",
|
|
34
|
+
"CheckoutOrderFormOwnership",
|
|
35
|
+
"vtex_session",
|
|
36
|
+
"vtex_segment",
|
|
37
|
+
"vtex_is_",
|
|
38
|
+
"janus_sid",
|
|
39
39
|
];
|
|
40
40
|
|
|
41
41
|
export type DropReason = "non_ascii" | "not_in_allowlist" | "malformed";
|
|
42
42
|
|
|
43
43
|
export interface DroppedCookie {
|
|
44
|
-
|
|
45
|
-
|
|
44
|
+
name: string;
|
|
45
|
+
reason: DropReason;
|
|
46
46
|
}
|
|
47
47
|
|
|
48
48
|
export interface CookieSanitizeResult {
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
49
|
+
/** Cookie header string ready to forward (may be empty). */
|
|
50
|
+
cookies: string;
|
|
51
|
+
/** Cookies that were filtered out, with the reason per cookie. */
|
|
52
|
+
dropped: DroppedCookie[];
|
|
53
53
|
}
|
|
54
54
|
|
|
55
55
|
export interface CookieSanitizeOptions {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
56
|
+
/**
|
|
57
|
+
* When true, additionally enforces an allowlist: only cookies whose name
|
|
58
|
+
* starts with one of `VTEX_COOKIE_PREFIXES` are kept.
|
|
59
|
+
* @default false
|
|
60
|
+
*/
|
|
61
|
+
allowlist?: boolean;
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/**
|
|
@@ -93,47 +93,47 @@ const ASCII_VALUE_RE = /^[\x20-\x7E]*$/;
|
|
|
93
93
|
* ```
|
|
94
94
|
*/
|
|
95
95
|
export function sanitizeOutboundCookieHeader(
|
|
96
|
-
|
|
97
|
-
|
|
96
|
+
raw: string,
|
|
97
|
+
opts: CookieSanitizeOptions = {},
|
|
98
98
|
): CookieSanitizeResult {
|
|
99
|
-
|
|
99
|
+
if (!raw) return { cookies: "", dropped: [] };
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
101
|
+
const kept: string[] = [];
|
|
102
|
+
const dropped: DroppedCookie[] = [];
|
|
103
|
+
const allowlist = opts.allowlist === true;
|
|
104
104
|
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
105
|
+
for (const segment of raw.split(";")) {
|
|
106
|
+
const pair = segment.trim();
|
|
107
|
+
if (!pair) continue;
|
|
108
108
|
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
109
|
+
const eq = pair.indexOf("=");
|
|
110
|
+
if (eq <= 0) {
|
|
111
|
+
dropped.push({ name: pair.slice(0, 32), reason: "malformed" });
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
114
|
|
|
115
|
-
|
|
116
|
-
|
|
115
|
+
const name = pair.slice(0, eq);
|
|
116
|
+
const value = pair.slice(eq + 1);
|
|
117
117
|
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
118
|
+
if (!TOKEN_RE.test(name)) {
|
|
119
|
+
dropped.push({ name, reason: "malformed" });
|
|
120
|
+
continue;
|
|
121
|
+
}
|
|
122
122
|
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
123
|
+
if (!ASCII_VALUE_RE.test(value)) {
|
|
124
|
+
dropped.push({ name, reason: "non_ascii" });
|
|
125
|
+
continue;
|
|
126
|
+
}
|
|
127
127
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
128
|
+
if (allowlist && !VTEX_COOKIE_PREFIXES.some((p) => name.startsWith(p))) {
|
|
129
|
+
dropped.push({ name, reason: "not_in_allowlist" });
|
|
130
|
+
continue;
|
|
131
|
+
}
|
|
132
132
|
|
|
133
|
-
|
|
134
|
-
|
|
133
|
+
kept.push(`${name}=${value}`);
|
|
134
|
+
}
|
|
135
135
|
|
|
136
|
-
|
|
136
|
+
return { cookies: kept.join("; "), dropped };
|
|
137
137
|
}
|
|
138
138
|
|
|
139
139
|
/**
|
|
@@ -142,7 +142,7 @@ export function sanitizeOutboundCookieHeader(
|
|
|
142
142
|
* Equivalent to `sanitizeOutboundCookieHeader(raw, { allowlist: true }).cookies`.
|
|
143
143
|
*/
|
|
144
144
|
export function extractVtexCookies(raw: string): string {
|
|
145
|
-
|
|
145
|
+
return sanitizeOutboundCookieHeader(raw, { allowlist: true }).cookies;
|
|
146
146
|
}
|
|
147
147
|
|
|
148
148
|
/**
|
|
@@ -158,16 +158,16 @@ const _warned = new Set<string>();
|
|
|
158
158
|
* arbitrary `host` label so we can correlate in logs.
|
|
159
159
|
*/
|
|
160
160
|
export function warnDroppedCookies(dropped: DroppedCookie[], host: string): void {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
161
|
+
if (dropped.length === 0) return;
|
|
162
|
+
for (const d of dropped) {
|
|
163
|
+
const key = `${host}::${d.name}::${d.reason}`;
|
|
164
|
+
if (_warned.has(key)) continue;
|
|
165
|
+
_warned.add(key);
|
|
166
|
+
console.warn(`[vtex.cookie.dropped] host=${host} name=${d.name} reason=${d.reason}`);
|
|
167
|
+
}
|
|
168
168
|
}
|
|
169
169
|
|
|
170
170
|
/** Reset the dedup set — exposed for tests. */
|
|
171
171
|
export function _resetCookieWarnDedupForTests(): void {
|
|
172
|
-
|
|
172
|
+
_warned.clear();
|
|
173
173
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* URL-derived operation name router for VTEX API calls.
|
|
3
3
|
*
|
|
4
|
-
* Plugged into `@decocms/
|
|
4
|
+
* Plugged into `createInstrumentedFetch` (`@decocms/blocks/fetch`) via the
|
|
5
5
|
* `resolveOperation(url, method)` option. The resolved string becomes the
|
|
6
6
|
* span suffix (`vtex.<operation>`) and the `fetch.operation` span +
|
|
7
7
|
* histogram label, so it must be:
|
|
@@ -25,8 +25,8 @@
|
|
|
25
25
|
type OperationResolver = string | ((match: RegExpMatchArray, method: string) => string);
|
|
26
26
|
|
|
27
27
|
interface Matcher {
|
|
28
|
-
|
|
29
|
-
|
|
28
|
+
pattern: RegExp;
|
|
29
|
+
operation: OperationResolver;
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
const m = (pattern: RegExp, operation: OperationResolver): Matcher => ({ pattern, operation });
|
|
@@ -42,66 +42,66 @@ const m = (pattern: RegExp, operation: OperationResolver): Matcher => ({ pattern
|
|
|
42
42
|
* prefixes them with the integration name at span time.
|
|
43
43
|
*/
|
|
44
44
|
const MATCHERS: ReadonlyArray<Matcher> = [
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
45
|
+
m(/^\/api\/io\/_v\/api\/intelligent-search\/([a-z_-]+)/, (mm) => `intelligent-search.${mm[1]}`),
|
|
46
|
+
m(/^\/_v\/private\/graphql\/v1/, "io.graphql"),
|
|
47
|
+
m(/^\/_v\/segment\//, "io.segment"),
|
|
48
48
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
49
|
+
m(/^\/api\/checkout\/pub\/orderForm\/[^/]+\/items\/update/, (_mm, method) =>
|
|
50
|
+
method === "POST" ? "checkout.orderform.items.update" : "checkout.orderform.items",
|
|
51
|
+
),
|
|
52
|
+
m(/^\/api\/checkout\/pub\/orderForm\/[^/]+\/items/, (_mm, method) => {
|
|
53
|
+
if (method === "DELETE") return "checkout.orderform.items.remove";
|
|
54
|
+
if (method === "PATCH" || method === "PUT") return "checkout.orderform.items.update";
|
|
55
|
+
return "checkout.orderform.items.add";
|
|
56
|
+
}),
|
|
57
|
+
m(/^\/api\/checkout\/pub\/orderForm\/[^/]+\/coupons/, "checkout.orderform.coupons"),
|
|
58
|
+
m(/^\/api\/checkout\/pub\/orderForm\/[^/]+\/profile/, "checkout.orderform.profile"),
|
|
59
|
+
m(/^\/api\/checkout\/pub\/orderForm\/[^/]+\/shippingData/, "checkout.orderform.shipping"),
|
|
60
|
+
m(/^\/api\/checkout\/pub\/orderForm\/[^/]+\/paymentData/, "checkout.orderform.payment"),
|
|
61
|
+
m(/^\/api\/checkout\/pub\/orderForm\/[^/]+/, (_mm, method) =>
|
|
62
|
+
method === "GET" ? "checkout.orderform.get" : "checkout.orderform.update",
|
|
63
|
+
),
|
|
64
|
+
m(/^\/api\/checkout\/pub\/orderForm(?:\/?$)/, (_mm, method) =>
|
|
65
|
+
method === "POST" ? "checkout.orderform.create" : "checkout.orderform.get",
|
|
66
|
+
),
|
|
67
|
+
m(/^\/api\/checkout\/pub\/orderForms\/simulation/, "checkout.simulation"),
|
|
68
|
+
m(/^\/api\/checkout\/pub\/regions/, "checkout.regions"),
|
|
69
|
+
m(/^\/api\/checkout\/pub\/postal-code/, "checkout.postal-code"),
|
|
70
70
|
|
|
71
|
-
|
|
72
|
-
|
|
71
|
+
m(/^\/api\/sessions/, (_mm, method) => (method === "POST" ? "sessions.update" : "sessions.get")),
|
|
72
|
+
m(/^\/api\/segments\//, "segments.get"),
|
|
73
73
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
74
|
+
m(/^\/api\/catalog_system\/pub\/portal\/pagetype\//, "catalog.pagetype"),
|
|
75
|
+
m(
|
|
76
|
+
/^\/api\/catalog_system\/pub\/products\/crossselling\/([^/]+)/,
|
|
77
|
+
(mm) => `catalog.crossselling.${mm[1]}`,
|
|
78
|
+
),
|
|
79
|
+
m(/^\/api\/catalog_system\/pub\/products\/variations\//, "catalog.products.variations"),
|
|
80
|
+
m(/^\/api\/catalog_system\/pub\/products\/search/, "catalog.products.search"),
|
|
81
|
+
m(/^\/api\/catalog_system\/pub\/facets\/search/, "catalog.facets.search"),
|
|
82
|
+
m(/^\/api\/catalog_system\/pub\/category\/tree/, "catalog.category.tree"),
|
|
83
|
+
m(/^\/api\/catalog_system\/(?:pub|pvt)\/specification/, "catalog.specification"),
|
|
84
|
+
m(/^\/api\/catalog_system\/pub\/brand/, "catalog.brand"),
|
|
85
|
+
m(/^\/api\/catalog_system\/pvt\/sku\//, "catalog.sku"),
|
|
86
|
+
m(/^\/api\/catalog_system\//, "catalog.other"),
|
|
87
87
|
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
88
|
+
m(/^\/api\/wishlist\//, "wishlist"),
|
|
89
|
+
m(/^\/api\/profile-system\/profile\//, "profile"),
|
|
90
|
+
m(/^\/api\/dataentities\/([^/]+)/, (mm) => `masterdata.${mm[1]}`),
|
|
91
91
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
92
|
+
m(/^\/api\/oms\/user\/orders\/[^/]+\/cancel/, "oms.orders.cancel"),
|
|
93
|
+
m(/^\/api\/oms\/user\/orders/, "oms.orders"),
|
|
94
|
+
m(/^\/api\/oms\/pvt\/orders/, "oms.orders.pvt"),
|
|
95
95
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
96
|
+
m(/^\/api\/vtexid\/pub\/logout/, "vtexid.logout"),
|
|
97
|
+
m(/^\/api\/vtexid\/pub\/authentication\/start/, "vtexid.authentication.start"),
|
|
98
|
+
m(/^\/api\/vtexid\/pub\/authentication\/[a-z]+\/validate/, "vtexid.authentication.validate"),
|
|
99
|
+
m(/^\/api\/vtexid\/pub\/authenticated\/user/, "vtexid.user"),
|
|
100
|
+
m(/^\/api\/vtexid\//, "vtexid.other"),
|
|
101
101
|
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
102
|
+
m(/^\/api\/events\/v1\//, "events.send"),
|
|
103
|
+
m(/^\/sitemap.*\.xml$/, "sitemap"),
|
|
104
|
+
m(/^\/api\/license-manager/, "license-manager"),
|
|
105
105
|
];
|
|
106
106
|
|
|
107
107
|
/**
|
|
@@ -118,22 +118,26 @@ const MATCHERS: ReadonlyArray<Matcher> = [
|
|
|
118
118
|
* });
|
|
119
119
|
* ```
|
|
120
120
|
*/
|
|
121
|
+
/** The URL's pathname; tolerates relative or malformed URLs by stripping `?`/`#`. */
|
|
122
|
+
function extractPathname(url: string): string {
|
|
123
|
+
try {
|
|
124
|
+
return new URL(url).pathname;
|
|
125
|
+
} catch {
|
|
126
|
+
const qs = url.indexOf("?");
|
|
127
|
+
const hash = url.indexOf("#");
|
|
128
|
+
const end = [qs, hash].filter((i) => i >= 0).sort((a, b) => a - b)[0];
|
|
129
|
+
return end === undefined ? url : url.slice(0, end);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
|
|
121
133
|
export function vtexOperationRouter(url: string, method: string): string | undefined {
|
|
122
|
-
|
|
123
|
-
try {
|
|
124
|
-
pathname = new URL(url).pathname;
|
|
125
|
-
} catch {
|
|
126
|
-
const qs = url.indexOf("?");
|
|
127
|
-
const hash = url.indexOf("#");
|
|
128
|
-
const end = [qs, hash].filter((i) => i >= 0).sort((a, b) => a - b)[0];
|
|
129
|
-
pathname = end === undefined ? url : url.slice(0, end);
|
|
130
|
-
}
|
|
134
|
+
const pathname = extractPathname(url);
|
|
131
135
|
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
136
|
+
const upperMethod = method.toUpperCase();
|
|
137
|
+
for (const { pattern, operation } of MATCHERS) {
|
|
138
|
+
const match = pathname.match(pattern);
|
|
139
|
+
if (!match) continue;
|
|
140
|
+
return typeof operation === "function" ? operation(match, upperMethod) : operation;
|
|
141
|
+
}
|
|
142
|
+
return undefined;
|
|
139
143
|
}
|