@faststore/api 1.12.48 → 1.12.51

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/api.esm.js CHANGED
@@ -18,7 +18,7 @@ import { trace, context, SpanKind } from '@opentelemetry/api';
18
18
  import { SeverityNumber } from '@opentelemetry/api-logs';
19
19
 
20
20
  var name = "@faststore/api";
21
- var version = "1.12.47";
21
+ var version = "1.12.50";
22
22
  var license = "MIT";
23
23
  var main = "dist/index.js";
24
24
  var typings = "dist/index.d.ts";
@@ -54,7 +54,7 @@ var dependencies = {
54
54
  };
55
55
  var devDependencies = {
56
56
  "@envelop/core": "^2.6.0",
57
- "@faststore/shared": "^1.12.43",
57
+ "@faststore/shared": "^1.12.48",
58
58
  "@graphql-codegen/cli": "2.2.0",
59
59
  "@graphql-codegen/typescript": "2.2.2",
60
60
  concurrently: "^6.2.1",
@@ -88,7 +88,7 @@ var packageJson = {
88
88
  };
89
89
 
90
90
  const USER_AGENT = `${packageJson.name}@${packageJson.version}`;
91
- const fetchAPI = async (info, init) => {
91
+ const fetchAPI = async (info, init, options) => {
92
92
  const response = await fetch(info, { ...init,
93
93
  headers: { ...(init == null ? void 0 : init.headers),
94
94
  'User-Agent': USER_AGENT
@@ -96,6 +96,10 @@ const fetchAPI = async (info, init) => {
96
96
  });
97
97
 
98
98
  if (response.ok) {
99
+ if (options != null && options.storeCookies) {
100
+ options.storeCookies(response.headers);
101
+ }
102
+
99
103
  return response.status !== 204 ? response.json() : undefined;
100
104
  }
101
105
 
@@ -104,17 +108,169 @@ const fetchAPI = async (info, init) => {
104
108
  throw new Error(text);
105
109
  };
106
110
 
111
+ const MATCH_FIRST_SET_COOKIE_KEY_VALUE = /^([^=]+)=([^;]*)/;
112
+ /**
113
+ * Splits a combined `set-cookie` header value (cookies joined by `, `) into
114
+ * individual cookies. Commas inside `expires=...` dates are kept.
115
+ */
116
+
117
+ const splitSetCookieHeader = value => value.split(/,(?=\s*[^;,=\s]+=)/).map(setCookie => setCookie.trim());
118
+ /**
119
+ * Returns every `set-cookie` value of a fetch response.
120
+ *
121
+ * `Headers.getSetCookie()` only exists on Node >= 18.14 (undici). FastStore v1
122
+ * stores may still run on Node 16, where isomorphic-unfetch falls back to
123
+ * node-fetch v2, which exposes repeated headers through `raw()` instead.
124
+ */
125
+
126
+
127
+ const getSetCookies = headers => {
128
+ const maybeHeaders = headers;
129
+
130
+ if (typeof maybeHeaders.getSetCookie === 'function') {
131
+ return maybeHeaders.getSetCookie();
132
+ }
133
+
134
+ if (typeof maybeHeaders.raw === 'function') {
135
+ var _maybeHeaders$raw$set;
136
+
137
+ return (_maybeHeaders$raw$set = maybeHeaders.raw()['set-cookie']) != null ? _maybeHeaders$raw$set : [];
138
+ }
139
+
140
+ const setCookie = headers.get('set-cookie');
141
+ return setCookie ? splitSetCookieHeader(setCookie) : [];
142
+ };
143
+ /**
144
+ * This function updates the ctx.storage.cookies, that is used in each request.
145
+ *
146
+ * ctx.storage.cookies is a Map<string[1], Record<string, string>[2]> where,
147
+ * [1] cookie key
148
+ * [2] {
149
+ * value: cookie value,
150
+ * setCookie: setCookie used in browser response
151
+ * }
152
+ */
153
+
154
+ const updatesContextStorageCookies = (ctx, setCookieValue) => {
155
+ const matchCookie = setCookieValue.match(MATCH_FIRST_SET_COOKIE_KEY_VALUE);
156
+
157
+ if (matchCookie) {
158
+ const cookieKey = matchCookie[1];
159
+ const cookieValue = matchCookie[2];
160
+ ctx.storage.cookies.set(cookieKey, {
161
+ value: cookieValue,
162
+ setCookie: setCookieValue
163
+ });
164
+ }
165
+ };
166
+ const setCookie = (ctx, headers) => {
167
+ getSetCookies(headers).forEach(setCookieValue => updatesContextStorageCookies(ctx, setCookieValue));
168
+ };
169
+ const getStoreCookie = ctx => headers => setCookie(ctx, headers);
170
+ /**
171
+ * This function returns a modified copy of the original cookie header (ctx.headers.cookie from the first request)
172
+ * with the cookie values that comes in each request (ctx.storage.cookies).
173
+ * If there is no cookies in storage, the ctx.headers?.cookie is used
174
+ *
175
+ * ctx.storage.cookies is a Map<string[1], Record<string, string>[2]> where,
176
+ * [1] cookie key
177
+ * [2] {
178
+ * value: cookie value,
179
+ * setCookie: setCookie used in browser response
180
+ * }
181
+ */
182
+
183
+ const getUpdatedCookie = ctx => {
184
+ var _ctx$headers;
185
+
186
+ if (!((_ctx$headers = ctx.headers) != null && _ctx$headers.cookie)) {
187
+ return null;
188
+ }
189
+
190
+ const contextStorageCookies = Array.from(ctx.storage.cookies.entries());
191
+
192
+ if (contextStorageCookies.length === 0) {
193
+ return ctx.headers.cookie;
194
+ }
195
+
196
+ return contextStorageCookies.reduce((existingCookies, [storageCookieKey, {
197
+ value: storageCookieValue
198
+ }]) => updatesCookieValueByKey(existingCookies, storageCookieKey, storageCookieValue), ctx.headers.cookie);
199
+ };
200
+ const getWithCookie = ctx => function withCookie(headers) {
201
+ const updatedCookie = getUpdatedCookie(ctx);
202
+
203
+ if (!updatedCookie) {
204
+ return headers;
205
+ }
206
+
207
+ return { ...headers,
208
+ cookie: updatedCookie
209
+ };
210
+ };
211
+ /**
212
+ * This function updates the cookie value based on its key
213
+ *
214
+ * const existingCookies = 'key=value1; key2=; key3=value3';
215
+ * const storageCookieKey = 'key2';
216
+ * const storageCookieValue = 'value2'
217
+ *
218
+ * updatesCookieValueByKey(existingCookies, storageCookieKey, storageCookieValue) returns 'key=value1; key2=value2; key3=value3';
219
+ */
220
+
221
+ const updatesCookieValueByKey = (existingCookies, storageCookieKey, storageCookieValue) => {
222
+ const MATCH_COOKIE_KEY_VALUE = new RegExp(`(${storageCookieKey})=([^;]*)`);
223
+ const cookieParts = existingCookies.match(MATCH_COOKIE_KEY_VALUE); // replaces original cookie with the one coming from storage
224
+
225
+ if (cookieParts) {
226
+ return existingCookies.replace(cookieParts[0], `${cookieParts[1]}=${storageCookieValue}`);
227
+ } // add new storage cookie to the original list of cookies
228
+
229
+
230
+ return `${existingCookies};${storageCookieKey}=${storageCookieValue}`;
231
+ };
232
+
107
233
  const BASE_INIT = {
108
234
  method: 'POST',
109
235
  headers: {
110
236
  'content-type': 'application/json'
111
237
  }
112
238
  };
239
+
240
+ const getHeader = (headers, name) => {
241
+ const value = headers == null ? void 0 : headers[name];
242
+ return Array.isArray(value) ? value[0] : value;
243
+ };
244
+ /**
245
+ * Host the Checkout API uses as the domain of the cookies it sets. Checkout
246
+ * only honors it for hosts registered in the account, so a forged value is
247
+ * ignored there.
248
+ */
249
+
250
+
251
+ const getForwardedHost = headers => {
252
+ var _getHeader;
253
+
254
+ // Proxies may append values: "client, proxy". Only the first one is the store host.
255
+ const host = ((_getHeader = getHeader(headers, 'x-forwarded-host')) == null ? void 0 : _getHeader.split(',')[0].trim()) || getHeader(headers, 'host') || '';
256
+ return host.trim().replace(/^www\./, '');
257
+ };
258
+
113
259
  const VtexCommerce = ({
114
260
  account,
115
261
  environment
116
262
  }, ctx) => {
117
263
  const base = `https://${account}.${environment}.com.br`;
264
+ const storeCookies = getStoreCookie(ctx);
265
+ const withCookie = getWithCookie(ctx);
266
+ const forwardedHost = getForwardedHost(ctx.headers); // Checkout calls forward the shopper cookies and keep the cookies Checkout
267
+ // sets (e.g. CheckoutOrderFormOwnership) so they can reach the browser.
268
+
269
+ const checkoutHeaders = () => withCookie({
270
+ 'content-type': 'application/json',
271
+ 'X-FORWARDED-HOST': forwardedHost
272
+ });
273
+
118
274
  return {
119
275
  catalog: {
120
276
  salesChannel: sc => fetchAPI(`${base}/api/catalog_system/pub/saleschannel/${sc}`),
@@ -165,7 +321,10 @@ const VtexCommerce = ({
165
321
  }
166
322
 
167
323
  return fetchAPI(`${base}/api/checkout/pub/orderForm/${id}/attachments/shippingData`, { ...BASE_INIT,
324
+ headers: checkoutHeaders(),
168
325
  body: JSON.stringify(body)
326
+ }, {
327
+ storeCookies
169
328
  });
170
329
  },
171
330
  orderForm: ({
@@ -180,7 +339,11 @@ const VtexCommerce = ({
180
339
  refreshOutdatedData: refreshOutdatedData.toString(),
181
340
  sc: salesChannel
182
341
  });
183
- return fetchAPI(`${base}/api/checkout/pub/orderForm/${id}?${params.toString()}`, BASE_INIT);
342
+ return fetchAPI(`${base}/api/checkout/pub/orderForm/${id}?${params.toString()}`, { ...BASE_INIT,
343
+ headers: checkoutHeaders()
344
+ }, {
345
+ storeCookies
346
+ });
184
347
  },
185
348
  updateOrderFormItems: ({
186
349
  id,
@@ -194,11 +357,14 @@ const VtexCommerce = ({
194
357
  sc: salesChannel
195
358
  });
196
359
  return fetchAPI(`${base}/api/checkout/pub/orderForm/${id}/items?${params}`, { ...BASE_INIT,
360
+ headers: checkoutHeaders(),
197
361
  body: JSON.stringify({
198
362
  orderItems,
199
363
  noSplitItem: !shouldSplitItem
200
364
  }),
201
365
  method: 'PATCH'
366
+ }, {
367
+ storeCookies
202
368
  });
203
369
  },
204
370
  setCustomData: ({
@@ -208,10 +374,13 @@ const VtexCommerce = ({
208
374
  value
209
375
  }) => {
210
376
  return fetchAPI(`${base}/api/checkout/pub/orderForm/${id}/customData/${appId}/${key}`, { ...BASE_INIT,
377
+ headers: checkoutHeaders(),
211
378
  body: JSON.stringify({
212
379
  value
213
380
  }),
214
381
  method: 'PUT'
382
+ }, {
383
+ storeCookies
215
384
  });
216
385
  },
217
386
  region: async ({
@@ -276,22 +445,23 @@ const IntelligentSearch = ({
276
445
  };
277
446
  };
278
447
 
279
- const getRegionFacet = () => {
448
+ const getRegionFacet = regionIdFromQuery => {
280
449
  const {
281
450
  regionId
282
451
  } = ctx.storage.channel;
452
+ const facet = regionIdFromQuery != null ? regionIdFromQuery : regionId;
283
453
 
284
- if (!regionId) {
454
+ if (!facet) {
285
455
  return null;
286
456
  }
287
457
 
288
458
  return {
289
459
  key: REGION_KEY,
290
- value: regionId
460
+ value: facet
291
461
  };
292
462
  };
293
463
 
294
- const addDefaultFacets = facets => {
464
+ const addDefaultFacets = (facets, regionIdFromQuery) => {
295
465
  var _facets$find, _facets$find2;
296
466
 
297
467
  const withDefaltFacets = facets.filter(({
@@ -302,7 +472,7 @@ const IntelligentSearch = ({
302
472
  }) => key === POLICY_KEY)) != null ? _facets$find : getPolicyFacet();
303
473
  const regionFacet = (_facets$find2 = facets.find(({
304
474
  key
305
- }) => key === REGION_KEY)) != null ? _facets$find2 : getRegionFacet();
475
+ }) => key === REGION_KEY)) != null ? _facets$find2 : getRegionFacet(regionIdFromQuery);
306
476
 
307
477
  if (policyFacet !== null) {
308
478
  withDefaltFacets.push(policyFacet);
@@ -322,7 +492,9 @@ const IntelligentSearch = ({
322
492
  sort = '',
323
493
  selectedFacets = [],
324
494
  type,
325
- fuzzy = 'auto'
495
+ fuzzy = 'auto',
496
+ showInvisibleItems,
497
+ regionId
326
498
  }) => {
327
499
  const params = new URLSearchParams({
328
500
  page: (page + 1).toString(),
@@ -333,11 +505,15 @@ const IntelligentSearch = ({
333
505
  locale: ctx.storage.locale
334
506
  });
335
507
 
508
+ if (showInvisibleItems) {
509
+ params.append('show-invisible-items', 'true');
510
+ }
511
+
336
512
  if (hideUnavailableItems !== undefined) {
337
513
  params.append('hideUnavailableItems', hideUnavailableItems.toString());
338
514
  }
339
515
 
340
- const pathname = addDefaultFacets(selectedFacets).map(({
516
+ const pathname = addDefaultFacets(selectedFacets, regionId).map(({
341
517
  key,
342
518
  value
343
519
  }) => `${key}/${value}`).join('/');
@@ -530,13 +706,16 @@ const isBadRequestError = error => {
530
706
  };
531
707
 
532
708
  const getSkuLoader = (_, clients) => {
533
- const loader = async skuIds => {
709
+ const loader = async keys => {
710
+ const skuIds = keys.map(key => key.split('-')[0]);
711
+ const showInvisibleItems = keys.some(key => key.split('-')[1] === 'invisibleItems');
534
712
  const {
535
713
  products
536
714
  } = await clients.search.products({
537
715
  query: `sku:${skuIds.join(';')}`,
538
716
  page: 0,
539
- count: skuIds.length
717
+ count: skuIds.length,
718
+ showInvisibleItems
540
719
  });
541
720
  const skuBySkuId = products.reduce((acc, product) => {
542
721
  for (const sku of product.items) {
@@ -916,6 +1095,11 @@ const findChannel = facets => {
916
1095
 
917
1096
  return (_facets$find$value4 = facets == null ? void 0 : (_facets$find4 = facets.find(facet => facet.key === 'channel')) == null ? void 0 : _facets$find4.value) != null ? _facets$find$value4 : null;
918
1097
  };
1098
+ const findRegionId = facets => {
1099
+ var _facets$find$value5, _facets$find5;
1100
+
1101
+ return (_facets$find$value5 = facets == null ? void 0 : (_facets$find5 = facets.find(facet => facet.key === 'regionId')) == null ? void 0 : _facets$find5.value) != null ? _facets$find$value5 : undefined;
1102
+ };
919
1103
 
920
1104
  /**
921
1105
  * More info at: https://en.wikipedia.org/wiki/Order_statistic
@@ -1154,7 +1338,7 @@ const orderFormToCart = async (form, skuLoader) => {
1154
1338
  order: {
1155
1339
  orderNumber: form.orderFormId,
1156
1340
  acceptedOffer: form.items.map(async item => ({ ...item,
1157
- product: await skuLoader.load(item.id)
1341
+ product: await skuLoader.load(`${item.id}-invisibleItems`)
1158
1342
  }))
1159
1343
  },
1160
1344
  messages: form.messages.map(({
@@ -1793,6 +1977,7 @@ const Query = {
1793
1977
  // Insert channel in context for later usage
1794
1978
  const channel = findChannel(locator);
1795
1979
  const locale = findLocale(locator);
1980
+ const regionId = findRegionId(locator);
1796
1981
  const id = findSkuId(locator);
1797
1982
  const slug = findSlug(locator);
1798
1983
 
@@ -1853,7 +2038,8 @@ const Query = {
1853
2038
  } = await search.products({
1854
2039
  page: 0,
1855
2040
  count: 1,
1856
- query: `product:${route.id}`
2041
+ query: `product:${route.id}`,
2042
+ regionId
1857
2043
  });
1858
2044
 
1859
2045
  if (!product) {
@@ -2447,7 +2633,8 @@ const getContextFactory = options => ctx => {
2447
2633
  ctx.storage = {
2448
2634
  channel: ChannelMarshal.parse(options.channel),
2449
2635
  flags: (_options$flags = options.flags) != null ? _options$flags : {},
2450
- locale: options.locale
2636
+ locale: options.locale,
2637
+ cookies: new Map()
2451
2638
  };
2452
2639
  ctx.clients = getClients(options, ctx);
2453
2640
  ctx.loaders = getLoaders(options, ctx);