@faststore/api 1.12.50 → 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.
@@ -22,7 +22,7 @@ var api = require('@opentelemetry/api');
22
22
  var apiLogs = require('@opentelemetry/api-logs');
23
23
 
24
24
  var name = "@faststore/api";
25
- var version = "1.12.49";
25
+ var version = "1.12.50";
26
26
  var license = "MIT";
27
27
  var main = "dist/index.js";
28
28
  var typings = "dist/index.d.ts";
@@ -92,7 +92,7 @@ var packageJson = {
92
92
  };
93
93
 
94
94
  const USER_AGENT = `${packageJson.name}@${packageJson.version}`;
95
- const fetchAPI = async (info, init) => {
95
+ const fetchAPI = async (info, init, options) => {
96
96
  const response = await fetch(info, { ...init,
97
97
  headers: { ...(init == null ? void 0 : init.headers),
98
98
  'User-Agent': USER_AGENT
@@ -100,6 +100,10 @@ const fetchAPI = async (info, init) => {
100
100
  });
101
101
 
102
102
  if (response.ok) {
103
+ if (options != null && options.storeCookies) {
104
+ options.storeCookies(response.headers);
105
+ }
106
+
103
107
  return response.status !== 204 ? response.json() : undefined;
104
108
  }
105
109
 
@@ -108,17 +112,169 @@ const fetchAPI = async (info, init) => {
108
112
  throw new Error(text);
109
113
  };
110
114
 
115
+ const MATCH_FIRST_SET_COOKIE_KEY_VALUE = /^([^=]+)=([^;]*)/;
116
+ /**
117
+ * Splits a combined `set-cookie` header value (cookies joined by `, `) into
118
+ * individual cookies. Commas inside `expires=...` dates are kept.
119
+ */
120
+
121
+ const splitSetCookieHeader = value => value.split(/,(?=\s*[^;,=\s]+=)/).map(setCookie => setCookie.trim());
122
+ /**
123
+ * Returns every `set-cookie` value of a fetch response.
124
+ *
125
+ * `Headers.getSetCookie()` only exists on Node >= 18.14 (undici). FastStore v1
126
+ * stores may still run on Node 16, where isomorphic-unfetch falls back to
127
+ * node-fetch v2, which exposes repeated headers through `raw()` instead.
128
+ */
129
+
130
+
131
+ const getSetCookies = headers => {
132
+ const maybeHeaders = headers;
133
+
134
+ if (typeof maybeHeaders.getSetCookie === 'function') {
135
+ return maybeHeaders.getSetCookie();
136
+ }
137
+
138
+ if (typeof maybeHeaders.raw === 'function') {
139
+ var _maybeHeaders$raw$set;
140
+
141
+ return (_maybeHeaders$raw$set = maybeHeaders.raw()['set-cookie']) != null ? _maybeHeaders$raw$set : [];
142
+ }
143
+
144
+ const setCookie = headers.get('set-cookie');
145
+ return setCookie ? splitSetCookieHeader(setCookie) : [];
146
+ };
147
+ /**
148
+ * This function updates the ctx.storage.cookies, that is used in each request.
149
+ *
150
+ * ctx.storage.cookies is a Map<string[1], Record<string, string>[2]> where,
151
+ * [1] cookie key
152
+ * [2] {
153
+ * value: cookie value,
154
+ * setCookie: setCookie used in browser response
155
+ * }
156
+ */
157
+
158
+ const updatesContextStorageCookies = (ctx, setCookieValue) => {
159
+ const matchCookie = setCookieValue.match(MATCH_FIRST_SET_COOKIE_KEY_VALUE);
160
+
161
+ if (matchCookie) {
162
+ const cookieKey = matchCookie[1];
163
+ const cookieValue = matchCookie[2];
164
+ ctx.storage.cookies.set(cookieKey, {
165
+ value: cookieValue,
166
+ setCookie: setCookieValue
167
+ });
168
+ }
169
+ };
170
+ const setCookie = (ctx, headers) => {
171
+ getSetCookies(headers).forEach(setCookieValue => updatesContextStorageCookies(ctx, setCookieValue));
172
+ };
173
+ const getStoreCookie = ctx => headers => setCookie(ctx, headers);
174
+ /**
175
+ * This function returns a modified copy of the original cookie header (ctx.headers.cookie from the first request)
176
+ * with the cookie values that comes in each request (ctx.storage.cookies).
177
+ * If there is no cookies in storage, the ctx.headers?.cookie is used
178
+ *
179
+ * ctx.storage.cookies is a Map<string[1], Record<string, string>[2]> where,
180
+ * [1] cookie key
181
+ * [2] {
182
+ * value: cookie value,
183
+ * setCookie: setCookie used in browser response
184
+ * }
185
+ */
186
+
187
+ const getUpdatedCookie = ctx => {
188
+ var _ctx$headers;
189
+
190
+ if (!((_ctx$headers = ctx.headers) != null && _ctx$headers.cookie)) {
191
+ return null;
192
+ }
193
+
194
+ const contextStorageCookies = Array.from(ctx.storage.cookies.entries());
195
+
196
+ if (contextStorageCookies.length === 0) {
197
+ return ctx.headers.cookie;
198
+ }
199
+
200
+ return contextStorageCookies.reduce((existingCookies, [storageCookieKey, {
201
+ value: storageCookieValue
202
+ }]) => updatesCookieValueByKey(existingCookies, storageCookieKey, storageCookieValue), ctx.headers.cookie);
203
+ };
204
+ const getWithCookie = ctx => function withCookie(headers) {
205
+ const updatedCookie = getUpdatedCookie(ctx);
206
+
207
+ if (!updatedCookie) {
208
+ return headers;
209
+ }
210
+
211
+ return { ...headers,
212
+ cookie: updatedCookie
213
+ };
214
+ };
215
+ /**
216
+ * This function updates the cookie value based on its key
217
+ *
218
+ * const existingCookies = 'key=value1; key2=; key3=value3';
219
+ * const storageCookieKey = 'key2';
220
+ * const storageCookieValue = 'value2'
221
+ *
222
+ * updatesCookieValueByKey(existingCookies, storageCookieKey, storageCookieValue) returns 'key=value1; key2=value2; key3=value3';
223
+ */
224
+
225
+ const updatesCookieValueByKey = (existingCookies, storageCookieKey, storageCookieValue) => {
226
+ const MATCH_COOKIE_KEY_VALUE = new RegExp(`(${storageCookieKey})=([^;]*)`);
227
+ const cookieParts = existingCookies.match(MATCH_COOKIE_KEY_VALUE); // replaces original cookie with the one coming from storage
228
+
229
+ if (cookieParts) {
230
+ return existingCookies.replace(cookieParts[0], `${cookieParts[1]}=${storageCookieValue}`);
231
+ } // add new storage cookie to the original list of cookies
232
+
233
+
234
+ return `${existingCookies};${storageCookieKey}=${storageCookieValue}`;
235
+ };
236
+
111
237
  const BASE_INIT = {
112
238
  method: 'POST',
113
239
  headers: {
114
240
  'content-type': 'application/json'
115
241
  }
116
242
  };
243
+
244
+ const getHeader = (headers, name) => {
245
+ const value = headers == null ? void 0 : headers[name];
246
+ return Array.isArray(value) ? value[0] : value;
247
+ };
248
+ /**
249
+ * Host the Checkout API uses as the domain of the cookies it sets. Checkout
250
+ * only honors it for hosts registered in the account, so a forged value is
251
+ * ignored there.
252
+ */
253
+
254
+
255
+ const getForwardedHost = headers => {
256
+ var _getHeader;
257
+
258
+ // Proxies may append values: "client, proxy". Only the first one is the store host.
259
+ const host = ((_getHeader = getHeader(headers, 'x-forwarded-host')) == null ? void 0 : _getHeader.split(',')[0].trim()) || getHeader(headers, 'host') || '';
260
+ return host.trim().replace(/^www\./, '');
261
+ };
262
+
117
263
  const VtexCommerce = ({
118
264
  account,
119
265
  environment
120
266
  }, ctx) => {
121
267
  const base = `https://${account}.${environment}.com.br`;
268
+ const storeCookies = getStoreCookie(ctx);
269
+ const withCookie = getWithCookie(ctx);
270
+ const forwardedHost = getForwardedHost(ctx.headers); // Checkout calls forward the shopper cookies and keep the cookies Checkout
271
+ // sets (e.g. CheckoutOrderFormOwnership) so they can reach the browser.
272
+
273
+ const checkoutHeaders = () => withCookie({
274
+ 'content-type': 'application/json',
275
+ 'X-FORWARDED-HOST': forwardedHost
276
+ });
277
+
122
278
  return {
123
279
  catalog: {
124
280
  salesChannel: sc => fetchAPI(`${base}/api/catalog_system/pub/saleschannel/${sc}`),
@@ -169,7 +325,10 @@ const VtexCommerce = ({
169
325
  }
170
326
 
171
327
  return fetchAPI(`${base}/api/checkout/pub/orderForm/${id}/attachments/shippingData`, { ...BASE_INIT,
328
+ headers: checkoutHeaders(),
172
329
  body: JSON.stringify(body)
330
+ }, {
331
+ storeCookies
173
332
  });
174
333
  },
175
334
  orderForm: ({
@@ -184,7 +343,11 @@ const VtexCommerce = ({
184
343
  refreshOutdatedData: refreshOutdatedData.toString(),
185
344
  sc: salesChannel
186
345
  });
187
- return fetchAPI(`${base}/api/checkout/pub/orderForm/${id}?${params.toString()}`, BASE_INIT);
346
+ return fetchAPI(`${base}/api/checkout/pub/orderForm/${id}?${params.toString()}`, { ...BASE_INIT,
347
+ headers: checkoutHeaders()
348
+ }, {
349
+ storeCookies
350
+ });
188
351
  },
189
352
  updateOrderFormItems: ({
190
353
  id,
@@ -198,11 +361,14 @@ const VtexCommerce = ({
198
361
  sc: salesChannel
199
362
  });
200
363
  return fetchAPI(`${base}/api/checkout/pub/orderForm/${id}/items?${params}`, { ...BASE_INIT,
364
+ headers: checkoutHeaders(),
201
365
  body: JSON.stringify({
202
366
  orderItems,
203
367
  noSplitItem: !shouldSplitItem
204
368
  }),
205
369
  method: 'PATCH'
370
+ }, {
371
+ storeCookies
206
372
  });
207
373
  },
208
374
  setCustomData: ({
@@ -212,10 +378,13 @@ const VtexCommerce = ({
212
378
  value
213
379
  }) => {
214
380
  return fetchAPI(`${base}/api/checkout/pub/orderForm/${id}/customData/${appId}/${key}`, { ...BASE_INIT,
381
+ headers: checkoutHeaders(),
215
382
  body: JSON.stringify({
216
383
  value
217
384
  }),
218
385
  method: 'PUT'
386
+ }, {
387
+ storeCookies
219
388
  });
220
389
  },
221
390
  region: async ({
@@ -2468,7 +2637,8 @@ const getContextFactory = options => ctx => {
2468
2637
  ctx.storage = {
2469
2638
  channel: ChannelMarshal.parse(options.channel),
2470
2639
  flags: (_options$flags = options.flags) != null ? _options$flags : {},
2471
- locale: options.locale
2640
+ locale: options.locale,
2641
+ cookies: new Map()
2472
2642
  };
2473
2643
  ctx.clients = getClients(options, ctx);
2474
2644
  ctx.loaders = getLoaders(options, ctx);