ts-patch-mongoose 2.9.2 → 2.9.3
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/biome.json +1 -1
- package/dist/index.cjs +31 -32
- package/dist/index.d.cts +6 -20
- package/dist/index.d.mts +6 -20
- package/dist/index.mjs +31 -32
- package/package.json +13 -13
- package/src/hooks/delete-hooks.ts +4 -4
- package/src/hooks/update-hooks.ts +10 -10
- package/src/index.ts +3 -5
- package/src/patch.ts +11 -11
- package/tests/patch.test.ts +1 -1
- package/tests/plugin-event-created.test.ts +1 -1
- package/tests/plugin-global.test.ts +1 -1
- package/vite.config.mts +11 -1
package/biome.json
CHANGED
package/dist/index.cjs
CHANGED
|
@@ -1,18 +1,14 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
-
var
|
|
4
|
-
var EventEmitter = require('node:events');
|
|
3
|
+
var lodashEs = require('lodash-es');
|
|
5
4
|
var ms = require('ms');
|
|
6
5
|
var mongoose = require('mongoose');
|
|
7
6
|
var jsonpatch = require('fast-json-patch');
|
|
8
7
|
var omit = require('omit-deep');
|
|
8
|
+
var EventEmitter = require('node:events');
|
|
9
9
|
var powerAssign = require('power-assign');
|
|
10
10
|
var semver = require('semver');
|
|
11
11
|
|
|
12
|
-
class PatchEventEmitter extends EventEmitter {
|
|
13
|
-
}
|
|
14
|
-
const em = new PatchEventEmitter();
|
|
15
|
-
|
|
16
12
|
const HistorySchema = new mongoose.Schema(
|
|
17
13
|
{
|
|
18
14
|
op: {
|
|
@@ -92,6 +88,10 @@ const setPatchHistoryTTL = async (ttl) => {
|
|
|
92
88
|
}
|
|
93
89
|
};
|
|
94
90
|
|
|
91
|
+
class PatchEventEmitter extends EventEmitter {
|
|
92
|
+
}
|
|
93
|
+
const em = new PatchEventEmitter();
|
|
94
|
+
|
|
95
95
|
function isPatchHistoryEnabled(opts, context) {
|
|
96
96
|
return !opts.patchHistoryDisabled && !context.ignorePatchHistory;
|
|
97
97
|
}
|
|
@@ -104,24 +104,24 @@ function getJsonOmit(opts, doc) {
|
|
|
104
104
|
}
|
|
105
105
|
function getObjectOmit(opts, doc) {
|
|
106
106
|
if (opts.omit) {
|
|
107
|
-
return omit(
|
|
107
|
+
return omit(lodashEs.isFunction(doc?.toObject) ? doc.toObject() : doc, opts.omit);
|
|
108
108
|
}
|
|
109
109
|
return doc;
|
|
110
110
|
}
|
|
111
111
|
async function getUser(opts, doc) {
|
|
112
|
-
if (
|
|
112
|
+
if (lodashEs.isFunction(opts.getUser)) {
|
|
113
113
|
return await opts.getUser(doc);
|
|
114
114
|
}
|
|
115
115
|
return void 0;
|
|
116
116
|
}
|
|
117
117
|
async function getReason(opts, doc) {
|
|
118
|
-
if (
|
|
118
|
+
if (lodashEs.isFunction(opts.getReason)) {
|
|
119
119
|
return await opts.getReason(doc);
|
|
120
120
|
}
|
|
121
121
|
return void 0;
|
|
122
122
|
}
|
|
123
123
|
async function getMetadata(opts, doc) {
|
|
124
|
-
if (
|
|
124
|
+
if (lodashEs.isFunction(opts.getMetadata)) {
|
|
125
125
|
return await opts.getMetadata(doc);
|
|
126
126
|
}
|
|
127
127
|
return void 0;
|
|
@@ -144,11 +144,11 @@ async function bulkPatch(opts, context, eventKey, docsKey) {
|
|
|
144
144
|
const event = opts[eventKey];
|
|
145
145
|
const docs = context[docsKey];
|
|
146
146
|
const key = eventKey === "eventCreated" ? "doc" : "oldDoc";
|
|
147
|
-
if (
|
|
148
|
-
const chunks =
|
|
149
|
-
for
|
|
147
|
+
if (lodashEs.isEmpty(docs) || !event && !history) return;
|
|
148
|
+
const chunks = lodashEs.chunk(docs, 1e3);
|
|
149
|
+
for (const chunk2 of chunks) {
|
|
150
150
|
const bulk = [];
|
|
151
|
-
for (const doc of
|
|
151
|
+
for (const doc of chunk2) {
|
|
152
152
|
emitEvent(context, event, { [key]: doc });
|
|
153
153
|
if (history) {
|
|
154
154
|
const [user, reason, metadata] = await getData(opts, doc);
|
|
@@ -169,7 +169,7 @@ async function bulkPatch(opts, context, eventKey, docsKey) {
|
|
|
169
169
|
});
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
|
-
if (history && !
|
|
172
|
+
if (history && !lodashEs.isEmpty(bulk)) {
|
|
173
173
|
await HistoryModel.bulkWrite(bulk, { ordered: false }).catch((error) => {
|
|
174
174
|
console.error(error.message);
|
|
175
175
|
});
|
|
@@ -183,9 +183,9 @@ async function updatePatch(opts, context, current, original) {
|
|
|
183
183
|
const history = isPatchHistoryEnabled(opts, context);
|
|
184
184
|
const currentObject = getJsonOmit(opts, current);
|
|
185
185
|
const originalObject = getJsonOmit(opts, original);
|
|
186
|
-
if (
|
|
186
|
+
if (lodashEs.isEmpty(originalObject) || lodashEs.isEmpty(currentObject)) return;
|
|
187
187
|
const patch = jsonpatch.compare(originalObject, currentObject, true);
|
|
188
|
-
if (
|
|
188
|
+
if (lodashEs.isEmpty(patch)) return;
|
|
189
189
|
emitEvent(context, opts.eventUpdated, { oldDoc: original, doc: current, patch });
|
|
190
190
|
if (history) {
|
|
191
191
|
let version = 0;
|
|
@@ -227,16 +227,16 @@ const deleteHooksInitialize = (schema, opts) => {
|
|
|
227
227
|
};
|
|
228
228
|
if (["remove", "deleteMany"].includes(this._context.op) && !options.single) {
|
|
229
229
|
const docs = await model.find(filter).lean().exec();
|
|
230
|
-
if (!
|
|
230
|
+
if (!lodashEs.isEmpty(docs)) {
|
|
231
231
|
this._context.deletedDocs = docs;
|
|
232
232
|
}
|
|
233
233
|
} else {
|
|
234
234
|
const doc = await model.findOne(filter).lean().exec();
|
|
235
|
-
if (!
|
|
235
|
+
if (!lodashEs.isEmpty(doc)) {
|
|
236
236
|
this._context.deletedDocs = [doc];
|
|
237
237
|
}
|
|
238
238
|
}
|
|
239
|
-
if (opts.preDelete &&
|
|
239
|
+
if (opts.preDelete && lodashEs.isArray(this._context.deletedDocs) && !lodashEs.isEmpty(this._context.deletedDocs)) {
|
|
240
240
|
await opts.preDelete(this._context.deletedDocs);
|
|
241
241
|
}
|
|
242
242
|
});
|
|
@@ -272,7 +272,7 @@ const saveHooksInitialize = (schema, opts) => {
|
|
|
272
272
|
const updateMethods = ["update", "updateOne", "replaceOne", "updateMany", "findOneAndUpdate", "findOneAndReplace", "findByIdAndUpdate"];
|
|
273
273
|
const assignUpdate = (document, update, commands) => {
|
|
274
274
|
let updated = powerAssign.assign(document.toObject(toObjectOptions), update);
|
|
275
|
-
|
|
275
|
+
lodashEs.forEach(commands, (command) => {
|
|
276
276
|
try {
|
|
277
277
|
updated = powerAssign.assign(updated, command);
|
|
278
278
|
} catch {
|
|
@@ -285,11 +285,11 @@ const assignUpdate = (document, update, commands) => {
|
|
|
285
285
|
const splitUpdateAndCommands = (updateQuery) => {
|
|
286
286
|
let update = {};
|
|
287
287
|
const commands = [];
|
|
288
|
-
if (!
|
|
289
|
-
update =
|
|
290
|
-
const keysWithDollarSign =
|
|
291
|
-
if (!
|
|
292
|
-
|
|
288
|
+
if (!lodashEs.isEmpty(updateQuery) && !lodashEs.isArray(updateQuery) && lodashEs.isObjectLike(updateQuery)) {
|
|
289
|
+
update = lodashEs.cloneDeep(updateQuery);
|
|
290
|
+
const keysWithDollarSign = lodashEs.keys(update).filter((key) => key.startsWith("$"));
|
|
291
|
+
if (!lodashEs.isEmpty(keysWithDollarSign)) {
|
|
292
|
+
lodashEs.forEach(keysWithDollarSign, (key) => {
|
|
293
293
|
commands.push({ [key]: update[key] });
|
|
294
294
|
delete update[key];
|
|
295
295
|
});
|
|
@@ -330,13 +330,13 @@ const updateHooksInitialize = (schema, opts) => {
|
|
|
330
330
|
let current = null;
|
|
331
331
|
const filter = this.getFilter();
|
|
332
332
|
const combined = assignUpdate(model.hydrate({}), update, commands);
|
|
333
|
-
if (!
|
|
333
|
+
if (!lodashEs.isEmpty(update) && !current) {
|
|
334
334
|
current = await model.findOne(update).sort("desc").lean().exec();
|
|
335
335
|
}
|
|
336
|
-
if (!
|
|
336
|
+
if (!lodashEs.isEmpty(combined) && !current) {
|
|
337
337
|
current = await model.findOne(combined).sort("desc").lean().exec();
|
|
338
338
|
}
|
|
339
|
-
if (!
|
|
339
|
+
if (!lodashEs.isEmpty(filter) && !current) {
|
|
340
340
|
console.log("filter", filter);
|
|
341
341
|
current = await model.findOne(filter).sort("desc").lean().exec();
|
|
342
342
|
}
|
|
@@ -355,7 +355,6 @@ if (isMongoose6) {
|
|
|
355
355
|
}
|
|
356
356
|
|
|
357
357
|
const remove = isMongooseLessThan7 ? "remove" : "deleteOne";
|
|
358
|
-
const patchEventEmitter = em;
|
|
359
358
|
const patchHistoryPlugin = function plugin(schema, opts) {
|
|
360
359
|
saveHooksInitialize(schema, opts);
|
|
361
360
|
updateHooksInitialize(schema, opts);
|
|
@@ -372,7 +371,7 @@ const patchHistoryPlugin = function plugin(schema, opts) {
|
|
|
372
371
|
if (isMongooseLessThan8) {
|
|
373
372
|
schema.pre(remove, { document: true, query: false }, async function() {
|
|
374
373
|
const original = this.toObject(toObjectOptions);
|
|
375
|
-
if (opts.preDelete && !
|
|
374
|
+
if (opts.preDelete && !lodashEs.isEmpty(original)) {
|
|
376
375
|
await opts.preDelete([original]);
|
|
377
376
|
}
|
|
378
377
|
});
|
|
@@ -390,6 +389,6 @@ const patchHistoryPlugin = function plugin(schema, opts) {
|
|
|
390
389
|
}
|
|
391
390
|
};
|
|
392
391
|
|
|
393
|
-
exports.patchEventEmitter =
|
|
392
|
+
exports.patchEventEmitter = em;
|
|
394
393
|
exports.patchHistoryPlugin = patchHistoryPlugin;
|
|
395
394
|
exports.setPatchHistoryTTL = setPatchHistoryTTL;
|
package/dist/index.d.cts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { HydratedDocument, Types, Query, Schema } from 'mongoose';
|
|
2
2
|
import { Operation } from 'fast-json-patch';
|
|
3
|
+
import EventEmitter from 'node:events';
|
|
3
4
|
import ms from 'ms';
|
|
4
5
|
|
|
5
6
|
interface History {
|
|
@@ -49,28 +50,13 @@ interface PluginOptions<T> {
|
|
|
49
50
|
preDelete?: (docs: HydratedDocument<T>[]) => Promise<void>;
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
declare
|
|
53
|
+
declare class PatchEventEmitter extends EventEmitter {
|
|
54
|
+
}
|
|
55
|
+
declare const em: PatchEventEmitter;
|
|
53
56
|
|
|
54
|
-
declare const
|
|
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
|
-
};
|
|
57
|
+
declare const setPatchHistoryTTL: (ttl: number | ms.StringValue) => Promise<void>;
|
|
72
58
|
|
|
73
59
|
declare const patchHistoryPlugin: <T>(schema: Schema<T>, opts: PluginOptions<T>) => void;
|
|
74
60
|
|
|
75
|
-
export { patchEventEmitter, patchHistoryPlugin, setPatchHistoryTTL };
|
|
61
|
+
export { em as patchEventEmitter, patchHistoryPlugin, setPatchHistoryTTL };
|
|
76
62
|
export type { History, HookContext, Metadata, PatchContext, PatchEvent, PluginOptions, User };
|
package/dist/index.d.mts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { HydratedDocument, Types, Query, Schema } from 'mongoose';
|
|
2
2
|
import { Operation } from 'fast-json-patch';
|
|
3
|
+
import EventEmitter from 'node:events';
|
|
3
4
|
import ms from 'ms';
|
|
4
5
|
|
|
5
6
|
interface History {
|
|
@@ -49,28 +50,13 @@ interface PluginOptions<T> {
|
|
|
49
50
|
preDelete?: (docs: HydratedDocument<T>[]) => Promise<void>;
|
|
50
51
|
}
|
|
51
52
|
|
|
52
|
-
declare
|
|
53
|
+
declare class PatchEventEmitter extends EventEmitter {
|
|
54
|
+
}
|
|
55
|
+
declare const em: PatchEventEmitter;
|
|
53
56
|
|
|
54
|
-
declare const
|
|
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
|
-
};
|
|
57
|
+
declare const setPatchHistoryTTL: (ttl: number | ms.StringValue) => Promise<void>;
|
|
72
58
|
|
|
73
59
|
declare const patchHistoryPlugin: <T>(schema: Schema<T>, opts: PluginOptions<T>) => void;
|
|
74
60
|
|
|
75
|
-
export { patchEventEmitter, patchHistoryPlugin, setPatchHistoryTTL };
|
|
61
|
+
export { em as patchEventEmitter, patchHistoryPlugin, setPatchHistoryTTL };
|
|
76
62
|
export type { History, HookContext, Metadata, PatchContext, PatchEvent, PluginOptions, User };
|
package/dist/index.mjs
CHANGED
|
@@ -1,16 +1,12 @@
|
|
|
1
|
-
import
|
|
2
|
-
import EventEmitter from 'node:events';
|
|
1
|
+
import { isEmpty, chunk, isFunction, isArray, isObjectLike, cloneDeep, keys, forEach } from 'lodash-es';
|
|
3
2
|
import ms from 'ms';
|
|
4
3
|
import mongoose, { Schema, model } from 'mongoose';
|
|
5
4
|
import jsonpatch from 'fast-json-patch';
|
|
6
5
|
import omit from 'omit-deep';
|
|
6
|
+
import EventEmitter from 'node:events';
|
|
7
7
|
import { assign } from 'power-assign';
|
|
8
8
|
import { satisfies } from 'semver';
|
|
9
9
|
|
|
10
|
-
class PatchEventEmitter extends EventEmitter {
|
|
11
|
-
}
|
|
12
|
-
const em = new PatchEventEmitter();
|
|
13
|
-
|
|
14
10
|
const HistorySchema = new Schema(
|
|
15
11
|
{
|
|
16
12
|
op: {
|
|
@@ -90,6 +86,10 @@ const setPatchHistoryTTL = async (ttl) => {
|
|
|
90
86
|
}
|
|
91
87
|
};
|
|
92
88
|
|
|
89
|
+
class PatchEventEmitter extends EventEmitter {
|
|
90
|
+
}
|
|
91
|
+
const em = new PatchEventEmitter();
|
|
92
|
+
|
|
93
93
|
function isPatchHistoryEnabled(opts, context) {
|
|
94
94
|
return !opts.patchHistoryDisabled && !context.ignorePatchHistory;
|
|
95
95
|
}
|
|
@@ -102,24 +102,24 @@ function getJsonOmit(opts, doc) {
|
|
|
102
102
|
}
|
|
103
103
|
function getObjectOmit(opts, doc) {
|
|
104
104
|
if (opts.omit) {
|
|
105
|
-
return omit(
|
|
105
|
+
return omit(isFunction(doc?.toObject) ? doc.toObject() : doc, opts.omit);
|
|
106
106
|
}
|
|
107
107
|
return doc;
|
|
108
108
|
}
|
|
109
109
|
async function getUser(opts, doc) {
|
|
110
|
-
if (
|
|
110
|
+
if (isFunction(opts.getUser)) {
|
|
111
111
|
return await opts.getUser(doc);
|
|
112
112
|
}
|
|
113
113
|
return void 0;
|
|
114
114
|
}
|
|
115
115
|
async function getReason(opts, doc) {
|
|
116
|
-
if (
|
|
116
|
+
if (isFunction(opts.getReason)) {
|
|
117
117
|
return await opts.getReason(doc);
|
|
118
118
|
}
|
|
119
119
|
return void 0;
|
|
120
120
|
}
|
|
121
121
|
async function getMetadata(opts, doc) {
|
|
122
|
-
if (
|
|
122
|
+
if (isFunction(opts.getMetadata)) {
|
|
123
123
|
return await opts.getMetadata(doc);
|
|
124
124
|
}
|
|
125
125
|
return void 0;
|
|
@@ -142,11 +142,11 @@ async function bulkPatch(opts, context, eventKey, docsKey) {
|
|
|
142
142
|
const event = opts[eventKey];
|
|
143
143
|
const docs = context[docsKey];
|
|
144
144
|
const key = eventKey === "eventCreated" ? "doc" : "oldDoc";
|
|
145
|
-
if (
|
|
146
|
-
const chunks =
|
|
147
|
-
for
|
|
145
|
+
if (isEmpty(docs) || !event && !history) return;
|
|
146
|
+
const chunks = chunk(docs, 1e3);
|
|
147
|
+
for (const chunk2 of chunks) {
|
|
148
148
|
const bulk = [];
|
|
149
|
-
for (const doc of
|
|
149
|
+
for (const doc of chunk2) {
|
|
150
150
|
emitEvent(context, event, { [key]: doc });
|
|
151
151
|
if (history) {
|
|
152
152
|
const [user, reason, metadata] = await getData(opts, doc);
|
|
@@ -167,7 +167,7 @@ async function bulkPatch(opts, context, eventKey, docsKey) {
|
|
|
167
167
|
});
|
|
168
168
|
}
|
|
169
169
|
}
|
|
170
|
-
if (history && !
|
|
170
|
+
if (history && !isEmpty(bulk)) {
|
|
171
171
|
await HistoryModel.bulkWrite(bulk, { ordered: false }).catch((error) => {
|
|
172
172
|
console.error(error.message);
|
|
173
173
|
});
|
|
@@ -181,9 +181,9 @@ async function updatePatch(opts, context, current, original) {
|
|
|
181
181
|
const history = isPatchHistoryEnabled(opts, context);
|
|
182
182
|
const currentObject = getJsonOmit(opts, current);
|
|
183
183
|
const originalObject = getJsonOmit(opts, original);
|
|
184
|
-
if (
|
|
184
|
+
if (isEmpty(originalObject) || isEmpty(currentObject)) return;
|
|
185
185
|
const patch = jsonpatch.compare(originalObject, currentObject, true);
|
|
186
|
-
if (
|
|
186
|
+
if (isEmpty(patch)) return;
|
|
187
187
|
emitEvent(context, opts.eventUpdated, { oldDoc: original, doc: current, patch });
|
|
188
188
|
if (history) {
|
|
189
189
|
let version = 0;
|
|
@@ -225,16 +225,16 @@ const deleteHooksInitialize = (schema, opts) => {
|
|
|
225
225
|
};
|
|
226
226
|
if (["remove", "deleteMany"].includes(this._context.op) && !options.single) {
|
|
227
227
|
const docs = await model.find(filter).lean().exec();
|
|
228
|
-
if (!
|
|
228
|
+
if (!isEmpty(docs)) {
|
|
229
229
|
this._context.deletedDocs = docs;
|
|
230
230
|
}
|
|
231
231
|
} else {
|
|
232
232
|
const doc = await model.findOne(filter).lean().exec();
|
|
233
|
-
if (!
|
|
233
|
+
if (!isEmpty(doc)) {
|
|
234
234
|
this._context.deletedDocs = [doc];
|
|
235
235
|
}
|
|
236
236
|
}
|
|
237
|
-
if (opts.preDelete &&
|
|
237
|
+
if (opts.preDelete && isArray(this._context.deletedDocs) && !isEmpty(this._context.deletedDocs)) {
|
|
238
238
|
await opts.preDelete(this._context.deletedDocs);
|
|
239
239
|
}
|
|
240
240
|
});
|
|
@@ -270,7 +270,7 @@ const saveHooksInitialize = (schema, opts) => {
|
|
|
270
270
|
const updateMethods = ["update", "updateOne", "replaceOne", "updateMany", "findOneAndUpdate", "findOneAndReplace", "findByIdAndUpdate"];
|
|
271
271
|
const assignUpdate = (document, update, commands) => {
|
|
272
272
|
let updated = assign(document.toObject(toObjectOptions), update);
|
|
273
|
-
|
|
273
|
+
forEach(commands, (command) => {
|
|
274
274
|
try {
|
|
275
275
|
updated = assign(updated, command);
|
|
276
276
|
} catch {
|
|
@@ -283,11 +283,11 @@ const assignUpdate = (document, update, commands) => {
|
|
|
283
283
|
const splitUpdateAndCommands = (updateQuery) => {
|
|
284
284
|
let update = {};
|
|
285
285
|
const commands = [];
|
|
286
|
-
if (!
|
|
287
|
-
update =
|
|
288
|
-
const keysWithDollarSign =
|
|
289
|
-
if (!
|
|
290
|
-
|
|
286
|
+
if (!isEmpty(updateQuery) && !isArray(updateQuery) && isObjectLike(updateQuery)) {
|
|
287
|
+
update = cloneDeep(updateQuery);
|
|
288
|
+
const keysWithDollarSign = keys(update).filter((key) => key.startsWith("$"));
|
|
289
|
+
if (!isEmpty(keysWithDollarSign)) {
|
|
290
|
+
forEach(keysWithDollarSign, (key) => {
|
|
291
291
|
commands.push({ [key]: update[key] });
|
|
292
292
|
delete update[key];
|
|
293
293
|
});
|
|
@@ -328,13 +328,13 @@ const updateHooksInitialize = (schema, opts) => {
|
|
|
328
328
|
let current = null;
|
|
329
329
|
const filter = this.getFilter();
|
|
330
330
|
const combined = assignUpdate(model.hydrate({}), update, commands);
|
|
331
|
-
if (!
|
|
331
|
+
if (!isEmpty(update) && !current) {
|
|
332
332
|
current = await model.findOne(update).sort("desc").lean().exec();
|
|
333
333
|
}
|
|
334
|
-
if (!
|
|
334
|
+
if (!isEmpty(combined) && !current) {
|
|
335
335
|
current = await model.findOne(combined).sort("desc").lean().exec();
|
|
336
336
|
}
|
|
337
|
-
if (!
|
|
337
|
+
if (!isEmpty(filter) && !current) {
|
|
338
338
|
console.log("filter", filter);
|
|
339
339
|
current = await model.findOne(filter).sort("desc").lean().exec();
|
|
340
340
|
}
|
|
@@ -353,7 +353,6 @@ if (isMongoose6) {
|
|
|
353
353
|
}
|
|
354
354
|
|
|
355
355
|
const remove = isMongooseLessThan7 ? "remove" : "deleteOne";
|
|
356
|
-
const patchEventEmitter = em;
|
|
357
356
|
const patchHistoryPlugin = function plugin(schema, opts) {
|
|
358
357
|
saveHooksInitialize(schema, opts);
|
|
359
358
|
updateHooksInitialize(schema, opts);
|
|
@@ -370,7 +369,7 @@ const patchHistoryPlugin = function plugin(schema, opts) {
|
|
|
370
369
|
if (isMongooseLessThan8) {
|
|
371
370
|
schema.pre(remove, { document: true, query: false }, async function() {
|
|
372
371
|
const original = this.toObject(toObjectOptions);
|
|
373
|
-
if (opts.preDelete && !
|
|
372
|
+
if (opts.preDelete && !isEmpty(original)) {
|
|
374
373
|
await opts.preDelete([original]);
|
|
375
374
|
}
|
|
376
375
|
});
|
|
@@ -388,4 +387,4 @@ const patchHistoryPlugin = function plugin(schema, opts) {
|
|
|
388
387
|
}
|
|
389
388
|
};
|
|
390
389
|
|
|
391
|
-
export { patchEventEmitter, patchHistoryPlugin, setPatchHistoryTTL };
|
|
390
|
+
export { em as patchEventEmitter, patchHistoryPlugin, setPatchHistoryTTL };
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ts-patch-mongoose",
|
|
3
|
-
"version": "2.9.
|
|
3
|
+
"version": "2.9.3",
|
|
4
4
|
"description": "Patch history & events for mongoose models",
|
|
5
5
|
"author": "ilovepixelart",
|
|
6
6
|
"license": "MIT",
|
|
@@ -71,27 +71,27 @@
|
|
|
71
71
|
"release": "npm install && npm run biome && npm run type:check && npm run build && np"
|
|
72
72
|
},
|
|
73
73
|
"dependencies": {
|
|
74
|
-
"@types/lodash": "4.17.
|
|
74
|
+
"@types/lodash-es": "4.17.12",
|
|
75
75
|
"@types/ms": "2.1.0",
|
|
76
|
-
"@types/semver": "7.7.
|
|
76
|
+
"@types/semver": "7.7.1",
|
|
77
77
|
"fast-json-patch": "3.1.1",
|
|
78
|
-
"lodash": "4.17.21",
|
|
78
|
+
"lodash-es": "4.17.21",
|
|
79
79
|
"ms": "2.1.3",
|
|
80
80
|
"omit-deep": "0.3.0",
|
|
81
81
|
"power-assign": "0.2.10",
|
|
82
|
-
"semver": "7.7.
|
|
82
|
+
"semver": "7.7.3"
|
|
83
83
|
},
|
|
84
84
|
"devDependencies": {
|
|
85
|
-
"@biomejs/biome": "2.2.
|
|
86
|
-
"@types/node": "24.
|
|
87
|
-
"@vitest/coverage-v8": "
|
|
88
|
-
"mongodb-memory-server": "10.2.
|
|
89
|
-
"mongoose": "8.
|
|
85
|
+
"@biomejs/biome": "2.2.7",
|
|
86
|
+
"@types/node": "24.9.1",
|
|
87
|
+
"@vitest/coverage-v8": "4.0.2",
|
|
88
|
+
"mongodb-memory-server": "10.2.3",
|
|
89
|
+
"mongoose": "8.19.2",
|
|
90
90
|
"open-cli": "8.0.0",
|
|
91
|
-
"pkgroll": "2.
|
|
91
|
+
"pkgroll": "2.20.1",
|
|
92
92
|
"simple-git-hooks": "2.13.1",
|
|
93
|
-
"typescript": "5.9.
|
|
94
|
-
"vitest": "
|
|
93
|
+
"typescript": "5.9.3",
|
|
94
|
+
"vitest": "4.0.2"
|
|
95
95
|
},
|
|
96
96
|
"peerDependencies": {
|
|
97
97
|
"mongoose": ">=6.6.0 < 9"
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { isArray, isEmpty } from 'lodash-es'
|
|
2
2
|
import { isHookIgnored } from '../helpers'
|
|
3
3
|
import { deletePatch } from '../patch'
|
|
4
4
|
|
|
@@ -25,17 +25,17 @@ export const deleteHooksInitialize = <T>(schema: Schema<T>, opts: PluginOptions<
|
|
|
25
25
|
|
|
26
26
|
if (['remove', 'deleteMany'].includes(this._context.op) && !options.single) {
|
|
27
27
|
const docs = await model.find<T>(filter).lean().exec()
|
|
28
|
-
if (!
|
|
28
|
+
if (!isEmpty(docs)) {
|
|
29
29
|
this._context.deletedDocs = docs as HydratedDocument<T>[]
|
|
30
30
|
}
|
|
31
31
|
} else {
|
|
32
32
|
const doc = await model.findOne<T>(filter).lean().exec()
|
|
33
|
-
if (!
|
|
33
|
+
if (!isEmpty(doc)) {
|
|
34
34
|
this._context.deletedDocs = [doc] as HydratedDocument<T>[]
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
-
if (opts.preDelete &&
|
|
38
|
+
if (opts.preDelete && isArray(this._context.deletedDocs) && !isEmpty(this._context.deletedDocs)) {
|
|
39
39
|
await opts.preDelete(this._context.deletedDocs)
|
|
40
40
|
}
|
|
41
41
|
})
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { cloneDeep, forEach, isArray, isEmpty, isObjectLike, keys } from 'lodash-es'
|
|
2
2
|
import { assign } from 'power-assign'
|
|
3
3
|
import { isHookIgnored, toObjectOptions } from '../helpers'
|
|
4
4
|
import { createPatch, updatePatch } from '../patch'
|
|
@@ -11,7 +11,7 @@ const updateMethods = ['update', 'updateOne', 'replaceOne', 'updateMany', 'findO
|
|
|
11
11
|
export const assignUpdate = <T>(document: HydratedDocument<T>, update: UpdateQuery<T>, commands: Record<string, unknown>[]): HydratedDocument<T> => {
|
|
12
12
|
let updated = assign(document.toObject(toObjectOptions), update)
|
|
13
13
|
// Try catch not working for of loop, keep it as is
|
|
14
|
-
|
|
14
|
+
forEach(commands, (command) => {
|
|
15
15
|
try {
|
|
16
16
|
updated = assign(updated, command)
|
|
17
17
|
} catch {
|
|
@@ -28,11 +28,11 @@ export const splitUpdateAndCommands = <T>(updateQuery: UpdateWithAggregationPipe
|
|
|
28
28
|
let update: UpdateQuery<T> = {}
|
|
29
29
|
const commands: Record<string, unknown>[] = []
|
|
30
30
|
|
|
31
|
-
if (!
|
|
32
|
-
update =
|
|
33
|
-
const keysWithDollarSign =
|
|
34
|
-
if (!
|
|
35
|
-
|
|
31
|
+
if (!isEmpty(updateQuery) && !isArray(updateQuery) && isObjectLike(updateQuery)) {
|
|
32
|
+
update = cloneDeep(updateQuery)
|
|
33
|
+
const keysWithDollarSign = keys(update).filter((key) => key.startsWith('$'))
|
|
34
|
+
if (!isEmpty(keysWithDollarSign)) {
|
|
35
|
+
forEach(keysWithDollarSign, (key) => {
|
|
36
36
|
commands.push({ [key]: update[key] as unknown })
|
|
37
37
|
delete update[key]
|
|
38
38
|
})
|
|
@@ -83,15 +83,15 @@ export const updateHooksInitialize = <T>(schema: Schema<T>, opts: PluginOptions<
|
|
|
83
83
|
let current: HydratedDocument<T> | null = null
|
|
84
84
|
const filter = this.getFilter()
|
|
85
85
|
const combined = assignUpdate(model.hydrate({}), update, commands)
|
|
86
|
-
if (!
|
|
86
|
+
if (!isEmpty(update) && !current) {
|
|
87
87
|
current = (await model.findOne(update).sort('desc').lean().exec()) as HydratedDocument<T>
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
if (!
|
|
90
|
+
if (!isEmpty(combined) && !current) {
|
|
91
91
|
current = (await model.findOne(combined).sort('desc').lean().exec()) as HydratedDocument<T>
|
|
92
92
|
}
|
|
93
93
|
|
|
94
|
-
if (!
|
|
94
|
+
if (!isEmpty(filter) && !current) {
|
|
95
95
|
console.log('filter', filter)
|
|
96
96
|
current = (await model.findOne(filter).sort('desc').lean().exec()) as HydratedDocument<T>
|
|
97
97
|
}
|
package/src/index.ts
CHANGED
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
import
|
|
2
|
-
import em from './em'
|
|
1
|
+
import { isEmpty } from 'lodash-es'
|
|
3
2
|
import { toObjectOptions } from './helpers'
|
|
4
3
|
import { deleteHooksInitialize } from './hooks/delete-hooks'
|
|
5
4
|
import { saveHooksInitialize } from './hooks/save-hooks'
|
|
@@ -15,8 +14,7 @@ const remove = isMongooseLessThan7 ? 'remove' : 'deleteOne'
|
|
|
15
14
|
/**
|
|
16
15
|
* @description Event emitter for patch operations.
|
|
17
16
|
*/
|
|
18
|
-
export
|
|
19
|
-
|
|
17
|
+
export { default as patchEventEmitter } from './em'
|
|
20
18
|
export { setPatchHistoryTTL } from './helpers'
|
|
21
19
|
export * from './types'
|
|
22
20
|
|
|
@@ -65,7 +63,7 @@ export const patchHistoryPlugin = function plugin<T>(schema: Schema<T>, opts: Pl
|
|
|
65
63
|
// @ts-expect-error - Mongoose 7 and below
|
|
66
64
|
const original = this.toObject(toObjectOptions) as HydratedDocument<T>
|
|
67
65
|
|
|
68
|
-
if (opts.preDelete && !
|
|
66
|
+
if (opts.preDelete && !isEmpty(original)) {
|
|
69
67
|
await opts.preDelete([original])
|
|
70
68
|
}
|
|
71
69
|
})
|
package/src/patch.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import jsonpatch from 'fast-json-patch'
|
|
2
|
-
import
|
|
2
|
+
import { chunk, isEmpty, isFunction } from 'lodash-es'
|
|
3
3
|
import omit from 'omit-deep'
|
|
4
4
|
import em from './em'
|
|
5
5
|
import { HistoryModel } from './model'
|
|
@@ -23,28 +23,28 @@ export function getJsonOmit<T>(opts: PluginOptions<T>, doc: HydratedDocument<T>)
|
|
|
23
23
|
|
|
24
24
|
export function getObjectOmit<T>(opts: PluginOptions<T>, doc: HydratedDocument<T>): Partial<T> {
|
|
25
25
|
if (opts.omit) {
|
|
26
|
-
return omit(
|
|
26
|
+
return omit(isFunction(doc?.toObject) ? doc.toObject() : doc, opts.omit)
|
|
27
27
|
}
|
|
28
28
|
|
|
29
29
|
return doc
|
|
30
30
|
}
|
|
31
31
|
|
|
32
32
|
export async function getUser<T>(opts: PluginOptions<T>, doc: HydratedDocument<T>): Promise<User | undefined> {
|
|
33
|
-
if (
|
|
33
|
+
if (isFunction(opts.getUser)) {
|
|
34
34
|
return await opts.getUser(doc)
|
|
35
35
|
}
|
|
36
36
|
return undefined
|
|
37
37
|
}
|
|
38
38
|
|
|
39
39
|
export async function getReason<T>(opts: PluginOptions<T>, doc: HydratedDocument<T>): Promise<string | undefined> {
|
|
40
|
-
if (
|
|
40
|
+
if (isFunction(opts.getReason)) {
|
|
41
41
|
return await opts.getReason(doc)
|
|
42
42
|
}
|
|
43
43
|
return undefined
|
|
44
44
|
}
|
|
45
45
|
|
|
46
46
|
export async function getMetadata<T>(opts: PluginOptions<T>, doc: HydratedDocument<T>): Promise<Metadata | undefined> {
|
|
47
|
-
if (
|
|
47
|
+
if (isFunction(opts.getMetadata)) {
|
|
48
48
|
return await opts.getMetadata(doc)
|
|
49
49
|
}
|
|
50
50
|
return undefined
|
|
@@ -72,10 +72,10 @@ export async function bulkPatch<T>(opts: PluginOptions<T>, context: PatchContext
|
|
|
72
72
|
const docs = context[docsKey]
|
|
73
73
|
const key = eventKey === 'eventCreated' ? 'doc' : 'oldDoc'
|
|
74
74
|
|
|
75
|
-
if (
|
|
75
|
+
if (isEmpty(docs) || (!event && !history)) return
|
|
76
76
|
|
|
77
|
-
const chunks =
|
|
78
|
-
for
|
|
77
|
+
const chunks = chunk(docs, 1000)
|
|
78
|
+
for (const chunk of chunks) {
|
|
79
79
|
const bulk = []
|
|
80
80
|
|
|
81
81
|
for (const doc of chunk) {
|
|
@@ -101,7 +101,7 @@ export async function bulkPatch<T>(opts: PluginOptions<T>, context: PatchContext
|
|
|
101
101
|
}
|
|
102
102
|
}
|
|
103
103
|
|
|
104
|
-
if (history && !
|
|
104
|
+
if (history && !isEmpty(bulk)) {
|
|
105
105
|
await HistoryModel.bulkWrite(bulk, { ordered: false }).catch((error: MongooseError) => {
|
|
106
106
|
console.error(error.message)
|
|
107
107
|
})
|
|
@@ -118,10 +118,10 @@ export async function updatePatch<T>(opts: PluginOptions<T>, context: PatchConte
|
|
|
118
118
|
|
|
119
119
|
const currentObject = getJsonOmit(opts, current)
|
|
120
120
|
const originalObject = getJsonOmit(opts, original)
|
|
121
|
-
if (
|
|
121
|
+
if (isEmpty(originalObject) || isEmpty(currentObject)) return
|
|
122
122
|
|
|
123
123
|
const patch = jsonpatch.compare(originalObject, currentObject, true)
|
|
124
|
-
if (
|
|
124
|
+
if (isEmpty(patch)) return
|
|
125
125
|
|
|
126
126
|
emitEvent(context, opts.eventUpdated, { oldDoc: original, doc: current, patch })
|
|
127
127
|
|
package/tests/patch.test.ts
CHANGED
package/vite.config.mts
CHANGED
|
@@ -6,8 +6,18 @@ export default defineConfig({
|
|
|
6
6
|
name: 'node',
|
|
7
7
|
environment: 'node',
|
|
8
8
|
coverage: {
|
|
9
|
-
|
|
9
|
+
provider: 'v8',
|
|
10
|
+
reporter: ['text', 'json', 'html', 'lcov'],
|
|
10
11
|
include: ['src/**/*.ts'],
|
|
12
|
+
exclude: [
|
|
13
|
+
'node_modules/**',
|
|
14
|
+
'dist/**',
|
|
15
|
+
'coverage/**',
|
|
16
|
+
'**/*.d.ts',
|
|
17
|
+
'**/*.config.*',
|
|
18
|
+
'**/tests/**',
|
|
19
|
+
'examples/**',
|
|
20
|
+
],
|
|
11
21
|
},
|
|
12
22
|
},
|
|
13
23
|
})
|