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