@unchainedshop/api 4.8.4 → 5.0.0-alpha.1

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 (110) hide show
  1. package/lib/acl.d.ts +2 -2
  2. package/lib/acl.js +36 -8
  3. package/lib/api-index.d.ts +3 -1
  4. package/lib/chat/generateImageHandler.js +6 -5
  5. package/lib/context.d.ts +4 -1
  6. package/lib/context.js +9 -3
  7. package/lib/events.d.ts +2 -0
  8. package/lib/events.js +2 -0
  9. package/lib/express/createMCPMiddleware.js +1 -1
  10. package/lib/express/index.d.ts +6 -9
  11. package/lib/express/index.js +52 -125
  12. package/lib/fastify/index.d.ts +6 -9
  13. package/lib/fastify/index.js +51 -117
  14. package/lib/fastify/mcpHandler.js +1 -1
  15. package/lib/loaders/buildTextMap.js +6 -3
  16. package/lib/locale-context.d.ts +4 -2
  17. package/lib/locale-context.js +2 -6
  18. package/lib/mcp/tools/assortment/assortmentManagement.d.ts +2 -2
  19. package/lib/mcp/tools/assortment/assortmentManagement.js +2 -2
  20. package/lib/mcp/tools/assortment/handlers/getAssortment.js +1 -1
  21. package/lib/mcp/tools/assortment/schemas.d.ts +322 -141
  22. package/lib/mcp/tools/assortment/schemas.js +179 -96
  23. package/lib/mcp/tools/filter/filterManagement.d.ts +2 -2
  24. package/lib/mcp/tools/filter/filterManagement.js +2 -2
  25. package/lib/mcp/tools/filter/schemas.d.ts +184 -75
  26. package/lib/mcp/tools/filter/schemas.js +87 -51
  27. package/lib/mcp/tools/localization/getLocalizationsConfig.d.ts +1 -1
  28. package/lib/mcp/tools/localization/localizationManagement.d.ts +2 -2
  29. package/lib/mcp/tools/localization/localizationManagement.js +2 -2
  30. package/lib/mcp/tools/localization/schemas.d.ts +144 -47
  31. package/lib/mcp/tools/localization/schemas.js +51 -36
  32. package/lib/mcp/tools/order/handlers/getTopCustomers.d.ts +2 -0
  33. package/lib/mcp/tools/order/orderManagement.d.ts +2 -2
  34. package/lib/mcp/tools/order/orderManagement.js +2 -2
  35. package/lib/mcp/tools/order/schemas.d.ts +184 -75
  36. package/lib/mcp/tools/order/schemas.js +113 -51
  37. package/lib/mcp/tools/product/handlers/getProduct.js +1 -1
  38. package/lib/mcp/tools/product/productManagement.d.ts +2 -2
  39. package/lib/mcp/tools/product/productManagement.js +2 -2
  40. package/lib/mcp/tools/product/schemas.d.ts +642 -317
  41. package/lib/mcp/tools/product/schemas.js +287 -191
  42. package/lib/mcp/tools/provider/providerManagement.d.ts +2 -2
  43. package/lib/mcp/tools/provider/providerManagement.js +2 -2
  44. package/lib/mcp/tools/provider/schemas.d.ts +142 -47
  45. package/lib/mcp/tools/provider/schemas.js +67 -32
  46. package/lib/mcp/tools/quotation/handlers/getQuotation.d.ts +2 -0
  47. package/lib/mcp/tools/quotation/handlers/listQuotations.d.ts +2 -0
  48. package/lib/mcp/tools/quotation/handlers/makeQuotationProposal.d.ts +2 -0
  49. package/lib/mcp/tools/quotation/handlers/rejectQuotation.d.ts +2 -0
  50. package/lib/mcp/tools/quotation/handlers/verifyQuotation.d.ts +2 -0
  51. package/lib/mcp/tools/quotation/quotationManagement.d.ts +2 -2
  52. package/lib/mcp/tools/quotation/quotationManagement.js +2 -2
  53. package/lib/mcp/tools/quotation/schemas.d.ts +111 -40
  54. package/lib/mcp/tools/quotation/schemas.js +54 -29
  55. package/lib/mcp/tools/system/handlers/countEvents.d.ts +8 -1
  56. package/lib/mcp/tools/system/handlers/countWork.d.ts +10 -1
  57. package/lib/mcp/tools/system/handlers/countWork.js +1 -0
  58. package/lib/mcp/tools/system/handlers/index.d.ts +4 -4
  59. package/lib/mcp/tools/system/handlers/listEvents.d.ts +12 -1
  60. package/lib/mcp/tools/system/handlers/listWork.d.ts +14 -1
  61. package/lib/mcp/tools/system/handlers/listWork.js +1 -0
  62. package/lib/mcp/tools/system/schemas.d.ts +179 -84
  63. package/lib/mcp/tools/system/schemas.js +188 -67
  64. package/lib/mcp/tools/system/systemManagement.d.ts +1 -1
  65. package/lib/mcp/tools/system/systemManagement.js +1 -1
  66. package/lib/mcp/tools/users/handlers/addUserEmail.d.ts +2 -0
  67. package/lib/mcp/tools/users/handlers/createUser.d.ts +2 -0
  68. package/lib/mcp/tools/users/handlers/enrollUser.d.ts +2 -0
  69. package/lib/mcp/tools/users/handlers/getCurrentUser.d.ts +2 -0
  70. package/lib/mcp/tools/users/handlers/getUser.d.ts +2 -0
  71. package/lib/mcp/tools/users/handlers/listUsers.d.ts +2 -0
  72. package/lib/mcp/tools/users/handlers/setUserTags.d.ts +2 -0
  73. package/lib/mcp/tools/users/handlers/setUserUsername.d.ts +2 -0
  74. package/lib/mcp/tools/users/handlers/updateUser.d.ts +2 -0
  75. package/lib/mcp/tools/users/schemas.d.ts +296 -163
  76. package/lib/mcp/tools/users/schemas.js +152 -71
  77. package/lib/mcp/tools/users/usersManagement.d.ts +2 -2
  78. package/lib/mcp/tools/users/usersManagement.js +2 -2
  79. package/lib/mcp/utils/getNormalizedQuotationDetails.d.ts +2 -0
  80. package/lib/mcp/utils/getNormalizedUserDetails.d.ts +2 -0
  81. package/lib/mcp/utils/normalizeMediaUrl.js +1 -1
  82. package/lib/mcp/utils/sanitizeLocalizationEntityData.d.ts +1 -1
  83. package/lib/mcp/utils/sharedSchemas.d.ts +28 -39
  84. package/lib/mcp/utils/sharedSchemas.js +28 -47
  85. package/lib/mcp/utils/validateIsoCode.d.ts +1 -1
  86. package/lib/resolvers/mutations/accounts/changePassword.js +1 -2
  87. package/lib/resolvers/mutations/index.d.ts +150 -155
  88. package/lib/resolvers/mutations/index.js +2 -12
  89. package/lib/resolvers/mutations/orders/updateCart.js +7 -1
  90. package/lib/resolvers/mutations/users/setRoles.d.ts +1 -1
  91. package/lib/resolvers/mutations/users/setRoles.js +8 -1
  92. package/lib/resolvers/queries/events/registeredEventTypes.d.ts +1 -1
  93. package/lib/resolvers/queries/events/registeredEventTypes.js +1 -1
  94. package/lib/resolvers/queries/index.d.ts +71 -71
  95. package/lib/resolvers/queries/index.js +1 -1
  96. package/lib/resolvers/type/index.d.ts +0 -1
  97. package/lib/resolvers/type/login-method-response-types.js +2 -1
  98. package/lib/resolvers/type/order/order-delivery-pickup-types.d.ts +0 -1
  99. package/lib/resolvers/type/order/order-delivery-pickup-types.js +0 -7
  100. package/lib/resolvers/type/shop-types.js +1 -1
  101. package/lib/resolvers/type/user-types.js +3 -3
  102. package/lib/roles/all.js +5 -3
  103. package/lib/roles/index.d.ts +4 -11
  104. package/lib/roles/index.js +16 -11
  105. package/lib/roles/loggedIn.js +2 -0
  106. package/lib/schema/mutation.js +6 -44
  107. package/lib/schema/query.js +5 -5
  108. package/lib/schema/types/index.js +2 -2
  109. package/lib/schema/types/order/delivery.js +0 -2
  110. package/package.json +13 -23
@@ -1,66 +1,59 @@
1
1
  import { getCurrentContextResolver } from "../context.js";
2
- import bulkImportHandler from "./bulkImportHandler.js";
3
- import ercMetadataHandler from "./ercMetadataHandler.js";
4
- import MongoStore from "../mongo-store.js";
5
- import { emit } from '@unchainedshop/events';
6
- import { API_EVENTS } from "../events.js";
7
- import fastifySession from '@fastify/session';
2
+ import { createAuthContext } from "../middleware/createAuthMiddleware.js";
3
+ import { pluginRegistry } from '@unchainedshop/core';
8
4
  import fastifyCookie from '@fastify/cookie';
9
- import fastifyMultipart from '@fastify/multipart';
10
5
  import { createLogger } from '@unchainedshop/logger';
11
6
  import mcpHandler from "./mcpHandler.js";
12
- import tempUploadHandler from "./tempUploadHandler.js";
13
7
  import { connectChat } from "./chatHandler.js";
8
+ import { mountRoutes } from "./mountRoutes.js";
14
9
  import { readFileSync } from 'node:fs';
15
- const resolveUserRemoteAddress = (req) => {
16
- const remoteAddress = req.headers['x-real-ip'] ||
17
- req.headers['x-forwarded-for'] ||
18
- req.socket?.remoteAddress;
10
+ import { createBackchannelLogoutRoute } from "../handlers/createBackchannelLogoutHandler.js";
11
+ const resolveUserRemoteAddress = (req, trustProxy = false) => {
12
+ let remoteAddress;
13
+ if (trustProxy) {
14
+ const forwardedFor = req.headers['x-forwarded-for'];
15
+ const forwardedIps = forwardedFor?.split(',').map((ip) => ip.trim());
16
+ remoteAddress =
17
+ req.headers['x-real-ip'] ||
18
+ forwardedIps?.[forwardedIps.length - 1] ||
19
+ req.socket?.remoteAddress;
20
+ }
21
+ else {
22
+ remoteAddress = req.socket?.remoteAddress;
23
+ }
19
24
  const remotePort = req.socket?.remotePort;
20
25
  return { remoteAddress, remotePort };
21
26
  };
22
- const { MCP_API_PATH = '/mcp', GRAPHQL_API_PATH = '/graphql', BULK_IMPORT_API_PATH = '/bulk-import', TEMP_UPLOAD_API_PATH = '/temp-upload', ERC_METADATA_API_PATH = '/erc-metadata/:productId/:localeOrTokenFilename/:tokenFileName?', UNCHAINED_COOKIE_NAME = 'unchained_token', UNCHAINED_COOKIE_PATH = '/', UNCHAINED_COOKIE_DOMAIN, UNCHAINED_COOKIE_SAMESITE = 'none', UNCHAINED_COOKIE_INSECURE, } = process.env;
23
- const middlewareHook = async function middlewareHook(req, reply) {
27
+ const { MCP_API_PATH = '/mcp' } = process.env;
28
+ const createMiddlewareHook = (authConfig, trustProxy = false) => async function middlewareHook(req, reply) {
24
29
  const setHeader = (key, value) => reply.header(key, value);
25
30
  const getHeader = (key) => req.headers[key];
26
- const { remoteAddress, remotePort } = resolveUserRemoteAddress(req);
27
- const context = getCurrentContextResolver();
28
- const login = async function (user, options = {}) {
29
- const { impersonator } = options;
30
- req.session.userId = user._id;
31
- req.session.impersonatorId = impersonator?._id;
32
- await req.session.save();
33
- const tokenObject = {
34
- _id: req.session.sessionId,
35
- userId: user._id,
36
- tokenExpires: new Date(req.session.cookie._expires),
37
- };
38
- await emit(API_EVENTS.API_LOGIN_TOKEN_CREATED, tokenObject);
39
- user._inLoginMethodResponse = true;
40
- return { user, ...tokenObject };
41
- };
42
- const logout = async function logout() {
43
- const tokenObject = {
44
- _id: req.session.sessionId,
45
- userId: req.session?.userId,
46
- };
47
- req.session.userId = null;
48
- req.session.impersonatorId = null;
49
- await req.session.save();
50
- await emit(API_EVENTS.API_LOGOUT, tokenObject);
51
- return true;
31
+ const getCookie = (name) => req.cookies?.[name];
32
+ const setCookie = (name, value, options) => reply.setCookie(name, value, options);
33
+ const clearCookie = (name, options) => reply.clearCookie(name, { ...options, maxAge: 0 });
34
+ const { remoteAddress, remotePort } = resolveUserRemoteAddress(req, trustProxy);
35
+ const authContextParams = {
36
+ setHeader,
37
+ getHeader,
38
+ getCookie,
39
+ setCookie,
40
+ clearCookie,
41
+ remoteAddress,
42
+ remotePort,
52
43
  };
53
- const [, accessToken] = req.headers.authorization?.split(' ') || [];
44
+ const authContext = await createAuthContext(authContextParams, authConfig);
45
+ const context = getCurrentContextResolver();
54
46
  req.unchainedContext = await context({
55
47
  setHeader,
56
48
  getHeader,
57
49
  remoteAddress,
58
50
  remotePort,
59
- login,
60
- logout,
61
- accessToken,
62
- userId: req.session.userId,
63
- impersonatorId: req.session.impersonatorId,
51
+ login: authContext.login,
52
+ logout: authContext.logout,
53
+ accessToken: authContext.accessToken,
54
+ userId: authContext.userId,
55
+ impersonatorId: authContext.impersonatorId,
56
+ tokenVersion: authContext.tokenVersion,
64
57
  }, req, reply);
65
58
  };
66
59
  export const unchainedLogger = (prefix) => {
@@ -79,8 +72,12 @@ export const unchainedLogger = (prefix) => {
79
72
  };
80
73
  return new Logger();
81
74
  };
82
- export const connect = (fastify, { graphqlHandler, db, unchainedAPI, }, { allowRemoteToLocalhostSecureCookies = false, adminUI = false, chat, initPluginMiddlewares, } = {}) => {
75
+ export const connect = async (fastify, { graphqlHandler, unchainedAPI, }, { allowRemoteToLocalhostSecureCookies = false, adminUI = false, chat, authConfig, trustProxy = false, } = {}) => {
83
76
  if (allowRemoteToLocalhostSecureCookies) {
77
+ if (process.env.NODE_ENV === 'production') {
78
+ throw new Error('allowRemoteToLocalhostSecureCookies is not allowed in production. ' +
79
+ 'Configure a proper CORS policy with specific allowed origins instead.');
80
+ }
84
81
  fastify.addHook('preHandler', async function (request) {
85
82
  request.headers['x-forwarded-proto'] = 'https';
86
83
  });
@@ -94,96 +91,34 @@ export const connect = (fastify, { graphqlHandler, db, unchainedAPI, }, { allowR
94
91
  });
95
92
  });
96
93
  }
97
- const cookieName = UNCHAINED_COOKIE_NAME;
98
- const domain = UNCHAINED_COOKIE_DOMAIN;
99
- const path = UNCHAINED_COOKIE_PATH;
100
- const secure = UNCHAINED_COOKIE_INSECURE ? false : true;
101
- const sameSite = ({
102
- none: 'none',
103
- lax: 'lax',
104
- strict: 'strict',
105
- '1': true,
106
- '0': false,
107
- }[UNCHAINED_COOKIE_SAMESITE?.trim()?.toLowerCase()] || false);
108
94
  if (!fastify.hasPlugin('@fastify/cookie')) {
109
95
  fastify.register(fastifyCookie);
110
96
  }
111
- fastify.register(fastifySession, {
112
- secret: process.env.UNCHAINED_TOKEN_SECRET,
113
- cookieName,
114
- rolling: false,
115
- saveUninitialized: false,
116
- store: MongoStore.create({
117
- client: db.client,
118
- dbName: db.databaseName,
119
- collectionName: 'sessions',
120
- }),
121
- cookie: {
122
- domain,
123
- httpOnly: true,
124
- path,
125
- secure,
126
- sameSite,
127
- maxAge: 1000 * 60 * 60 * 24 * 7,
128
- },
129
- });
130
97
  fastify.decorateRequest('unchainedContext');
131
- fastify.addHook('onRequest', middlewareHook);
98
+ fastify.addHook('onRequest', createMiddlewareHook(authConfig, trustProxy || allowRemoteToLocalhostSecureCookies));
132
99
  fastify.route({
133
- url: GRAPHQL_API_PATH,
100
+ url: graphqlHandler.graphqlEndpoint,
134
101
  method: ['GET', 'POST', 'OPTIONS'],
135
102
  handler: async (req, reply) => {
136
- const response = await graphqlHandler.handleNodeRequestAndResponse(req, reply, {
103
+ return graphqlHandler.handleNodeRequestAndResponse(req, reply, {
137
104
  req,
138
105
  reply,
139
106
  });
140
- response.headers.forEach((value, key) => {
141
- reply.header(key, value);
142
- });
143
- reply.status(response.status);
144
- reply.send(response.body);
145
- return reply;
146
107
  },
147
108
  });
148
- fastify.route({
149
- url: ERC_METADATA_API_PATH,
150
- method: ['GET'],
151
- handler: ercMetadataHandler,
152
- });
153
109
  fastify.route({
154
110
  url: MCP_API_PATH,
155
111
  method: ['GET', 'POST', 'DELETE'],
156
112
  handler: mcpHandler,
157
113
  });
158
- fastify.register((s, opts, registered) => {
159
- s.register(fastifyMultipart, { throwFileSizeLimit: true, limits: { fileSize: 1024 * 1024 * 35 } });
160
- s.route({
161
- url: TEMP_UPLOAD_API_PATH,
162
- method: ['POST'],
163
- bodyLimit: 1024 * 1024 * 35,
164
- handler: tempUploadHandler,
165
- });
166
- registered();
167
- });
168
- fastify.register((s, opts, registered) => {
169
- s.removeAllContentTypeParsers();
170
- s.addContentTypeParser('*', function (req, payload, done) {
171
- done(null);
172
- });
173
- s.route({
174
- url: BULK_IMPORT_API_PATH,
175
- method: ['POST'],
176
- bodyLimit: 1024 * 1024 * 1024 * 5,
177
- handler: bulkImportHandler,
178
- });
179
- registered();
180
- });
181
114
  if (chat) {
182
115
  connectChat(fastify, chat);
183
116
  }
184
- if (initPluginMiddlewares) {
185
- initPluginMiddlewares(fastify, { unchainedAPI });
117
+ const routes = pluginRegistry.getRoutes();
118
+ if (authConfig?.oidcProviders?.length) {
119
+ routes.push(createBackchannelLogoutRoute(authConfig.oidcProviders));
186
120
  }
121
+ mountRoutes(fastify, unchainedAPI, routes);
187
122
  if (adminUI) {
188
123
  fastify.register(adminUIRouter, {
189
124
  enabled: true,
@@ -247,5 +182,4 @@ export const adminUIRouter = async (fastify, opts) => {
247
182
  }
248
183
  }
249
184
  };
250
- export const fastifyRouter = adminUIRouter;
251
185
  export { connectChat };
@@ -1,3 +1,4 @@
1
+ import initMCPServer from "../mcp/index.js";
1
2
  import { createLogger } from '@unchainedshop/logger';
2
3
  const logger = createLogger('unchained:api:mcp');
3
4
  let StreamableHTTPServerTransport;
@@ -44,7 +45,6 @@ const mcpHandler = async (req, res) => {
44
45
  }
45
46
  };
46
47
  const roles = req.unchainedContext.user.roles || [];
47
- const { default: initMCPServer } = await import("../mcp/index.js");
48
48
  const server = initMCPServer(new McpServer({
49
49
  name: 'Unchained MCP Server',
50
50
  version: '1.0.0',
@@ -1,13 +1,16 @@
1
1
  export default function buildTextMap(localeMap, texts, buildId) {
2
2
  const textsMap = {};
3
3
  const localeMapKeys = Object.keys(localeMap);
4
- for (const originLocale of localeMapKeys) {
4
+ for (let i = 0; i < localeMapKeys.length; i++) {
5
+ const originLocale = localeMapKeys[i];
5
6
  const localesForText = localeMap[originLocale];
6
- for (const text of texts) {
7
+ for (let j = 0; j < texts.length; j++) {
8
+ const text = texts[j];
7
9
  if (originLocale !== text.locale)
8
10
  continue;
9
11
  const idPart = buildId(text);
10
- for (const locale of localesForText) {
12
+ for (let k = 0; k < localesForText.length; k++) {
13
+ const locale = localesForText[k];
11
14
  const key = locale + idPart;
12
15
  if (textsMap[key] && locale !== text.locale) {
13
16
  continue;
@@ -6,10 +6,12 @@ export interface UnchainedLocaleContext {
6
6
  currencyCode: string;
7
7
  }
8
8
  export type GetHeaderFn = (key: string) => string | string[];
9
- export declare const resolveDefaultContext: ({ acceptLang, acceptCountry }: {
9
+ export declare const resolveDefaultContext: (({ acceptLang, acceptCountry }: {
10
10
  acceptLang: any;
11
11
  acceptCountry: any;
12
- }, unchainedAPI: UnchainedCore) => Promise<UnchainedLocaleContext>;
12
+ }, unchainedAPI: UnchainedCore) => Promise<UnchainedLocaleContext>) & {
13
+ clear: () => void;
14
+ };
13
15
  export declare const getLocaleContext: ({ getHeader, }: {
14
16
  getHeader: UnchainedHTTPServerContext["getHeader"];
15
17
  }, unchainedAPI: UnchainedCore) => Promise<UnchainedLocaleContext>;
@@ -1,9 +1,6 @@
1
- import { resolveBestSupported, resolveBestCurrency } from '@unchainedshop/utils';
2
- import pMemoize from 'p-memoize';
3
- import ExpiryMap from 'expiry-map';
1
+ import { resolveBestSupported, resolveBestCurrency, memoizeWithTTL } from '@unchainedshop/utils';
4
2
  import { createLogger } from '@unchainedshop/logger';
5
3
  const logger = createLogger('unchained:api');
6
- const memoizeCache = new ExpiryMap(process.env.NODE_ENV === 'production' ? 1000 * 60 : 1);
7
4
  const uncachedResolveDefaultContext = async ({ acceptLang, acceptCountry }, unchainedAPI) => {
8
5
  const languages = await unchainedAPI.modules.languages.findLanguages({ includeInactive: false }, { projection: { isoCode: 1, isActive: 1 } });
9
6
  const countries = await unchainedAPI.modules.countries.findCountries({ includeInactive: false }, { projection: { isoCode: 1, isActive: 1 } });
@@ -19,8 +16,7 @@ const uncachedResolveDefaultContext = async ({ acceptLang, acceptCountry }, unch
19
16
  };
20
17
  return newContext;
21
18
  };
22
- export const resolveDefaultContext = pMemoize(uncachedResolveDefaultContext, {
23
- cache: memoizeCache,
19
+ export const resolveDefaultContext = memoizeWithTTL(uncachedResolveDefaultContext, process.env.NODE_ENV === 'production' ? 1000 * 60 : 1, {
24
20
  cacheKey: (args) => {
25
21
  const [{ acceptLang, acceptCountry }] = args;
26
22
  return `${acceptLang}-${acceptCountry}`;
@@ -1,6 +1,6 @@
1
1
  import type { Context } from '../../../context.ts';
2
- import { AssortmentManagementSchema, type AssortmentManagementParams } from './schemas.ts';
3
- export { AssortmentManagementSchema };
2
+ import { AssortmentManagementSchema, AssortmentManagementZodSchema, type AssortmentManagementParams } from './schemas.ts';
3
+ export { AssortmentManagementSchema, AssortmentManagementZodSchema };
4
4
  export type { AssortmentManagementParams };
5
5
  export declare function assortmentManagement(context: Context, params: AssortmentManagementParams): Promise<{
6
6
  content: {
@@ -1,8 +1,8 @@
1
1
  import { log } from '@unchainedshop/logger';
2
- import { actionValidators, AssortmentManagementSchema, } from "./schemas.js";
2
+ import { actionValidators, AssortmentManagementSchema, AssortmentManagementZodSchema, } from "./schemas.js";
3
3
  import actionHandlers from "./handlers/index.js";
4
4
  import { createMcpResponse, createMcpErrorResponse } from "../../utils/sharedSchemas.js";
5
- export { AssortmentManagementSchema };
5
+ export { AssortmentManagementSchema, AssortmentManagementZodSchema };
6
6
  export async function assortmentManagement(context, params) {
7
7
  const { action, ...actionParams } = params;
8
8
  log('MCP assortmentManagement', { userId: context.userId, params });
@@ -3,7 +3,7 @@ import { getNormalizedAssortmentDetails } from "../../../utils/getNormalizedAsso
3
3
  export default async function getAssortment(context, params) {
4
4
  const { modules } = context;
5
5
  const { assortmentId, slug } = params;
6
- let query;
6
+ let query = {};
7
7
  if (assortmentId)
8
8
  query = { assortmentId };
9
9
  else if (slug)