@unchainedshop/api 4.4.0 → 4.5.0

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.
Files changed (92) hide show
  1. package/lib/api-index.js +3 -0
  2. package/lib/context.js +2 -13
  3. package/lib/errors.d.ts +1 -0
  4. package/lib/errors.js +2 -1
  5. package/lib/events.js +0 -2
  6. package/lib/fastify/index.js +9 -3
  7. package/lib/loaders/assortmentMediaTextLoader.js +1 -1
  8. package/lib/loaders/assortmentMediasLoader.js +1 -1
  9. package/lib/loaders/assortmentTextLoader.js +1 -1
  10. package/lib/loaders/countryLoader.js +1 -1
  11. package/lib/loaders/currencyLoader.js +1 -1
  12. package/lib/loaders/deliveryProviderLoader.js +1 -1
  13. package/lib/loaders/fileLoader.js +1 -1
  14. package/lib/loaders/filterTextLoader.js +1 -1
  15. package/lib/loaders/languageLoader.js +1 -1
  16. package/lib/loaders/orderLoader.js +1 -1
  17. package/lib/loaders/paymentProviderLoader.js +1 -1
  18. package/lib/loaders/productBySKULoader.js +2 -4
  19. package/lib/loaders/productLoader.js +1 -3
  20. package/lib/loaders/productMediaTextLoader.js +1 -1
  21. package/lib/loaders/productMediasLoader.js +1 -1
  22. package/lib/loaders/productProxiesLoader.js +2 -10
  23. package/lib/loaders/productTextLoader.js +1 -1
  24. package/lib/loaders/productVariationByKeyLoader.js +1 -1
  25. package/lib/loaders/productVariationLoader.js +1 -1
  26. package/lib/loaders/productVariationTextLoader.js +1 -1
  27. package/lib/loaders/quotationLoader.js +1 -1
  28. package/lib/loaders/userLoader.js +1 -1
  29. package/lib/loaders/warehousingProviderLoader.js +1 -1
  30. package/lib/mcp/tools/order/handlers/getTopCustomers.d.ts +5 -5
  31. package/lib/mcp/tools/order/handlers/getTopCustomers.js +4 -54
  32. package/lib/mcp/tools/order/handlers/getTopProducts.d.ts +3 -3
  33. package/lib/mcp/tools/order/handlers/getTopProducts.js +3 -42
  34. package/lib/mcp/tools/provider/handlers/listProviders.js +4 -8
  35. package/lib/resolvers/mutations/accounts/addWeb3Address.d.ts +2 -2
  36. package/lib/resolvers/mutations/accounts/addWeb3Address.js +1 -14
  37. package/lib/resolvers/mutations/accounts/addWebAuthnCredentials.d.ts +2 -2
  38. package/lib/resolvers/mutations/accounts/addWebAuthnCredentials.js +2 -6
  39. package/lib/resolvers/mutations/accounts/createUser.js +3 -1
  40. package/lib/resolvers/mutations/accounts/createWebAuthnCredentialCreationOptions.d.ts +1 -4
  41. package/lib/resolvers/mutations/accounts/createWebAuthnCredentialRequestOptions.d.ts +1 -4
  42. package/lib/resolvers/mutations/accounts/loginAsGuest.js +1 -3
  43. package/lib/resolvers/mutations/accounts/removeWeb3Address.d.ts +1 -1
  44. package/lib/resolvers/mutations/accounts/removeWeb3Address.js +3 -7
  45. package/lib/resolvers/mutations/accounts/removeWebAuthnCredentials.d.ts +1 -1
  46. package/lib/resolvers/mutations/accounts/removeWebAuthnCredentials.js +1 -5
  47. package/lib/resolvers/mutations/accounts/verifyWeb3Address.d.ts +2 -2
  48. package/lib/resolvers/mutations/accounts/verifyWeb3Address.js +8 -89
  49. package/lib/resolvers/mutations/filters/removeFilter.d.ts +2 -2
  50. package/lib/resolvers/mutations/filters/removeFilter.js +3 -5
  51. package/lib/resolvers/mutations/orders/addMultipleCartProducts.js +23 -31
  52. package/lib/resolvers/mutations/orders/removeCartDiscount.d.ts +2 -2
  53. package/lib/resolvers/mutations/orders/removeCartDiscount.js +9 -14
  54. package/lib/resolvers/mutations/orders/updateCart.js +2 -8
  55. package/lib/resolvers/mutations/users/removeUser.js +1 -1
  56. package/lib/resolvers/mutations/warehousing/exportToken.js +9 -17
  57. package/lib/resolvers/queries/payment/paymentProvidersCount.d.ts +3 -3
  58. package/lib/resolvers/queries/payment/paymentProvidersCount.js +1 -1
  59. package/lib/resolvers/queries/shopInfo.js +4 -36
  60. package/lib/resolvers/type/delivery-provider-interface.d.ts +1 -7
  61. package/lib/resolvers/type/delivery-provider-interface.js +10 -20
  62. package/lib/resolvers/type/delivery-provider-pickup-types.d.ts +1 -7
  63. package/lib/resolvers/type/delivery-provider-shipping-types.d.ts +1 -7
  64. package/lib/resolvers/type/delivery-provider-types.d.ts +1 -7
  65. package/lib/resolvers/type/index.d.ts +33 -54
  66. package/lib/resolvers/type/media-types.d.ts +1 -1
  67. package/lib/resolvers/type/media-types.js +7 -5
  68. package/lib/resolvers/type/order/order-item-types.d.ts +2 -9
  69. package/lib/resolvers/type/order/order-item-types.js +2 -37
  70. package/lib/resolvers/type/order/order-statistics-types.d.ts +10 -10
  71. package/lib/resolvers/type/order/order-statistics-types.js +10 -64
  72. package/lib/resolvers/type/payment/payment-provider-types.d.ts +1 -7
  73. package/lib/resolvers/type/payment/payment-provider-types.js +7 -17
  74. package/lib/resolvers/type/price-types.d.ts +0 -1
  75. package/lib/resolvers/type/product/product-bundle-types.d.ts +1 -1
  76. package/lib/resolvers/type/product/product-configurable-types.d.ts +1 -1
  77. package/lib/resolvers/type/product/product-configurable-types.js +9 -32
  78. package/lib/resolvers/type/product/product-plan-types.d.ts +1 -1
  79. package/lib/resolvers/type/product/product-simple-types.d.ts +1 -1
  80. package/lib/resolvers/type/product/product-tokenized-types.d.ts +1 -1
  81. package/lib/resolvers/type/product/product-types.d.ts +1 -1
  82. package/lib/resolvers/type/product/product-types.js +7 -17
  83. package/lib/resolvers/type/shop-types.js +2 -5
  84. package/lib/resolvers/type/token-types.d.ts +3 -3
  85. package/lib/resolvers/type/token-types.js +4 -25
  86. package/lib/resolvers/type/user-types.js +1 -4
  87. package/lib/resolvers/type/webauthn-credentials-types.d.ts +10 -1
  88. package/lib/roles/index.d.ts +2 -1
  89. package/lib/roles/index.js +7 -1
  90. package/lib/utils/getConfiguredTags.d.ts +1 -0
  91. package/lib/utils/getConfiguredTags.js +9 -0
  92. package/package.json +7 -17
package/lib/api-index.js CHANGED
@@ -1,5 +1,7 @@
1
+ import { registerEvents } from '@unchainedshop/events';
1
2
  import createGraphQLServer, {} from "./createGraphQLServer.js";
2
3
  import { createContextResolver, setCurrentContextResolver, getCurrentContextResolver, } from "./context.js";
4
+ import { API_EVENTS } from "./events.js";
3
5
  export * from "./events.js";
4
6
  export * from "./context.js";
5
7
  export * from "./locale-context.js";
@@ -12,6 +14,7 @@ import { buildDefaultTypeDefs } from "./schema/index.js";
12
14
  import resolvers from "./resolvers/index.js";
13
15
  import { actions } from "./roles/index.js";
14
16
  export const startAPIServer = async (options) => {
17
+ registerEvents(Object.keys(API_EVENTS));
15
18
  const { unchainedAPI, context: customContext, roles, adminUiConfig = {}, ...serverOptions } = options;
16
19
  const contextResolver = createContextResolver(unchainedAPI, {
17
20
  roles,
package/lib/context.js CHANGED
@@ -1,22 +1,11 @@
1
- var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
- if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
- return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
- return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
- });
6
- }
7
- return path;
8
- };
9
1
  import instantiateLoaders, {} from "./loaders/index.js";
10
2
  import { getLocaleContext } from "./locale-context.js";
3
+ const { npm_package_version, UNCHAINED_API_VERSION } = process.env;
11
4
  let context;
12
5
  export const getCurrentContextResolver = () => context;
13
6
  export const setCurrentContextResolver = (newContext) => {
14
7
  context = newContext;
15
8
  };
16
- const { default: packageJson } = await import(__rewriteRelativeImportExtension(`${import.meta.dirname}/../package.json`), {
17
- with: { type: 'json' },
18
- });
19
- const { UNCHAINED_API_VERSION = packageJson?.version || process.env.npm_package_version || 'n/a' } = process.env;
20
9
  export const createContextResolver = (unchainedAPI, unchainedConfig) => async ({ getHeader, setHeader, remoteAddress, remotePort, userId, impersonatorId, accessToken, login, logout, }) => {
21
10
  const abstractHttpServerContext = { remoteAddress, remotePort, getHeader, setHeader };
22
11
  const loaders = instantiateLoaders(unchainedAPI);
@@ -43,6 +32,6 @@ export const createContextResolver = (unchainedAPI, unchainedConfig) => async ({
43
32
  ...userContext,
44
33
  ...abstractHttpServerContext,
45
34
  loaders,
46
- version: UNCHAINED_API_VERSION,
35
+ version: UNCHAINED_API_VERSION || npm_package_version || 'n/a',
47
36
  };
48
37
  };
package/lib/errors.d.ts CHANGED
@@ -66,6 +66,7 @@ export declare const UsernameAlreadyExistsError: any;
66
66
  export declare const UsernameOrEmailRequiredError: any;
67
67
  export declare const PasswordInvalidError: any;
68
68
  export declare const PasswordOrWebAuthnPublicKeyRequiredError: any;
69
+ export declare const WebAuthnVerificationFailedError: any;
69
70
  export declare const InvalidCredentialsError: any;
70
71
  export declare const InvalidResetTokenError: any;
71
72
  export declare const InvalidEmailVerificationTokenError: any;
package/lib/errors.js CHANGED
@@ -1,6 +1,6 @@
1
1
  import { GraphQLError } from 'graphql';
2
2
  export const createError = (code, message) => class extends GraphQLError {
3
- constructor({ message: explicitMessage, ...data }) {
3
+ constructor({ message: explicitMessage, ...data } = {}) {
4
4
  super(explicitMessage || message, {
5
5
  extensions: {
6
6
  code,
@@ -76,6 +76,7 @@ export const UsernameAlreadyExistsError = createError('UsernameAlreadyExists', '
76
76
  export const UsernameOrEmailRequiredError = createError('UsernameOrEmailRequired', 'No username or email is provided.');
77
77
  export const PasswordInvalidError = createError('PasswordInvalidError', 'The provided password is invalid, maybe too insecure');
78
78
  export const PasswordOrWebAuthnPublicKeyRequiredError = createError('PasswordOrWebAuthnPublicKeyRequired', 'A password or a WebAuthn public key is required');
79
+ export const WebAuthnVerificationFailedError = createError('WebAuthnVerificationFailedError', 'WebAuthn credential verification failed');
79
80
  export const InvalidCredentialsError = createError('InvalidCredentials', 'Invalid credentials provided');
80
81
  export const InvalidResetTokenError = createError('InvalidResetTokenError', 'Token invalid or expired');
81
82
  export const InvalidEmailVerificationTokenError = createError('InvalidEmailVerificationTokenError', 'Token invalid or expired');
package/lib/events.js CHANGED
@@ -1,6 +1,4 @@
1
- import { registerEvents } from '@unchainedshop/events';
2
1
  export const API_EVENTS = {
3
2
  API_LOGIN_TOKEN_CREATED: 'API_LOGIN_TOKEN_CREATED',
4
3
  API_LOGOUT: 'API_LOGOUT',
5
4
  };
6
- registerEvents(Object.keys(API_EVENTS));
@@ -189,9 +189,15 @@ export const connect = (fastify, { graphqlHandler, db, unchainedAPI, }, { allowR
189
189
  };
190
190
  const fallbackLandingPageHandler = (request, reply) => {
191
191
  if (request.raw.method === 'GET') {
192
- const staticURL = import.meta.resolve('@unchainedshop/api/index.html');
193
- const staticPath = new URL(staticURL).pathname.split('/').slice(0, -1).join('/');
194
- return reply.type('text/html').send(readFileSync(staticPath + '/index.html'));
192
+ try {
193
+ const staticURL = import.meta.resolve('@unchainedshop/api/index.html');
194
+ const staticPath = new URL(staticURL).pathname;
195
+ return reply.type('text/html').send(readFileSync(staticPath));
196
+ }
197
+ catch {
198
+ const fallbackPath = new URL('../../index.html', import.meta.url).pathname;
199
+ return reply.type('text/html').send(readFileSync(fallbackPath));
200
+ }
195
201
  }
196
202
  else {
197
203
  return reply.status(404).send();
@@ -3,7 +3,7 @@ import buildTextMap from "./buildTextMap.js";
3
3
  import buildLocaleMap from "./buildLocaleMap.js";
4
4
  export default (unchainedAPI) => new DataLoader(async (queries) => {
5
5
  const assortmentMediaIds = [...new Set(queries.map((q) => q.assortmentMediaId).filter(Boolean))];
6
- const texts = await unchainedAPI.modules.assortments.media.texts.findMediaTexts({ assortmentMediaId: { $in: assortmentMediaIds } }, {
6
+ const texts = await unchainedAPI.modules.assortments.media.texts.findMediaTexts({ assortmentMediaIds }, {
7
7
  sort: {
8
8
  assortmentMediaId: 1,
9
9
  },
@@ -2,7 +2,7 @@ import DataLoader from 'dataloader';
2
2
  export default (unchainedAPI) => new DataLoader(async (queries) => {
3
3
  const assortmentIds = [...new Set(queries.map((q) => q.assortmentId).filter(Boolean))];
4
4
  const assortmentMediaItems = await unchainedAPI.modules.assortments.media.findAssortmentMedias({
5
- assortmentId: { $in: assortmentIds },
5
+ assortmentIds,
6
6
  });
7
7
  const assortmentMediaMap = {};
8
8
  for (const assortmentMedia of assortmentMediaItems) {
@@ -3,7 +3,7 @@ import buildTextMap from "./buildTextMap.js";
3
3
  import buildLocaleMap from "./buildLocaleMap.js";
4
4
  export default (unchainedAPI) => new DataLoader(async (queries) => {
5
5
  const assortmentIds = [...new Set(queries.map((q) => q.assortmentId).filter(Boolean))];
6
- const texts = await unchainedAPI.modules.assortments.texts.findTexts({ assortmentId: { $in: assortmentIds } }, {
6
+ const texts = await unchainedAPI.modules.assortments.texts.findTexts({ assortmentIds }, {
7
7
  sort: {
8
8
  assortmentId: 1,
9
9
  },
@@ -2,7 +2,7 @@ import DataLoader from 'dataloader';
2
2
  export default (unchainedAPI) => new DataLoader(async (queries) => {
3
3
  const isoCodes = [...new Set(queries.map((q) => q.isoCode).filter(Boolean))];
4
4
  const countries = await unchainedAPI.modules.countries.findCountries({
5
- isoCode: { $in: isoCodes },
5
+ isoCodes,
6
6
  includeInactive: true,
7
7
  });
8
8
  const countryMap = {};
@@ -2,7 +2,7 @@ import DataLoader from 'dataloader';
2
2
  export default (unchainedAPI) => new DataLoader(async (queries) => {
3
3
  const isoCodes = [...new Set(queries.map((q) => q.isoCode).filter(Boolean))];
4
4
  const currencies = await unchainedAPI.modules.currencies.findCurrencies({
5
- isoCode: { $in: isoCodes },
5
+ isoCodes,
6
6
  includeInactive: true,
7
7
  });
8
8
  const currencyMap = {};
@@ -2,7 +2,7 @@ import DataLoader from 'dataloader';
2
2
  export default (unchainedAPI) => new DataLoader(async (queries) => {
3
3
  const deliveryProviderIds = [...new Set(queries.map((q) => q.deliveryProviderId).filter(Boolean))];
4
4
  const deliveryProviders = await unchainedAPI.modules.delivery.findProviders({
5
- _id: { $in: deliveryProviderIds },
5
+ deliveryProviderIds,
6
6
  includeDeleted: true,
7
7
  });
8
8
  const deliveryProviderMap = {};
@@ -2,7 +2,7 @@ import DataLoader from 'dataloader';
2
2
  export default (unchainedAPI) => new DataLoader(async (queries) => {
3
3
  const fileIds = [...new Set(queries.map((q) => q.fileId).filter(Boolean))];
4
4
  const files = await unchainedAPI.modules.files.findFiles({
5
- _id: { $in: fileIds },
5
+ fileIds,
6
6
  });
7
7
  const fileMap = {};
8
8
  for (const file of files) {
@@ -3,7 +3,7 @@ import buildLocaleMap from "./buildLocaleMap.js";
3
3
  import buildTextMap from "./buildTextMap.js";
4
4
  export default (unchainedAPI) => new DataLoader(async (queries) => {
5
5
  const filterIds = [...new Set(queries.map((q) => q.filterId).filter(Boolean))];
6
- const texts = await unchainedAPI.modules.filters.texts.findTexts({ filterId: { $in: filterIds } }, {
6
+ const texts = await unchainedAPI.modules.filters.texts.findTexts({ filterIds }, {
7
7
  sort: {
8
8
  filterId: 1,
9
9
  },
@@ -2,7 +2,7 @@ import DataLoader from 'dataloader';
2
2
  export default (unchainedAPI) => new DataLoader(async (queries) => {
3
3
  const isoCodes = [...new Set(queries.map((q) => q.isoCode).filter(Boolean))];
4
4
  const languages = await unchainedAPI.modules.languages.findLanguages({
5
- isoCode: { $in: isoCodes },
5
+ isoCodes,
6
6
  includeInactive: true,
7
7
  });
8
8
  const languageMap = {};
@@ -3,7 +3,7 @@ export default (unchainedAPI) => new DataLoader(async (queries) => {
3
3
  const orderIds = [...new Set(queries.map((q) => q.orderId).filter(Boolean))];
4
4
  const orders = await unchainedAPI.modules.orders.findOrders({
5
5
  includeCarts: true,
6
- _id: { $in: orderIds },
6
+ orderIds,
7
7
  });
8
8
  const orderMap = {};
9
9
  for (const order of orders) {
@@ -2,7 +2,7 @@ import DataLoader from 'dataloader';
2
2
  export default (unchainedAPI) => new DataLoader(async (queries) => {
3
3
  const paymentProviderIds = [...new Set(queries.map((q) => q.paymentProviderId).filter(Boolean))];
4
4
  const paymentProviders = await unchainedAPI.modules.payment.paymentProviders.findProviders({
5
- _id: { $in: paymentProviderIds },
5
+ paymentProviderIds,
6
6
  includeDeleted: true,
7
7
  });
8
8
  const paymentProviderMap = {};
@@ -2,10 +2,8 @@ import DataLoader from 'dataloader';
2
2
  export default (unchainedAPI) => new DataLoader(async (queries) => {
3
3
  const skus = [...new Set(queries.map((q) => q.sku).filter(Boolean))];
4
4
  const products = await unchainedAPI.modules.products.findProducts({
5
- productSelector: {
6
- 'warehousing.sku': { $in: skus },
7
- status: { $exists: true },
8
- },
5
+ skus,
6
+ includeDeleted: true,
9
7
  });
10
8
  const productMap = {};
11
9
  for (const product of products) {
@@ -3,9 +3,7 @@ export default (unchainedAPI) => new DataLoader(async (queries) => {
3
3
  const productIds = [...new Set(queries.map((q) => q.productId).filter(Boolean))];
4
4
  const products = await unchainedAPI.modules.products.findProducts({
5
5
  productIds,
6
- productSelector: {
7
- status: { $exists: true },
8
- },
6
+ includeDeleted: true,
9
7
  });
10
8
  const productMap = {};
11
9
  for (const product of products) {
@@ -3,7 +3,7 @@ import buildLocaleMap from "./buildLocaleMap.js";
3
3
  import buildTextMap from "./buildTextMap.js";
4
4
  export default (unchainedAPI) => new DataLoader(async (queries) => {
5
5
  const productMediaIds = [...new Set(queries.map((q) => q.productMediaId).filter(Boolean))];
6
- const texts = await unchainedAPI.modules.products.media.texts.findMediaTexts({ productMediaId: { $in: productMediaIds } }, {
6
+ const texts = await unchainedAPI.modules.products.media.texts.findMediaTexts({ productMediaIds }, {
7
7
  sort: {
8
8
  productMediaId: 1,
9
9
  },
@@ -2,7 +2,7 @@ import DataLoader from 'dataloader';
2
2
  export default (unchainedAPI) => new DataLoader(async (queries) => {
3
3
  const productIds = [...new Set(queries.map((q) => q.productId).filter(Boolean))];
4
4
  const productMediaItems = await unchainedAPI.modules.products.media.findProductMedias({
5
- productId: { $in: productIds },
5
+ productIds,
6
6
  });
7
7
  const productMediaMap = {};
8
8
  for (const productMedia of productMediaItems) {
@@ -3,16 +3,8 @@ export default (unchainedAPI) => new DataLoader(async (queries) => {
3
3
  const productIds = [...new Set(queries.map((q) => q.productId).filter(Boolean))];
4
4
  const productProxies = await unchainedAPI.modules.products.findProducts({
5
5
  includeDrafts: true,
6
- productSelector: {
7
- $or: [
8
- {
9
- 'bundleItems.productId': { $in: productIds },
10
- },
11
- {
12
- 'proxy.assignments.productId': { $in: productIds },
13
- },
14
- ],
15
- },
6
+ bundleItemProductIds: productIds,
7
+ proxyAssignmentProductIds: productIds,
16
8
  });
17
9
  const productProxyMap = {};
18
10
  for (const productProxy of productProxies) {
@@ -3,7 +3,7 @@ import buildTextMap from "./buildTextMap.js";
3
3
  import buildLocaleMap from "./buildLocaleMap.js";
4
4
  export default (unchainedAPI) => new DataLoader(async (queries) => {
5
5
  const productIds = [...new Set(queries.map((q) => q.productId))].filter(Boolean);
6
- const texts = await unchainedAPI.modules.products.texts.findTexts({ productId: { $in: productIds } }, {
6
+ const texts = await unchainedAPI.modules.products.texts.findTexts({ productIds }, {
7
7
  sort: {
8
8
  productId: 1,
9
9
  },
@@ -2,7 +2,7 @@ import DataLoader from 'dataloader';
2
2
  export default (unchainedAPI) => new DataLoader(async (queries) => {
3
3
  const productIds = [...new Set(queries.map((q) => q.productId).filter(Boolean))];
4
4
  const variations = await unchainedAPI.modules.products.variations.findProductVariations({
5
- productId: { $in: productIds },
5
+ productIds,
6
6
  });
7
7
  const variationMap = {};
8
8
  for (const variation of variations) {
@@ -2,7 +2,7 @@ import DataLoader from 'dataloader';
2
2
  export default (unchainedAPI) => new DataLoader(async (queries) => {
3
3
  const productVariationIds = [...new Set(queries.map((q) => q.productVariationId).filter(Boolean))];
4
4
  const variations = await unchainedAPI.modules.products.variations.findProductVariations({
5
- _id: { $in: productVariationIds },
5
+ productVariationIds,
6
6
  });
7
7
  const variationMap = {};
8
8
  for (const variation of variations) {
@@ -4,7 +4,7 @@ import buildLocaleMap from "./buildLocaleMap.js";
4
4
  export default (unchainedAPI) => new DataLoader(async (queries) => {
5
5
  const productVariationIds = [...new Set(queries.map((q) => q.productVariationId).filter(Boolean))];
6
6
  const texts = await unchainedAPI.modules.products.variations.texts.findVariationTexts({
7
- productVariationId: { $in: productVariationIds },
7
+ productVariationIds,
8
8
  }, {
9
9
  sort: {
10
10
  productVariationId: 1,
@@ -2,7 +2,7 @@ import DataLoader from 'dataloader';
2
2
  export default (unchainedAPI) => new DataLoader(async (queries) => {
3
3
  const quotationIds = [...new Set(queries.map((q) => q.quotationId).filter(Boolean))];
4
4
  const quotations = await unchainedAPI.modules.quotations.findQuotations({
5
- _id: { $in: quotationIds },
5
+ quotationIds,
6
6
  });
7
7
  const quotationMap = {};
8
8
  for (const quotation of quotations) {
@@ -4,7 +4,7 @@ export default (unchainedAPI) => new DataLoader(async (queries) => {
4
4
  const users = await unchainedAPI.modules.users.findUsers({
5
5
  includeGuests: true,
6
6
  includeDeleted: true,
7
- _id: { $in: userIds },
7
+ userIds,
8
8
  });
9
9
  const userMap = {};
10
10
  for (const user of users) {
@@ -4,7 +4,7 @@ export default (unchainedAPI) => new DataLoader(async (queries) => {
4
4
  ...new Set(queries.map((q) => q.warehousingProviderId).filter(Boolean)),
5
5
  ];
6
6
  const warehousingProviders = await unchainedAPI.modules.warehousing.findProviders({
7
- _id: { $in: warehousingProviderIds },
7
+ warehousingProviderIds,
8
8
  includeDeleted: true,
9
9
  });
10
10
  const warehousingProviderMap = {};
@@ -2,7 +2,7 @@ import type { Context } from '../../../../context.ts';
2
2
  import type { Params } from '../schemas.ts';
3
3
  export default function getTopCustomers(context: Context, params: Params<'TOP_CUSTOMERS'>): Promise<{
4
4
  customers: {
5
- userId: any;
5
+ userId: string;
6
6
  user: {
7
7
  avatar: import("@unchainedshop/core-files").File;
8
8
  meta?: any;
@@ -24,10 +24,10 @@ export default function getTopCustomers(context: Context, params: Params<'TOP_CU
24
24
  username?: string | undefined;
25
25
  _id: string;
26
26
  } | null;
27
- currencyCode: any;
28
- totalSpent: any;
29
- orderCount: any;
30
- lastOrderDate: any;
27
+ currencyCode: string | null;
28
+ totalSpent: number;
29
+ orderCount: number;
30
+ lastOrderDate: Date;
31
31
  averageOrderValue: number;
32
32
  }[];
33
33
  }>;
@@ -2,8 +2,7 @@ import { OrderStatus } from '@unchainedshop/core-orders';
2
2
  import { resolveDateRange } from "../../../utils/orderFilters.js";
3
3
  export default async function getTopCustomers(context, params) {
4
4
  const { modules, loaders } = context;
5
- const { customerStatus, from: dateStart, to: dateEnd, limit = 10 } = params;
6
- const match = {};
5
+ const { from: dateStart, to: dateEnd, limit = 10 } = params;
7
6
  const { startDate, endDate } = resolveDateRange(dateStart, dateEnd);
8
7
  const orders = await modules.orders.findOrders({
9
8
  status: [OrderStatus.CONFIRMED, OrderStatus.FULLFILLED],
@@ -14,64 +13,15 @@ export default async function getTopCustomers(context, params) {
14
13
  },
15
14
  });
16
15
  const orderIds = orders.map(({ _id }) => _id);
17
- if (startDate)
18
- match.created = { ...(match.created || {}), $gte: startDate };
19
- if (endDate)
20
- match.created = { ...(match.created || {}), $lte: endDate };
21
- if (customerStatus)
22
- match.status = customerStatus;
23
- if (orderIds?.length)
24
- match._id = { $in: orderIds };
25
- const topCustomers = await modules.orders.aggregateOrders({
26
- match,
27
- project: {
28
- userId: 1,
29
- created: 1,
30
- currencyCode: 1,
31
- itemAmount: {
32
- $let: {
33
- vars: {
34
- item: {
35
- $first: {
36
- $filter: {
37
- input: '$calculation',
38
- as: 'c',
39
- cond: { $eq: ['$$c.category', 'ITEMS'] },
40
- },
41
- },
42
- },
43
- },
44
- in: '$$item.amount',
45
- },
46
- },
47
- },
48
- group: {
49
- _id: { userId: '$userId', currencyCode: '$currencyCode' },
50
- totalSpent: { $sum: '$itemAmount' },
51
- orderCount: { $sum: 1 },
52
- lastOrderDate: { $max: '$created' },
53
- },
54
- matchAfterGroup: {
55
- totalSpent: { $gt: 0 },
56
- },
57
- addFields: {
58
- averageOrderValue: {
59
- $cond: [{ $eq: ['$orderCount', 0] }, 0, { $divide: ['$totalSpent', '$orderCount'] }],
60
- },
61
- currencyCode: '$_id.currencyCode',
62
- _id: '$_id.userId',
63
- },
64
- sort: { totalSpent: -1 },
65
- limit,
66
- });
16
+ const topCustomers = await modules.orders.statistics.getTopCustomers(orderIds, { limit });
67
17
  const normalizedCustomers = await Promise.all(topCustomers.map(async (c) => {
68
- const user = await modules.users.findUserById(c._id);
18
+ const user = await modules.users.findUserById(c.userId);
69
19
  const avatar = user?.avatarId &&
70
20
  (await loaders.fileLoader.load({
71
21
  fileId: user?.avatarId,
72
22
  }));
73
23
  return {
74
- userId: c._id?.toString?.() ?? null,
24
+ userId: c.userId?.toString?.() ?? null,
75
25
  user: avatar
76
26
  ? {
77
27
  ...user,
@@ -2,7 +2,7 @@ import type { Context } from '../../../../context.ts';
2
2
  import type { Params } from '../schemas.ts';
3
3
  export default function getTopProducts(context: Context, params: Params<'TOP_PRODUCTS'>): Promise<{
4
4
  products: {
5
- productId: any;
5
+ productId: string;
6
6
  product: {
7
7
  texts: import("@unchainedshop/core-products").ProductText;
8
8
  media: {
@@ -34,8 +34,8 @@ export default function getTopProducts(context: Context, params: Params<'TOP_PRO
34
34
  updated?: Date;
35
35
  deleted?: Date | null;
36
36
  } | null;
37
- totalSold: any;
38
- totalRevenue: any;
37
+ totalSold: number;
38
+ totalRevenue: number;
39
39
  }[];
40
40
  dateRange: {
41
41
  start: string | null;
@@ -4,7 +4,6 @@ import { getNormalizedProductDetails } from "../../../utils/getNormalizedProduct
4
4
  export default async function getTopProducts(context, params) {
5
5
  const { modules } = context;
6
6
  const { from, to, limit = 10 } = params;
7
- const match = {};
8
7
  const { startDate, endDate } = resolveDateRange(from, to);
9
8
  const orders = await modules.orders.findOrders({
10
9
  status: [OrderStatus.CONFIRMED, OrderStatus.FULLFILLED],
@@ -15,49 +14,11 @@ export default async function getTopProducts(context, params) {
15
14
  },
16
15
  });
17
16
  const orderIds = orders.map(({ _id }) => _id);
18
- if (startDate)
19
- match.created = { ...(match.created || {}), $gte: startDate };
20
- if (endDate)
21
- match.created = { ...(match.created || {}), $lte: endDate };
22
- if (orderIds?.length)
23
- match.orderId = { $in: orderIds };
24
- const topProducts = await modules.orders.positions.aggregatePositions({
25
- match,
26
- project: {
27
- productId: 1,
28
- quantity: 1,
29
- itemAmount: {
30
- $let: {
31
- vars: {
32
- item: {
33
- $first: {
34
- $filter: {
35
- input: '$calculation',
36
- as: 'c',
37
- cond: { $eq: ['$$c.category', 'ITEM'] },
38
- },
39
- },
40
- },
41
- },
42
- in: '$$item.amount',
43
- },
44
- },
45
- },
46
- group: {
47
- _id: '$productId',
48
- totalSold: { $sum: '$quantity' },
49
- totalRevenue: { $sum: '$itemAmount' },
50
- },
51
- matchAfterGroup: {
52
- totalSold: { $gt: 0 },
53
- },
54
- sort: { totalSold: -1 },
55
- limit,
56
- });
17
+ const topProducts = await modules.orders.positions.getTopProducts(orderIds, { limit });
57
18
  const normalizedTopSellingProducts = await Promise.all(topProducts.map(async (p) => {
58
- const product = await getNormalizedProductDetails(p._id, context);
19
+ const product = await getNormalizedProductDetails(p.productId, context);
59
20
  return {
60
- productId: p._id?.toString?.() ?? null,
21
+ productId: p.productId?.toString?.() ?? null,
61
22
  product,
62
23
  totalSold: p.totalSold,
63
24
  totalRevenue: p.totalRevenue,
@@ -2,13 +2,9 @@ import { getProviderConfig } from "../utils/getProviderConfig.js";
2
2
  export default async function listProviders(context, params) {
3
3
  const { providerType, typeFilter, queryString } = params;
4
4
  const config = getProviderConfig(context, providerType);
5
- const selector = {};
6
- if (typeFilter)
7
- selector.type = typeFilter;
8
- if (queryString) {
9
- const regex = new RegExp(queryString, 'i');
10
- selector.$or = [{ _id: regex }, { adapterKey: regex }];
11
- }
12
- const providers = await config.module.findProviders(selector);
5
+ const providers = await config.module.findProviders({
6
+ type: typeFilter,
7
+ queryString,
8
+ });
13
9
  return { providers };
14
10
  }
@@ -1,4 +1,4 @@
1
1
  import type { Context } from '../../../context.ts';
2
2
  export default function addWeb3Address(root: never, { address }: {
3
- address: any;
4
- }, { modules, userId, user }: Context): Promise<import("@unchainedshop/core-users").User | null | undefined>;
3
+ address: string;
4
+ }, { modules, userId, user }: Context): Promise<import("@unchainedshop/core-users").User | null>;
@@ -7,18 +7,5 @@ export default async function addWeb3Address(root, { address }, { modules, userI
7
7
  if (!address) {
8
8
  throw new UserWeb3InvalidAddressError({ userId, address });
9
9
  }
10
- const foundAlreadyExistingEntryForAddress = user.services?.web3?.some((service) => {
11
- return service.address === address;
12
- });
13
- if (foundAlreadyExistingEntryForAddress)
14
- return user;
15
- const nonce = Math.floor(Math.random() * 1000000).toString();
16
- return modules.users.updateUser({ _id: userId }, {
17
- $push: {
18
- 'services.web3': {
19
- address,
20
- nonce,
21
- },
22
- },
23
- }, {});
10
+ return modules.users.addWeb3Address(userId, address);
24
11
  }
@@ -1,4 +1,4 @@
1
1
  import type { Context } from '../../../context.ts';
2
- export default function addWebAuthnCredentials(root: never, { credentials }: {
2
+ export default function addWebAuthnCredentials(_root: never, { credentials }: {
3
3
  credentials: any;
4
- }, { modules, userId, user }: Context): Promise<import("mongodb").WithId<import("@unchainedshop/core-users").User> | null>;
4
+ }, { modules, userId, user }: Context): Promise<import("@unchainedshop/core-users").User | null>;
@@ -1,15 +1,11 @@
1
1
  import { log } from '@unchainedshop/logger';
2
2
  import { WebAuthnDisabledError } from "../../../errors.js";
3
- export default async function addWebAuthnCredentials(root, { credentials }, { modules, userId, user }) {
3
+ export default async function addWebAuthnCredentials(_root, { credentials }, { modules, userId, user }) {
4
4
  log(`mutation addWebAuthnCredentials ${user.username}`, {
5
5
  userId,
6
6
  });
7
7
  const webAuthnService = await modules.users.webAuthn.verifyCredentialCreation(user.username, credentials);
8
8
  if (!webAuthnService)
9
9
  throw new WebAuthnDisabledError();
10
- return modules.users.updateUser({ _id: userId }, {
11
- $push: {
12
- 'services.webAuthn': webAuthnService,
13
- },
14
- }, {});
10
+ return modules.users.addWebAuthnCredential(userId, webAuthnService);
15
11
  }
@@ -1,5 +1,5 @@
1
1
  import { log } from '@unchainedshop/logger';
2
- import { AuthOperationFailedError, EmailAlreadyExistsError, UsernameAlreadyExistsError, UsernameOrEmailRequiredError, PasswordOrWebAuthnPublicKeyRequiredError, PasswordInvalidError, } from "../../../errors.js";
2
+ import { AuthOperationFailedError, EmailAlreadyExistsError, UsernameAlreadyExistsError, UsernameOrEmailRequiredError, PasswordOrWebAuthnPublicKeyRequiredError, PasswordInvalidError, WebAuthnVerificationFailedError, } from "../../../errors.js";
3
3
  export default async function createUser(root, params, context) {
4
4
  const { modules, userId } = context;
5
5
  log('mutation createUser', { email: params.email, username: params.username, userId });
@@ -36,6 +36,8 @@ export default async function createUser(root, params, context) {
36
36
  throw new UsernameAlreadyExistsError({ username: params?.username });
37
37
  else if (e.cause === 'PASSWORD_INVALID')
38
38
  throw new PasswordInvalidError({ username: params?.username });
39
+ else if (e.cause === 'WEBAUTHN_INVALID')
40
+ throw new WebAuthnVerificationFailedError({ username: params?.username });
39
41
  else
40
42
  throw new AuthOperationFailedError({ username: params?.username, email: params.email });
41
43
  }