@classytic/mongokit 3.0.0 → 3.0.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +251 -846
- package/dist/actions/index.d.ts +2 -2
- package/dist/actions/index.js +13 -2
- package/dist/{index-CgOJ2pqz.d.ts → index-CMCrkd2v.d.ts} +11 -11
- package/dist/index.d.ts +19 -17
- package/dist/index.js +250 -23
- package/dist/{memory-cache-DG2oSSbx.d.ts → memory-cache-Bn_-Kk-0.d.ts} +1 -1
- package/dist/pagination/PaginationEngine.d.ts +1 -1
- package/dist/pagination/PaginationEngine.js +0 -2
- package/dist/plugins/index.d.ts +37 -2
- package/dist/plugins/index.js +181 -4
- package/dist/{types-Nxhmi1aI.d.ts → types-B3dPUKjs.d.ts} +28 -2
- package/dist/utils/index.d.ts +2 -2
- package/dist/utils/index.js +0 -2
- package/package.json +2 -1
- package/dist/actions/index.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/pagination/PaginationEngine.js.map +0 -1
- package/dist/plugins/index.js.map +0 -1
- package/dist/utils/index.js.map +0 -1
package/dist/actions/index.d.ts
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
export { a as aggregate, c as create, _ as deleteActions, r as read, u as update } from '../index-
|
|
1
|
+
export { a as aggregate, c as create, _ as deleteActions, r as read, u as update } from '../index-CMCrkd2v.js';
|
|
2
2
|
import 'mongoose';
|
|
3
|
-
import '../types-
|
|
3
|
+
import '../types-B3dPUKjs.js';
|
package/dist/actions/index.js
CHANGED
|
@@ -147,6 +147,14 @@ __export(update_exports, {
|
|
|
147
147
|
updateWithConstraints: () => updateWithConstraints,
|
|
148
148
|
updateWithValidation: () => updateWithValidation
|
|
149
149
|
});
|
|
150
|
+
function assertUpdatePipelineAllowed(update2, updatePipeline) {
|
|
151
|
+
if (Array.isArray(update2) && updatePipeline !== true) {
|
|
152
|
+
throw createError(
|
|
153
|
+
400,
|
|
154
|
+
"Update pipelines (array updates) are disabled by default; pass `{ updatePipeline: true }` to explicitly allow pipeline-style updates."
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
150
158
|
function parsePopulate2(populate) {
|
|
151
159
|
if (!populate) return [];
|
|
152
160
|
if (typeof populate === "string") {
|
|
@@ -158,6 +166,7 @@ function parsePopulate2(populate) {
|
|
|
158
166
|
return [populate];
|
|
159
167
|
}
|
|
160
168
|
async function update(Model, id, data, options = {}) {
|
|
169
|
+
assertUpdatePipelineAllowed(data, options.updatePipeline);
|
|
161
170
|
const document = await Model.findByIdAndUpdate(id, data, {
|
|
162
171
|
new: true,
|
|
163
172
|
runValidators: true,
|
|
@@ -170,6 +179,7 @@ async function update(Model, id, data, options = {}) {
|
|
|
170
179
|
return document;
|
|
171
180
|
}
|
|
172
181
|
async function updateWithConstraints(Model, id, data, constraints = {}, options = {}) {
|
|
182
|
+
assertUpdatePipelineAllowed(data, options.updatePipeline);
|
|
173
183
|
const query = { _id: id, ...constraints };
|
|
174
184
|
const document = await Model.findOneAndUpdate(query, data, {
|
|
175
185
|
new: true,
|
|
@@ -181,6 +191,7 @@ async function updateWithConstraints(Model, id, data, constraints = {}, options
|
|
|
181
191
|
}
|
|
182
192
|
async function updateWithValidation(Model, id, data, validationOptions = {}, options = {}) {
|
|
183
193
|
const { buildConstraints, validateUpdate } = validationOptions;
|
|
194
|
+
assertUpdatePipelineAllowed(data, options.updatePipeline);
|
|
184
195
|
if (buildConstraints) {
|
|
185
196
|
const constraints = buildConstraints(data);
|
|
186
197
|
const document = await updateWithConstraints(Model, id, data, constraints, options);
|
|
@@ -215,6 +226,7 @@ async function updateWithValidation(Model, id, data, validationOptions = {}, opt
|
|
|
215
226
|
return { success: true, data: updated };
|
|
216
227
|
}
|
|
217
228
|
async function updateMany(Model, query, data, options = {}) {
|
|
229
|
+
assertUpdatePipelineAllowed(data, options.updatePipeline);
|
|
218
230
|
const result = await Model.updateMany(query, data, {
|
|
219
231
|
runValidators: true,
|
|
220
232
|
session: options.session,
|
|
@@ -226,6 +238,7 @@ async function updateMany(Model, query, data, options = {}) {
|
|
|
226
238
|
};
|
|
227
239
|
}
|
|
228
240
|
async function updateByQuery(Model, query, data, options = {}) {
|
|
241
|
+
assertUpdatePipelineAllowed(data, options.updatePipeline);
|
|
229
242
|
const document = await Model.findOneAndUpdate(query, data, {
|
|
230
243
|
new: true,
|
|
231
244
|
runValidators: true,
|
|
@@ -469,5 +482,3 @@ async function minMax(Model, field, query = {}, options = {}) {
|
|
|
469
482
|
}
|
|
470
483
|
|
|
471
484
|
export { aggregate_exports as aggregate, create_exports as create, delete_exports as deleteActions, read_exports as read, update_exports as update };
|
|
472
|
-
//# sourceMappingURL=index.js.map
|
|
473
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Model, ClientSession, PipelineStage } from 'mongoose';
|
|
2
|
-
import { A as AnyDocument, C as CreateOptions,
|
|
2
|
+
import { A as AnyDocument, C as CreateOptions, f as ObjectId, n as OperationOptions, S as SelectSpec, g as PopulateSpec, h as SortSpec, U as UpdateOptions, p as UpdateWithValidationResult, o as UpdateManyResult, D as DeleteResult, N as GroupResult, M as LookupOptions, Q as MinMaxResult } from './types-B3dPUKjs.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
5
|
* Create Actions
|
|
@@ -48,7 +48,7 @@ declare namespace create$1 {
|
|
|
48
48
|
* @returns Document or null
|
|
49
49
|
* @throws Error if document not found and throwOnNotFound is true
|
|
50
50
|
*/
|
|
51
|
-
declare function getById<TDoc = AnyDocument>(Model: Model<TDoc>, id: string, options?: OperationOptions): Promise<TDoc | null>;
|
|
51
|
+
declare function getById<TDoc = AnyDocument>(Model: Model<TDoc>, id: string | ObjectId, options?: OperationOptions): Promise<TDoc | null>;
|
|
52
52
|
/**
|
|
53
53
|
* Get document by query
|
|
54
54
|
*
|
|
@@ -117,12 +117,12 @@ declare namespace read {
|
|
|
117
117
|
/**
|
|
118
118
|
* Update by ID
|
|
119
119
|
*/
|
|
120
|
-
declare function update<TDoc = AnyDocument>(Model: Model<TDoc>, id: string, data: Record<string, unknown>, options?: UpdateOptions): Promise<TDoc>;
|
|
120
|
+
declare function update<TDoc = AnyDocument>(Model: Model<TDoc>, id: string | ObjectId, data: Record<string, unknown>, options?: UpdateOptions): Promise<TDoc>;
|
|
121
121
|
/**
|
|
122
122
|
* Update with query constraints (optimized)
|
|
123
123
|
* Returns null if constraints not met (not an error)
|
|
124
124
|
*/
|
|
125
|
-
declare function updateWithConstraints<TDoc = AnyDocument>(Model: Model<TDoc>, id: string, data: Record<string, unknown>, constraints?: Record<string, unknown>, options?: UpdateOptions): Promise<TDoc | null>;
|
|
125
|
+
declare function updateWithConstraints<TDoc = AnyDocument>(Model: Model<TDoc>, id: string | ObjectId, data: Record<string, unknown>, constraints?: Record<string, unknown>, options?: UpdateOptions): Promise<TDoc | null>;
|
|
126
126
|
/**
|
|
127
127
|
* Validation options for smart update
|
|
128
128
|
*/
|
|
@@ -141,7 +141,7 @@ interface ValidationOptions {
|
|
|
141
141
|
* Update with validation (smart optimization)
|
|
142
142
|
* 1-query on success, 2-queries for detailed errors
|
|
143
143
|
*/
|
|
144
|
-
declare function updateWithValidation<TDoc = AnyDocument>(Model: Model<TDoc>, id: string, data: Record<string, unknown>, validationOptions?: ValidationOptions, options?: UpdateOptions): Promise<UpdateWithValidationResult<TDoc>>;
|
|
144
|
+
declare function updateWithValidation<TDoc = AnyDocument>(Model: Model<TDoc>, id: string | ObjectId, data: Record<string, unknown>, validationOptions?: ValidationOptions, options?: UpdateOptions): Promise<UpdateWithValidationResult<TDoc>>;
|
|
145
145
|
/**
|
|
146
146
|
* Update many documents
|
|
147
147
|
*/
|
|
@@ -156,15 +156,15 @@ declare function updateByQuery<TDoc = AnyDocument>(Model: Model<TDoc>, query: Re
|
|
|
156
156
|
/**
|
|
157
157
|
* Increment field
|
|
158
158
|
*/
|
|
159
|
-
declare function increment<TDoc = AnyDocument>(Model: Model<TDoc>, id: string, field: string, value?: number, options?: UpdateOptions): Promise<TDoc>;
|
|
159
|
+
declare function increment<TDoc = AnyDocument>(Model: Model<TDoc>, id: string | ObjectId, field: string, value?: number, options?: UpdateOptions): Promise<TDoc>;
|
|
160
160
|
/**
|
|
161
161
|
* Push to array
|
|
162
162
|
*/
|
|
163
|
-
declare function pushToArray<TDoc = AnyDocument>(Model: Model<TDoc>, id: string, field: string, value: unknown, options?: UpdateOptions): Promise<TDoc>;
|
|
163
|
+
declare function pushToArray<TDoc = AnyDocument>(Model: Model<TDoc>, id: string | ObjectId, field: string, value: unknown, options?: UpdateOptions): Promise<TDoc>;
|
|
164
164
|
/**
|
|
165
165
|
* Pull from array
|
|
166
166
|
*/
|
|
167
|
-
declare function pullFromArray<TDoc = AnyDocument>(Model: Model<TDoc>, id: string, field: string, value: unknown, options?: UpdateOptions): Promise<TDoc>;
|
|
167
|
+
declare function pullFromArray<TDoc = AnyDocument>(Model: Model<TDoc>, id: string | ObjectId, field: string, value: unknown, options?: UpdateOptions): Promise<TDoc>;
|
|
168
168
|
|
|
169
169
|
declare const update$1_increment: typeof increment;
|
|
170
170
|
declare const update$1_pullFromArray: typeof pullFromArray;
|
|
@@ -186,7 +186,7 @@ declare namespace update$1 {
|
|
|
186
186
|
/**
|
|
187
187
|
* Delete by ID
|
|
188
188
|
*/
|
|
189
|
-
declare function deleteById(Model: Model<any>, id: string, options?: {
|
|
189
|
+
declare function deleteById(Model: Model<any>, id: string | ObjectId, options?: {
|
|
190
190
|
session?: ClientSession;
|
|
191
191
|
}): Promise<DeleteResult>;
|
|
192
192
|
/**
|
|
@@ -205,14 +205,14 @@ declare function deleteByQuery(Model: Model<any>, query: Record<string, unknown>
|
|
|
205
205
|
/**
|
|
206
206
|
* Soft delete (set deleted flag)
|
|
207
207
|
*/
|
|
208
|
-
declare function softDelete<TDoc = AnyDocument>(Model: Model<TDoc>, id: string, options?: {
|
|
208
|
+
declare function softDelete<TDoc = AnyDocument>(Model: Model<TDoc>, id: string | ObjectId, options?: {
|
|
209
209
|
session?: ClientSession;
|
|
210
210
|
userId?: string;
|
|
211
211
|
}): Promise<DeleteResult>;
|
|
212
212
|
/**
|
|
213
213
|
* Restore soft deleted document
|
|
214
214
|
*/
|
|
215
|
-
declare function restore<TDoc = AnyDocument>(Model: Model<TDoc>, id: string, options?: {
|
|
215
|
+
declare function restore<TDoc = AnyDocument>(Model: Model<TDoc>, id: string | ObjectId, options?: {
|
|
216
216
|
session?: ClientSession;
|
|
217
217
|
}): Promise<DeleteResult>;
|
|
218
218
|
|
package/dist/index.d.ts
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { A as AnyDocument, e as PluginType, P as PaginationConfig, S as SelectSpec,
|
|
2
|
-
export { c as AggregatePaginationOptions,
|
|
1
|
+
import { A as AnyDocument, e as PluginType, P as PaginationConfig, R as RepositoryOptions, f as ObjectId, S as SelectSpec, g as PopulateSpec, h as SortSpec, a as OffsetPaginationResult, b as KeysetPaginationResult, U as UpdateOptions, d as AggregatePaginationResult, i as RepositoryContext, H as HttpError } from './types-B3dPUKjs.js';
|
|
2
|
+
export { c as AggregatePaginationOptions, j as AnyModel, T as CacheAdapter, X as CacheOperationOptions, W as CacheOptions, Y as CacheStats, _ as CascadeOptions, Z as CascadeRelation, C as CreateOptions, y as CrudSchemas, z as DecodedCursor, D as DeleteResult, E as EventPayload, F as FieldPreset, w as FieldRules, N as GroupResult, l as HookMode, J as JsonSchema, K as KeysetPaginationOptions, L as Logger, M as LookupOptions, Q as MinMaxResult, O as OffsetPaginationOptions, n as OperationOptions, m as PaginationResult, v as ParsedQuery, r as Plugin, s as PluginFunction, u as RepositoryEvent, t as RepositoryInstance, x as SchemaBuilderOptions, I as SoftDeleteOptions, k as SortDirection, o as UpdateManyResult, p as UpdateWithValidationResult, q as UserContext, G as ValidationChainOptions, V as ValidationResult, B as ValidatorDefinition } from './types-B3dPUKjs.js';
|
|
3
3
|
import * as mongoose from 'mongoose';
|
|
4
4
|
import { Model, ClientSession, PipelineStage, PopulateOptions } from 'mongoose';
|
|
5
5
|
import { PaginationEngine } from './pagination/PaginationEngine.js';
|
|
6
|
-
export { aggregateHelpersPlugin, auditLogPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, fieldFilterPlugin, immutableField, methodRegistryPlugin, mongoOperationsPlugin, requireField, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin } from './plugins/index.js';
|
|
7
|
-
export { b as createError, c as createFieldPreset, d as createMemoryCache, f as filterResponseData, g as getFieldsForUser, a as getMongooseProjection } from './memory-cache-
|
|
8
|
-
export { i as actions } from './index-
|
|
6
|
+
export { aggregateHelpersPlugin, auditLogPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, cascadePlugin, fieldFilterPlugin, immutableField, methodRegistryPlugin, mongoOperationsPlugin, requireField, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin } from './plugins/index.js';
|
|
7
|
+
export { b as createError, c as createFieldPreset, d as createMemoryCache, f as filterResponseData, g as getFieldsForUser, a as getMongooseProjection } from './memory-cache-Bn_-Kk-0.js';
|
|
8
|
+
export { i as actions } from './index-CMCrkd2v.js';
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
11
|
* Repository Pattern - Data Access Layer
|
|
@@ -44,8 +44,9 @@ declare class Repository<TDoc = AnyDocument> {
|
|
|
44
44
|
readonly model: string;
|
|
45
45
|
readonly _hooks: Map<string, HookListener[]>;
|
|
46
46
|
readonly _pagination: PaginationEngine<TDoc>;
|
|
47
|
+
private readonly _hookMode;
|
|
47
48
|
[key: string]: unknown;
|
|
48
|
-
constructor(Model: Model<TDoc>, plugins?: PluginType[], paginationConfig?: PaginationConfig);
|
|
49
|
+
constructor(Model: Model<TDoc>, plugins?: PluginType[], paginationConfig?: PaginationConfig, options?: RepositoryOptions);
|
|
49
50
|
/**
|
|
50
51
|
* Register a plugin
|
|
51
52
|
*/
|
|
@@ -55,9 +56,15 @@ declare class Repository<TDoc = AnyDocument> {
|
|
|
55
56
|
*/
|
|
56
57
|
on(event: string, listener: HookListener): this;
|
|
57
58
|
/**
|
|
58
|
-
* Emit event
|
|
59
|
+
* Emit event (sync - for backwards compatibility)
|
|
59
60
|
*/
|
|
60
61
|
emit(event: string, data: unknown): void;
|
|
62
|
+
/**
|
|
63
|
+
* Emit event and await all async handlers
|
|
64
|
+
*/
|
|
65
|
+
emitAsync(event: string, data: unknown): Promise<void>;
|
|
66
|
+
private _emitHook;
|
|
67
|
+
private _emitErrorHook;
|
|
61
68
|
/**
|
|
62
69
|
* Create single document
|
|
63
70
|
*/
|
|
@@ -74,7 +81,7 @@ declare class Repository<TDoc = AnyDocument> {
|
|
|
74
81
|
/**
|
|
75
82
|
* Get document by ID
|
|
76
83
|
*/
|
|
77
|
-
getById(id: string, options?: {
|
|
84
|
+
getById(id: string | ObjectId, options?: {
|
|
78
85
|
select?: SelectSpec;
|
|
79
86
|
populate?: PopulateSpec;
|
|
80
87
|
lean?: boolean;
|
|
@@ -161,16 +168,11 @@ declare class Repository<TDoc = AnyDocument> {
|
|
|
161
168
|
/**
|
|
162
169
|
* Update document by ID
|
|
163
170
|
*/
|
|
164
|
-
update(id: string, data: Record<string, unknown>, options?:
|
|
165
|
-
select?: SelectSpec;
|
|
166
|
-
populate?: PopulateSpec;
|
|
167
|
-
lean?: boolean;
|
|
168
|
-
session?: ClientSession;
|
|
169
|
-
}): Promise<TDoc>;
|
|
171
|
+
update(id: string | ObjectId, data: Record<string, unknown>, options?: UpdateOptions): Promise<TDoc>;
|
|
170
172
|
/**
|
|
171
173
|
* Delete document by ID
|
|
172
174
|
*/
|
|
173
|
-
delete(id: string, options?: {
|
|
175
|
+
delete(id: string | ObjectId, options?: {
|
|
174
176
|
session?: ClientSession;
|
|
175
177
|
}): Promise<{
|
|
176
178
|
success: boolean;
|
|
@@ -234,6 +236,6 @@ declare class Repository<TDoc = AnyDocument> {
|
|
|
234
236
|
* @example
|
|
235
237
|
* const userRepo = createRepository(UserModel, [timestampPlugin()]);
|
|
236
238
|
*/
|
|
237
|
-
declare function createRepository<TDoc>(Model: mongoose.Model<TDoc>, plugins?: PluginType[]): Repository<TDoc>;
|
|
239
|
+
declare function createRepository<TDoc>(Model: mongoose.Model<TDoc>, plugins?: PluginType[], paginationConfig?: PaginationConfig, options?: RepositoryOptions): Repository<TDoc>;
|
|
238
240
|
|
|
239
|
-
export { AggregatePaginationResult, AnyDocument, HttpError, KeysetPaginationResult, OffsetPaginationResult, PaginationConfig, PaginationEngine, PluginType, PopulateSpec, Repository, RepositoryContext, SelectSpec, SortSpec, createRepository, Repository as default };
|
|
241
|
+
export { AggregatePaginationResult, AnyDocument, HttpError, KeysetPaginationResult, ObjectId, OffsetPaginationResult, PaginationConfig, PaginationEngine, PluginType, PopulateSpec, Repository, RepositoryContext, RepositoryOptions, SelectSpec, SortSpec, UpdateOptions, createRepository, Repository as default };
|
package/dist/index.js
CHANGED
|
@@ -147,6 +147,14 @@ __export(update_exports, {
|
|
|
147
147
|
updateWithConstraints: () => updateWithConstraints,
|
|
148
148
|
updateWithValidation: () => updateWithValidation
|
|
149
149
|
});
|
|
150
|
+
function assertUpdatePipelineAllowed(update2, updatePipeline) {
|
|
151
|
+
if (Array.isArray(update2) && updatePipeline !== true) {
|
|
152
|
+
throw createError(
|
|
153
|
+
400,
|
|
154
|
+
"Update pipelines (array updates) are disabled by default; pass `{ updatePipeline: true }` to explicitly allow pipeline-style updates."
|
|
155
|
+
);
|
|
156
|
+
}
|
|
157
|
+
}
|
|
150
158
|
function parsePopulate2(populate) {
|
|
151
159
|
if (!populate) return [];
|
|
152
160
|
if (typeof populate === "string") {
|
|
@@ -158,6 +166,7 @@ function parsePopulate2(populate) {
|
|
|
158
166
|
return [populate];
|
|
159
167
|
}
|
|
160
168
|
async function update(Model, id, data, options = {}) {
|
|
169
|
+
assertUpdatePipelineAllowed(data, options.updatePipeline);
|
|
161
170
|
const document = await Model.findByIdAndUpdate(id, data, {
|
|
162
171
|
new: true,
|
|
163
172
|
runValidators: true,
|
|
@@ -170,6 +179,7 @@ async function update(Model, id, data, options = {}) {
|
|
|
170
179
|
return document;
|
|
171
180
|
}
|
|
172
181
|
async function updateWithConstraints(Model, id, data, constraints = {}, options = {}) {
|
|
182
|
+
assertUpdatePipelineAllowed(data, options.updatePipeline);
|
|
173
183
|
const query = { _id: id, ...constraints };
|
|
174
184
|
const document = await Model.findOneAndUpdate(query, data, {
|
|
175
185
|
new: true,
|
|
@@ -181,6 +191,7 @@ async function updateWithConstraints(Model, id, data, constraints = {}, options
|
|
|
181
191
|
}
|
|
182
192
|
async function updateWithValidation(Model, id, data, validationOptions = {}, options = {}) {
|
|
183
193
|
const { buildConstraints, validateUpdate } = validationOptions;
|
|
194
|
+
assertUpdatePipelineAllowed(data, options.updatePipeline);
|
|
184
195
|
if (buildConstraints) {
|
|
185
196
|
const constraints = buildConstraints(data);
|
|
186
197
|
const document = await updateWithConstraints(Model, id, data, constraints, options);
|
|
@@ -215,6 +226,7 @@ async function updateWithValidation(Model, id, data, validationOptions = {}, opt
|
|
|
215
226
|
return { success: true, data: updated };
|
|
216
227
|
}
|
|
217
228
|
async function updateMany(Model, query, data, options = {}) {
|
|
229
|
+
assertUpdatePipelineAllowed(data, options.updatePipeline);
|
|
218
230
|
const result = await Model.updateMany(query, data, {
|
|
219
231
|
runValidators: true,
|
|
220
232
|
session: options.session,
|
|
@@ -226,6 +238,7 @@ async function updateMany(Model, query, data, options = {}) {
|
|
|
226
238
|
};
|
|
227
239
|
}
|
|
228
240
|
async function updateByQuery(Model, query, data, options = {}) {
|
|
241
|
+
assertUpdatePipelineAllowed(data, options.updatePipeline);
|
|
229
242
|
const document = await Model.findOneAndUpdate(query, data, {
|
|
230
243
|
new: true,
|
|
231
244
|
runValidators: true,
|
|
@@ -829,11 +842,13 @@ var Repository = class {
|
|
|
829
842
|
model;
|
|
830
843
|
_hooks;
|
|
831
844
|
_pagination;
|
|
832
|
-
|
|
845
|
+
_hookMode;
|
|
846
|
+
constructor(Model, plugins = [], paginationConfig = {}, options = {}) {
|
|
833
847
|
this.Model = Model;
|
|
834
848
|
this.model = Model.modelName;
|
|
835
849
|
this._hooks = /* @__PURE__ */ new Map();
|
|
836
850
|
this._pagination = new PaginationEngine(Model, paginationConfig);
|
|
851
|
+
this._hookMode = options.hooks ?? "async";
|
|
837
852
|
plugins.forEach((plugin) => this.use(plugin));
|
|
838
853
|
}
|
|
839
854
|
/**
|
|
@@ -858,11 +873,48 @@ var Repository = class {
|
|
|
858
873
|
return this;
|
|
859
874
|
}
|
|
860
875
|
/**
|
|
861
|
-
* Emit event
|
|
876
|
+
* Emit event (sync - for backwards compatibility)
|
|
862
877
|
*/
|
|
863
878
|
emit(event, data) {
|
|
864
879
|
const listeners = this._hooks.get(event) || [];
|
|
865
|
-
|
|
880
|
+
for (const listener of listeners) {
|
|
881
|
+
try {
|
|
882
|
+
const result = listener(data);
|
|
883
|
+
if (result && typeof result.then === "function") {
|
|
884
|
+
void result.catch((error) => {
|
|
885
|
+
if (event === "error:hook") return;
|
|
886
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
887
|
+
this.emit("error:hook", { event, error: err });
|
|
888
|
+
});
|
|
889
|
+
}
|
|
890
|
+
} catch (error) {
|
|
891
|
+
if (event === "error:hook") continue;
|
|
892
|
+
const err = error instanceof Error ? error : new Error(String(error));
|
|
893
|
+
this.emit("error:hook", { event, error: err });
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
/**
|
|
898
|
+
* Emit event and await all async handlers
|
|
899
|
+
*/
|
|
900
|
+
async emitAsync(event, data) {
|
|
901
|
+
const listeners = this._hooks.get(event) || [];
|
|
902
|
+
for (const listener of listeners) {
|
|
903
|
+
await listener(data);
|
|
904
|
+
}
|
|
905
|
+
}
|
|
906
|
+
async _emitHook(event, data) {
|
|
907
|
+
if (this._hookMode === "async") {
|
|
908
|
+
await this.emitAsync(event, data);
|
|
909
|
+
return;
|
|
910
|
+
}
|
|
911
|
+
this.emit(event, data);
|
|
912
|
+
}
|
|
913
|
+
async _emitErrorHook(event, data) {
|
|
914
|
+
try {
|
|
915
|
+
await this._emitHook(event, data);
|
|
916
|
+
} catch {
|
|
917
|
+
}
|
|
866
918
|
}
|
|
867
919
|
/**
|
|
868
920
|
* Create single document
|
|
@@ -871,10 +923,10 @@ var Repository = class {
|
|
|
871
923
|
const context = await this._buildContext("create", { data, ...options });
|
|
872
924
|
try {
|
|
873
925
|
const result = await create(this.Model, context.data || data, options);
|
|
874
|
-
this.
|
|
926
|
+
await this._emitHook("after:create", { context, result });
|
|
875
927
|
return result;
|
|
876
928
|
} catch (error) {
|
|
877
|
-
this.
|
|
929
|
+
await this._emitErrorHook("error:create", { context, error });
|
|
878
930
|
throw this._handleError(error);
|
|
879
931
|
}
|
|
880
932
|
}
|
|
@@ -885,10 +937,10 @@ var Repository = class {
|
|
|
885
937
|
const context = await this._buildContext("createMany", { dataArray, ...options });
|
|
886
938
|
try {
|
|
887
939
|
const result = await createMany(this.Model, context.dataArray || dataArray, options);
|
|
888
|
-
this.
|
|
940
|
+
await this._emitHook("after:createMany", { context, result });
|
|
889
941
|
return result;
|
|
890
942
|
} catch (error) {
|
|
891
|
-
this.
|
|
943
|
+
await this._emitErrorHook("error:createMany", { context, error });
|
|
892
944
|
throw this._handleError(error);
|
|
893
945
|
}
|
|
894
946
|
}
|
|
@@ -901,7 +953,7 @@ var Repository = class {
|
|
|
901
953
|
return context._cachedResult;
|
|
902
954
|
}
|
|
903
955
|
const result = await getById(this.Model, id, context);
|
|
904
|
-
this.
|
|
956
|
+
await this._emitHook("after:getById", { context, result });
|
|
905
957
|
return result;
|
|
906
958
|
}
|
|
907
959
|
/**
|
|
@@ -913,7 +965,7 @@ var Repository = class {
|
|
|
913
965
|
return context._cachedResult;
|
|
914
966
|
}
|
|
915
967
|
const result = await getByQuery(this.Model, query, context);
|
|
916
|
-
this.
|
|
968
|
+
await this._emitHook("after:getByQuery", { context, result });
|
|
917
969
|
return result;
|
|
918
970
|
}
|
|
919
971
|
/**
|
|
@@ -978,7 +1030,7 @@ var Repository = class {
|
|
|
978
1030
|
page
|
|
979
1031
|
});
|
|
980
1032
|
}
|
|
981
|
-
this.
|
|
1033
|
+
await this._emitHook("after:getAll", { context, result });
|
|
982
1034
|
return result;
|
|
983
1035
|
}
|
|
984
1036
|
/**
|
|
@@ -1006,10 +1058,10 @@ var Repository = class {
|
|
|
1006
1058
|
const context = await this._buildContext("update", { id, data, ...options });
|
|
1007
1059
|
try {
|
|
1008
1060
|
const result = await update(this.Model, id, context.data || data, context);
|
|
1009
|
-
this.
|
|
1061
|
+
await this._emitHook("after:update", { context, result });
|
|
1010
1062
|
return result;
|
|
1011
1063
|
} catch (error) {
|
|
1012
|
-
this.
|
|
1064
|
+
await this._emitErrorHook("error:update", { context, error });
|
|
1013
1065
|
throw this._handleError(error);
|
|
1014
1066
|
}
|
|
1015
1067
|
}
|
|
@@ -1021,14 +1073,14 @@ var Repository = class {
|
|
|
1021
1073
|
try {
|
|
1022
1074
|
if (context.softDeleted) {
|
|
1023
1075
|
const result2 = { success: true, message: "Soft deleted successfully" };
|
|
1024
|
-
this.
|
|
1076
|
+
await this._emitHook("after:delete", { context, result: result2 });
|
|
1025
1077
|
return result2;
|
|
1026
1078
|
}
|
|
1027
1079
|
const result = await deleteById(this.Model, id, options);
|
|
1028
|
-
this.
|
|
1080
|
+
await this._emitHook("after:delete", { context, result });
|
|
1029
1081
|
return result;
|
|
1030
1082
|
} catch (error) {
|
|
1031
|
-
this.
|
|
1083
|
+
await this._emitErrorHook("error:delete", { context, error });
|
|
1032
1084
|
throw this._handleError(error);
|
|
1033
1085
|
}
|
|
1034
1086
|
}
|
|
@@ -1077,10 +1129,10 @@ var Repository = class {
|
|
|
1077
1129
|
const context = await this._buildContext(operation, {});
|
|
1078
1130
|
try {
|
|
1079
1131
|
const result = await buildQuery(this.Model);
|
|
1080
|
-
this.
|
|
1132
|
+
await this._emitHook(`after:${operation}`, { context, result });
|
|
1081
1133
|
return result;
|
|
1082
1134
|
} catch (error) {
|
|
1083
|
-
this.
|
|
1135
|
+
await this._emitErrorHook(`error:${operation}`, { context, error });
|
|
1084
1136
|
throw this._handleError(error);
|
|
1085
1137
|
}
|
|
1086
1138
|
}
|
|
@@ -1561,9 +1613,16 @@ function batchOperationsPlugin() {
|
|
|
1561
1613
|
const context = await _buildContext.call(this, "updateMany", { query, data, options });
|
|
1562
1614
|
try {
|
|
1563
1615
|
this.emit("before:updateMany", context);
|
|
1616
|
+
if (Array.isArray(data) && options.updatePipeline !== true) {
|
|
1617
|
+
throw createError(
|
|
1618
|
+
400,
|
|
1619
|
+
"Update pipelines (array updates) are disabled by default; pass `{ updatePipeline: true }` to explicitly allow pipeline-style updates."
|
|
1620
|
+
);
|
|
1621
|
+
}
|
|
1564
1622
|
const result = await this.Model.updateMany(query, data, {
|
|
1565
1623
|
runValidators: true,
|
|
1566
|
-
session: options.session
|
|
1624
|
+
session: options.session,
|
|
1625
|
+
...options.updatePipeline !== void 0 ? { updatePipeline: options.updatePipeline } : {}
|
|
1567
1626
|
}).exec();
|
|
1568
1627
|
this.emit("after:updateMany", { context, result });
|
|
1569
1628
|
return result;
|
|
@@ -1992,6 +2051,176 @@ function cachePlugin(options) {
|
|
|
1992
2051
|
}
|
|
1993
2052
|
};
|
|
1994
2053
|
}
|
|
2054
|
+
function cascadePlugin(options) {
|
|
2055
|
+
const { relations, parallel = true, logger } = options;
|
|
2056
|
+
if (!relations || relations.length === 0) {
|
|
2057
|
+
throw new Error("cascadePlugin requires at least one relation");
|
|
2058
|
+
}
|
|
2059
|
+
return {
|
|
2060
|
+
name: "cascade",
|
|
2061
|
+
apply(repo) {
|
|
2062
|
+
repo.on("after:delete", async (payload) => {
|
|
2063
|
+
const { context } = payload;
|
|
2064
|
+
const deletedId = context.id;
|
|
2065
|
+
if (!deletedId) {
|
|
2066
|
+
logger?.warn?.("Cascade delete skipped: no document ID in context", {
|
|
2067
|
+
model: context.model
|
|
2068
|
+
});
|
|
2069
|
+
return;
|
|
2070
|
+
}
|
|
2071
|
+
const isSoftDelete = context.softDeleted === true;
|
|
2072
|
+
const cascadeDelete = async (relation) => {
|
|
2073
|
+
const RelatedModel = mongoose.models[relation.model];
|
|
2074
|
+
if (!RelatedModel) {
|
|
2075
|
+
logger?.warn?.(`Cascade delete skipped: model '${relation.model}' not found`, {
|
|
2076
|
+
parentModel: context.model,
|
|
2077
|
+
parentId: String(deletedId)
|
|
2078
|
+
});
|
|
2079
|
+
return;
|
|
2080
|
+
}
|
|
2081
|
+
const query = { [relation.foreignKey]: deletedId };
|
|
2082
|
+
try {
|
|
2083
|
+
const shouldSoftDelete = relation.softDelete ?? isSoftDelete;
|
|
2084
|
+
if (shouldSoftDelete) {
|
|
2085
|
+
const updateResult = await RelatedModel.updateMany(
|
|
2086
|
+
query,
|
|
2087
|
+
{
|
|
2088
|
+
deletedAt: /* @__PURE__ */ new Date(),
|
|
2089
|
+
...context.user ? { deletedBy: context.user._id || context.user.id } : {}
|
|
2090
|
+
},
|
|
2091
|
+
{ session: context.session }
|
|
2092
|
+
);
|
|
2093
|
+
logger?.info?.(`Cascade soft-deleted ${updateResult.modifiedCount} documents`, {
|
|
2094
|
+
parentModel: context.model,
|
|
2095
|
+
parentId: String(deletedId),
|
|
2096
|
+
relatedModel: relation.model,
|
|
2097
|
+
foreignKey: relation.foreignKey,
|
|
2098
|
+
count: updateResult.modifiedCount
|
|
2099
|
+
});
|
|
2100
|
+
} else {
|
|
2101
|
+
const deleteResult = await RelatedModel.deleteMany(query, {
|
|
2102
|
+
session: context.session
|
|
2103
|
+
});
|
|
2104
|
+
logger?.info?.(`Cascade deleted ${deleteResult.deletedCount} documents`, {
|
|
2105
|
+
parentModel: context.model,
|
|
2106
|
+
parentId: String(deletedId),
|
|
2107
|
+
relatedModel: relation.model,
|
|
2108
|
+
foreignKey: relation.foreignKey,
|
|
2109
|
+
count: deleteResult.deletedCount
|
|
2110
|
+
});
|
|
2111
|
+
}
|
|
2112
|
+
} catch (error) {
|
|
2113
|
+
logger?.error?.(`Cascade delete failed for model '${relation.model}'`, {
|
|
2114
|
+
parentModel: context.model,
|
|
2115
|
+
parentId: String(deletedId),
|
|
2116
|
+
relatedModel: relation.model,
|
|
2117
|
+
foreignKey: relation.foreignKey,
|
|
2118
|
+
error: error.message
|
|
2119
|
+
});
|
|
2120
|
+
throw error;
|
|
2121
|
+
}
|
|
2122
|
+
};
|
|
2123
|
+
if (parallel) {
|
|
2124
|
+
await Promise.all(relations.map(cascadeDelete));
|
|
2125
|
+
} else {
|
|
2126
|
+
for (const relation of relations) {
|
|
2127
|
+
await cascadeDelete(relation);
|
|
2128
|
+
}
|
|
2129
|
+
}
|
|
2130
|
+
});
|
|
2131
|
+
repo.on("after:deleteMany", async (payload) => {
|
|
2132
|
+
const { context, result } = payload;
|
|
2133
|
+
const query = context.query;
|
|
2134
|
+
if (!query || Object.keys(query).length === 0) {
|
|
2135
|
+
logger?.warn?.("Cascade deleteMany skipped: empty query", {
|
|
2136
|
+
model: context.model
|
|
2137
|
+
});
|
|
2138
|
+
return;
|
|
2139
|
+
}
|
|
2140
|
+
logger?.warn?.("Cascade deleteMany: use before:deleteMany hook for complete cascade support", {
|
|
2141
|
+
model: context.model
|
|
2142
|
+
});
|
|
2143
|
+
});
|
|
2144
|
+
repo.on("before:deleteMany", async (context) => {
|
|
2145
|
+
const query = context.query;
|
|
2146
|
+
if (!query || Object.keys(query).length === 0) {
|
|
2147
|
+
return;
|
|
2148
|
+
}
|
|
2149
|
+
const docs = await repo.Model.find(query, { _id: 1 }).lean().session(context.session ?? null);
|
|
2150
|
+
const ids = docs.map((doc) => doc._id);
|
|
2151
|
+
context._cascadeIds = ids;
|
|
2152
|
+
});
|
|
2153
|
+
const originalAfterDeleteMany = repo._hooks.get("after:deleteMany") || [];
|
|
2154
|
+
repo._hooks.set("after:deleteMany", [
|
|
2155
|
+
...originalAfterDeleteMany,
|
|
2156
|
+
async (payload) => {
|
|
2157
|
+
const { context } = payload;
|
|
2158
|
+
const ids = context._cascadeIds;
|
|
2159
|
+
if (!ids || ids.length === 0) {
|
|
2160
|
+
return;
|
|
2161
|
+
}
|
|
2162
|
+
const isSoftDelete = context.softDeleted === true;
|
|
2163
|
+
const cascadeDeleteMany = async (relation) => {
|
|
2164
|
+
const RelatedModel = mongoose.models[relation.model];
|
|
2165
|
+
if (!RelatedModel) {
|
|
2166
|
+
logger?.warn?.(`Cascade deleteMany skipped: model '${relation.model}' not found`, {
|
|
2167
|
+
parentModel: context.model
|
|
2168
|
+
});
|
|
2169
|
+
return;
|
|
2170
|
+
}
|
|
2171
|
+
const query = { [relation.foreignKey]: { $in: ids } };
|
|
2172
|
+
const shouldSoftDelete = relation.softDelete ?? isSoftDelete;
|
|
2173
|
+
try {
|
|
2174
|
+
if (shouldSoftDelete) {
|
|
2175
|
+
const updateResult = await RelatedModel.updateMany(
|
|
2176
|
+
query,
|
|
2177
|
+
{
|
|
2178
|
+
deletedAt: /* @__PURE__ */ new Date(),
|
|
2179
|
+
...context.user ? { deletedBy: context.user._id || context.user.id } : {}
|
|
2180
|
+
},
|
|
2181
|
+
{ session: context.session }
|
|
2182
|
+
);
|
|
2183
|
+
logger?.info?.(`Cascade soft-deleted ${updateResult.modifiedCount} documents (bulk)`, {
|
|
2184
|
+
parentModel: context.model,
|
|
2185
|
+
parentCount: ids.length,
|
|
2186
|
+
relatedModel: relation.model,
|
|
2187
|
+
foreignKey: relation.foreignKey,
|
|
2188
|
+
count: updateResult.modifiedCount
|
|
2189
|
+
});
|
|
2190
|
+
} else {
|
|
2191
|
+
const deleteResult = await RelatedModel.deleteMany(query, {
|
|
2192
|
+
session: context.session
|
|
2193
|
+
});
|
|
2194
|
+
logger?.info?.(`Cascade deleted ${deleteResult.deletedCount} documents (bulk)`, {
|
|
2195
|
+
parentModel: context.model,
|
|
2196
|
+
parentCount: ids.length,
|
|
2197
|
+
relatedModel: relation.model,
|
|
2198
|
+
foreignKey: relation.foreignKey,
|
|
2199
|
+
count: deleteResult.deletedCount
|
|
2200
|
+
});
|
|
2201
|
+
}
|
|
2202
|
+
} catch (error) {
|
|
2203
|
+
logger?.error?.(`Cascade deleteMany failed for model '${relation.model}'`, {
|
|
2204
|
+
parentModel: context.model,
|
|
2205
|
+
relatedModel: relation.model,
|
|
2206
|
+
foreignKey: relation.foreignKey,
|
|
2207
|
+
error: error.message
|
|
2208
|
+
});
|
|
2209
|
+
throw error;
|
|
2210
|
+
}
|
|
2211
|
+
};
|
|
2212
|
+
if (parallel) {
|
|
2213
|
+
await Promise.all(relations.map(cascadeDeleteMany));
|
|
2214
|
+
} else {
|
|
2215
|
+
for (const relation of relations) {
|
|
2216
|
+
await cascadeDeleteMany(relation);
|
|
2217
|
+
}
|
|
2218
|
+
}
|
|
2219
|
+
}
|
|
2220
|
+
]);
|
|
2221
|
+
}
|
|
2222
|
+
};
|
|
2223
|
+
}
|
|
1995
2224
|
|
|
1996
2225
|
// src/utils/memory-cache.ts
|
|
1997
2226
|
function createMemoryCache(maxEntries = 1e3) {
|
|
@@ -2060,8 +2289,8 @@ __export(actions_exports, {
|
|
|
2060
2289
|
});
|
|
2061
2290
|
|
|
2062
2291
|
// src/index.ts
|
|
2063
|
-
function createRepository(Model, plugins = []) {
|
|
2064
|
-
return new Repository(Model, plugins);
|
|
2292
|
+
function createRepository(Model, plugins = [], paginationConfig = {}, options = {}) {
|
|
2293
|
+
return new Repository(Model, plugins, paginationConfig, options);
|
|
2065
2294
|
}
|
|
2066
2295
|
var index_default = Repository;
|
|
2067
2296
|
/**
|
|
@@ -2103,6 +2332,4 @@ var index_default = Repository;
|
|
|
2103
2332
|
* ```
|
|
2104
2333
|
*/
|
|
2105
2334
|
|
|
2106
|
-
export { PaginationEngine, Repository, actions_exports as actions, aggregateHelpersPlugin, auditLogPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, createError, createFieldPreset, createMemoryCache, createRepository, index_default as default, fieldFilterPlugin, filterResponseData, getFieldsForUser, getMongooseProjection, immutableField, methodRegistryPlugin, mongoOperationsPlugin, requireField, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin };
|
|
2107
|
-
//# sourceMappingURL=index.js.map
|
|
2108
|
-
//# sourceMappingURL=index.js.map
|
|
2335
|
+
export { PaginationEngine, Repository, actions_exports as actions, aggregateHelpersPlugin, auditLogPlugin, autoInject, batchOperationsPlugin, blockIf, cachePlugin, cascadePlugin, createError, createFieldPreset, createMemoryCache, createRepository, index_default as default, fieldFilterPlugin, filterResponseData, getFieldsForUser, getMongooseProjection, immutableField, methodRegistryPlugin, mongoOperationsPlugin, requireField, softDeletePlugin, subdocumentPlugin, timestampPlugin, uniqueField, validationChainPlugin };
|