ts-patch-mongoose 2.8.0 → 2.8.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 (109) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +8 -8
  3. package/dist/index.cjs +395 -0
  4. package/dist/index.d.cts +75 -0
  5. package/dist/index.d.mts +75 -0
  6. package/dist/index.mjs +391 -0
  7. package/package.json +36 -29
  8. package/src/helpers.ts +1 -1
  9. package/src/hooks/delete-hooks.ts +4 -4
  10. package/src/hooks/update-hooks.ts +10 -10
  11. package/src/{plugin.ts → index.ts} +6 -2
  12. package/src/{models/History.ts → model.ts} +1 -1
  13. package/src/patch.ts +11 -11
  14. package/src/version.ts +2 -0
  15. package/tests/em.test.ts +1 -1
  16. package/tests/helpers.test.ts +2 -2
  17. package/tests/patch.test.ts +1 -1
  18. package/tests/plugin-event-created.test.ts +2 -2
  19. package/tests/plugin-event-deleted.test.ts +2 -2
  20. package/tests/plugin-event-updated.test.ts +2 -2
  21. package/tests/plugin-global.test.ts +2 -2
  22. package/tests/plugin-omit-all.test.ts +2 -2
  23. package/tests/plugin-patch-history-disabled.test.ts +2 -2
  24. package/tests/plugin-pre-delete.test.ts +1 -1
  25. package/tests/plugin-pre-save.test.ts +1 -1
  26. package/tests/plugin.test.ts +2 -2
  27. package/tsconfig.json +5 -5
  28. package/vite.config.mts +1 -0
  29. package/dist/cjs/em.js +0 -9
  30. package/dist/cjs/em.js.map +0 -1
  31. package/dist/cjs/helpers.js +0 -43
  32. package/dist/cjs/helpers.js.map +0 -1
  33. package/dist/cjs/hooks/delete-hooks.js +0 -46
  34. package/dist/cjs/hooks/delete-hooks.js.map +0 -1
  35. package/dist/cjs/hooks/save-hooks.js +0 -30
  36. package/dist/cjs/hooks/save-hooks.js.map +0 -1
  37. package/dist/cjs/hooks/update-hooks.js +0 -93
  38. package/dist/cjs/hooks/update-hooks.js.map +0 -1
  39. package/dist/cjs/models/History.js +0 -46
  40. package/dist/cjs/models/History.js.map +0 -1
  41. package/dist/cjs/patch.js +0 -144
  42. package/dist/cjs/patch.js.map +0 -1
  43. package/dist/cjs/plugin.js +0 -51
  44. package/dist/cjs/plugin.js.map +0 -1
  45. package/dist/cjs/types/em.d.ts +0 -6
  46. package/dist/cjs/types/em.d.ts.map +0 -1
  47. package/dist/cjs/types/helpers.d.ts +0 -6
  48. package/dist/cjs/types/helpers.d.ts.map +0 -1
  49. package/dist/cjs/types/hooks/delete-hooks.d.ts +0 -4
  50. package/dist/cjs/types/hooks/delete-hooks.d.ts.map +0 -1
  51. package/dist/cjs/types/hooks/save-hooks.d.ts +0 -4
  52. package/dist/cjs/types/hooks/save-hooks.d.ts.map +0 -1
  53. package/dist/cjs/types/hooks/update-hooks.d.ts +0 -9
  54. package/dist/cjs/types/hooks/update-hooks.d.ts.map +0 -1
  55. package/dist/cjs/types/models/History.d.ts +0 -17
  56. package/dist/cjs/types/models/History.d.ts.map +0 -1
  57. package/dist/cjs/types/patch.d.ts +0 -15
  58. package/dist/cjs/types/patch.d.ts.map +0 -1
  59. package/dist/cjs/types/plugin.d.ts +0 -23
  60. package/dist/cjs/types/plugin.d.ts.map +0 -1
  61. package/dist/cjs/types/types.d.ts +0 -49
  62. package/dist/cjs/types/types.d.ts.map +0 -1
  63. package/dist/cjs/types/version.d.ts +0 -4
  64. package/dist/cjs/types/version.d.ts.map +0 -1
  65. package/dist/cjs/types.js +0 -3
  66. package/dist/cjs/types.js.map +0 -1
  67. package/dist/cjs/version.js +0 -13
  68. package/dist/cjs/version.js.map +0 -1
  69. package/dist/esm/em.js +0 -6
  70. package/dist/esm/em.js.map +0 -1
  71. package/dist/esm/helpers.js +0 -37
  72. package/dist/esm/helpers.js.map +0 -1
  73. package/dist/esm/hooks/delete-hooks.js +0 -42
  74. package/dist/esm/hooks/delete-hooks.js.map +0 -1
  75. package/dist/esm/hooks/save-hooks.js +0 -26
  76. package/dist/esm/hooks/save-hooks.js.map +0 -1
  77. package/dist/esm/hooks/update-hooks.js +0 -87
  78. package/dist/esm/hooks/update-hooks.js.map +0 -1
  79. package/dist/esm/models/History.js +0 -43
  80. package/dist/esm/models/History.js.map +0 -1
  81. package/dist/esm/package.json +0 -1
  82. package/dist/esm/patch.js +0 -129
  83. package/dist/esm/patch.js.map +0 -1
  84. package/dist/esm/plugin.js +0 -45
  85. package/dist/esm/plugin.js.map +0 -1
  86. package/dist/esm/types/em.d.ts +0 -6
  87. package/dist/esm/types/em.d.ts.map +0 -1
  88. package/dist/esm/types/helpers.d.ts +0 -6
  89. package/dist/esm/types/helpers.d.ts.map +0 -1
  90. package/dist/esm/types/hooks/delete-hooks.d.ts +0 -4
  91. package/dist/esm/types/hooks/delete-hooks.d.ts.map +0 -1
  92. package/dist/esm/types/hooks/save-hooks.d.ts +0 -4
  93. package/dist/esm/types/hooks/save-hooks.d.ts.map +0 -1
  94. package/dist/esm/types/hooks/update-hooks.d.ts +0 -9
  95. package/dist/esm/types/hooks/update-hooks.d.ts.map +0 -1
  96. package/dist/esm/types/models/History.d.ts +0 -17
  97. package/dist/esm/types/models/History.d.ts.map +0 -1
  98. package/dist/esm/types/patch.d.ts +0 -15
  99. package/dist/esm/types/patch.d.ts.map +0 -1
  100. package/dist/esm/types/plugin.d.ts +0 -23
  101. package/dist/esm/types/plugin.d.ts.map +0 -1
  102. package/dist/esm/types/types.d.ts +0 -49
  103. package/dist/esm/types/types.d.ts.map +0 -1
  104. package/dist/esm/types/version.d.ts +0 -4
  105. package/dist/esm/types/version.d.ts.map +0 -1
  106. package/dist/esm/types.js +0 -2
  107. package/dist/esm/types.js.map +0 -1
  108. package/dist/esm/version.js +0 -9
  109. package/dist/esm/version.js.map +0 -1
package/LICENSE CHANGED
@@ -1,6 +1,6 @@
1
1
  The MIT License (MIT)
2
2
 
3
- Copyright (c) 2024 ilovepixelart
3
+ Copyright (c) 2025 ilovepixelart
4
4
 
5
5
  Permission is hereby granted, free of charge, to any person obtaining a copy
6
6
  of this software and associated documentation files (the "Software"), to deal
package/README.md CHANGED
@@ -30,13 +30,13 @@ I need to track changes of mongoose models and save them as patch history (audit
30
30
 
31
31
  ## Features
32
32
 
33
- - [x] Track changes in mongoose models
34
- - [x] Save changes in a separate collection as a patch history
35
- - [x] Emit events when a model is created, updated or deleted
36
- - [x] Omit fields that you don't want to track in patch history
37
- - [x] Subscribe to one/many types of event
38
- - [x] Use events or patch history or both
39
- - [x] Supports ESM and CommonJS
33
+ - Track changes in mongoose models
34
+ - Save changes in a separate collection as a patch history
35
+ - Emit events when a model is created, updated or deleted
36
+ - Omit fields that you don't want to track in patch history
37
+ - Subscribe to one/many types of event
38
+ - Use events or patch history or both
39
+ - Supports ESM and CommonJS
40
40
 
41
41
  ## Installation
42
42
 
@@ -71,7 +71,7 @@ bun add mongoose@8
71
71
 
72
72
  ## Example
73
73
 
74
- How to use it with express [ts-express-swc](https://github.com/ilovepixelart/ts-express-swc)
74
+ How to use it with express [ts-express-tsx](https://github.com/ilovepixelart/ts-express-tsx)
75
75
 
76
76
  Create your event constants `events.ts`
77
77
 
package/dist/index.cjs ADDED
@@ -0,0 +1,395 @@
1
+ 'use strict';
2
+
3
+ var _ = require('lodash');
4
+ var EventEmitter = require('node:events');
5
+ var ms = require('ms');
6
+ var mongoose = require('mongoose');
7
+ var jsonpatch = require('fast-json-patch');
8
+ var omit = require('omit-deep');
9
+ var semver = require('semver');
10
+ var powerAssign = require('power-assign');
11
+
12
+ class PatchEventEmitter extends EventEmitter {
13
+ }
14
+ const em = new PatchEventEmitter();
15
+
16
+ const HistorySchema = new mongoose.Schema(
17
+ {
18
+ op: {
19
+ type: String,
20
+ required: true
21
+ },
22
+ modelName: {
23
+ type: String,
24
+ required: true
25
+ },
26
+ collectionName: {
27
+ type: String,
28
+ required: true
29
+ },
30
+ collectionId: {
31
+ type: mongoose.Schema.Types.ObjectId,
32
+ required: true
33
+ },
34
+ doc: {
35
+ type: Object
36
+ },
37
+ patch: {
38
+ type: Array
39
+ },
40
+ user: {
41
+ type: Object
42
+ },
43
+ reason: {
44
+ type: String
45
+ },
46
+ metadata: {
47
+ type: Object
48
+ },
49
+ version: {
50
+ type: Number,
51
+ min: 0,
52
+ default: 0
53
+ }
54
+ },
55
+ { timestamps: true }
56
+ );
57
+ HistorySchema.index({ collectionId: 1, version: -1 });
58
+ HistorySchema.index({ op: 1, modelName: 1, collectionName: 1, collectionId: 1, reason: 1, version: 1 });
59
+ const HistoryModel = mongoose.model("History", HistorySchema, "history");
60
+
61
+ const isHookIgnored = (options) => {
62
+ return options.ignoreHook === true || options.ignoreEvent === true && options.ignorePatchHistory === true;
63
+ };
64
+ const toObjectOptions = {
65
+ depopulate: true,
66
+ virtuals: false
67
+ };
68
+ const setPatchHistoryTTL = async (ttl) => {
69
+ const name = "createdAt_1_TTL";
70
+ try {
71
+ const indexes = await HistoryModel.collection.indexes();
72
+ const existingIndex = indexes?.find((index) => index.name === name);
73
+ if (!ttl && existingIndex) {
74
+ await HistoryModel.collection.dropIndex(name);
75
+ return;
76
+ }
77
+ const milliseconds = typeof ttl === "string" ? ms(ttl) : ttl;
78
+ if (milliseconds < 1e3 && existingIndex) {
79
+ await HistoryModel.collection.dropIndex(name);
80
+ return;
81
+ }
82
+ const expireAfterSeconds = milliseconds / 1e3;
83
+ if (existingIndex && existingIndex.expireAfterSeconds === expireAfterSeconds) {
84
+ return;
85
+ }
86
+ if (existingIndex) {
87
+ await HistoryModel.collection.dropIndex(name);
88
+ }
89
+ await HistoryModel.collection.createIndex({ createdAt: 1 }, { expireAfterSeconds, name });
90
+ } catch (err) {
91
+ console.error("Couldn't create or update index for history collection", err);
92
+ }
93
+ };
94
+
95
+ function isPatchHistoryEnabled(opts, context) {
96
+ return !opts.patchHistoryDisabled && !context.ignorePatchHistory;
97
+ }
98
+ function getJsonOmit(opts, doc) {
99
+ const object = JSON.parse(JSON.stringify(doc));
100
+ if (opts.omit) {
101
+ return omit(object, opts.omit);
102
+ }
103
+ return object;
104
+ }
105
+ function getObjectOmit(opts, doc) {
106
+ if (opts.omit) {
107
+ return omit(_.isFunction(doc?.toObject) ? doc.toObject() : doc, opts.omit);
108
+ }
109
+ return doc;
110
+ }
111
+ async function getUser(opts) {
112
+ if (_.isFunction(opts.getUser)) {
113
+ return await opts.getUser();
114
+ }
115
+ return void 0;
116
+ }
117
+ async function getReason(opts) {
118
+ if (_.isFunction(opts.getReason)) {
119
+ return await opts.getReason();
120
+ }
121
+ return void 0;
122
+ }
123
+ async function getMetadata(opts) {
124
+ if (_.isFunction(opts.getMetadata)) {
125
+ return await opts.getMetadata();
126
+ }
127
+ return void 0;
128
+ }
129
+ function getValue(item) {
130
+ return item.status === "fulfilled" ? item.value : void 0;
131
+ }
132
+ async function getData(opts) {
133
+ return Promise.allSettled([getUser(opts), getReason(opts), getMetadata(opts)]).then(([user, reason, metadata]) => {
134
+ return [getValue(user), getValue(reason), getValue(metadata)];
135
+ });
136
+ }
137
+ function emitEvent(context, event, data) {
138
+ if (event && !context.ignoreEvent) {
139
+ em.emit(event, data);
140
+ }
141
+ }
142
+ async function bulkPatch(opts, context, eventKey, docsKey) {
143
+ const history = isPatchHistoryEnabled(opts, context);
144
+ const event = opts[eventKey];
145
+ const docs = context[docsKey];
146
+ const key = eventKey === "eventCreated" ? "doc" : "oldDoc";
147
+ if (_.isEmpty(docs) || !event && !history) return;
148
+ const [user, reason, metadata] = await getData(opts);
149
+ const chunks = _.chunk(docs, 1e3);
150
+ for await (const chunk of chunks) {
151
+ const bulk = [];
152
+ for (const doc of chunk) {
153
+ emitEvent(context, event, { [key]: doc });
154
+ if (history) {
155
+ bulk.push({
156
+ insertOne: {
157
+ document: {
158
+ op: context.op,
159
+ modelName: context.modelName,
160
+ collectionName: context.collectionName,
161
+ collectionId: doc._id,
162
+ doc: getObjectOmit(opts, doc),
163
+ user,
164
+ reason,
165
+ metadata,
166
+ version: 0
167
+ }
168
+ }
169
+ });
170
+ }
171
+ }
172
+ if (history && !_.isEmpty(bulk)) {
173
+ await HistoryModel.bulkWrite(bulk, { ordered: false }).catch((error) => {
174
+ console.error(error.message);
175
+ });
176
+ }
177
+ }
178
+ }
179
+ async function createPatch(opts, context) {
180
+ await bulkPatch(opts, context, "eventCreated", "createdDocs");
181
+ }
182
+ async function updatePatch(opts, context, current, original) {
183
+ const history = isPatchHistoryEnabled(opts, context);
184
+ const currentObject = getJsonOmit(opts, current);
185
+ const originalObject = getJsonOmit(opts, original);
186
+ if (_.isEmpty(originalObject) || _.isEmpty(currentObject)) return;
187
+ const patch = jsonpatch.compare(originalObject, currentObject, true);
188
+ if (_.isEmpty(patch)) return;
189
+ emitEvent(context, opts.eventUpdated, { oldDoc: original, doc: current, patch });
190
+ if (history) {
191
+ let version = 0;
192
+ const lastHistory = await HistoryModel.findOne({ collectionId: original._id }).sort("-version").exec();
193
+ if (lastHistory && lastHistory.version >= 0) {
194
+ version = lastHistory.version + 1;
195
+ }
196
+ const [user, reason, metadata] = await getData(opts);
197
+ await HistoryModel.create({
198
+ op: context.op,
199
+ modelName: context.modelName,
200
+ collectionName: context.collectionName,
201
+ collectionId: original._id,
202
+ patch,
203
+ user,
204
+ reason,
205
+ metadata,
206
+ version
207
+ });
208
+ }
209
+ }
210
+ async function deletePatch(opts, context) {
211
+ await bulkPatch(opts, context, "eventDeleted", "deletedDocs");
212
+ }
213
+
214
+ const isMongooseLessThan8 = semver.satisfies(mongoose.version, "<8");
215
+ const isMongooseLessThan7 = semver.satisfies(mongoose.version, "<7");
216
+ const isMongoose6 = semver.satisfies(mongoose.version, "6");
217
+ if (isMongoose6) {
218
+ mongoose.set("strictQuery", false);
219
+ }
220
+
221
+ const deleteMethods = ["remove", "findOneAndDelete", "findOneAndRemove", "findByIdAndDelete", "findByIdAndRemove", "deleteOne", "deleteMany"];
222
+ const deleteHooksInitialize = (schema, opts) => {
223
+ schema.pre(deleteMethods, { document: false, query: true }, async function() {
224
+ const options = this.getOptions();
225
+ if (isHookIgnored(options)) return;
226
+ const model = this.model;
227
+ const filter = this.getFilter();
228
+ this._context = {
229
+ op: this.op,
230
+ modelName: opts.modelName ?? this.model.modelName,
231
+ collectionName: opts.collectionName ?? this.model.collection.collectionName,
232
+ ignoreEvent: options.ignoreEvent,
233
+ ignorePatchHistory: options.ignorePatchHistory
234
+ };
235
+ if (["remove", "deleteMany"].includes(this._context.op) && !options.single) {
236
+ const docs = await model.find(filter).lean().exec();
237
+ if (!_.isEmpty(docs)) {
238
+ this._context.deletedDocs = docs;
239
+ }
240
+ } else {
241
+ const doc = await model.findOne(filter).lean().exec();
242
+ if (!_.isEmpty(doc)) {
243
+ this._context.deletedDocs = [doc];
244
+ }
245
+ }
246
+ if (opts.preDelete && _.isArray(this._context.deletedDocs) && !_.isEmpty(this._context.deletedDocs)) {
247
+ await opts.preDelete(this._context.deletedDocs);
248
+ }
249
+ });
250
+ schema.post(deleteMethods, { document: false, query: true }, async function() {
251
+ const options = this.getOptions();
252
+ if (isHookIgnored(options)) return;
253
+ await deletePatch(opts, this._context);
254
+ });
255
+ };
256
+
257
+ const saveHooksInitialize = (schema, opts) => {
258
+ schema.pre("save", async function() {
259
+ if (this.constructor.name !== "model") return;
260
+ const current = this.toObject(toObjectOptions);
261
+ const model = this.constructor;
262
+ const context = {
263
+ op: this.isNew ? "create" : "update",
264
+ modelName: opts.modelName ?? model.modelName,
265
+ collectionName: opts.collectionName ?? model.collection.collectionName,
266
+ createdDocs: [current]
267
+ };
268
+ if (this.isNew) {
269
+ await createPatch(opts, context);
270
+ } else {
271
+ const original = await model.findById(current._id).lean().exec();
272
+ if (original) {
273
+ await updatePatch(opts, context, current, original);
274
+ }
275
+ }
276
+ });
277
+ };
278
+
279
+ const updateMethods = ["update", "updateOne", "replaceOne", "updateMany", "findOneAndUpdate", "findOneAndReplace", "findByIdAndUpdate"];
280
+ const assignUpdate = (document, update, commands) => {
281
+ let updated = powerAssign.assign(document.toObject(toObjectOptions), update);
282
+ _.forEach(commands, (command) => {
283
+ try {
284
+ updated = powerAssign.assign(updated, command);
285
+ } catch {
286
+ }
287
+ });
288
+ const doc = document.set(updated).toObject(toObjectOptions);
289
+ if (update.createdAt) doc.createdAt = update.createdAt;
290
+ return doc;
291
+ };
292
+ const splitUpdateAndCommands = (updateQuery) => {
293
+ let update = {};
294
+ const commands = [];
295
+ if (!_.isEmpty(updateQuery) && !_.isArray(updateQuery) && _.isObjectLike(updateQuery)) {
296
+ update = _.cloneDeep(updateQuery);
297
+ const keysWithDollarSign = _.keys(update).filter((key) => key.startsWith("$"));
298
+ if (!_.isEmpty(keysWithDollarSign)) {
299
+ _.forEach(keysWithDollarSign, (key) => {
300
+ commands.push({ [key]: update[key] });
301
+ delete update[key];
302
+ });
303
+ }
304
+ }
305
+ return { update, commands };
306
+ };
307
+ const updateHooksInitialize = (schema, opts) => {
308
+ schema.pre(updateMethods, async function() {
309
+ const options = this.getOptions();
310
+ if (isHookIgnored(options)) return;
311
+ const model = this.model;
312
+ const filter = this.getFilter();
313
+ const count = await this.model.countDocuments(filter).exec();
314
+ this._context = {
315
+ op: this.op,
316
+ modelName: opts.modelName ?? this.model.modelName,
317
+ collectionName: opts.collectionName ?? this.model.collection.collectionName,
318
+ isNew: Boolean(options.upsert) && count === 0,
319
+ ignoreEvent: options.ignoreEvent,
320
+ ignorePatchHistory: options.ignorePatchHistory
321
+ };
322
+ const updateQuery = this.getUpdate();
323
+ const { update, commands } = splitUpdateAndCommands(updateQuery);
324
+ const cursor = model.find(filter).cursor();
325
+ await cursor.eachAsync(async (doc) => {
326
+ const origDoc = doc.toObject(toObjectOptions);
327
+ await updatePatch(opts, this._context, assignUpdate(doc, update, commands), origDoc);
328
+ });
329
+ });
330
+ schema.post(updateMethods, async function() {
331
+ const options = this.getOptions();
332
+ if (isHookIgnored(options)) return;
333
+ if (!this._context.isNew) return;
334
+ const model = this.model;
335
+ const updateQuery = this.getUpdate();
336
+ const { update, commands } = splitUpdateAndCommands(updateQuery);
337
+ let current = null;
338
+ const filter = this.getFilter();
339
+ const combined = assignUpdate(model.hydrate({}), update, commands);
340
+ if (!_.isEmpty(update) && !current) {
341
+ current = await model.findOne(update).sort("desc").lean().exec();
342
+ }
343
+ if (!_.isEmpty(combined) && !current) {
344
+ current = await model.findOne(combined).sort("desc").lean().exec();
345
+ }
346
+ if (!_.isEmpty(filter) && !current) {
347
+ console.log("filter", filter);
348
+ current = await model.findOne(filter).sort("desc").lean().exec();
349
+ }
350
+ if (current) {
351
+ this._context.createdDocs = [current];
352
+ await createPatch(opts, this._context);
353
+ }
354
+ });
355
+ };
356
+
357
+ const remove = isMongooseLessThan7 ? "remove" : "deleteOne";
358
+ const patchEventEmitter = em;
359
+ const patchHistoryPlugin = function plugin(schema, opts) {
360
+ saveHooksInitialize(schema, opts);
361
+ updateHooksInitialize(schema, opts);
362
+ deleteHooksInitialize(schema, opts);
363
+ schema.post("insertMany", async function(docs) {
364
+ const context = {
365
+ op: "create",
366
+ modelName: opts.modelName ?? this.modelName,
367
+ collectionName: opts.collectionName ?? this.collection.collectionName,
368
+ createdDocs: docs
369
+ };
370
+ await createPatch(opts, context);
371
+ });
372
+ if (isMongooseLessThan8) {
373
+ schema.pre(remove, { document: true, query: false }, async function() {
374
+ const original = this.toObject(toObjectOptions);
375
+ if (opts.preDelete && !_.isEmpty(original)) {
376
+ await opts.preDelete([original]);
377
+ }
378
+ });
379
+ schema.post(remove, { document: true, query: false }, async function() {
380
+ const original = this.toObject(toObjectOptions);
381
+ const model = this.constructor;
382
+ const context = {
383
+ op: "delete",
384
+ modelName: opts.modelName ?? model.modelName,
385
+ collectionName: opts.collectionName ?? model.collection.collectionName,
386
+ deletedDocs: [original]
387
+ };
388
+ await deletePatch(opts, context);
389
+ });
390
+ }
391
+ };
392
+
393
+ exports.patchEventEmitter = patchEventEmitter;
394
+ exports.patchHistoryPlugin = patchHistoryPlugin;
395
+ exports.setPatchHistoryTTL = setPatchHistoryTTL;
@@ -0,0 +1,75 @@
1
+ import { HydratedDocument, Types, Query, Schema } from 'mongoose';
2
+ import { Operation } from 'fast-json-patch';
3
+ import ms from 'ms';
4
+
5
+ interface History {
6
+ op: string;
7
+ modelName: string;
8
+ collectionName: string;
9
+ collectionId: Types.ObjectId;
10
+ version: number;
11
+ doc?: object;
12
+ user?: object;
13
+ reason?: string;
14
+ metadata?: object;
15
+ patch?: Operation[];
16
+ }
17
+ interface PatchEvent<T> {
18
+ oldDoc?: HydratedDocument<T>;
19
+ doc?: HydratedDocument<T>;
20
+ patch?: Operation[];
21
+ }
22
+ interface PatchContext<T> {
23
+ op: string;
24
+ modelName: string;
25
+ collectionName: string;
26
+ isNew?: boolean;
27
+ createdDocs?: HydratedDocument<T>[];
28
+ deletedDocs?: HydratedDocument<T>[];
29
+ ignoreEvent?: boolean;
30
+ ignorePatchHistory?: boolean;
31
+ }
32
+ type HookContext<T> = Query<T, T> & {
33
+ op: string;
34
+ _context: PatchContext<T>;
35
+ };
36
+ type User = Record<string, unknown>;
37
+ type Metadata = Record<string, unknown>;
38
+ interface PluginOptions<T> {
39
+ modelName?: string;
40
+ collectionName?: string;
41
+ eventUpdated?: string;
42
+ eventCreated?: string;
43
+ eventDeleted?: string;
44
+ getUser?: () => Promise<User> | User;
45
+ getReason?: () => Promise<string> | string;
46
+ getMetadata?: () => Promise<Metadata> | Metadata;
47
+ omit?: string[];
48
+ patchHistoryDisabled?: boolean;
49
+ preDelete?: (docs: HydratedDocument<T>[]) => Promise<void>;
50
+ }
51
+
52
+ declare const setPatchHistoryTTL: (ttl: number | ms.StringValue) => Promise<void>;
53
+
54
+ declare const patchEventEmitter: {
55
+ [EventEmitter.captureRejectionSymbol]?<K>(error: Error, event: string | symbol, ...args: any[]): void;
56
+ addListener<K>(eventName: string | symbol, listener: (...args: any[]) => void): any;
57
+ on<K>(eventName: string | symbol, listener: (...args: any[]) => void): any;
58
+ once<K>(eventName: string | symbol, listener: (...args: any[]) => void): any;
59
+ removeListener<K>(eventName: string | symbol, listener: (...args: any[]) => void): any;
60
+ off<K>(eventName: string | symbol, listener: (...args: any[]) => void): any;
61
+ removeAllListeners(eventName?: string | symbol | undefined): any;
62
+ setMaxListeners(n: number): any;
63
+ getMaxListeners(): number;
64
+ listeners<K>(eventName: string | symbol): Function[];
65
+ rawListeners<K>(eventName: string | symbol): Function[];
66
+ emit<K>(eventName: string | symbol, ...args: any[]): boolean;
67
+ listenerCount<K>(eventName: string | symbol, listener?: Function | undefined): number;
68
+ prependListener<K>(eventName: string | symbol, listener: (...args: any[]) => void): any;
69
+ prependOnceListener<K>(eventName: string | symbol, listener: (...args: any[]) => void): any;
70
+ eventNames(): (string | symbol)[];
71
+ };
72
+
73
+ declare const patchHistoryPlugin: <T>(schema: Schema<T>, opts: PluginOptions<T>) => void;
74
+
75
+ export { type History, type HookContext, type Metadata, type PatchContext, type PatchEvent, type PluginOptions, type User, patchEventEmitter, patchHistoryPlugin, setPatchHistoryTTL };
@@ -0,0 +1,75 @@
1
+ import { HydratedDocument, Types, Query, Schema } from 'mongoose';
2
+ import { Operation } from 'fast-json-patch';
3
+ import ms from 'ms';
4
+
5
+ interface History {
6
+ op: string;
7
+ modelName: string;
8
+ collectionName: string;
9
+ collectionId: Types.ObjectId;
10
+ version: number;
11
+ doc?: object;
12
+ user?: object;
13
+ reason?: string;
14
+ metadata?: object;
15
+ patch?: Operation[];
16
+ }
17
+ interface PatchEvent<T> {
18
+ oldDoc?: HydratedDocument<T>;
19
+ doc?: HydratedDocument<T>;
20
+ patch?: Operation[];
21
+ }
22
+ interface PatchContext<T> {
23
+ op: string;
24
+ modelName: string;
25
+ collectionName: string;
26
+ isNew?: boolean;
27
+ createdDocs?: HydratedDocument<T>[];
28
+ deletedDocs?: HydratedDocument<T>[];
29
+ ignoreEvent?: boolean;
30
+ ignorePatchHistory?: boolean;
31
+ }
32
+ type HookContext<T> = Query<T, T> & {
33
+ op: string;
34
+ _context: PatchContext<T>;
35
+ };
36
+ type User = Record<string, unknown>;
37
+ type Metadata = Record<string, unknown>;
38
+ interface PluginOptions<T> {
39
+ modelName?: string;
40
+ collectionName?: string;
41
+ eventUpdated?: string;
42
+ eventCreated?: string;
43
+ eventDeleted?: string;
44
+ getUser?: () => Promise<User> | User;
45
+ getReason?: () => Promise<string> | string;
46
+ getMetadata?: () => Promise<Metadata> | Metadata;
47
+ omit?: string[];
48
+ patchHistoryDisabled?: boolean;
49
+ preDelete?: (docs: HydratedDocument<T>[]) => Promise<void>;
50
+ }
51
+
52
+ declare const setPatchHistoryTTL: (ttl: number | ms.StringValue) => Promise<void>;
53
+
54
+ declare const patchEventEmitter: {
55
+ [EventEmitter.captureRejectionSymbol]?<K>(error: Error, event: string | symbol, ...args: any[]): void;
56
+ addListener<K>(eventName: string | symbol, listener: (...args: any[]) => void): any;
57
+ on<K>(eventName: string | symbol, listener: (...args: any[]) => void): any;
58
+ once<K>(eventName: string | symbol, listener: (...args: any[]) => void): any;
59
+ removeListener<K>(eventName: string | symbol, listener: (...args: any[]) => void): any;
60
+ off<K>(eventName: string | symbol, listener: (...args: any[]) => void): any;
61
+ removeAllListeners(eventName?: string | symbol | undefined): any;
62
+ setMaxListeners(n: number): any;
63
+ getMaxListeners(): number;
64
+ listeners<K>(eventName: string | symbol): Function[];
65
+ rawListeners<K>(eventName: string | symbol): Function[];
66
+ emit<K>(eventName: string | symbol, ...args: any[]): boolean;
67
+ listenerCount<K>(eventName: string | symbol, listener?: Function | undefined): number;
68
+ prependListener<K>(eventName: string | symbol, listener: (...args: any[]) => void): any;
69
+ prependOnceListener<K>(eventName: string | symbol, listener: (...args: any[]) => void): any;
70
+ eventNames(): (string | symbol)[];
71
+ };
72
+
73
+ declare const patchHistoryPlugin: <T>(schema: Schema<T>, opts: PluginOptions<T>) => void;
74
+
75
+ export { type History, type HookContext, type Metadata, type PatchContext, type PatchEvent, type PluginOptions, type User, patchEventEmitter, patchHistoryPlugin, setPatchHistoryTTL };