@unchainedshop/core-products 5.0.0-alpha.3 → 5.0.0-alpha.5

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.
@@ -41,11 +41,13 @@ export const ProductsCollection = async (db) => {
41
41
  },
42
42
  ]);
43
43
  await buildDbIndexes(Products, [
44
- { index: { deleted: 1, status: 1, sequence: 1 } },
44
+ { index: { status: 1, sequence: 1, published: -1 } },
45
45
  { index: { slugs: 1 } },
46
46
  { index: { tags: 1 } },
47
47
  { index: { 'warehousing.sku': 1 }, options: { sparse: true } },
48
- ]);
48
+ { index: { 'bundleItems.productId': 1 }, options: { sparse: true } },
49
+ { index: { 'proxy.assignments.productId': 1 }, options: { sparse: true } },
50
+ ], { rebuild: false });
49
51
  await buildDbIndexes(ProductTexts, [
50
52
  { index: { productId: 1 } },
51
53
  { index: { slug: 1 } },
@@ -54,7 +54,7 @@ export const configureProductMediaModule = async ({ db }) => {
54
54
  const mediaList = ProductMedias.find(selector, {
55
55
  skip: offset,
56
56
  limit,
57
- sort: { sortKey: 1 },
57
+ sort: { sortKey: 1, _id: 1 },
58
58
  ...options,
59
59
  });
60
60
  return mediaList.toArray();
@@ -64,7 +64,7 @@ export const configureProductMediaModule = async ({ db }) => {
64
64
  const lastProductMedia = (await ProductMedias.findOne({
65
65
  productId: doc.productId,
66
66
  }, {
67
- sort: { sortKey: -1 },
67
+ sort: { sortKey: -1, _id: -1 },
68
68
  })) || { sortKey: 0 };
69
69
  sortKey = lastProductMedia.sortKey + 1;
70
70
  }
@@ -123,7 +123,7 @@ export const configureProductMediaModule = async ({ db }) => {
123
123
  }));
124
124
  const productMedias = await ProductMedias.find({
125
125
  _id: { $in: changedProductMediaIds },
126
- }, { sort: { sortKey: 1 } }).toArray();
126
+ }, { sort: { sortKey: 1, _id: 1 } }).toArray();
127
127
  await emit('PRODUCT_REORDER_MEDIA', { productMedias });
128
128
  return productMedias;
129
129
  },
@@ -108,7 +108,7 @@ export const configureProductPricesModule = ({ proxyProducts, db, }) => {
108
108
  ],
109
109
  timestamp: { $lte: referenceDate },
110
110
  expiresAt: { $gte: referenceDate },
111
- }, { sort: { timestamp: -1 } });
111
+ }, { sort: { timestamp: -1, _id: -1 } });
112
112
  if (!mostRecentCurrencyRate)
113
113
  return null;
114
114
  const rate = normalizeRate(baseCurrency, quoteCurrency, mostRecentCurrencyRate);
@@ -119,6 +119,21 @@ export declare const configureProductsModule: (moduleInput: ModuleInput<Products
119
119
  }) => Promise<number>;
120
120
  publish: (product: Product) => Promise<boolean>;
121
121
  unpublish: (product: Product) => Promise<boolean>;
122
+ bulkPublish: (productIds: string[]) => Promise<{
123
+ successIds: string[];
124
+ failedIds: string[];
125
+ }>;
126
+ bulkUnpublish: (productIds: string[]) => Promise<{
127
+ successIds: string[];
128
+ failedIds: string[];
129
+ }>;
130
+ bulkUpdateTags: (productIds: string[], { add, remove }: {
131
+ add?: string[];
132
+ remove?: string[];
133
+ }) => Promise<{
134
+ successIds: string[];
135
+ failedIds: string[];
136
+ }>;
122
137
  assignments: {
123
138
  addProxyAssignment: ({ productId, proxyId, vectors, }: {
124
139
  productId: string;
@@ -1,6 +1,6 @@
1
1
  import { emit, registerEvents } from '@unchainedshop/events';
2
2
  import { findPreservingIds, generateDbFilterById, buildSortOptions, mongodb, generateDbObjectId, } from '@unchainedshop/mongodb';
3
- import { SortDirection } from '@unchainedshop/utils';
3
+ import { executeBulkOperation, SortDirection } from '@unchainedshop/utils';
4
4
  import { ProductsCollection, ProductStatus, ProductType, } from "../db/ProductsCollection.js";
5
5
  import { configureProductMediaModule } from "./configureProductMediaModule.js";
6
6
  import { configureProductPricesModule } from "./configureProductPrices.js";
@@ -126,6 +126,16 @@ export const configureProductsModule = async (moduleInput) => {
126
126
  }
127
127
  return false;
128
128
  };
129
+ const updateProduct = async (productId, doc) => {
130
+ const product = await Products.findOneAndUpdate(generateDbFilterById(productId), {
131
+ $set: {
132
+ updated: new Date(),
133
+ ...doc,
134
+ },
135
+ }, { returnDocument: 'after' });
136
+ await emit('PRODUCT_UPDATE', { productId, ...doc, product });
137
+ return productId;
138
+ };
129
139
  const proxyProducts = async (product, vectors = [], { includeInactive = false } = {}) => {
130
140
  const { proxy } = product;
131
141
  let filtered = [...(proxy?.assignments || [])];
@@ -149,7 +159,7 @@ export const configureProductsModule = async (moduleInput) => {
149
159
  return {
150
160
  findProduct: async (params) => {
151
161
  if ('sku' in params) {
152
- return Products.findOne({ 'warehousing.sku': params.sku }, { sort: { sequence: 1 } });
162
+ return Products.findOne({ 'warehousing.sku': params.sku }, { sort: { sequence: 1, _id: 1 } });
153
163
  }
154
164
  if ('slug' in params && params.slug != null) {
155
165
  return Products.findOne({ slugs: params.slug }, {});
@@ -260,17 +270,7 @@ export const configureProductsModule = async (moduleInput) => {
260
270
  await emit('PRODUCT_CREATE', { product });
261
271
  return product;
262
272
  },
263
- update: async (productId, doc) => {
264
- const updateDoc = doc;
265
- const product = await Products.findOneAndUpdate(generateDbFilterById(productId), {
266
- $set: {
267
- updated: new Date(),
268
- ...updateDoc,
269
- },
270
- }, { returnDocument: 'after' });
271
- await emit('PRODUCT_UPDATE', { productId, ...updateDoc, product });
272
- return productId;
273
- },
273
+ update: updateProduct,
274
274
  firstActiveProductProxy: async (productId) => {
275
275
  return Products.findOne({ 'proxy.assignments.productId': productId });
276
276
  },
@@ -292,6 +292,31 @@ export const configureProductsModule = async (moduleInput) => {
292
292
  deleteProductPermanently,
293
293
  publish: publishProduct,
294
294
  unpublish: unpublishProduct,
295
+ bulkPublish: async (productIds) => executeBulkOperation(productIds, async (productId) => {
296
+ const product = await Products.findOne(generateDbFilterById(productId), {});
297
+ if (!product || !(await publishProduct(product)))
298
+ throw new Error('publish-failed');
299
+ }),
300
+ bulkUnpublish: async (productIds) => executeBulkOperation(productIds, async (productId) => {
301
+ const product = await Products.findOne(generateDbFilterById(productId), {});
302
+ if (!product || !(await unpublishProduct(product)))
303
+ throw new Error('unpublish-failed');
304
+ }),
305
+ bulkUpdateTags: async (productIds, { add = [], remove = [] }) => {
306
+ const tagsToRemove = new Set(remove);
307
+ return executeBulkOperation(productIds, async (productId) => {
308
+ const product = await Products.findOne(generateDbFilterById(productId), {});
309
+ if (!product || (product.status !== ProductStatus.ACTIVE && !isDraftStatus(product.status))) {
310
+ throw new Error('not-found');
311
+ }
312
+ const tags = (product.tags || []).filter((tag) => !tagsToRemove.has(tag));
313
+ for (const tag of add) {
314
+ if (!tags.includes(tag))
315
+ tags.push(tag);
316
+ }
317
+ await updateProduct(productId, { tags });
318
+ });
319
+ },
295
320
  assignments: {
296
321
  addProxyAssignment: async ({ productId, proxyId, vectors, }) => {
297
322
  const assignment = {
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@unchainedshop/core-products",
3
3
  "description": "Product catalog management module for the Unchained Engine",
4
- "version": "5.0.0-alpha.3",
4
+ "version": "5.0.0-alpha.5",
5
5
  "main": "lib/products-index.js",
6
6
  "types": "lib/products-index.d.ts",
7
7
  "type": "module",
@@ -35,13 +35,13 @@
35
35
  },
36
36
  "homepage": "https://github.com/unchainedshop/unchained#readme",
37
37
  "dependencies": {
38
- "@unchainedshop/events": "^5.0.0-alpha.1",
39
- "@unchainedshop/logger": "^5.0.0-alpha.1",
40
- "@unchainedshop/mongodb": "^5.0.0-alpha.1",
41
- "@unchainedshop/utils": "^5.0.0-alpha.1"
38
+ "@unchainedshop/events": "^5.0.0-alpha.5",
39
+ "@unchainedshop/logger": "^5.0.0-alpha.5",
40
+ "@unchainedshop/mongodb": "^5.0.0-alpha.5",
41
+ "@unchainedshop/utils": "^5.0.0-alpha.5"
42
42
  },
43
43
  "devDependencies": {
44
44
  "@types/node": "^26.2.0",
45
- "typescript": "^5.8.3"
45
+ "typescript": "^6.0.3"
46
46
  }
47
47
  }