ts-patch-mongoose 2.6.7 → 2.6.8

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 CHANGED
@@ -105,9 +105,15 @@ import { Schema, model } from 'mongoose'
105
105
  import type { HydratedDocument, Types } from 'mongoose'
106
106
  import type IBook from '../interfaces/IBook'
107
107
 
108
- import { patchHistoryPlugin } from 'ts-patch-mongoose'
108
+ import { patchHistoryPlugin, setPatchHistoryTTL } from 'ts-patch-mongoose'
109
109
  import { BOOK_CREATED, BOOK_UPDATED, BOOK_DELETED } from '../constants/events'
110
110
 
111
+ // You can set patch history TTL in plain english or in milliseconds as you wish.
112
+ // This will determine how long you want to keep patch history.
113
+ // You don't need to use this global config in case you want to keep patch history forever.
114
+ // Execute this method after you connected to you database somewhere in your application.
115
+ setPatchHistoryTTL('1 month')
116
+
111
117
  const BookSchema = new Schema<IBook>({
112
118
  name: {
113
119
  title: String,
@@ -1,6 +1,9 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.toObjectOptions = exports.isHookIgnored = void 0;
3
+ exports.setPatchHistoryTTL = exports.toObjectOptions = exports.isHookIgnored = void 0;
4
+ const tslib_1 = require("tslib");
5
+ const ms_1 = tslib_1.__importDefault(require("ms"));
6
+ const History_1 = tslib_1.__importDefault(require("./models/History"));
4
7
  const isHookIgnored = (options) => {
5
8
  return options.ignoreHook === true || (options.ignoreEvent === true && options.ignorePatchHistory === true);
6
9
  };
@@ -9,4 +12,32 @@ exports.toObjectOptions = {
9
12
  depopulate: true,
10
13
  virtuals: false,
11
14
  };
15
+ const setPatchHistoryTTL = async (ttl) => {
16
+ const name = 'createdAt_1_TTL';
17
+ try {
18
+ const indexes = await History_1.default.collection.indexes();
19
+ const existingIndex = indexes?.find((index) => index.name === name);
20
+ if (!ttl && existingIndex) {
21
+ await History_1.default.collection.dropIndex(name);
22
+ return;
23
+ }
24
+ const milliseconds = (0, ms_1.default)(ttl);
25
+ if (milliseconds < 1000 && existingIndex) {
26
+ await History_1.default.collection.dropIndex(name);
27
+ return;
28
+ }
29
+ const expireAfterSeconds = milliseconds / 1000;
30
+ if (existingIndex && existingIndex.expireAfterSeconds === expireAfterSeconds) {
31
+ return;
32
+ }
33
+ if (existingIndex) {
34
+ await History_1.default.collection.dropIndex(name);
35
+ }
36
+ await History_1.default.collection.createIndex({ createdAt: 1 }, { expireAfterSeconds, name });
37
+ }
38
+ catch (err) {
39
+ console.error("Couldn't create or update index for history collection", err);
40
+ }
41
+ };
42
+ exports.setPatchHistoryTTL = setPatchHistoryTTL;
12
43
  //# sourceMappingURL=helpers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/helpers.ts"],"names":[],"mappings":";;;AAEO,MAAM,aAAa,GAAG,CAAI,OAAwB,EAAW,EAAE;IACpE,OAAO,OAAO,CAAC,UAAU,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW,KAAK,IAAI,IAAI,OAAO,CAAC,kBAAkB,KAAK,IAAI,CAAC,CAAA;AAC7G,CAAC,CAAA;AAFY,QAAA,aAAa,iBAEzB;AAEY,QAAA,eAAe,GAAoB;IAC9C,UAAU,EAAE,IAAI;IAChB,QAAQ,EAAE,KAAK;CAChB,CAAA"}
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/helpers.ts"],"names":[],"mappings":";;;;AAAA,oDAAmB;AAEnB,uEAAsC;AAI/B,MAAM,aAAa,GAAG,CAAI,OAAwB,EAAW,EAAE;IACpE,OAAO,OAAO,CAAC,UAAU,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW,KAAK,IAAI,IAAI,OAAO,CAAC,kBAAkB,KAAK,IAAI,CAAC,CAAA;AAC7G,CAAC,CAAA;AAFY,QAAA,aAAa,iBAEzB;AAEY,QAAA,eAAe,GAAoB;IAC9C,UAAU,EAAE,IAAI;IAChB,QAAQ,EAAE,KAAK;CAChB,CAAA;AAEM,MAAM,kBAAkB,GAAG,KAAK,EAAE,GAAoB,EAAiB,EAAE;IAC9E,MAAM,IAAI,GAAG,iBAAiB,CAAA;IAC9B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,iBAAO,CAAC,UAAU,CAAC,OAAO,EAAE,CAAA;QAClD,MAAM,aAAa,GAAG,OAAO,EAAE,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA;QAGnE,IAAI,CAAC,GAAG,IAAI,aAAa,EAAE,CAAC;YAC1B,MAAM,iBAAO,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;YACxC,OAAM;QACR,CAAC;QAED,MAAM,YAAY,GAAG,IAAA,YAAE,EAAC,GAAa,CAAC,CAAA;QAGtC,IAAI,YAAY,GAAG,IAAI,IAAI,aAAa,EAAE,CAAC;YACzC,MAAM,iBAAO,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;YACxC,OAAM;QACR,CAAC;QAED,MAAM,kBAAkB,GAAG,YAAY,GAAG,IAAI,CAAA;QAE9C,IAAI,aAAa,IAAI,aAAa,CAAC,kBAAkB,KAAK,kBAAkB,EAAE,CAAC;YAE7E,OAAM;QACR,CAAC;QAED,IAAI,aAAa,EAAE,CAAC;YAElB,MAAM,iBAAO,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;QAC1C,CAAC;QAGD,MAAM,iBAAO,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAA;IACtF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,wDAAwD,EAAE,GAAG,CAAC,CAAA;IAC9E,CAAC;AACH,CAAC,CAAA;AArCY,QAAA,kBAAkB,sBAqC9B"}
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.patchHistoryPlugin = exports.patchEventEmitter = void 0;
3
+ exports.patchHistoryPlugin = exports.setPatchHistoryTTL = exports.patchEventEmitter = void 0;
4
4
  const tslib_1 = require("tslib");
5
5
  const lodash_1 = tslib_1.__importDefault(require("lodash"));
6
6
  const em_1 = tslib_1.__importDefault(require("./em"));
@@ -12,6 +12,8 @@ const save_hooks_1 = require("./hooks/save-hooks");
12
12
  const update_hooks_1 = require("./hooks/update-hooks");
13
13
  const remove = version_1.isMongooseLessThan7 ? 'remove' : 'deleteOne';
14
14
  exports.patchEventEmitter = em_1.default;
15
+ var helpers_2 = require("./helpers");
16
+ Object.defineProperty(exports, "setPatchHistoryTTL", { enumerable: true, get: function () { return helpers_2.setPatchHistoryTTL; } });
15
17
  const patchHistoryPlugin = function plugin(schema, opts) {
16
18
  (0, save_hooks_1.saveHooksInitialize)(schema, opts);
17
19
  (0, update_hooks_1.updateHooksInitialize)(schema, opts);
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/plugin.ts"],"names":[],"mappings":";;;;AAAA,4DAAsB;AACtB,sDAAqB;AAErB,uCAA2C;AAC3C,mCAAkD;AAClD,uCAAoE;AAEpE,uDAA4D;AAC5D,mDAAwD;AACxD,uDAA4D;AAM5D,MAAM,MAAM,GAAG,6BAAmB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAA;AAK9C,QAAA,iBAAiB,GAAG,YAAE,CAAA;AAQ5B,MAAM,kBAAkB,GAAG,SAAS,MAAM,CAAI,MAAiB,EAAE,IAAuB;IAE7F,IAAA,gCAAmB,EAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IACjC,IAAA,oCAAqB,EAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IACnC,IAAA,oCAAqB,EAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IAGnC,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,WAAW,IAAI;QAC5C,MAAM,OAAO,GAAG;YACd,EAAE,EAAE,QAAQ;YACZ,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,SAAS;YAC3C,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,UAAU,CAAC,cAAc;YACrE,WAAW,EAAE,IAAwC;SACtD,CAAA;QAED,MAAM,IAAA,mBAAW,EAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAClC,CAAC,CAAC,CAAA;IAIF,IAAI,6BAAmB,EAAE,CAAC;QAExB,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,KAAK;YAExD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,yBAAe,CAAwB,CAAA;YAEtE,IAAI,IAAI,CAAC,SAAS,IAAI,CAAC,gBAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC3C,MAAM,IAAI,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAA;YAClC,CAAC;QACH,CAAC,CAAC,CAAA;QAGF,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,KAAK;YACzD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,yBAAe,CAAwB,CAAA;YACtE,MAAM,KAAK,GAAG,IAAI,CAAC,WAAuB,CAAA;YAE1C,MAAM,OAAO,GAAgB;gBAC3B,EAAE,EAAE,QAAQ;gBACZ,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS;gBAC5C,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,KAAK,CAAC,UAAU,CAAC,cAAc;gBACtE,WAAW,EAAE,CAAC,QAAQ,CAAC;aACxB,CAAA;YAED,MAAM,IAAA,mBAAW,EAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QAClC,CAAC,CAAC,CAAA;IACJ,CAAC;AACH,CAAC,CAAA;AA9CY,QAAA,kBAAkB,sBA8C9B"}
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/plugin.ts"],"names":[],"mappings":";;;;AAAA,4DAAsB;AACtB,sDAAqB;AAErB,uCAA2C;AAC3C,mCAAkD;AAClD,uCAAoE;AAEpE,uDAA4D;AAC5D,mDAAwD;AACxD,uDAA4D;AAM5D,MAAM,MAAM,GAAG,6BAAmB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAA;AAK9C,QAAA,iBAAiB,GAAG,YAAE,CAAA;AAEnC,qCAA8C;AAArC,6GAAA,kBAAkB,OAAA;AAQpB,MAAM,kBAAkB,GAAG,SAAS,MAAM,CAAI,MAAiB,EAAE,IAAuB;IAE7F,IAAA,gCAAmB,EAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IACjC,IAAA,oCAAqB,EAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IACnC,IAAA,oCAAqB,EAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IAGnC,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,WAAW,IAAI;QAC5C,MAAM,OAAO,GAAG;YACd,EAAE,EAAE,QAAQ;YACZ,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,SAAS;YAC3C,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,UAAU,CAAC,cAAc;YACrE,WAAW,EAAE,IAAwC;SACtD,CAAA;QAED,MAAM,IAAA,mBAAW,EAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAClC,CAAC,CAAC,CAAA;IAIF,IAAI,6BAAmB,EAAE,CAAC;QAExB,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,KAAK;YAExD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,yBAAe,CAAwB,CAAA;YAEtE,IAAI,IAAI,CAAC,SAAS,IAAI,CAAC,gBAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC3C,MAAM,IAAI,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAA;YAClC,CAAC;QACH,CAAC,CAAC,CAAA;QAGF,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,KAAK;YACzD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,yBAAe,CAAwB,CAAA;YACtE,MAAM,KAAK,GAAG,IAAI,CAAC,WAAuB,CAAA;YAE1C,MAAM,OAAO,GAAgB;gBAC3B,EAAE,EAAE,QAAQ;gBACZ,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS;gBAC5C,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,KAAK,CAAC,UAAU,CAAC,cAAc;gBACtE,WAAW,EAAE,CAAC,QAAQ,CAAC;aACxB,CAAA;YAED,MAAM,IAAA,mBAAW,EAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QAClC,CAAC,CAAC,CAAA;IACJ,CAAC;AACH,CAAC,CAAA;AA9CY,QAAA,kBAAkB,sBA8C9B"}
@@ -1,4 +1,5 @@
1
1
  import type { QueryOptions, ToObjectOptions } from 'mongoose';
2
2
  export declare const isHookIgnored: <T>(options: QueryOptions<T>) => boolean;
3
3
  export declare const toObjectOptions: ToObjectOptions;
4
+ export declare const setPatchHistoryTTL: (ttl: number | string) => Promise<void>;
4
5
  //# sourceMappingURL=helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAE7D,eAAO,MAAM,aAAa,GAAI,CAAC,WAAW,YAAY,CAAC,CAAC,CAAC,KAAG,OAE3D,CAAA;AAED,eAAO,MAAM,eAAe,EAAE,eAG7B,CAAA"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/helpers.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAE7D,eAAO,MAAM,aAAa,GAAI,CAAC,WAAW,YAAY,CAAC,CAAC,CAAC,KAAG,OAE3D,CAAA;AAED,eAAO,MAAM,eAAe,EAAE,eAG7B,CAAA;AAED,eAAO,MAAM,kBAAkB,QAAe,MAAM,GAAG,MAAM,KAAG,OAAO,CAAC,IAAI,CAqC3E,CAAA"}
@@ -18,5 +18,6 @@ export declare const patchEventEmitter: {
18
18
  prependOnceListener<K>(eventName: string | symbol, listener: (...args: any[]) => void): any;
19
19
  eventNames(): (string | symbol)[];
20
20
  };
21
+ export { setPatchHistoryTTL } from './helpers';
21
22
  export declare const patchHistoryPlugin: <T>(schema: Schema<T>, opts: IPluginOptions<T>) => void;
22
23
  //# sourceMappingURL=plugin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/plugin.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAA2B,MAAM,EAAE,MAAM,UAAU,CAAA;AAE/D,OAAO,KAAK,cAAc,MAAM,6BAA6B,CAAA;AAO7D,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;CAAK,CAAA;AAQnC,eAAO,MAAM,kBAAkB,GAAmB,CAAC,UAAU,MAAM,CAAC,CAAC,CAAC,QAAQ,cAAc,CAAC,CAAC,CAAC,KAAG,IA8CjG,CAAA"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/plugin.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAA2B,MAAM,EAAE,MAAM,UAAU,CAAA;AAE/D,OAAO,KAAK,cAAc,MAAM,6BAA6B,CAAA;AAO7D,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;CAAK,CAAA;AAEnC,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA;AAQ9C,eAAO,MAAM,kBAAkB,GAAmB,CAAC,UAAU,MAAM,CAAC,CAAC,CAAC,QAAQ,cAAc,CAAC,CAAC,CAAC,KAAG,IA8CjG,CAAA"}
@@ -1,3 +1,5 @@
1
+ import ms from 'ms';
2
+ import History from './models/History.js';
1
3
  export const isHookIgnored = (options) => {
2
4
  return options.ignoreHook === true || (options.ignoreEvent === true && options.ignorePatchHistory === true);
3
5
  };
@@ -5,4 +7,31 @@ export const toObjectOptions = {
5
7
  depopulate: true,
6
8
  virtuals: false,
7
9
  };
10
+ export const setPatchHistoryTTL = async (ttl) => {
11
+ const name = 'createdAt_1_TTL';
12
+ try {
13
+ const indexes = await History.collection.indexes();
14
+ const existingIndex = indexes?.find((index) => index.name === name);
15
+ if (!ttl && existingIndex) {
16
+ await History.collection.dropIndex(name);
17
+ return;
18
+ }
19
+ const milliseconds = ms(ttl);
20
+ if (milliseconds < 1000 && existingIndex) {
21
+ await History.collection.dropIndex(name);
22
+ return;
23
+ }
24
+ const expireAfterSeconds = milliseconds / 1000;
25
+ if (existingIndex && existingIndex.expireAfterSeconds === expireAfterSeconds) {
26
+ return;
27
+ }
28
+ if (existingIndex) {
29
+ await History.collection.dropIndex(name);
30
+ }
31
+ await History.collection.createIndex({ createdAt: 1 }, { expireAfterSeconds, name });
32
+ }
33
+ catch (err) {
34
+ console.error("Couldn't create or update index for history collection", err);
35
+ }
36
+ };
8
37
  //# sourceMappingURL=helpers.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/helpers.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,aAAa,GAAG,CAAI,OAAwB,EAAW,EAAE;IACpE,OAAO,OAAO,CAAC,UAAU,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW,KAAK,IAAI,IAAI,OAAO,CAAC,kBAAkB,KAAK,IAAI,CAAC,CAAA;AAC7G,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,eAAe,GAAoB;IAC9C,UAAU,EAAE,IAAI;IAChB,QAAQ,EAAE,KAAK;CAChB,CAAA"}
1
+ {"version":3,"file":"helpers.js","sourceRoot":"","sources":["../../src/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,IAAI,CAAA;AAEnB,OAAO,OAAO,MAAM,kBAAkB,CAAA;AAItC,MAAM,CAAC,MAAM,aAAa,GAAG,CAAI,OAAwB,EAAW,EAAE;IACpE,OAAO,OAAO,CAAC,UAAU,KAAK,IAAI,IAAI,CAAC,OAAO,CAAC,WAAW,KAAK,IAAI,IAAI,OAAO,CAAC,kBAAkB,KAAK,IAAI,CAAC,CAAA;AAC7G,CAAC,CAAA;AAED,MAAM,CAAC,MAAM,eAAe,GAAoB;IAC9C,UAAU,EAAE,IAAI;IAChB,QAAQ,EAAE,KAAK;CAChB,CAAA;AAED,MAAM,CAAC,MAAM,kBAAkB,GAAG,KAAK,EAAE,GAAoB,EAAiB,EAAE;IAC9E,MAAM,IAAI,GAAG,iBAAiB,CAAA;IAC9B,IAAI,CAAC;QACH,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,UAAU,CAAC,OAAO,EAAE,CAAA;QAClD,MAAM,aAAa,GAAG,OAAO,EAAE,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,KAAK,IAAI,CAAC,CAAA;QAGnE,IAAI,CAAC,GAAG,IAAI,aAAa,EAAE,CAAC;YAC1B,MAAM,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;YACxC,OAAM;QACR,CAAC;QAED,MAAM,YAAY,GAAG,EAAE,CAAC,GAAa,CAAC,CAAA;QAGtC,IAAI,YAAY,GAAG,IAAI,IAAI,aAAa,EAAE,CAAC;YACzC,MAAM,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;YACxC,OAAM;QACR,CAAC;QAED,MAAM,kBAAkB,GAAG,YAAY,GAAG,IAAI,CAAA;QAE9C,IAAI,aAAa,IAAI,aAAa,CAAC,kBAAkB,KAAK,kBAAkB,EAAE,CAAC;YAE7E,OAAM;QACR,CAAC;QAED,IAAI,aAAa,EAAE,CAAC;YAElB,MAAM,OAAO,CAAC,UAAU,CAAC,SAAS,CAAC,IAAI,CAAC,CAAA;QAC1C,CAAC;QAGD,MAAM,OAAO,CAAC,UAAU,CAAC,WAAW,CAAC,EAAE,SAAS,EAAE,CAAC,EAAE,EAAE,EAAE,kBAAkB,EAAE,IAAI,EAAE,CAAC,CAAA;IACtF,CAAC;IAAC,OAAO,GAAG,EAAE,CAAC;QACb,OAAO,CAAC,KAAK,CAAC,wDAAwD,EAAE,GAAG,CAAC,CAAA;IAC9E,CAAC;AACH,CAAC,CAAA"}
@@ -8,6 +8,7 @@ import { saveHooksInitialize } from './hooks/save-hooks.js';
8
8
  import { updateHooksInitialize } from './hooks/update-hooks.js';
9
9
  const remove = isMongooseLessThan7 ? 'remove' : 'deleteOne';
10
10
  export const patchEventEmitter = em;
11
+ export { setPatchHistoryTTL } from './helpers';
11
12
  export const patchHistoryPlugin = function plugin(schema, opts) {
12
13
  saveHooksInitialize(schema, opts);
13
14
  updateHooksInitialize(schema, opts);
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,QAAQ,CAAA;AACtB,OAAO,EAAE,MAAM,MAAM,CAAA;AAErB,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAClD,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AAEpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAM5D,MAAM,MAAM,GAAG,mBAAmB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAA;AAK3D,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAA;AAQnC,MAAM,CAAC,MAAM,kBAAkB,GAAG,SAAS,MAAM,CAAI,MAAiB,EAAE,IAAuB;IAE7F,mBAAmB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IACjC,qBAAqB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IACnC,qBAAqB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IAGnC,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,WAAW,IAAI;QAC5C,MAAM,OAAO,GAAG;YACd,EAAE,EAAE,QAAQ;YACZ,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,SAAS;YAC3C,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,UAAU,CAAC,cAAc;YACrE,WAAW,EAAE,IAAwC;SACtD,CAAA;QAED,MAAM,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAClC,CAAC,CAAC,CAAA;IAIF,IAAI,mBAAmB,EAAE,CAAC;QAExB,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,KAAK;YAExD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAwB,CAAA;YAEtE,IAAI,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC3C,MAAM,IAAI,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAA;YAClC,CAAC;QACH,CAAC,CAAC,CAAA;QAGF,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,KAAK;YACzD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAwB,CAAA;YACtE,MAAM,KAAK,GAAG,IAAI,CAAC,WAAuB,CAAA;YAE1C,MAAM,OAAO,GAAgB;gBAC3B,EAAE,EAAE,QAAQ;gBACZ,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS;gBAC5C,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,KAAK,CAAC,UAAU,CAAC,cAAc;gBACtE,WAAW,EAAE,CAAC,QAAQ,CAAC;aACxB,CAAA;YAED,MAAM,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QAClC,CAAC,CAAC,CAAA;IACJ,CAAC;AACH,CAAC,CAAA"}
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/plugin.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,QAAQ,CAAA;AACtB,OAAO,EAAE,MAAM,MAAM,CAAA;AAErB,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAA;AAC3C,OAAO,EAAE,WAAW,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AAClD,OAAO,EAAE,mBAAmB,EAAE,mBAAmB,EAAE,MAAM,WAAW,CAAA;AAEpE,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAC5D,OAAO,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAA;AACxD,OAAO,EAAE,qBAAqB,EAAE,MAAM,sBAAsB,CAAA;AAM5D,MAAM,MAAM,GAAG,mBAAmB,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,WAAW,CAAA;AAK3D,MAAM,CAAC,MAAM,iBAAiB,GAAG,EAAE,CAAA;AAEnC,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA;AAQ9C,MAAM,CAAC,MAAM,kBAAkB,GAAG,SAAS,MAAM,CAAI,MAAiB,EAAE,IAAuB;IAE7F,mBAAmB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IACjC,qBAAqB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IACnC,qBAAqB,CAAC,MAAM,EAAE,IAAI,CAAC,CAAA;IAGnC,MAAM,CAAC,IAAI,CAAC,YAAY,EAAE,KAAK,WAAW,IAAI;QAC5C,MAAM,OAAO,GAAG;YACd,EAAE,EAAE,QAAQ;YACZ,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,IAAI,CAAC,SAAS;YAC3C,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,IAAI,CAAC,UAAU,CAAC,cAAc;YACrE,WAAW,EAAE,IAAwC;SACtD,CAAA;QAED,MAAM,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;IAClC,CAAC,CAAC,CAAA;IAIF,IAAI,mBAAmB,EAAE,CAAC;QAExB,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,KAAK;YAExD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAwB,CAAA;YAEtE,IAAI,IAAI,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC3C,MAAM,IAAI,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAA;YAClC,CAAC;QACH,CAAC,CAAC,CAAA;QAGF,MAAM,CAAC,IAAI,CAAC,MAAM,EAAE,EAAE,QAAQ,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,EAAE,EAAE,KAAK;YACzD,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAwB,CAAA;YACtE,MAAM,KAAK,GAAG,IAAI,CAAC,WAAuB,CAAA;YAE1C,MAAM,OAAO,GAAgB;gBAC3B,EAAE,EAAE,QAAQ;gBACZ,SAAS,EAAE,IAAI,CAAC,SAAS,IAAI,KAAK,CAAC,SAAS;gBAC5C,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,KAAK,CAAC,UAAU,CAAC,cAAc;gBACtE,WAAW,EAAE,CAAC,QAAQ,CAAC;aACxB,CAAA;YAED,MAAM,WAAW,CAAC,IAAI,EAAE,OAAO,CAAC,CAAA;QAClC,CAAC,CAAC,CAAA;IACJ,CAAC;AACH,CAAC,CAAA"}
@@ -1,4 +1,5 @@
1
1
  import type { QueryOptions, ToObjectOptions } from 'mongoose';
2
2
  export declare const isHookIgnored: <T>(options: QueryOptions<T>) => boolean;
3
3
  export declare const toObjectOptions: ToObjectOptions;
4
+ export declare const setPatchHistoryTTL: (ttl: number | string) => Promise<void>;
4
5
  //# sourceMappingURL=helpers.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAE7D,eAAO,MAAM,aAAa,GAAI,CAAC,WAAW,YAAY,CAAC,CAAC,CAAC,KAAG,OAE3D,CAAA;AAED,eAAO,MAAM,eAAe,EAAE,eAG7B,CAAA"}
1
+ {"version":3,"file":"helpers.d.ts","sourceRoot":"","sources":["../../../src/helpers.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,UAAU,CAAA;AAE7D,eAAO,MAAM,aAAa,GAAI,CAAC,WAAW,YAAY,CAAC,CAAC,CAAC,KAAG,OAE3D,CAAA;AAED,eAAO,MAAM,eAAe,EAAE,eAG7B,CAAA;AAED,eAAO,MAAM,kBAAkB,QAAe,MAAM,GAAG,MAAM,KAAG,OAAO,CAAC,IAAI,CAqC3E,CAAA"}
@@ -18,5 +18,6 @@ export declare const patchEventEmitter: {
18
18
  prependOnceListener<K>(eventName: string | symbol, listener: (...args: any[]) => void): any;
19
19
  eventNames(): (string | symbol)[];
20
20
  };
21
+ export { setPatchHistoryTTL } from './helpers';
21
22
  export declare const patchHistoryPlugin: <T>(schema: Schema<T>, opts: IPluginOptions<T>) => void;
22
23
  //# sourceMappingURL=plugin.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/plugin.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAA2B,MAAM,EAAE,MAAM,UAAU,CAAA;AAE/D,OAAO,KAAK,cAAc,MAAM,6BAA6B,CAAA;AAO7D,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;CAAK,CAAA;AAQnC,eAAO,MAAM,kBAAkB,GAAmB,CAAC,UAAU,MAAM,CAAC,CAAC,CAAC,QAAQ,cAAc,CAAC,CAAC,CAAC,KAAG,IA8CjG,CAAA"}
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/plugin.ts"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAA2B,MAAM,EAAE,MAAM,UAAU,CAAA;AAE/D,OAAO,KAAK,cAAc,MAAM,6BAA6B,CAAA;AAO7D,eAAO,MAAM,iBAAiB;;;;;;;;;;;;;;;;;CAAK,CAAA;AAEnC,OAAO,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAA;AAQ9C,eAAO,MAAM,kBAAkB,GAAmB,CAAC,UAAU,MAAM,CAAC,CAAC,CAAC,QAAQ,cAAc,CAAC,CAAC,CAAC,KAAG,IA8CjG,CAAA"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "ts-patch-mongoose",
3
- "version": "2.6.7",
3
+ "version": "2.6.8",
4
4
  "description": "Patch history & events for mongoose models",
5
5
  "author": "Alex Eagle",
6
6
  "license": "MIT",
@@ -77,6 +77,7 @@
77
77
  "dependencies": {
78
78
  "fast-json-patch": "3.1.1",
79
79
  "lodash": "4.17.21",
80
+ "ms": "2.1.3",
80
81
  "omit-deep": "0.3.0",
81
82
  "power-assign": "0.2.10",
82
83
  "semver": "7.6.3"
@@ -84,12 +85,13 @@
84
85
  "devDependencies": {
85
86
  "@biomejs/biome": "1.9.4",
86
87
  "@types/lodash": "4.17.13",
88
+ "@types/ms": "0.7.34",
87
89
  "@types/node": "22.10.1",
88
90
  "@types/semver": "7.5.8",
89
91
  "@vitest/coverage-v8": "2.1.8",
90
92
  "merge": "2.1.1",
91
93
  "mongodb-memory-server": "10.1.2",
92
- "mongoose": "8.8.3",
94
+ "mongoose": "8.8.4",
93
95
  "open-cli": "8.0.0",
94
96
  "typescript": "5.7.2",
95
97
  "vitest": "2.1.8"
package/src/helpers.ts CHANGED
@@ -1,3 +1,7 @@
1
+ import ms from 'ms'
2
+
3
+ import History from './models/History'
4
+
1
5
  import type { QueryOptions, ToObjectOptions } from 'mongoose'
2
6
 
3
7
  export const isHookIgnored = <T>(options: QueryOptions<T>): boolean => {
@@ -8,3 +12,42 @@ export const toObjectOptions: ToObjectOptions = {
8
12
  depopulate: true,
9
13
  virtuals: false,
10
14
  }
15
+
16
+ export const setPatchHistoryTTL = async (ttl: number | string): Promise<void> => {
17
+ const name = 'createdAt_1_TTL' // To avoid collision with user defined index / manually created index
18
+ try {
19
+ const indexes = await History.collection.indexes()
20
+ const existingIndex = indexes?.find((index) => index.name === name)
21
+
22
+ // Drop the index if historyTTL is not set and index exists
23
+ if (!ttl && existingIndex) {
24
+ await History.collection.dropIndex(name)
25
+ return
26
+ }
27
+
28
+ const milliseconds = ms(ttl as string)
29
+
30
+ // Drop the index if historyTTL is less than 1 second and index exists
31
+ if (milliseconds < 1000 && existingIndex) {
32
+ await History.collection.dropIndex(name)
33
+ return
34
+ }
35
+
36
+ const expireAfterSeconds = milliseconds / 1000
37
+
38
+ if (existingIndex && existingIndex.expireAfterSeconds === expireAfterSeconds) {
39
+ // Index already exists with the correct TTL, no need to recreate
40
+ return
41
+ }
42
+
43
+ if (existingIndex) {
44
+ // Drop the existing index if it exists and TTL is different
45
+ await History.collection.dropIndex(name)
46
+ }
47
+
48
+ // Create a new index with the correct TTL if it doesn't exist or if the TTL is different
49
+ await History.collection.createIndex({ createdAt: 1 }, { expireAfterSeconds, name })
50
+ } catch (err) {
51
+ console.error("Couldn't create or update index for history collection", err)
52
+ }
53
+ }
package/src/plugin.ts CHANGED
@@ -20,6 +20,8 @@ const remove = isMongooseLessThan7 ? 'remove' : 'deleteOne'
20
20
  */
21
21
  export const patchEventEmitter = em
22
22
 
23
+ export { setPatchHistoryTTL } from './helpers'
24
+
23
25
  /**
24
26
  * @description Patch history plugin
25
27
  * @param {Schema} schema
@@ -0,0 +1,86 @@
1
+ import ms from 'ms'
2
+ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest'
3
+
4
+ import History from '../src/models/History'
5
+
6
+ import { setPatchHistoryTTL } from '../src/helpers'
7
+
8
+ import type { Mock, MockInstance } from 'vitest'
9
+
10
+ vi.mock('../src/models/History', () => ({
11
+ default: {
12
+ collection: {
13
+ indexes: vi.fn(),
14
+ dropIndex: vi.fn(),
15
+ createIndex: vi.fn(),
16
+ },
17
+ },
18
+ }))
19
+
20
+ const name = 'createdAt_1_TTL'
21
+
22
+ describe('useTTL', () => {
23
+ let dropIndexSpy: MockInstance
24
+ let createIndexSpy: MockInstance
25
+ const indexes = History.collection.indexes as Mock
26
+
27
+ beforeEach(() => {
28
+ vi.clearAllMocks()
29
+ dropIndexSpy = vi.spyOn(History.collection, 'dropIndex')
30
+ createIndexSpy = vi.spyOn(History.collection, 'createIndex')
31
+ })
32
+
33
+ afterEach(() => {
34
+ vi.restoreAllMocks()
35
+ })
36
+
37
+ it('should drop the index if historyTTL is not set and index exists', async () => {
38
+ indexes.mockResolvedValue([{ name }])
39
+
40
+ // @ts-expect-error ttl can't be undefined in this case but we want to test it
41
+ await setPatchHistoryTTL(undefined)
42
+ expect(dropIndexSpy).toHaveBeenCalledWith(name)
43
+ })
44
+
45
+ it('should drop the index if historyTTL is less than 1 second and index exists', async () => {
46
+ indexes.mockResolvedValue([{ name }])
47
+
48
+ await setPatchHistoryTTL('500ms')
49
+ expect(dropIndexSpy).toHaveBeenCalledWith(name)
50
+ })
51
+
52
+ it('should not recreate the index if it already exists with the correct TTL', async () => {
53
+ const ttl = '1h'
54
+ const expireAfterSeconds = ms(ttl) / 1000
55
+
56
+ indexes.mockResolvedValue([{ name, expireAfterSeconds }])
57
+
58
+ await setPatchHistoryTTL(ttl)
59
+ expect(dropIndexSpy).not.toHaveBeenCalled()
60
+ expect(createIndexSpy).not.toHaveBeenCalled()
61
+ })
62
+
63
+ it('should drop and recreate the index if TTL is different', async () => {
64
+ const ttlBefore = '1h'
65
+ const ttlAfter = '2h'
66
+
67
+ const expireAfterSecondsBefore = ms(ttlBefore) / 1000
68
+ const expireAfterSecondsAfter = ms(ttlAfter) / 1000
69
+
70
+ indexes.mockResolvedValue([{ name, expireAfterSeconds: expireAfterSecondsBefore }])
71
+
72
+ await setPatchHistoryTTL(ttlAfter)
73
+ expect(dropIndexSpy).toHaveBeenCalledWith(name)
74
+ expect(createIndexSpy).toHaveBeenCalledWith({ createdAt: 1 }, { expireAfterSeconds: expireAfterSecondsAfter, name })
75
+ })
76
+
77
+ it('should create the index if it does not exist', async () => {
78
+ const ttl = '1h'
79
+ const expireAfterSeconds = ms(ttl) / 1000
80
+
81
+ indexes.mockResolvedValue([])
82
+
83
+ await setPatchHistoryTTL(ttl)
84
+ expect(createIndexSpy).toHaveBeenCalledWith({ createdAt: 1 }, { expireAfterSeconds, name })
85
+ })
86
+ })