@faststore/api 2.2.0-alpha.0 → 2.2.0-alpha.10

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.
@@ -6,6 +6,7 @@ var schema = require('@graphql-tools/schema');
6
6
  var fetch = _interopDefault(require('isomorphic-unfetch'));
7
7
  var DataLoader = _interopDefault(require('dataloader'));
8
8
  var pLimit = _interopDefault(require('p-limit'));
9
+ var sanitizeHtmlLib = _interopDefault(require('sanitize-html'));
9
10
  var deepEquals = _interopDefault(require('fast-deep-equal'));
10
11
  var crypto = _interopDefault(require('crypto'));
11
12
  var graphql = require('graphql');
@@ -22,7 +23,7 @@ var api = require('@opentelemetry/api');
22
23
  var apiLogs = require('@opentelemetry/api-logs');
23
24
 
24
25
  var name = "@faststore/api";
25
- var version = "2.1.78";
26
+ var version = "2.2.0-alpha.6";
26
27
  var license = "MIT";
27
28
  var main = "dist/index.js";
28
29
  var typings = "dist/index.d.ts";
@@ -54,15 +55,17 @@ var dependencies = {
54
55
  dataloader: "^2.1.0",
55
56
  "fast-deep-equal": "^3.1.3",
56
57
  "isomorphic-unfetch": "^3.1.0",
57
- "p-limit": "^3.1.0"
58
+ "p-limit": "^3.1.0",
59
+ "sanitize-html": "^2.11.0"
58
60
  };
59
61
  var devDependencies = {
60
62
  "@envelop/core": "^2.6.0",
61
- "@faststore/eslint-config": "^2.1.78",
62
- "@faststore/shared": "^2.1.78",
63
+ "@faststore/eslint-config": "^2.2.0-alpha.1",
64
+ "@faststore/shared": "^2.2.0-alpha.1",
63
65
  "@graphql-codegen/cli": "2.2.0",
64
66
  "@graphql-codegen/typescript": "2.2.2",
65
67
  "@types/express": "^4.17.16",
68
+ "@types/sanitize-html": "^2.9.1",
66
69
  concurrently: "^6.2.1",
67
70
  eslint: "7.32.0",
68
71
  express: "^4.17.3",
@@ -111,16 +114,6 @@ const fetchAPI = async (info, init) => {
111
114
  throw new Error(text);
112
115
  };
113
116
 
114
- const getCookie = (name, cookie) => {
115
- const value = `; ${cookie}`;
116
- const parts = value.split(`; ${name}=`);
117
- if (parts.length === 2) {
118
- var _parts$pop$split$shif, _parts$pop;
119
- return (_parts$pop$split$shif = parts == null ? void 0 : (_parts$pop = parts.pop()) == null ? void 0 : _parts$pop.split(';').shift()) != null ? _parts$pop$split$shif : '';
120
- }
121
- return '';
122
- };
123
-
124
117
  const BASE_INIT = {
125
118
  method: 'POST',
126
119
  headers: {
@@ -215,7 +208,14 @@ const VtexCommerce = ({
215
208
  refreshOutdatedData: refreshOutdatedData.toString(),
216
209
  sc: salesChannel
217
210
  });
218
- return fetchAPI(`${base}/api/checkout/pub/orderForm/${id}?${params.toString()}`, BASE_INIT);
211
+ const requestInit = ctx.headers ? {
212
+ ...BASE_INIT,
213
+ headers: {
214
+ 'content-type': 'application/json',
215
+ cookie: ctx.headers.cookie
216
+ }
217
+ } : BASE_INIT;
218
+ return fetchAPI(`${base}/api/checkout/pub/orderForm/${id}?${params.toString()}`, requestInit);
219
219
  },
220
220
  updateOrderFormItems: ({
221
221
  id,
@@ -228,14 +228,25 @@ const VtexCommerce = ({
228
228
  allowOutdatedData,
229
229
  sc: salesChannel
230
230
  });
231
- return fetchAPI(`${base}/api/checkout/pub/orderForm/${id}/items?${params}`, {
232
- ...BASE_INIT,
233
- body: JSON.stringify({
234
- orderItems,
235
- noSplitItem: !shouldSplitItem
236
- }),
237
- method: 'PATCH'
231
+ const items = JSON.stringify({
232
+ orderItems,
233
+ noSplitItem: !shouldSplitItem
238
234
  });
235
+ const requestInit = ctx.headers ? {
236
+ headers: {
237
+ 'content-type': 'application/json',
238
+ cookie: ctx.headers.cookie
239
+ },
240
+ body: items,
241
+ method: 'PATCH'
242
+ } : {
243
+ headers: {
244
+ 'content-type': 'application/json'
245
+ },
246
+ body: items,
247
+ method: 'PATCH'
248
+ };
249
+ return fetchAPI(`${base}/api/checkout/pub/orderForm/${id}/items?${params}`, requestInit);
239
250
  },
240
251
  setCustomData: ({
241
252
  id,
@@ -275,34 +286,13 @@ const VtexCommerce = ({
275
286
  session: search => {
276
287
  const params = new URLSearchParams(search);
277
288
  params.set('items', 'profile.id,profile.email,profile.firstName,profile.lastName,store.channel,store.countryCode,store.cultureInfo,store.currencyCode,store.currencySymbol');
278
- if (getCookie('vtex_session', ctx.headers.cookie)) {
279
- // cookie set
280
- return fetchAPI(`${base}/api/sessions?${params.toString()}`, {
281
- method: 'GET',
282
- headers: {
283
- 'content-type': 'application/json',
284
- cookie: ctx.headers.cookie
285
- }
286
- });
287
- } else {
288
- // cookie unset -> create session
289
- return fetchAPI(`${base}/api/sessions?${params.toString()}`, {
290
- method: 'POST',
291
- headers: {
292
- 'content-type': 'application/json',
293
- cookie: ctx.headers.cookie
294
- },
295
- body: '{}'
296
- });
297
- }
298
- },
299
- getSessionOrder: () => {
300
- return fetchAPI(`${base}/api/sessions?items=public.orderFormId`, {
301
- method: 'GET',
289
+ return fetchAPI(`${base}/api/sessions?${params.toString()}`, {
290
+ method: 'POST',
302
291
  headers: {
303
292
  'content-type': 'application/json',
304
293
  cookie: ctx.headers.cookie
305
- }
294
+ },
295
+ body: '{}'
306
296
  });
307
297
  },
308
298
  subscribeToNewsletter: data => {
@@ -528,9 +518,32 @@ const getSimulationLoader = (_, clients) => {
528
518
  });
529
519
  };
530
520
 
521
+ /**
522
+ * For now, we're using sanitize-html's default set
523
+ * of allowed tags and attributes, which don't even include img elements
524
+ *
525
+ * It is known many client depends on pontentially vulnerable tags, such as script tags
526
+ * We chose to be restrictive at first, and document those restrictions later.
527
+ *
528
+ * When expanding the set of allowed tags and attributes, please consider performance, privacy and security.
529
+ *
530
+ * This possibily breaks compatibility with Portal and Store Framework,
531
+ * which both allows an enormous amount of tags and attributes
532
+ *
533
+ * This was a thoughtful decision that can be reviewed in the future given
534
+ * research was made to back up those changes.
535
+ */
536
+ const sanitizeHtml = (dirty, options) => sanitizeHtmlLib(dirty, options);
537
+
538
+ function sanitizeProduct(product) {
539
+ return {
540
+ ...product,
541
+ description: product.description ? sanitizeHtml(product.description) : product.description
542
+ };
543
+ }
531
544
  const enhanceSku = (item, product) => ({
532
545
  ...item,
533
- isVariantOf: product
546
+ isVariantOf: sanitizeProduct(product)
534
547
  });
535
548
 
536
549
  class FastStoreError extends Error {
@@ -1033,55 +1046,31 @@ function getPropertyId(item) {
1033
1046
  }
1034
1047
 
1035
1048
  const shouldUpdateShippingData = (orderForm, session) => {
1036
- var _orderForm$shippingDa;
1049
+ var _orderForm$shippingDa, _orderForm$shippingDa2;
1037
1050
  if (!hasSessionPostalCodeOrGeoCoordinates(session)) {
1038
1051
  return {
1039
1052
  updateShipping: false,
1040
1053
  addressChanged: false
1041
1054
  };
1042
1055
  }
1043
- const selectedAddress = (_orderForm$shippingDa = orderForm.shippingData) == null ? void 0 : _orderForm$shippingDa.selectedAddresses[0];
1044
- if (checkPostalCode(selectedAddress, session.postalCode)) {
1045
- return {
1046
- updateShipping: true,
1047
- addressChanged: true
1048
- };
1049
- }
1050
- if (checkGeoCoordinates(selectedAddress, session.geoCoordinates, session.postalCode)) {
1056
+ if (!hasItems(orderForm)) {
1051
1057
  return {
1052
- updateShipping: true,
1053
- addressChanged: true
1058
+ updateShipping: false,
1059
+ addressChanged: false
1054
1060
  };
1055
1061
  }
1056
- if (checkAddressType(selectedAddress, session.addressType)) {
1062
+ const [selectedAddress] = (_orderForm$shippingDa = orderForm == null ? void 0 : (_orderForm$shippingDa2 = orderForm.shippingData) == null ? void 0 : _orderForm$shippingDa2.selectedAddresses) != null ? _orderForm$shippingDa : [];
1063
+ if (checkPostalCode(selectedAddress, session.postalCode) || checkGeoCoordinates(selectedAddress, session.geoCoordinates) || checkAddressType(selectedAddress, session.addressType)) {
1057
1064
  return {
1058
1065
  updateShipping: true,
1059
1066
  addressChanged: true
1060
1067
  };
1061
1068
  }
1062
- if (!hasItems(orderForm)) {
1063
- return {
1064
- updateShipping: false,
1065
- addressChanged: false
1066
- };
1067
- }
1068
1069
  // The logisticsInfo will always exist if there´s at least one item inside the cart
1069
1070
  const {
1070
1071
  logisticsInfo
1071
1072
  } = orderForm.shippingData;
1072
- if (shouldUpdateDeliveryChannel(logisticsInfo, session)) {
1073
- return {
1074
- updateShipping: true,
1075
- addressChanged: false
1076
- };
1077
- }
1078
- if (shouldUpdateDeliveryMethod(logisticsInfo, session)) {
1079
- return {
1080
- updateShipping: true,
1081
- addressChanged: false
1082
- };
1083
- }
1084
- if (shouldUpdateDeliveryWindow(logisticsInfo, session)) {
1073
+ if (shouldUpdateDeliveryInfo(logisticsInfo, session)) {
1085
1074
  return {
1086
1075
  updateShipping: true,
1087
1076
  addressChanged: false
@@ -1094,15 +1083,16 @@ const shouldUpdateShippingData = (orderForm, session) => {
1094
1083
  };
1095
1084
  // Validate if theres any postal Code or GeoCoordinates set at the session
1096
1085
  const hasSessionPostalCodeOrGeoCoordinates = session => {
1097
- return !!session.postalCode || session.geoCoordinates && session.geoCoordinates.latitude && session.geoCoordinates.longitude;
1086
+ var _session$geoCoordinat, _session$geoCoordinat2;
1087
+ return !!session.postalCode || ((_session$geoCoordinat = session.geoCoordinates) == null ? void 0 : _session$geoCoordinat.latitude) && ((_session$geoCoordinat2 = session.geoCoordinates) == null ? void 0 : _session$geoCoordinat2.longitude);
1098
1088
  };
1099
1089
  // Validate if theres a difference between the session postal code and orderForm postal code
1100
1090
  const checkPostalCode = (address, postalCode) => {
1101
1091
  return typeof postalCode === 'string' && (address == null ? void 0 : address.postalCode) !== postalCode;
1102
1092
  };
1103
1093
  // Validate if theres a difference between the session geoCoords and orderForm geoCoords
1104
- const checkGeoCoordinates = (address, geoCoordinates, postalCode) => {
1105
- return typeof (geoCoordinates == null ? void 0 : geoCoordinates.latitude) === 'number' && typeof (geoCoordinates == null ? void 0 : geoCoordinates.longitude) === 'number' && ((address == null ? void 0 : address.geoCoordinates[0]) !== (geoCoordinates == null ? void 0 : geoCoordinates.longitude) || (address == null ? void 0 : address.geoCoordinates[1]) !== (geoCoordinates == null ? void 0 : geoCoordinates.latitude)) && (address == null ? void 0 : address.postalCode) !== postalCode;
1094
+ const checkGeoCoordinates = (address, geoCoordinates) => {
1095
+ return typeof (geoCoordinates == null ? void 0 : geoCoordinates.latitude) === 'number' && typeof (geoCoordinates == null ? void 0 : geoCoordinates.longitude) === 'number' && ((address == null ? void 0 : address.geoCoordinates[0]) !== (geoCoordinates == null ? void 0 : geoCoordinates.longitude) || (address == null ? void 0 : address.geoCoordinates[1]) !== (geoCoordinates == null ? void 0 : geoCoordinates.latitude));
1106
1096
  };
1107
1097
  const checkAddressType = (address, addressType) => {
1108
1098
  return typeof addressType === 'string' && (address == null ? void 0 : address.addressType) !== addressType;
@@ -1111,67 +1101,29 @@ const checkAddressType = (address, addressType) => {
1111
1101
  const hasItems = orderForm => {
1112
1102
  return orderForm.items.length !== 0;
1113
1103
  };
1114
- // Validate if the deliveryChannel from the session is different from the selected delivery channel
1115
- // and if so needs to validate if the deliveryChannel for the session is available inside the slas for the item
1116
- const shouldUpdateDeliveryChannel = (logisticsInfo, session) => {
1117
- var _session$deliveryMode;
1118
- if (!(session != null && (_session$deliveryMode = session.deliveryMode) != null && _session$deliveryMode.deliveryChannel)) {
1119
- return false;
1120
- }
1121
- const {
1122
- deliveryChannel
1123
- } = session.deliveryMode;
1124
- for (const item of logisticsInfo) {
1125
- if (item.selectedDeliveryChannel !== deliveryChannel) {
1126
- const matchingSla = item.slas.find(sla => sla.deliveryChannel === deliveryChannel);
1127
- if (matchingSla) {
1128
- return true;
1129
- }
1130
- }
1131
- }
1132
- return false;
1133
- };
1134
- // Validate if the deliveryMethod from the session is different from the selectedSLA
1135
- // and if so needs to validate if the deliveryMethod for the session is available inside the slas for the item
1136
- const shouldUpdateDeliveryMethod = (logisticsInfo, session) => {
1137
- var _session$deliveryMode2;
1138
- if (!(session != null && (_session$deliveryMode2 = session.deliveryMode) != null && _session$deliveryMode2.deliveryMethod)) {
1139
- return false;
1140
- }
1141
- const {
1142
- deliveryMethod
1143
- } = session.deliveryMode;
1144
- for (const item of logisticsInfo) {
1145
- if (item.selectedSla !== deliveryMethod) {
1146
- const matchingSla = item.slas.find(sla => sla.id === deliveryMethod);
1147
- if (matchingSla) {
1148
- return true;
1149
- }
1150
- }
1151
- }
1152
- return false;
1153
- };
1154
- // Validate if the deliveryWindow from the session is different from the deliveryWindow of the SLA
1155
- // and if so needs to validate if the deliveryWindow for the session is available inside the availableDeliveryWindows for the item
1156
- const shouldUpdateDeliveryWindow = (logisticsInfo, session) => {
1157
- var _session$deliveryMode3, _session$deliveryMode4, _session$deliveryMode5, _session$deliveryMode6;
1158
- if (!(session != null && (_session$deliveryMode3 = session.deliveryMode) != null && (_session$deliveryMode4 = _session$deliveryMode3.deliveryWindow) != null && _session$deliveryMode4.startDate) || !(session != null && (_session$deliveryMode5 = session.deliveryMode) != null && (_session$deliveryMode6 = _session$deliveryMode5.deliveryWindow) != null && _session$deliveryMode6.endDate)) {
1159
- return false;
1160
- }
1104
+ const shouldUpdateDeliveryInfo = (logisticsInfo, session) => {
1105
+ var _session$deliveryMode, _session$deliveryMode2, _session$deliveryMode3;
1106
+ const deliveryChannel = session == null ? void 0 : (_session$deliveryMode = session.deliveryMode) == null ? void 0 : _session$deliveryMode.deliveryChannel;
1107
+ const deliveryMethod = session == null ? void 0 : (_session$deliveryMode2 = session.deliveryMode) == null ? void 0 : _session$deliveryMode2.deliveryMethod;
1161
1108
  const {
1162
1109
  startDate,
1163
1110
  endDate
1164
- } = session.deliveryMode.deliveryWindow;
1165
- for (const item of logisticsInfo) {
1166
- for (const sla of item.slas) {
1167
- var _sla$availableDeliver;
1168
- const matchingWindow = (_sla$availableDeliver = sla.availableDeliveryWindows) == null ? void 0 : _sla$availableDeliver.some(window => window.startDateUtc === startDate && window.endDateUtc === endDate);
1169
- if (matchingWindow) {
1111
+ } = (session == null ? void 0 : (_session$deliveryMode3 = session.deliveryMode) == null ? void 0 : _session$deliveryMode3.deliveryWindow) || {};
1112
+ return logisticsInfo.some(({
1113
+ selectedDeliveryChannel,
1114
+ selectedSla,
1115
+ slas
1116
+ }) => {
1117
+ const checkDeliveryChannel = deliveryChannel && selectedDeliveryChannel !== deliveryChannel;
1118
+ const checkDeliveryMethod = deliveryMethod && selectedSla !== deliveryMethod;
1119
+ return slas == null ? void 0 : slas.some(sla => {
1120
+ var _sla$deliveryWindow, _sla$deliveryWindow2, _sla$availableDeliver;
1121
+ if (checkDeliveryChannel && sla.deliveryChannel === deliveryChannel || checkDeliveryMethod && sla.id === deliveryMethod) {
1170
1122
  return true;
1171
1123
  }
1172
- }
1173
- }
1174
- return false;
1124
+ return startDate && endDate && sla.deliveryChannel === deliveryChannel && sla.id === deliveryMethod && (!(sla != null && sla.deliveryWindow) || (sla == null ? void 0 : (_sla$deliveryWindow = sla.deliveryWindow) == null ? void 0 : _sla$deliveryWindow.startDateUtc) !== startDate || (sla == null ? void 0 : (_sla$deliveryWindow2 = sla.deliveryWindow) == null ? void 0 : _sla$deliveryWindow2.endDateUtc) !== endDate) && ((_sla$availableDeliver = sla.availableDeliveryWindows) == null ? void 0 : _sla$availableDeliver.some(window => (window == null ? void 0 : window.startDateUtc) === startDate && (window == null ? void 0 : window.endDateUtc) === endDate));
1125
+ });
1126
+ });
1175
1127
  };
1176
1128
 
1177
1129
  const getAddressOrderForm = (orderForm, session, addressChanged) => {
@@ -1305,7 +1257,10 @@ const joinItems = form => {
1305
1257
  items: Object.values(itemsById).map(items => {
1306
1258
  const [item] = items;
1307
1259
  const quantity = items.reduce((acc, i) => acc + i.quantity, 0);
1308
- const totalPrice = items.reduce((acc, i) => acc + i.quantity * i.sellingPrice, 0);
1260
+ const totalPrice = items.reduce((acc, i) => {
1261
+ var _i$priceDefinition$to, _i$priceDefinition, _i$quantity, _i$sellingPrice;
1262
+ return acc + ((_i$priceDefinition$to = i == null ? void 0 : (_i$priceDefinition = i.priceDefinition) == null ? void 0 : _i$priceDefinition.total) != null ? _i$priceDefinition$to : ((_i$quantity = i == null ? void 0 : i.quantity) != null ? _i$quantity : 0) * ((_i$sellingPrice = i == null ? void 0 : i.sellingPrice) != null ? _i$sellingPrice : 0));
1263
+ }, 0);
1309
1264
  return {
1310
1265
  ...item,
1311
1266
  quantity,
@@ -1364,17 +1319,6 @@ const isOrderFormStale = form => {
1364
1319
  const newEtag = getOrderFormEtag(form);
1365
1320
  return newEtag !== oldEtag;
1366
1321
  };
1367
- async function getOrderNumberFromSession(headers = {}, commerce) {
1368
- const cookieSession = getCookie('vtex_session', headers.cookie);
1369
- if (cookieSession) {
1370
- var _namespaces$public$or, _namespaces$public, _namespaces$public$or2;
1371
- const {
1372
- namespaces
1373
- } = await commerce.getSessionOrder();
1374
- return (_namespaces$public$or = (_namespaces$public = namespaces.public) == null ? void 0 : (_namespaces$public$or2 = _namespaces$public.orderFormId) == null ? void 0 : _namespaces$public$or2.value) != null ? _namespaces$public$or : undefined;
1375
- }
1376
- return;
1377
- }
1378
1322
  // Returns the regionalized orderForm
1379
1323
  const getOrderForm = async (id, {
1380
1324
  clients: {
@@ -1446,9 +1390,8 @@ const validateCart = async (_, {
1446
1390
  },
1447
1391
  session
1448
1392
  }, ctx) => {
1449
- var _ref;
1450
1393
  const {
1451
- orderNumber: orderNumberFromCart,
1394
+ orderNumber,
1452
1395
  acceptedOffer,
1453
1396
  shouldSplitItem
1454
1397
  } = order;
@@ -1458,8 +1401,7 @@ const validateCart = async (_, {
1458
1401
  },
1459
1402
  loaders: {
1460
1403
  skuLoader
1461
- },
1462
- headers
1404
+ }
1463
1405
  } = ctx;
1464
1406
  const channel = session == null ? void 0 : session.channel;
1465
1407
  const locale = session == null ? void 0 : session.locale;
@@ -1469,19 +1411,9 @@ const validateCart = async (_, {
1469
1411
  if (locale) {
1470
1412
  mutateLocaleContext(ctx, locale);
1471
1413
  }
1472
- const orderNumberFromSession = await getOrderNumberFromSession(headers, commerce);
1473
- const orderNumber = (_ref = orderNumberFromSession != null ? orderNumberFromSession : orderNumberFromCart) != null ? _ref : '';
1474
1414
  // Step1: Get OrderForm from VTEX Commerce
1475
1415
  const orderForm = await getOrderForm(orderNumber, ctx);
1476
- // Step1.1: Checks if the orderForm id has changed. There are three cases for this:
1477
- // Social Selling: the vtex_session cookie contains a new orderForm id with Social Selling data
1478
- // My Orders: the customer clicks on reordering through generating a new cart and when returning to the faststore, this information needs to be returned by vtex_session cookie.
1479
- // New session: a new user enters the website and has no orderForm attributed to it (has no relation to the vtex_session cookie).
1480
- // In all cases, the origin orderForm should replace the copy that's in the browser
1481
- if (orderForm.orderFormId != orderNumberFromCart) {
1482
- return orderFormToCart(orderForm, skuLoader);
1483
- }
1484
- // Step1.2: Check if another system changed the orderForm with this orderNumber
1416
+ // Step1.5: Check if another system changed the orderForm with this orderNumber
1485
1417
  // If so, this means the user interacted with this cart elsewhere and expects
1486
1418
  // to see this new cart state instead of what's stored on the user's browser.
1487
1419
  const isStale = isOrderFormStale(orderForm);