@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.
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.49";
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";
@@ -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 ({
@@ -2464,7 +2633,8 @@ const getContextFactory = options => ctx => {
2464
2633
  ctx.storage = {
2465
2634
  channel: ChannelMarshal.parse(options.channel),
2466
2635
  flags: (_options$flags = options.flags) != null ? _options$flags : {},
2467
- locale: options.locale
2636
+ locale: options.locale,
2637
+ cookies: new Map()
2468
2638
  };
2469
2639
  ctx.clients = getClients(options, ctx);
2470
2640
  ctx.loaders = getLoaders(options, ctx);