@classytic/mongokit 2.0.0 → 3.0.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 (105) hide show
  1. package/README.md +221 -7
  2. package/dist/actions/index.d.ts +3 -0
  3. package/dist/actions/index.js +473 -0
  4. package/dist/actions/index.js.map +1 -0
  5. package/dist/index-CgOJ2pqz.d.ts +337 -0
  6. package/dist/index.d.ts +239 -0
  7. package/dist/index.js +2108 -0
  8. package/dist/index.js.map +1 -0
  9. package/dist/memory-cache-DG2oSSbx.d.ts +142 -0
  10. package/dist/pagination/PaginationEngine.d.ts +117 -0
  11. package/dist/pagination/PaginationEngine.js +369 -0
  12. package/dist/pagination/PaginationEngine.js.map +1 -0
  13. package/dist/plugins/index.d.ts +275 -0
  14. package/dist/plugins/index.js +857 -0
  15. package/dist/plugins/index.js.map +1 -0
  16. package/dist/types-Nxhmi1aI.d.ts +510 -0
  17. package/dist/utils/index.d.ts +189 -0
  18. package/dist/utils/index.js +643 -0
  19. package/dist/utils/index.js.map +1 -0
  20. package/package.json +38 -21
  21. package/src/Repository.js +0 -296
  22. package/src/actions/aggregate.js +0 -266
  23. package/src/actions/create.js +0 -59
  24. package/src/actions/delete.js +0 -88
  25. package/src/actions/index.js +0 -11
  26. package/src/actions/read.js +0 -188
  27. package/src/actions/update.js +0 -176
  28. package/src/hooks/lifecycle.js +0 -146
  29. package/src/index.js +0 -71
  30. package/src/pagination/PaginationEngine.js +0 -348
  31. package/src/pagination/utils/cursor.js +0 -119
  32. package/src/pagination/utils/filter.js +0 -42
  33. package/src/pagination/utils/limits.js +0 -82
  34. package/src/pagination/utils/sort.js +0 -101
  35. package/src/plugins/aggregate-helpers.plugin.js +0 -71
  36. package/src/plugins/audit-log.plugin.js +0 -60
  37. package/src/plugins/batch-operations.plugin.js +0 -66
  38. package/src/plugins/field-filter.plugin.js +0 -27
  39. package/src/plugins/index.js +0 -19
  40. package/src/plugins/method-registry.plugin.js +0 -140
  41. package/src/plugins/mongo-operations.plugin.js +0 -317
  42. package/src/plugins/soft-delete.plugin.js +0 -46
  43. package/src/plugins/subdocument.plugin.js +0 -66
  44. package/src/plugins/timestamp.plugin.js +0 -19
  45. package/src/plugins/validation-chain.plugin.js +0 -145
  46. package/src/types.d.ts +0 -87
  47. package/src/utils/error.js +0 -12
  48. package/src/utils/field-selection.js +0 -156
  49. package/src/utils/index.js +0 -12
  50. package/types/Repository.d.ts +0 -95
  51. package/types/Repository.d.ts.map +0 -1
  52. package/types/actions/aggregate.d.ts +0 -112
  53. package/types/actions/aggregate.d.ts.map +0 -1
  54. package/types/actions/create.d.ts +0 -21
  55. package/types/actions/create.d.ts.map +0 -1
  56. package/types/actions/delete.d.ts +0 -37
  57. package/types/actions/delete.d.ts.map +0 -1
  58. package/types/actions/index.d.ts +0 -6
  59. package/types/actions/index.d.ts.map +0 -1
  60. package/types/actions/read.d.ts +0 -135
  61. package/types/actions/read.d.ts.map +0 -1
  62. package/types/actions/update.d.ts +0 -58
  63. package/types/actions/update.d.ts.map +0 -1
  64. package/types/hooks/lifecycle.d.ts +0 -44
  65. package/types/hooks/lifecycle.d.ts.map +0 -1
  66. package/types/index.d.ts +0 -25
  67. package/types/index.d.ts.map +0 -1
  68. package/types/pagination/PaginationEngine.d.ts +0 -386
  69. package/types/pagination/PaginationEngine.d.ts.map +0 -1
  70. package/types/pagination/utils/cursor.d.ts +0 -40
  71. package/types/pagination/utils/cursor.d.ts.map +0 -1
  72. package/types/pagination/utils/filter.d.ts +0 -28
  73. package/types/pagination/utils/filter.d.ts.map +0 -1
  74. package/types/pagination/utils/limits.d.ts +0 -64
  75. package/types/pagination/utils/limits.d.ts.map +0 -1
  76. package/types/pagination/utils/sort.d.ts +0 -41
  77. package/types/pagination/utils/sort.d.ts.map +0 -1
  78. package/types/plugins/aggregate-helpers.plugin.d.ts +0 -6
  79. package/types/plugins/aggregate-helpers.plugin.d.ts.map +0 -1
  80. package/types/plugins/audit-log.plugin.d.ts +0 -6
  81. package/types/plugins/audit-log.plugin.d.ts.map +0 -1
  82. package/types/plugins/batch-operations.plugin.d.ts +0 -6
  83. package/types/plugins/batch-operations.plugin.d.ts.map +0 -1
  84. package/types/plugins/field-filter.plugin.d.ts +0 -6
  85. package/types/plugins/field-filter.plugin.d.ts.map +0 -1
  86. package/types/plugins/index.d.ts +0 -11
  87. package/types/plugins/index.d.ts.map +0 -1
  88. package/types/plugins/method-registry.plugin.d.ts +0 -3
  89. package/types/plugins/method-registry.plugin.d.ts.map +0 -1
  90. package/types/plugins/mongo-operations.plugin.d.ts +0 -4
  91. package/types/plugins/mongo-operations.plugin.d.ts.map +0 -1
  92. package/types/plugins/soft-delete.plugin.d.ts +0 -6
  93. package/types/plugins/soft-delete.plugin.d.ts.map +0 -1
  94. package/types/plugins/subdocument.plugin.d.ts +0 -6
  95. package/types/plugins/subdocument.plugin.d.ts.map +0 -1
  96. package/types/plugins/timestamp.plugin.d.ts +0 -6
  97. package/types/plugins/timestamp.plugin.d.ts.map +0 -1
  98. package/types/plugins/validation-chain.plugin.d.ts +0 -31
  99. package/types/plugins/validation-chain.plugin.d.ts.map +0 -1
  100. package/types/utils/error.d.ts +0 -11
  101. package/types/utils/error.d.ts.map +0 -1
  102. package/types/utils/field-selection.d.ts +0 -9
  103. package/types/utils/field-selection.d.ts.map +0 -1
  104. package/types/utils/index.d.ts +0 -2
  105. package/types/utils/index.d.ts.map +0 -1
@@ -1,71 +0,0 @@
1
- /**
2
- * Aggregate Helpers Plugin
3
- * Adds common aggregation helper methods
4
- */
5
-
6
- export const aggregateHelpersPlugin = () => ({
7
- name: 'aggregate-helpers',
8
-
9
- apply(repo) {
10
- if (!repo.registerMethod) {
11
- throw new Error('aggregateHelpersPlugin requires methodRegistryPlugin');
12
- }
13
-
14
- /**
15
- * Group by field
16
- */
17
- repo.registerMethod('groupBy', async function (field, options = {}) {
18
- const pipeline = [
19
- { $group: { _id: `$${field}`, count: { $sum: 1 } } },
20
- { $sort: { count: -1 } }
21
- ];
22
-
23
- if (options.limit) {
24
- pipeline.push(/** @type {any} */({ $limit: options.limit }));
25
- }
26
-
27
- return this.aggregate(pipeline, options);
28
- });
29
-
30
- // Helper: Generic aggregation operation
31
- const aggregateOperation = async function (field, operator, resultKey, query = {}, options = {}) {
32
- const pipeline = [
33
- { $match: query },
34
- { $group: { _id: null, [resultKey]: { [operator]: `$${field}` } } }
35
- ];
36
-
37
- const result = await this.aggregate(pipeline, options);
38
- return result[0]?.[resultKey] || 0;
39
- };
40
-
41
- /**
42
- * Sum field values
43
- */
44
- repo.registerMethod('sum', async function (field, query = {}, options = {}) {
45
- return aggregateOperation.call(this, field, '$sum', 'total', query, options);
46
- });
47
-
48
- /**
49
- * Average field values
50
- */
51
- repo.registerMethod('average', async function (field, query = {}, options = {}) {
52
- return aggregateOperation.call(this, field, '$avg', 'avg', query, options);
53
- });
54
-
55
- /**
56
- * Get minimum value
57
- */
58
- repo.registerMethod('min', async function (field, query = {}, options = {}) {
59
- return aggregateOperation.call(this, field, '$min', 'min', query, options);
60
- });
61
-
62
- /**
63
- * Get maximum value
64
- */
65
- repo.registerMethod('max', async function (field, query = {}, options = {}) {
66
- return aggregateOperation.call(this, field, '$max', 'max', query, options);
67
- });
68
- }
69
- });
70
-
71
- export default aggregateHelpersPlugin;
@@ -1,60 +0,0 @@
1
- export const auditLogPlugin = (logger) => ({
2
- name: 'auditLog',
3
-
4
- apply(repo) {
5
- repo.on('after:create', ({ context, result }) => {
6
- logger?.info?.('Document created', {
7
- model: context.model || repo.model,
8
- id: result._id,
9
- userId: context.user?._id || context.user?.id,
10
- organizationId: context.organizationId,
11
- });
12
- });
13
-
14
- repo.on('after:update', ({ context, result }) => {
15
- logger?.info?.('Document updated', {
16
- model: context.model || repo.model,
17
- id: context.id || result._id,
18
- userId: context.user?._id || context.user?.id,
19
- organizationId: context.organizationId,
20
- });
21
- });
22
-
23
- repo.on('after:delete', ({ context, result }) => {
24
- logger?.info?.('Document deleted', {
25
- model: context.model || repo.model,
26
- id: context.id,
27
- userId: context.user?._id || context.user?.id,
28
- organizationId: context.organizationId,
29
- });
30
- });
31
-
32
- repo.on('error:create', ({ context, error }) => {
33
- logger?.error?.('Create failed', {
34
- model: context.model || repo.model,
35
- error: error.message,
36
- userId: context.user?._id || context.user?.id,
37
- });
38
- });
39
-
40
- repo.on('error:update', ({ context, error }) => {
41
- logger?.error?.('Update failed', {
42
- model: context.model || repo.model,
43
- id: context.id,
44
- error: error.message,
45
- userId: context.user?._id || context.user?.id,
46
- });
47
- });
48
-
49
- repo.on('error:delete', ({ context, error }) => {
50
- logger?.error?.('Delete failed', {
51
- model: context.model || repo.model,
52
- id: context.id,
53
- error: error.message,
54
- userId: context.user?._id || context.user?.id,
55
- });
56
- });
57
- },
58
- });
59
-
60
- export default auditLogPlugin;
@@ -1,66 +0,0 @@
1
- /**
2
- * Batch Operations Plugin
3
- * Adds bulk update/delete operations with proper event emission
4
- */
5
-
6
- export const batchOperationsPlugin = () => ({
7
- name: 'batch-operations',
8
-
9
- apply(repo) {
10
- if (!repo.registerMethod) {
11
- throw new Error('batchOperationsPlugin requires methodRegistryPlugin');
12
- }
13
-
14
- /**
15
- * Update multiple documents
16
- * @param {Object} query - MongoDB query to match documents
17
- * @param {Object} data - Update data
18
- * @param {Object} options - Additional options (session, context)
19
- * @returns {Promise<Object>} MongoDB update result
20
- */
21
- repo.registerMethod('updateMany', async function (query, data, options = {}) {
22
- const context = await this._buildContext('updateMany', { query, data, options });
23
-
24
- try {
25
- this.emit('before:updateMany', context);
26
-
27
- const result = await this.Model.updateMany(query, data, {
28
- runValidators: true,
29
- session: options.session,
30
- }).exec();
31
-
32
- this.emit('after:updateMany', { context, result });
33
- return result;
34
- } catch (error) {
35
- this.emit('error:updateMany', { context, error });
36
- throw this._handleError(error);
37
- }
38
- });
39
-
40
- /**
41
- * Delete multiple documents
42
- * @param {Object} query - MongoDB query to match documents
43
- * @param {Object} options - Additional options (session, context)
44
- * @returns {Promise<Object>} MongoDB delete result
45
- */
46
- repo.registerMethod('deleteMany', async function (query, options = {}) {
47
- const context = await this._buildContext('deleteMany', { query, options });
48
-
49
- try {
50
- this.emit('before:deleteMany', context);
51
-
52
- const result = await this.Model.deleteMany(query, {
53
- session: options.session,
54
- }).exec();
55
-
56
- this.emit('after:deleteMany', { context, result });
57
- return result;
58
- } catch (error) {
59
- this.emit('error:deleteMany', { context, error });
60
- throw this._handleError(error);
61
- }
62
- });
63
- }
64
- });
65
-
66
- export default batchOperationsPlugin;
@@ -1,27 +0,0 @@
1
- import { getFieldsForUser } from '../utils/field-selection.js';
2
-
3
- export const fieldFilterPlugin = (fieldPreset) => ({
4
- name: 'fieldFilter',
5
-
6
- apply(repo) {
7
- const applyFieldFiltering = (context) => {
8
- if (!fieldPreset) return;
9
-
10
- const user = context.context?.user || context.user;
11
- const fields = getFieldsForUser(user, fieldPreset);
12
- const presetSelect = fields.join(' ');
13
-
14
- if (context.select) {
15
- context.select = `${presetSelect} ${context.select}`;
16
- } else {
17
- context.select = presetSelect;
18
- }
19
- };
20
-
21
- repo.on('before:getAll', applyFieldFiltering);
22
- repo.on('before:getById', applyFieldFiltering);
23
- repo.on('before:getByQuery', applyFieldFiltering);
24
- },
25
- });
26
-
27
- export default fieldFilterPlugin;
@@ -1,19 +0,0 @@
1
- // Core plugins
2
- export { fieldFilterPlugin } from './field-filter.plugin.js';
3
- export { timestampPlugin } from './timestamp.plugin.js';
4
- export { auditLogPlugin } from './audit-log.plugin.js';
5
- export { softDeletePlugin } from './soft-delete.plugin.js';
6
- export { methodRegistryPlugin } from './method-registry.plugin.js';
7
- export {
8
- validationChainPlugin,
9
- blockIf,
10
- requireField,
11
- autoInject,
12
- immutableField,
13
- uniqueField,
14
- } from './validation-chain.plugin.js';
15
- export { mongoOperationsPlugin } from './mongo-operations.plugin.js';
16
- export { batchOperationsPlugin } from './batch-operations.plugin.js';
17
- export { aggregateHelpersPlugin } from './aggregate-helpers.plugin.js';
18
- export { subdocumentPlugin } from './subdocument.plugin.js';
19
-
@@ -1,140 +0,0 @@
1
- /**
2
- * Method Registry Plugin
3
- *
4
- * Enables plugins to dynamically add methods to repository instances.
5
- * Foundation for extensibility - allows other plugins to extend repositories
6
- * with custom methods while maintaining type safety and proper binding.
7
- *
8
- * **Pattern:** Inspired by Stripe's extension system
9
- * **Philosophy:** Repositories start minimal, plugins add capabilities
10
- *
11
- * @module common/repositories/plugins/method-registry
12
- *
13
- * @example Basic Usage
14
- * ```js
15
- * import { Repository } from '../Repository.js';
16
- * import { methodRegistryPlugin } from './method-registry.plugin.js';
17
- *
18
- * class UserRepository extends Repository {
19
- * constructor() {
20
- * super(User, [methodRegistryPlugin()]);
21
- *
22
- * // Now you can register custom methods
23
- * this.registerMethod('findActive', async function() {
24
- * return this.getAll({ filters: { status: 'active' } });
25
- * });
26
- * }
27
- * }
28
- * ```
29
- *
30
- * @example Plugin Using Method Registry
31
- * ```js
32
- * // Other plugins can use registerMethod to add functionality
33
- * export const mongoOperationsPlugin = () => ({
34
- * name: 'mongo-operations',
35
- * apply(repo) {
36
- * repo.registerMethod('increment', async function(id, field, value = 1, options = {}) {
37
- * return this.update(id, { $inc: { [field]: value } }, options);
38
- * });
39
- * }
40
- * });
41
- * ```
42
- */
43
-
44
- /**
45
- * Method Registry Plugin
46
- *
47
- * Adds `registerMethod()` to repository instance, allowing dynamic method addition.
48
- *
49
- * @returns {Object} Plugin configuration
50
- */
51
- export const methodRegistryPlugin = () => ({
52
- name: 'method-registry',
53
-
54
- apply(repo) {
55
- /**
56
- * Register a new method on the repository instance
57
- *
58
- * **Rules:**
59
- * - Method name must not conflict with existing methods
60
- * - Method is automatically bound to repository instance
61
- * - Method has access to all repository methods via `this`
62
- * - Async methods are recommended for consistency
63
- *
64
- * @param {string} name - Method name
65
- * @param {Function} fn - Method implementation (will be bound to repo)
66
- * @throws {Error} If method name already exists
67
- *
68
- * @example
69
- * repo.registerMethod('findByEmail', async function(email) {
70
- * return this.getByQuery({ email }, { lean: true });
71
- * });
72
- *
73
- * @example With options
74
- * repo.registerMethod('incrementViews', async function(id, amount = 1) {
75
- * return this.update(id, { $inc: { views: amount } });
76
- * });
77
- */
78
- repo.registerMethod = function (name, fn) {
79
- // Check for naming conflicts
80
- if (repo[name]) {
81
- throw new Error(
82
- `Cannot register method '${name}': Method already exists on repository. ` +
83
- `Choose a different name or use a plugin that doesn't conflict.`
84
- );
85
- }
86
-
87
- // Validate method name
88
- if (!name || typeof name !== 'string') {
89
- throw new Error('Method name must be a non-empty string');
90
- }
91
-
92
- // Validate function
93
- if (typeof fn !== 'function') {
94
- throw new Error(`Method '${name}' must be a function`);
95
- }
96
-
97
- // Bind function to repository instance
98
- repo[name] = fn.bind(repo);
99
-
100
- // Emit event for plugin system awareness
101
- repo.emit('method:registered', { name, fn });
102
- };
103
-
104
- /**
105
- * Check if a method is registered
106
- *
107
- * @param {string} name - Method name to check
108
- * @returns {boolean} True if method exists
109
- *
110
- * @example
111
- * if (repo.hasMethod('increment')) {
112
- * await repo.increment(id, 'count', 1);
113
- * }
114
- */
115
- repo.hasMethod = function (name) {
116
- return typeof repo[name] === 'function';
117
- };
118
-
119
- /**
120
- * Get list of all dynamically registered methods
121
- *
122
- * @returns {Array<string>} Array of method names
123
- *
124
- * @example
125
- * const methods = repo.getRegisteredMethods();
126
- * console.log('Available methods:', methods);
127
- */
128
- repo.getRegisteredMethods = function () {
129
- const registeredMethods = [];
130
-
131
- repo.on('method:registered', ({ name }) => {
132
- registeredMethods.push(name);
133
- });
134
-
135
- return registeredMethods;
136
- };
137
- }
138
- });
139
-
140
- export default methodRegistryPlugin;
@@ -1,317 +0,0 @@
1
- /**
2
- * @typedef {import('../types.js').ObjectId} ObjectId
3
- */
4
-
5
- /**
6
- * MongoDB Operations Plugin
7
- *
8
- * Adds MongoDB-specific operations to repositories.
9
- * Requires method-registry.plugin.js to be loaded first.
10
- *
11
- * **Operations Added:**
12
- * - upsert(query, data, options) - Update or insert
13
- * - increment(id, field, value, options) - Atomic increment
14
- * - decrement(id, field, value, options) - Atomic decrement
15
- * - pushToArray(id, field, value, options) - Add to array
16
- * - pullFromArray(id, field, value, options) - Remove from array
17
- * - addToSet(id, field, value, options) - Add unique to array
18
- *
19
- * **Pattern:** Opt-in MongoDB features
20
- * **Philosophy:** Keep core pure, add database-specific features via plugins
21
- *
22
- * @module common/repositories/plugins/mongo-operations
23
- * @requires method-registry.plugin
24
- *
25
- * @example Basic Usage
26
- * ```js
27
- * import { Repository } from '../Repository.js';
28
- * import { method
29
-
30
- RegistryPlugin } from './method-registry.plugin.js';
31
- * import { mongoOperationsPlugin } from './mongo-operations.plugin.js';
32
- *
33
- * class ProductRepository extends Repository {
34
- * constructor() {
35
- * super(Product, [
36
- * methodRegistryPlugin(),
37
- * mongoOperationsPlugin(),
38
- * ]);
39
- * }
40
- * }
41
- *
42
- * // Now you can use MongoDB operations
43
- * await productRepo.increment(productId, 'views', 1);
44
- * await productRepo.pushToArray(productId, 'tags', 'featured');
45
- * ```
46
- */
47
-
48
- import { createError } from '../utils/error.js';
49
- import * as createActions from '../actions/create.js';
50
-
51
- /**
52
- * MongoDB Operations Plugin
53
- *
54
- * Adds common MongoDB atomic operations to repository.
55
- * All operations use repository's update method internally (events/plugins run).
56
- *
57
- * @returns {Object} Plugin configuration
58
- */
59
- export const mongoOperationsPlugin = () => ({
60
- name: 'mongo-operations',
61
-
62
- apply(repo) {
63
- // Check if method-registry is available
64
- if (!repo.registerMethod) {
65
- throw new Error(
66
- 'mongoOperationsPlugin requires methodRegistryPlugin. ' +
67
- 'Add methodRegistryPlugin() before mongoOperationsPlugin() in plugins array.'
68
- );
69
- }
70
-
71
- /**
72
- * Update existing document or insert new one
73
- *
74
- * @param {Object} query - Query to find existing document
75
- * @param {Object} data - Data to insert/update
76
- * @param {Object} [options={}] - Options
77
- * @returns {Promise<Object>} Upserted document
78
- *
79
- * @example
80
- * // Create or update user session
81
- * await repo.upsert(
82
- * { userId, deviceId },
83
- * { lastActive: new Date(), ipAddress },
84
- * { lean: true }
85
- * );
86
- */
87
- repo.registerMethod('upsert', async function (query, data, options = {}) {
88
- return createActions.upsert(this.Model, query, data, options);
89
- });
90
-
91
- // Helper: Validate and perform numeric operation
92
- const validateAndUpdateNumeric = function (id, field, value, operator, operationName, options) {
93
- if (typeof value !== 'number') {
94
- throw createError(400, `${operationName} value must be a number`);
95
- }
96
- return this.update(id, { [operator]: { [field]: value } }, options);
97
- };
98
-
99
- /**
100
- * Atomically increment numeric field
101
- *
102
- * @param {string|ObjectId} id - Document ID
103
- * @param {string} field - Field name to increment
104
- * @param {number} [value=1] - Amount to increment by
105
- * @param {Object} [options={}] - Options
106
- * @returns {Promise<Object>} Updated document
107
- *
108
- * @example
109
- * // Increment product views
110
- * await productRepo.increment(productId, 'views', 1);
111
- *
112
- * @example
113
- * // Increment multiple times
114
- * await productRepo.increment(userId, 'points', 100);
115
- */
116
- repo.registerMethod('increment', async function (id, field, value = 1, options = {}) {
117
- return validateAndUpdateNumeric.call(this, id, field, value, '$inc', 'Increment', options);
118
- });
119
-
120
- /**
121
- * Atomically decrement numeric field
122
- *
123
- * @param {string|ObjectId} id - Document ID
124
- * @param {string} field - Field name to decrement
125
- * @param {number} [value=1] - Amount to decrement by
126
- * @param {Object} [options={}] - Options
127
- * @returns {Promise<Object>} Updated document
128
- *
129
- * @example
130
- * // Decrement product stock
131
- * await productRepo.decrement(productId, 'stock', 1);
132
- */
133
- repo.registerMethod('decrement', async function (id, field, value = 1, options = {}) {
134
- return validateAndUpdateNumeric.call(this, id, field, -value, '$inc', 'Decrement', options);
135
- });
136
-
137
- // Helper: Generic MongoDB operator update
138
- const applyOperator = function (id, field, value, operator, options) {
139
- return this.update(id, { [operator]: { [field]: value } }, options);
140
- };
141
-
142
- /**
143
- * Push value to array field
144
- *
145
- * @param {string|ObjectId} id - Document ID
146
- * @param {string} field - Array field name
147
- * @param {*} value - Value to push
148
- * @param {Object} [options={}] - Options
149
- * @returns {Promise<Object>} Updated document
150
- *
151
- * @example
152
- * // Add tag to product
153
- * await productRepo.pushToArray(productId, 'tags', 'new-arrival');
154
- *
155
- * @example
156
- * // Add multiple items
157
- * await productRepo.pushToArray(userId, 'notifications', {
158
- * message: 'Welcome!',
159
- * createdAt: new Date()
160
- * });
161
- */
162
- repo.registerMethod('pushToArray', async function (id, field, value, options = {}) {
163
- return applyOperator.call(this, id, field, value, '$push', options);
164
- });
165
-
166
- /**
167
- * Remove value from array field
168
- *
169
- * @param {string|ObjectId} id - Document ID
170
- * @param {string} field - Array field name
171
- * @param {*} value - Value to remove (can be query object)
172
- * @param {Object} [options={}] - Options
173
- * @returns {Promise<Object>} Updated document
174
- *
175
- * @example
176
- * // Remove tag from product
177
- * await productRepo.pullFromArray(productId, 'tags', 'old-tag');
178
- *
179
- * @example
180
- * // Remove by query
181
- * await productRepo.pullFromArray(userId, 'notifications', { read: true });
182
- */
183
- repo.registerMethod('pullFromArray', async function (id, field, value, options = {}) {
184
- return applyOperator.call(this, id, field, value, '$pull', options);
185
- });
186
-
187
- /**
188
- * Add value to array only if not already present (unique)
189
- *
190
- * @param {string|ObjectId} id - Document ID
191
- * @param {string} field - Array field name
192
- * @param {*} value - Value to add
193
- * @param {Object} [options={}] - Options
194
- * @returns {Promise<Object>} Updated document
195
- *
196
- * @example
197
- * // Add unique follower
198
- * await userRepo.addToSet(userId, 'followers', followerId);
199
- */
200
- repo.registerMethod('addToSet', async function (id, field, value, options = {}) {
201
- return applyOperator.call(this, id, field, value, '$addToSet', options);
202
- });
203
-
204
- /**
205
- * Set field value (alias for update with $set)
206
- *
207
- * @param {string|ObjectId} id - Document ID
208
- * @param {string} field - Field name
209
- * @param {*} value - New value
210
- * @param {Object} [options={}] - Options
211
- * @returns {Promise<Object>} Updated document
212
- *
213
- * @example
214
- * // Set last login
215
- * await userRepo.setField(userId, 'lastLogin', new Date());
216
- */
217
- repo.registerMethod('setField', async function (id, field, value, options = {}) {
218
- return applyOperator.call(this, id, field, value, '$set', options);
219
- });
220
-
221
- /**
222
- * Unset (remove) field from document
223
- *
224
- * @param {string|ObjectId} id - Document ID
225
- * @param {string|string[]} fields - Field name(s) to remove
226
- * @param {Object} [options={}] - Options
227
- * @returns {Promise<Object>} Updated document
228
- *
229
- * @example
230
- * // Remove temporary field
231
- * await userRepo.unsetField(userId, 'tempToken');
232
- *
233
- * @example
234
- * // Remove multiple fields
235
- * await userRepo.unsetField(userId, ['tempToken', 'tempData']);
236
- */
237
- repo.registerMethod('unsetField', async function (id, fields, options = {}) {
238
- const fieldArray = Array.isArray(fields) ? fields : [fields];
239
- const unsetObj = fieldArray.reduce((acc, field) => {
240
- acc[field] = '';
241
- return acc;
242
- }, {});
243
-
244
- return this.update(id, { $unset: unsetObj }, options);
245
- });
246
-
247
- /**
248
- * Rename field in document
249
- *
250
- * @param {string|ObjectId} id - Document ID
251
- * @param {string} oldName - Current field name
252
- * @param {string} newName - New field name
253
- * @param {Object} [options={}] - Options
254
- * @returns {Promise<Object>} Updated document
255
- *
256
- * @example
257
- * // Rename field
258
- * await userRepo.renameField(userId, 'username', 'displayName');
259
- */
260
- repo.registerMethod('renameField', async function (id, oldName, newName, options = {}) {
261
- return this.update(id, { $rename: { [oldName]: newName } }, options);
262
- });
263
-
264
- /**
265
- * Multiply numeric field by value
266
- *
267
- * @param {string|ObjectId} id - Document ID
268
- * @param {string} field - Field name
269
- * @param {number} multiplier - Multiplier value
270
- * @param {Object} [options={}] - Options
271
- * @returns {Promise<Object>} Updated document
272
- *
273
- * @example
274
- * // Double points
275
- * await userRepo.multiplyField(userId, 'points', 2);
276
- */
277
- repo.registerMethod('multiplyField', async function (id, field, multiplier, options = {}) {
278
- return validateAndUpdateNumeric.call(this, id, field, multiplier, '$mul', 'Multiplier', options);
279
- });
280
-
281
- /**
282
- * Set field to minimum value (only if current value is greater)
283
- *
284
- * @param {string|ObjectId} id - Document ID
285
- * @param {string} field - Field name
286
- * @param {number} value - Minimum value
287
- * @param {Object} [options={}] - Options
288
- * @returns {Promise<Object>} Updated document
289
- *
290
- * @example
291
- * // Set minimum price
292
- * await productRepo.setMin(productId, 'price', 999);
293
- */
294
- repo.registerMethod('setMin', async function (id, field, value, options = {}) {
295
- return applyOperator.call(this, id, field, value, '$min', options);
296
- });
297
-
298
- /**
299
- * Set field to maximum value (only if current value is less)
300
- *
301
- * @param {string|ObjectId} id - Document ID
302
- * @param {string} field - Field name
303
- * @param {number} value - Maximum value
304
- * @param {Object} [options={}] - Options
305
- * @returns {Promise<Object>} Updated document
306
- *
307
- * @example
308
- * // Set maximum score
309
- * await gameRepo.setMax(gameId, 'highScore', newScore);
310
- */
311
- repo.registerMethod('setMax', async function (id, field, value, options = {}) {
312
- return applyOperator.call(this, id, field, value, '$max', options);
313
- });
314
- }
315
- });
316
-
317
- export default mongoOperationsPlugin;