@faststore/api 1.9.4 → 1.9.7
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/CHANGELOG.md +30 -0
- package/dist/api.cjs.development.js +153 -45
- package/dist/api.cjs.development.js.map +1 -1
- package/dist/api.cjs.production.min.js +1 -1
- package/dist/api.cjs.production.min.js.map +1 -1
- package/dist/api.esm.js +149 -46
- package/dist/api.esm.js.map +1 -1
- package/dist/index.d.ts +6 -4
- package/dist/platforms/errors.d.ts +19 -0
- package/dist/platforms/vtex/clients/commerce/types/Portal.d.ts +1 -1
- package/dist/platforms/vtex/index.d.ts +5 -4
- package/dist/platforms/vtex/loaders/index.d.ts +1 -1
- package/dist/platforms/vtex/loaders/sku.d.ts +1 -2
- package/dist/platforms/vtex/resolvers/mutation.d.ts +3 -3
- package/dist/platforms/vtex/resolvers/offer.d.ts +1 -1
- package/dist/platforms/vtex/resolvers/seo.d.ts +1 -0
- package/dist/platforms/vtex/resolvers/validateCart.d.ts +3 -3
- package/dist/platforms/vtex/utils/canonical.d.ts +2 -0
- package/dist/platforms/vtex/utils/facets.d.ts +2 -0
- package/dist/platforms/vtex/utils/orderStatistics.d.ts +4 -0
- package/dist/platforms/vtex/utils/sku.d.ts +8 -0
- package/package.json +2 -2
- package/src/index.ts +1 -0
- package/src/platforms/errors.ts +34 -0
- package/src/platforms/vtex/clients/commerce/index.ts +1 -1
- package/src/platforms/vtex/clients/commerce/types/Portal.ts +1 -1
- package/src/platforms/vtex/loaders/collection.ts +1 -1
- package/src/platforms/vtex/loaders/sku.ts +6 -19
- package/src/platforms/vtex/resolvers/offer.ts +6 -3
- package/src/platforms/vtex/resolvers/product.ts +6 -4
- package/src/platforms/vtex/resolvers/query.ts +44 -1
- package/src/platforms/vtex/resolvers/seo.ts +2 -2
- package/src/platforms/vtex/resolvers/validateCart.ts +3 -3
- package/src/platforms/vtex/utils/canonical.ts +3 -0
- package/src/platforms/vtex/utils/facets.ts +6 -0
- package/src/platforms/vtex/utils/orderStatistics.ts +16 -0
- package/src/platforms/vtex/utils/sku.ts +26 -0
- package/dist/platforms/vtex/utils/errors.d.ts +0 -6
- package/src/platforms/vtex/utils/errors.ts +0 -13
package/dist/api.esm.js
CHANGED
|
@@ -342,38 +342,51 @@ const getSimulationLoader = (_, clients) => {
|
|
|
342
342
|
});
|
|
343
343
|
};
|
|
344
344
|
|
|
345
|
-
|
|
346
|
-
|
|
345
|
+
const enhanceSku = (item, product) => ({ ...item,
|
|
346
|
+
isVariantOf: product
|
|
347
|
+
});
|
|
348
|
+
|
|
349
|
+
class FastStoreError extends Error {
|
|
350
|
+
constructor(extensions, message) {
|
|
347
351
|
super(message);
|
|
348
|
-
this.
|
|
352
|
+
this.extensions = extensions;
|
|
353
|
+
this.name = 'FastStoreError';
|
|
349
354
|
}
|
|
350
355
|
|
|
351
356
|
}
|
|
352
|
-
|
|
357
|
+
|
|
358
|
+
class BadRequestError extends FastStoreError {
|
|
353
359
|
constructor(message) {
|
|
354
|
-
super(
|
|
355
|
-
|
|
360
|
+
super({
|
|
361
|
+
status: 400,
|
|
362
|
+
type: 'BadRequestError'
|
|
363
|
+
}, message);
|
|
356
364
|
}
|
|
357
365
|
|
|
358
366
|
}
|
|
367
|
+
class NotFoundError extends FastStoreError {
|
|
368
|
+
constructor(message) {
|
|
369
|
+
super({
|
|
370
|
+
status: 404,
|
|
371
|
+
type: 'NotFoundError'
|
|
372
|
+
}, message);
|
|
373
|
+
}
|
|
359
374
|
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
375
|
+
}
|
|
376
|
+
const isFastStoreError = error => (error == null ? void 0 : error.name) === 'FastStoreError';
|
|
377
|
+
const isNotFoundError = error => {
|
|
378
|
+
var _error$extensions;
|
|
363
379
|
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
key
|
|
369
|
-
}) => key === 'id');
|
|
370
|
-
|
|
371
|
-
if (!maybeFacet) {
|
|
372
|
-
throw new BadRequestError('Error while loading SKU. Needs to pass an id to selected facets');
|
|
373
|
-
}
|
|
380
|
+
return (error == null ? void 0 : (_error$extensions = error.extensions) == null ? void 0 : _error$extensions.type) === 'NotFoundError';
|
|
381
|
+
};
|
|
382
|
+
const isBadRequestError = error => {
|
|
383
|
+
var _error$extensions2;
|
|
374
384
|
|
|
375
|
-
|
|
376
|
-
|
|
385
|
+
return (error == null ? void 0 : (_error$extensions2 = error.extensions) == null ? void 0 : _error$extensions2.type) === 'BadRequestError';
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
const getSkuLoader = (_, clients) => {
|
|
389
|
+
const loader = async skuIds => {
|
|
377
390
|
const {
|
|
378
391
|
products
|
|
379
392
|
} = await clients.search.products({
|
|
@@ -389,10 +402,10 @@ const getSkuLoader = (_, clients) => {
|
|
|
389
402
|
return acc;
|
|
390
403
|
}, {});
|
|
391
404
|
const skus = skuIds.map(skuId => skuBySkuId[skuId]);
|
|
392
|
-
const missingSkus =
|
|
405
|
+
const missingSkus = skuIds.filter(skuId => !skuBySkuId[skuId]);
|
|
393
406
|
|
|
394
407
|
if (missingSkus.length > 0) {
|
|
395
|
-
throw new
|
|
408
|
+
throw new NotFoundError(`Search API did not found the following skus: ${missingSkus.join(',')}`);
|
|
396
409
|
}
|
|
397
410
|
|
|
398
411
|
return skus;
|
|
@@ -716,15 +729,12 @@ const equals = (storeOrder, orderForm) => {
|
|
|
716
729
|
return isSameOrder && orderItemsAreSync;
|
|
717
730
|
};
|
|
718
731
|
|
|
719
|
-
const orderFormToCart = (form, skuLoader) => {
|
|
732
|
+
const orderFormToCart = async (form, skuLoader) => {
|
|
720
733
|
return {
|
|
721
734
|
order: {
|
|
722
735
|
orderNumber: form.orderFormId,
|
|
723
|
-
acceptedOffer: form.items.map(item => ({ ...item,
|
|
724
|
-
product: skuLoader.load(
|
|
725
|
-
key: 'id',
|
|
726
|
-
value: item.id
|
|
727
|
-
}])
|
|
736
|
+
acceptedOffer: form.items.map(async item => ({ ...item,
|
|
737
|
+
product: await skuLoader.load(item.id)
|
|
728
738
|
}))
|
|
729
739
|
},
|
|
730
740
|
messages: form.messages.map(({
|
|
@@ -1032,13 +1042,13 @@ const StoreOffer = {
|
|
|
1032
1042
|
|
|
1033
1043
|
return null;
|
|
1034
1044
|
},
|
|
1035
|
-
itemOffered:
|
|
1045
|
+
itemOffered: root => {
|
|
1036
1046
|
if (isSearchItem(root)) {
|
|
1037
1047
|
return root.product;
|
|
1038
1048
|
}
|
|
1039
1049
|
|
|
1040
1050
|
if (isOrderFormItem(root)) {
|
|
1041
|
-
return { ...
|
|
1051
|
+
return { ...root.product,
|
|
1042
1052
|
attachmentsValues: root.attachments
|
|
1043
1053
|
};
|
|
1044
1054
|
}
|
|
@@ -1060,6 +1070,10 @@ const StoreOffer = {
|
|
|
1060
1070
|
}
|
|
1061
1071
|
};
|
|
1062
1072
|
|
|
1073
|
+
const canonicalFromProduct = ({
|
|
1074
|
+
linkText
|
|
1075
|
+
}) => `/${linkText}/p`;
|
|
1076
|
+
|
|
1063
1077
|
const enhanceCommercialOffer = ({
|
|
1064
1078
|
offer,
|
|
1065
1079
|
seller,
|
|
@@ -1100,13 +1114,11 @@ const StoreProduct = {
|
|
|
1100
1114
|
}
|
|
1101
1115
|
}) => description,
|
|
1102
1116
|
seo: ({
|
|
1103
|
-
isVariantOf
|
|
1104
|
-
description,
|
|
1105
|
-
productName
|
|
1106
|
-
}
|
|
1117
|
+
isVariantOf
|
|
1107
1118
|
}) => ({
|
|
1108
|
-
title: productName,
|
|
1109
|
-
description
|
|
1119
|
+
title: isVariantOf.productName,
|
|
1120
|
+
description: isVariantOf.description,
|
|
1121
|
+
canonical: canonicalFromProduct(isVariantOf)
|
|
1110
1122
|
}),
|
|
1111
1123
|
brand: ({
|
|
1112
1124
|
isVariantOf: {
|
|
@@ -1166,7 +1178,7 @@ const StoreProduct = {
|
|
|
1166
1178
|
},
|
|
1167
1179
|
review: () => [],
|
|
1168
1180
|
aggregateRating: () => ({}),
|
|
1169
|
-
offers: root => root.sellers.
|
|
1181
|
+
offers: root => root.sellers.map(seller => enhanceCommercialOffer({
|
|
1170
1182
|
offer: seller.commertialOffer,
|
|
1171
1183
|
seller,
|
|
1172
1184
|
product: root
|
|
@@ -1264,15 +1276,25 @@ const transformSelectedFacet = ({
|
|
|
1264
1276
|
};
|
|
1265
1277
|
}
|
|
1266
1278
|
};
|
|
1267
|
-
const
|
|
1279
|
+
const findSlug = facets => {
|
|
1268
1280
|
var _facets$find$value, _facets$find;
|
|
1269
1281
|
|
|
1270
|
-
return (_facets$find$value = facets == null ? void 0 : (_facets$find = facets.find(x => x.key === '
|
|
1282
|
+
return (_facets$find$value = facets == null ? void 0 : (_facets$find = facets.find(x => x.key === 'slug')) == null ? void 0 : _facets$find.value) != null ? _facets$find$value : null;
|
|
1271
1283
|
};
|
|
1272
|
-
const
|
|
1284
|
+
const findSkuId = facets => {
|
|
1273
1285
|
var _facets$find$value2, _facets$find2;
|
|
1274
1286
|
|
|
1275
|
-
return (_facets$find$value2 = facets == null ? void 0 : (_facets$find2 = facets.find(
|
|
1287
|
+
return (_facets$find$value2 = facets == null ? void 0 : (_facets$find2 = facets.find(x => x.key === 'id')) == null ? void 0 : _facets$find2.value) != null ? _facets$find$value2 : null;
|
|
1288
|
+
};
|
|
1289
|
+
const findLocale = facets => {
|
|
1290
|
+
var _facets$find$value3, _facets$find3;
|
|
1291
|
+
|
|
1292
|
+
return (_facets$find$value3 = facets == null ? void 0 : (_facets$find3 = facets.find(x => x.key === 'locale')) == null ? void 0 : _facets$find3.value) != null ? _facets$find$value3 : null;
|
|
1293
|
+
};
|
|
1294
|
+
const findChannel = facets => {
|
|
1295
|
+
var _facets$find$value4, _facets$find4;
|
|
1296
|
+
|
|
1297
|
+
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;
|
|
1276
1298
|
};
|
|
1277
1299
|
|
|
1278
1300
|
const SORT_MAP = {
|
|
@@ -1286,6 +1308,42 @@ const SORT_MAP = {
|
|
|
1286
1308
|
score_desc: ''
|
|
1287
1309
|
};
|
|
1288
1310
|
|
|
1311
|
+
/**
|
|
1312
|
+
* More info at: https://en.wikipedia.org/wiki/Order_statistic
|
|
1313
|
+
*/
|
|
1314
|
+
// O(n) search to find the max of an array
|
|
1315
|
+
const min = (array, cmp) => {
|
|
1316
|
+
let best = 0;
|
|
1317
|
+
|
|
1318
|
+
for (let curr = 1; curr < array.length; curr++) {
|
|
1319
|
+
if (cmp(array[best], array[curr]) > 0) {
|
|
1320
|
+
best = curr;
|
|
1321
|
+
}
|
|
1322
|
+
}
|
|
1323
|
+
|
|
1324
|
+
return array[best];
|
|
1325
|
+
};
|
|
1326
|
+
|
|
1327
|
+
/**
|
|
1328
|
+
* This function implements Portal heuristics for returning the best sku for a product.
|
|
1329
|
+
*
|
|
1330
|
+
* The best sku is the one with the best (cheapest available) offer
|
|
1331
|
+
* */
|
|
1332
|
+
|
|
1333
|
+
const pickBestSku = skus => {
|
|
1334
|
+
const offersBySku = skus.flatMap(sku => sku.sellers.map(seller => ({
|
|
1335
|
+
offer: seller.commertialOffer,
|
|
1336
|
+
sku
|
|
1337
|
+
})));
|
|
1338
|
+
const best = min(offersBySku, ({
|
|
1339
|
+
offer: o1
|
|
1340
|
+
}, {
|
|
1341
|
+
offer: o2
|
|
1342
|
+
}) => bestOfferFirst(o1, o2));
|
|
1343
|
+
return best.sku;
|
|
1344
|
+
};
|
|
1345
|
+
const isValidSkuId = skuId => skuId !== '' && !Number.isNaN(Number(skuId));
|
|
1346
|
+
|
|
1289
1347
|
const Query = {
|
|
1290
1348
|
product: async (_, {
|
|
1291
1349
|
locator
|
|
@@ -1293,6 +1351,8 @@ const Query = {
|
|
|
1293
1351
|
// Insert channel in context for later usage
|
|
1294
1352
|
const channel = findChannel(locator);
|
|
1295
1353
|
const locale = findLocale(locator);
|
|
1354
|
+
const id = findSkuId(locator);
|
|
1355
|
+
const slug = findSlug(locator);
|
|
1296
1356
|
|
|
1297
1357
|
if (channel) {
|
|
1298
1358
|
mutateChannelContext(ctx, channel);
|
|
@@ -1305,9 +1365,50 @@ const Query = {
|
|
|
1305
1365
|
const {
|
|
1306
1366
|
loaders: {
|
|
1307
1367
|
skuLoader
|
|
1368
|
+
},
|
|
1369
|
+
clients: {
|
|
1370
|
+
commerce,
|
|
1371
|
+
search
|
|
1308
1372
|
}
|
|
1309
1373
|
} = ctx;
|
|
1310
|
-
|
|
1374
|
+
|
|
1375
|
+
try {
|
|
1376
|
+
var _ref;
|
|
1377
|
+
|
|
1378
|
+
const skuId = (_ref = id != null ? id : slug == null ? void 0 : slug.split('-').pop()) != null ? _ref : '';
|
|
1379
|
+
|
|
1380
|
+
if (!isValidSkuId(skuId)) {
|
|
1381
|
+
throw new Error('Invalid SkuId');
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
const sku = await skuLoader.load(skuId);
|
|
1385
|
+
return sku;
|
|
1386
|
+
} catch (err) {
|
|
1387
|
+
if (slug == null) {
|
|
1388
|
+
throw new BadRequestError('Missing slug or id');
|
|
1389
|
+
}
|
|
1390
|
+
|
|
1391
|
+
const route = await commerce.catalog.portal.pagetype(`${slug}/p`);
|
|
1392
|
+
|
|
1393
|
+
if (route.pageType !== 'Product' || !route.id) {
|
|
1394
|
+
throw new NotFoundError(`No product found for slug ${slug}`);
|
|
1395
|
+
}
|
|
1396
|
+
|
|
1397
|
+
const {
|
|
1398
|
+
products: [product]
|
|
1399
|
+
} = await search.products({
|
|
1400
|
+
page: 0,
|
|
1401
|
+
count: 1,
|
|
1402
|
+
query: `product:${route.id}`
|
|
1403
|
+
});
|
|
1404
|
+
|
|
1405
|
+
if (!product) {
|
|
1406
|
+
throw new NotFoundError(`No product found for id ${route.id}`);
|
|
1407
|
+
}
|
|
1408
|
+
|
|
1409
|
+
const sku = pickBestSku(product.items);
|
|
1410
|
+
return enhanceSku(sku, product);
|
|
1411
|
+
}
|
|
1311
1412
|
},
|
|
1312
1413
|
collection: (_, {
|
|
1313
1414
|
slug
|
|
@@ -1552,8 +1653,10 @@ const StoreSeo = {
|
|
|
1552
1653
|
description: ({
|
|
1553
1654
|
description
|
|
1554
1655
|
}) => description != null ? description : '',
|
|
1555
|
-
|
|
1556
|
-
|
|
1656
|
+
canonical: ({
|
|
1657
|
+
canonical
|
|
1658
|
+
}) => canonical != null ? canonical : '',
|
|
1659
|
+
titleTemplate: () => ''
|
|
1557
1660
|
};
|
|
1558
1661
|
|
|
1559
1662
|
const ObjectOrString = /*#__PURE__*/new GraphQLScalarType({
|
|
@@ -1732,5 +1835,5 @@ const getSchema = async options => makeExecutableSchema({
|
|
|
1732
1835
|
typeDefs
|
|
1733
1836
|
});
|
|
1734
1837
|
|
|
1735
|
-
export { getContextFactory$1 as getContextFactory, getResolvers$1 as getResolvers, getSchema, getTypeDefs };
|
|
1838
|
+
export { BadRequestError, NotFoundError, getContextFactory$1 as getContextFactory, getResolvers$1 as getResolvers, getSchema, getTypeDefs, isBadRequestError, isFastStoreError, isNotFoundError };
|
|
1736
1839
|
//# sourceMappingURL=api.esm.js.map
|