@decocms/apps 1.15.0 → 1.15.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.
Files changed (2) hide show
  1. package/package.json +1 -1
  2. package/vtex/client.ts +51 -6
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@decocms/apps",
3
- "version": "1.15.0",
3
+ "version": "1.15.1",
4
4
  "type": "module",
5
5
  "description": "Deco commerce apps for TanStack Start - Shopify, VTEX, commerce types, analytics utils",
6
6
  "exports": {
package/vtex/client.ts CHANGED
@@ -241,11 +241,7 @@ export async function vtexFetchResponse(
241
241
 
242
242
  const response = await _fetch(url, {
243
243
  ...init,
244
- headers: {
245
- ...authHeaders(),
246
- ...(segmentCookie ? { cookie: segmentCookie } : {}),
247
- ...init?.headers,
248
- },
244
+ headers: mergeHeaders(authHeaders(), segmentCookie, init?.headers),
249
245
  });
250
246
  if (!response.ok) {
251
247
  throw new Error(`VTEX API error: ${response.status} ${response.statusText} - ${url}`);
@@ -253,6 +249,37 @@ export async function vtexFetchResponse(
253
249
  return response;
254
250
  }
255
251
 
252
+ /**
253
+ * Combine framework headers + optional segment cookie + caller headers,
254
+ * preserving the precedence "caller wins" regardless of whether the
255
+ * caller passed `Headers`, `string[][]`, or `Record<string, string>`.
256
+ *
257
+ * Why a helper: the naive `{ ...authHeaders, ...init?.headers }` spread
258
+ * silently collapses a `Headers` instance to `{}` (Headers has no own
259
+ * enumerable entries), which means any cookies the caller put on a
260
+ * Headers object are lost on the wire. The `createVtexCheckoutProxy`
261
+ * factory passes init with Headers, which makes this the failure mode
262
+ * for every forwarder that relies on browser-supplied cookies reaching
263
+ * VTEX. Funneling all merges through the `Headers` constructor (which
264
+ * correctly absorbs every HeadersInit shape) keeps the bug from
265
+ * sneaking back in.
266
+ */
267
+ function mergeHeaders(
268
+ auth: Record<string, string>,
269
+ segmentCookie: string | null,
270
+ callerHeaders: HeadersInit | undefined,
271
+ ): Headers {
272
+ const merged = new Headers(auth);
273
+ if (segmentCookie) merged.set("cookie", segmentCookie);
274
+ if (callerHeaders) {
275
+ const incoming = new Headers(callerHeaders);
276
+ incoming.forEach((value, key) => {
277
+ merged.set(key, value);
278
+ });
279
+ }
280
+ return merged;
281
+ }
282
+
256
283
  export async function vtexFetch<T>(path: string, init?: InstrumentedFetchInit): Promise<T> {
257
284
  const response = await vtexFetchResponse(path, init);
258
285
  return response.json();
@@ -282,12 +309,22 @@ export async function vtexCachedFetch<T>(
282
309
  ? { ttl: cacheOpts.cacheTTL }
283
310
  : undefined;
284
311
 
312
+ // Mirrors vtexFetchResponse: Legacy Catalog and several other GET
313
+ // endpoints gate regional seller availability on the `vtex_segment`
314
+ // cookie. Cached GETs (PDP / shelf product lookups) must see the same
315
+ // regionalization the rest of the stack does — otherwise sites have
316
+ // to wrap _fetch themselves to forward the cookie, which is easy to
317
+ // get subtly wrong (especially around HeadersInit shapes). Inline
318
+ // here keeps the surface small; if a third callsite appears we
319
+ // extract a shared helper.
320
+ const segmentCookie = !hasCookieHeader(init?.headers) ? getSegmentCookieHeader() : null;
321
+
285
322
  return fetchWithCache<T>(
286
323
  url,
287
324
  () =>
288
325
  _fetch(url, {
289
326
  ...init,
290
- headers: { ...authHeaders(), ...init?.headers },
327
+ headers: mergeHeaders(authHeaders(), segmentCookie, init?.headers),
291
328
  }),
292
329
  opts,
293
330
  );
@@ -383,6 +420,14 @@ export async function intelligentSearch<T>(
383
420
  const headers: Record<string, string> = { ...authHeaders() };
384
421
  if (opts?.cookieHeader) {
385
422
  headers.cookie = opts.cookieHeader;
423
+ } else {
424
+ // IS already gets regionId on the query string above, but some
425
+ // internal IS flows (and downstream services it consults) still
426
+ // honor the `vtex_segment` cookie — forward it when the caller
427
+ // didn't pass an explicit one. See vtexCachedFetch for the same
428
+ // rationale.
429
+ const segmentCookie = getSegmentCookieHeader();
430
+ if (segmentCookie) headers.cookie = segmentCookie;
386
431
  }
387
432
 
388
433
  const fullUrl = url.toString();