ts-cache-mongoose 0.0.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 (76) hide show
  1. package/.eslintignore +4 -0
  2. package/.eslintrc +95 -0
  3. package/.swcrc +21 -0
  4. package/LICENSE +21 -0
  5. package/README.md +26 -0
  6. package/dist/cjs/cache/Cache.js +37 -0
  7. package/dist/cjs/cache/Cache.js.map +1 -0
  8. package/dist/cjs/cache/engine/MemoryCacheEngine.js +25 -0
  9. package/dist/cjs/cache/engine/MemoryCacheEngine.js.map +1 -0
  10. package/dist/cjs/cache/engine/RedisCacheEngine.js +28 -0
  11. package/dist/cjs/cache/engine/RedisCacheEngine.js.map +1 -0
  12. package/dist/cjs/crypto.js +28 -0
  13. package/dist/cjs/crypto.js.map +1 -0
  14. package/dist/cjs/interfaces/ICacheEngine.js +3 -0
  15. package/dist/cjs/interfaces/ICacheEngine.js.map +1 -0
  16. package/dist/cjs/interfaces/ICacheMongooseOptions.js +3 -0
  17. package/dist/cjs/interfaces/ICacheMongooseOptions.js.map +1 -0
  18. package/dist/cjs/plugin.js +82 -0
  19. package/dist/cjs/plugin.js.map +1 -0
  20. package/dist/cjs/types/cache/Cache.d.ts +12 -0
  21. package/dist/cjs/types/cache/Cache.d.ts.map +1 -0
  22. package/dist/cjs/types/cache/engine/MemoryCacheEngine.d.ts +11 -0
  23. package/dist/cjs/types/cache/engine/MemoryCacheEngine.d.ts.map +1 -0
  24. package/dist/cjs/types/cache/engine/RedisCacheEngine.d.ts +11 -0
  25. package/dist/cjs/types/cache/engine/RedisCacheEngine.d.ts.map +1 -0
  26. package/dist/cjs/types/crypto.d.ts +3 -0
  27. package/dist/cjs/types/crypto.d.ts.map +1 -0
  28. package/dist/cjs/types/interfaces/ICacheEngine.d.ts +8 -0
  29. package/dist/cjs/types/interfaces/ICacheEngine.d.ts.map +1 -0
  30. package/dist/cjs/types/interfaces/ICacheMongooseOptions.d.ts +10 -0
  31. package/dist/cjs/types/interfaces/ICacheMongooseOptions.d.ts.map +1 -0
  32. package/dist/cjs/types/plugin.d.ts +24 -0
  33. package/dist/cjs/types/plugin.d.ts.map +1 -0
  34. package/dist/esm/cache/Cache.js +33 -0
  35. package/dist/esm/cache/Cache.js.map +1 -0
  36. package/dist/esm/cache/engine/MemoryCacheEngine.js +23 -0
  37. package/dist/esm/cache/engine/MemoryCacheEngine.js.map +1 -0
  38. package/dist/esm/cache/engine/RedisCacheEngine.js +25 -0
  39. package/dist/esm/cache/engine/RedisCacheEngine.js.map +1 -0
  40. package/dist/esm/crypto.js +22 -0
  41. package/dist/esm/crypto.js.map +1 -0
  42. package/dist/esm/interfaces/ICacheEngine.js +2 -0
  43. package/dist/esm/interfaces/ICacheEngine.js.map +1 -0
  44. package/dist/esm/interfaces/ICacheMongooseOptions.js +2 -0
  45. package/dist/esm/interfaces/ICacheMongooseOptions.js.map +1 -0
  46. package/dist/esm/plugin.js.map +1 -0
  47. package/dist/esm/plugin.mjs +79 -0
  48. package/dist/esm/types/cache/Cache.d.ts +12 -0
  49. package/dist/esm/types/cache/Cache.d.ts.map +1 -0
  50. package/dist/esm/types/cache/engine/MemoryCacheEngine.d.ts +11 -0
  51. package/dist/esm/types/cache/engine/MemoryCacheEngine.d.ts.map +1 -0
  52. package/dist/esm/types/cache/engine/RedisCacheEngine.d.ts +11 -0
  53. package/dist/esm/types/cache/engine/RedisCacheEngine.d.ts.map +1 -0
  54. package/dist/esm/types/crypto.d.ts +3 -0
  55. package/dist/esm/types/crypto.d.ts.map +1 -0
  56. package/dist/esm/types/interfaces/ICacheEngine.d.ts +8 -0
  57. package/dist/esm/types/interfaces/ICacheEngine.d.ts.map +1 -0
  58. package/dist/esm/types/interfaces/ICacheMongooseOptions.d.ts +10 -0
  59. package/dist/esm/types/interfaces/ICacheMongooseOptions.d.ts.map +1 -0
  60. package/dist/esm/types/plugin.d.ts +24 -0
  61. package/dist/esm/types/plugin.d.ts.map +1 -0
  62. package/jest-mongodb-config.ts +10 -0
  63. package/jest.config.ts +33 -0
  64. package/package.json +112 -0
  65. package/src/cache/Cache.ts +46 -0
  66. package/src/cache/engine/MemoryCacheEngine.ts +32 -0
  67. package/src/cache/engine/RedisCacheEngine.ts +36 -0
  68. package/src/crypto.ts +27 -0
  69. package/src/interfaces/ICacheEngine.ts +8 -0
  70. package/src/interfaces/ICacheMongooseOptions.ts +10 -0
  71. package/src/plugin.ts +122 -0
  72. package/tests/cache.test.ts +51 -0
  73. package/tests/crypto.test.ts +60 -0
  74. package/tests/interfaces/IUser.ts +8 -0
  75. package/tests/schemas/UserSchema.ts +16 -0
  76. package/tsconfig.json +44 -0
@@ -0,0 +1,22 @@
1
+ import _ from 'lodash';
2
+ import { createHash } from 'crypto';
3
+ export function sortDeep(data) {
4
+ if (_.isObjectLike(data) && _.isArray(data)) {
5
+ return data.map(sortDeep);
6
+ }
7
+ if (_.isObjectLike(data) && !_.isArray(data)) {
8
+ const sortedKeys = _.keys(data).sort((a, b) => a.localeCompare(b));
9
+ const sortedObj = {};
10
+ sortedKeys.forEach((key) => {
11
+ sortedObj[key] = sortDeep(data[key]);
12
+ });
13
+ return sortedObj;
14
+ }
15
+ return data;
16
+ }
17
+ export function getKey(data) {
18
+ const sortedObj = sortDeep(data);
19
+ const sortedStr = JSON.stringify(sortedObj);
20
+ return createHash('sha256').update(sortedStr).digest('hex');
21
+ }
22
+ //# sourceMappingURL=crypto.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.js","sourceRoot":"","sources":["../../src/crypto.ts"],"names":[],"mappings":"AAAA,OAAO,CAAC,MAAM,QAAQ,CAAA;AACtB,OAAO,EAAE,UAAU,EAAE,MAAM,QAAQ,CAAA;AAEnC,MAAM,UAAU,QAAQ,CAAE,IAAyD;IACjF,IAAI,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;QAC3C,OAAO,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,CAAA;KAC1B;IAED,IAAI,CAAC,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,CAAC,IAAI,CAAC,EAAE;QAC5C,MAAM,UAAU,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAA;QAClE,MAAM,SAAS,GAA4B,EAAE,CAAA;QAE7C,UAAU,CAAC,OAAO,CAAC,CAAC,GAAG,EAAE,EAAE;YACzB,SAAS,CAAC,GAAG,CAAC,GAAG,QAAQ,CAAC,IAAI,CAAC,GAAG,CAAwD,CAAC,CAAA;QAC7F,CAAC,CAAC,CAAA;QAEF,OAAO,SAAS,CAAA;KACjB;IAED,OAAO,IAAI,CAAA;AACb,CAAC;AAED,MAAM,UAAU,MAAM,CAAE,IAAyD;IAC/E,MAAM,SAAS,GAAG,QAAQ,CAAC,IAAI,CAAC,CAAA;IAChC,MAAM,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,SAAS,CAAC,CAAA;IAC3C,OAAO,UAAU,CAAC,QAAQ,CAAC,CAAC,MAAM,CAAC,SAAS,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,CAAA;AAC7D,CAAC"}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ICacheEngine.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ICacheEngine.js","sourceRoot":"","sources":["../../../src/interfaces/ICacheEngine.ts"],"names":[],"mappings":""}
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ICacheMongooseOptions.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ICacheMongooseOptions.js","sourceRoot":"","sources":["../../../src/interfaces/ICacheMongooseOptions.ts"],"names":[],"mappings":""}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.js","sourceRoot":"","sources":["../../src/plugin.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,79 @@
1
+ import _ from 'lodash';
2
+ import ms from 'ms';
3
+ import Cache from './cache/Cache';
4
+ import { getKey } from './crypto';
5
+ class CacheMongoose {
6
+ constructor() {
7
+ }
8
+ static init(mongoose, cacheMongooseOptions) {
9
+ if (typeof mongoose.Model.hydrate !== 'function')
10
+ throw new Error('Cache is only compatible with versions of mongoose that implement the `model.hydrate` method');
11
+ if (!this.instance) {
12
+ this.instance = new CacheMongoose();
13
+ this.instance.cache = new Cache(cacheMongooseOptions.engine, cacheMongooseOptions.defaultTTL = '1 minute');
14
+ const cache = this.instance.cache;
15
+ const mongooseExec = mongoose.Query.prototype.exec;
16
+ mongoose.Query.prototype.getCacheKey = function () {
17
+ if (this._key)
18
+ return this._key;
19
+ const filter = this.getFilter();
20
+ const update = this.getUpdate();
21
+ const options = this.getOptions();
22
+ const data = {
23
+ model: this.model.modelName,
24
+ op: this.op,
25
+ filter,
26
+ update,
27
+ skip: options.skip,
28
+ limit: options.limit,
29
+ sort: options.sort,
30
+ _fields: this._fields,
31
+ _path: this._path,
32
+ _distinct: this._distinct
33
+ };
34
+ return getKey(data);
35
+ };
36
+ mongoose.Query.prototype.getCacheTTL = function () {
37
+ return this._ttl;
38
+ };
39
+ mongoose.Query.prototype.cache = function (ttl, customKey) {
40
+ this._ttl = ms(ttl ?? '1 minute');
41
+ this._key = customKey;
42
+ return this;
43
+ };
44
+ mongoose.Query.prototype.exec = async function () {
45
+ if (!_.has(this, '_ttl')) {
46
+ return mongooseExec.apply(this);
47
+ }
48
+ const key = this.getCacheKey();
49
+ const ttl = this.getCacheTTL();
50
+ const model = this.model.modelName;
51
+ const resultCache = await cache.get(key);
52
+ if (resultCache) {
53
+ const constructor = mongoose.model(model);
54
+ if (_.isArray(resultCache)) {
55
+ return resultCache.map((item) => constructor.hydrate(item));
56
+ }
57
+ else {
58
+ return constructor.hydrate(resultCache);
59
+ }
60
+ }
61
+ const result = await mongooseExec.call(this);
62
+ cache.set(key, result, ttl).catch((err) => {
63
+ console.error(err);
64
+ });
65
+ return result;
66
+ };
67
+ }
68
+ return this.instance;
69
+ }
70
+ async clearCache(customKey) {
71
+ if (!customKey) {
72
+ await this.cache.clear();
73
+ return;
74
+ }
75
+ await this.cache.del(customKey);
76
+ }
77
+ }
78
+ export default CacheMongoose;
79
+ //# sourceMappingURL=plugin.js.map
@@ -0,0 +1,12 @@
1
+ export declare const engines: readonly ["memory", "redis"];
2
+ declare class Cache {
3
+ private engine;
4
+ private defaultTTL;
5
+ constructor(engineName: 'memory' | 'redis', defaultTTL: string);
6
+ get(key: string): Promise<Record<string, unknown> | Record<string, unknown>[] | undefined>;
7
+ set(key: string, value: Record<string, unknown> | Record<string, unknown>[], ttl?: number): Promise<void>;
8
+ del(key: string): Promise<void>;
9
+ clear(): Promise<void>;
10
+ }
11
+ export default Cache;
12
+ //# sourceMappingURL=Cache.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Cache.d.ts","sourceRoot":"","sources":["../../../../src/cache/Cache.ts"],"names":[],"mappings":"AAOA,eAAO,MAAM,OAAO,8BAA+B,CAAA;AAEnD,cAAM,KAAK;IACT,OAAO,CAAC,MAAM,CAAc;IAC5B,OAAO,CAAC,UAAU,CAAQ;gBAEb,UAAU,EAAE,QAAQ,GAAG,OAAO,EAAE,UAAU,EAAE,MAAM;IAczD,GAAG,CAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,SAAS,CAAC;IAI3F,GAAG,CAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAK1G,GAAG,CAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhC,KAAK,IAAK,OAAO,CAAC,IAAI,CAAC;CAG9B;AAED,eAAe,KAAK,CAAA"}
@@ -0,0 +1,11 @@
1
+ import type ICacheEngine from '../../interfaces/ICacheEngine';
2
+ declare class MemoryCacheEngine implements ICacheEngine {
3
+ private cache;
4
+ constructor();
5
+ get(key: string): Record<string, unknown> | Record<string, unknown>[] | undefined;
6
+ set(key: string, value: Record<string, unknown> | Record<string, unknown>[], ttl?: number): void;
7
+ del(key: string): void;
8
+ clear(): void;
9
+ }
10
+ export default MemoryCacheEngine;
11
+ //# sourceMappingURL=MemoryCacheEngine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"MemoryCacheEngine.d.ts","sourceRoot":"","sources":["../../../../../src/cache/engine/MemoryCacheEngine.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,+BAA+B,CAAA;AAE7D,cAAM,iBAAkB,YAAW,YAAY;IAC7C,OAAO,CAAC,KAAK,CAA+G;;IAM5H,GAAG,CAAE,GAAG,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,SAAS;IAQlF,GAAG,CAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,GAAG,SAAW,GAAG,IAAI;IAInG,GAAG,CAAE,GAAG,EAAE,MAAM,GAAG,IAAI;IAKvB,KAAK,IAAK,IAAI;CAGf;AAED,eAAe,iBAAiB,CAAA"}
@@ -0,0 +1,11 @@
1
+ import type ICacheEngine from '../../interfaces/ICacheEngine';
2
+ declare class RedisCacheEngine implements ICacheEngine {
3
+ private client;
4
+ constructor();
5
+ get(key: string): Promise<Record<string, unknown> | Record<string, unknown>[] | undefined>;
6
+ set(key: string, value: unknown, ttl?: number): Promise<void>;
7
+ del(key: string): Promise<void>;
8
+ clear(): Promise<void>;
9
+ }
10
+ export default RedisCacheEngine;
11
+ //# sourceMappingURL=RedisCacheEngine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"RedisCacheEngine.d.ts","sourceRoot":"","sources":["../../../../../src/cache/engine/RedisCacheEngine.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,YAAY,MAAM,+BAA+B,CAAA;AAE7D,cAAM,gBAAiB,YAAW,YAAY;IAC5C,OAAO,CAAC,MAAM,CAAO;;IAMf,GAAG,CAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,SAAS,CAAC;IAQ3F,GAAG,CAAE,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,GAAG,SAAW,GAAG,OAAO,CAAC,IAAI,CAAC;IAKhE,GAAG,CAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAIhC,KAAK,IAAK,OAAO,CAAC,IAAI,CAAC;CAG9B;AAED,eAAe,gBAAgB,CAAA"}
@@ -0,0 +1,3 @@
1
+ export declare function sortDeep(data: Record<string, unknown>[] | Record<string, unknown>): unknown;
2
+ export declare function getKey(data: Record<string, unknown>[] | Record<string, unknown>): string;
3
+ //# sourceMappingURL=crypto.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"crypto.d.ts","sourceRoot":"","sources":["../../../src/crypto.ts"],"names":[],"mappings":"AAGA,wBAAgB,QAAQ,CAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,OAAO,CAiB5F;AAED,wBAAgB,MAAM,CAAE,IAAI,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAIzF"}
@@ -0,0 +1,8 @@
1
+ interface ICacheEngine {
2
+ get: (key: string) => Promise<Record<string, unknown> | Record<string, unknown>[] | undefined> | Record<string, unknown> | Record<string, unknown>[] | undefined;
3
+ set: (key: string, value: Record<string, unknown> | Record<string, unknown>[], ttl?: number) => Promise<void> | void;
4
+ del: (key: string) => Promise<void> | void;
5
+ clear: () => Promise<void> | void;
6
+ }
7
+ export default ICacheEngine;
8
+ //# sourceMappingURL=ICacheEngine.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ICacheEngine.d.ts","sourceRoot":"","sources":["../../../../src/interfaces/ICacheEngine.ts"],"names":[],"mappings":"AAAA,UAAU,YAAY;IACpB,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,SAAS,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,GAAG,SAAS,CAAA;IAChK,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IACpH,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;IAC1C,KAAK,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,GAAG,IAAI,CAAA;CAClC;AAED,eAAe,YAAY,CAAA"}
@@ -0,0 +1,10 @@
1
+ interface ICacheMongooseOptions {
2
+ engine: 'memory' | 'redis';
3
+ defaultTTL?: string;
4
+ port?: number;
5
+ host?: string;
6
+ password?: string;
7
+ client?: unknown;
8
+ }
9
+ export default ICacheMongooseOptions;
10
+ //# sourceMappingURL=ICacheMongooseOptions.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ICacheMongooseOptions.d.ts","sourceRoot":"","sources":["../../../../src/interfaces/ICacheMongooseOptions.ts"],"names":[],"mappings":"AAAA,UAAU,qBAAqB;IAC7B,MAAM,EAAE,QAAQ,GAAG,OAAO,CAAA;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,OAAO,CAAC;CAClB;AAED,eAAe,qBAAqB,CAAA"}
@@ -0,0 +1,24 @@
1
+ import type { Mongoose } from 'mongoose';
2
+ import type ICacheMongooseOptions from './interfaces/ICacheMongooseOptions';
3
+ declare module 'mongoose' {
4
+ interface Query<ResultType, DocType, THelpers, RawDocType> {
5
+ cache: (this: Query<ResultType, DocType, THelpers, RawDocType>, ttl?: string) => this;
6
+ _key?: string;
7
+ getCacheKey: (this: Query<ResultType, DocType, THelpers, RawDocType>) => string;
8
+ _ttl: number;
9
+ getCacheTTL: (this: Query<ResultType, DocType, THelpers, RawDocType>) => number;
10
+ op?: string;
11
+ _fields?: unknown;
12
+ _path?: unknown;
13
+ _distinct?: unknown;
14
+ }
15
+ }
16
+ declare class CacheMongoose {
17
+ private static instance;
18
+ private cache;
19
+ private constructor();
20
+ static init(mongoose: Mongoose, cacheMongooseOptions: ICacheMongooseOptions): CacheMongoose;
21
+ clearCache(customKey: string): Promise<void>;
22
+ }
23
+ export default CacheMongoose;
24
+ //# sourceMappingURL=plugin.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"plugin.d.ts","sourceRoot":"","sources":["../../../src/plugin.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,UAAU,CAAA;AACxC,OAAO,KAAK,qBAAqB,MAAM,oCAAoC,CAAA;AAG3E,OAAO,QAAQ,UAAU,CAAC;IACxB,UAAU,KAAK,CAAC,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU;QACvD,KAAK,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,EAAE,GAAG,CAAC,EAAE,MAAM,KAAK,IAAI,CAAA;QACrF,IAAI,CAAC,EAAE,MAAM,CAAA;QACb,WAAW,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,KAAK,MAAM,CAAA;QAC/E,IAAI,EAAE,MAAM,CAAA;QACZ,WAAW,EAAE,CAAC,IAAI,EAAE,KAAK,CAAC,UAAU,EAAE,OAAO,EAAE,QAAQ,EAAE,UAAU,CAAC,KAAK,MAAM,CAAA;QAC/E,EAAE,CAAC,EAAE,MAAM,CAAA;QACX,OAAO,CAAC,EAAE,OAAO,CAAA;QACjB,KAAK,CAAC,EAAE,OAAO,CAAA;QACf,SAAS,CAAC,EAAE,OAAO,CAAA;KACpB;CACF;AAED,cAAM,aAAa;IAEjB,OAAO,CAAC,MAAM,CAAC,QAAQ,CAA2B;IAClD,OAAO,CAAC,KAAK,CAAQ;IAErB,OAAO;WAKO,IAAI,CAAE,QAAQ,EAAE,QAAQ,EAAE,oBAAoB,EAAE,qBAAqB,GAAG,aAAa;IAgFtF,UAAU,CAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;CAO3D;AAED,eAAe,aAAa,CAAA"}
@@ -0,0 +1,10 @@
1
+ export default {
2
+ mongodbMemoryServerOptions: {
3
+ binary: {
4
+ skipMD5: true
5
+ },
6
+ autoStart: false,
7
+ instance: {}
8
+ },
9
+ useSharedDBForAllJestWorkers: false
10
+ }
package/jest.config.ts ADDED
@@ -0,0 +1,33 @@
1
+ // For a detailed explanation regarding each configuration property, visit:
2
+ // https://jestjs.io/docs/en/configuration.html
3
+ import { recursive } from 'merge'
4
+ import mongo from '@shelf/jest-mongodb/jest-preset'
5
+
6
+ const config = recursive(mongo, {
7
+ roots: [
8
+ '<rootDir>/src/',
9
+ '<rootDir>/tests/'
10
+ ],
11
+ clearMocks: true,
12
+ collectCoverage: true,
13
+ collectCoverageFrom: [
14
+ 'src/**/*.ts',
15
+ '!src/**/*.d.ts',
16
+ '!src/interfaces/**/*.ts'
17
+ ],
18
+ coverageDirectory: 'coverage',
19
+ testMatch: [
20
+ '<rootDir>/tests/**/*.test.ts'
21
+ ],
22
+ transform: {
23
+ '^.+\\.(t|j)sx?$': '@swc/jest'
24
+ },
25
+ testPathIgnorePatterns: [
26
+ 'node_modules'
27
+ ],
28
+ watchPathIgnorePatterns: [
29
+ 'globalConfig'
30
+ ]
31
+ })
32
+
33
+ export default config
package/package.json ADDED
@@ -0,0 +1,112 @@
1
+ {
2
+ "name": "ts-cache-mongoose",
3
+ "version": "0.0.1",
4
+ "description": "Cache plugin for mongoose",
5
+ "author": "Alex Eagle",
6
+ "license": "MIT",
7
+ "repository": {
8
+ "type": "git",
9
+ "url": "git://github.com/ilovepixelart/ts-cache-mongoose.git"
10
+ },
11
+ "bugs": {
12
+ "url": "https://github.com/ilovepixelart/ts-cache-mongoose/issues"
13
+ },
14
+ "homepage": "https://github.com/ilovepixelart/ts-cache-mongoose#readme",
15
+ "directories": {
16
+ "examples": "examples"
17
+ },
18
+ "keywords": [
19
+ "mongo",
20
+ "mongodb",
21
+ "mongoose",
22
+ "plugin",
23
+ "schema",
24
+ "db",
25
+ "nosql",
26
+ "ts",
27
+ "typescript",
28
+ "swc",
29
+ "cache",
30
+ "redis",
31
+ "store",
32
+ "memory",
33
+ "ttl",
34
+ "query"
35
+ ],
36
+ "engines": {
37
+ "node": "^14.15.0 || ^16.10.0 || >=18.0.0"
38
+ },
39
+ "files": [
40
+ "dist",
41
+ "src",
42
+ "tests",
43
+ ".swcrc",
44
+ "tsconfig.json",
45
+ "jest.config.ts",
46
+ "jest-mongodb-config.ts",
47
+ ".eslintrc",
48
+ ".eslintignore"
49
+ ],
50
+ "exports": {
51
+ ".": {
52
+ "import": {
53
+ "types": "./dist/esm/types/plugin.d.ts",
54
+ "default": "./dist/esm/plugin.mjs"
55
+ },
56
+ "require": {
57
+ "types": "./dist/cjs/types/plugin.d.ts",
58
+ "default": "./dist/cjs/plugin.js"
59
+ }
60
+ }
61
+ },
62
+ "main": "dist/cjs/plugin.js",
63
+ "types": "dist/cjs/types/plugin.d.ts",
64
+ "scripts": {
65
+ "lint": "eslint . --ext .js,.ts",
66
+ "lint:fix": "eslint . --fix --ext .js,.ts",
67
+ "test": "jest --config jest.config.ts --detectOpenHandles --coverage",
68
+ "test:open": "npm run test && open-cli coverage/lcov-report/index.html",
69
+ "clean": "rm -rf ./dist",
70
+ "build": "npm run clean && npm run build:cjs && npm run build:esm",
71
+ "build:cjs": "tsc -p config/tsconfig.cjs.json",
72
+ "build:esm": "tsc -p config/tsconfig.esm.json && mv ./dist/esm/plugin.js ./dist/esm/plugin.mjs",
73
+ "release": "npm install && npm run lint && npm run build && np"
74
+ },
75
+ "dependencies": {
76
+ "ioredis": "^5.3.2",
77
+ "lodash": "^4.17.21",
78
+ "ms": "^2.1.3"
79
+ },
80
+ "devDependencies": {
81
+ "@shelf/jest-mongodb": "4.1.7",
82
+ "@swc/cli": "0.1.62",
83
+ "@swc/core": "1.3.53",
84
+ "@swc/helpers": "0.5.0",
85
+ "@swc/jest": "0.2.26",
86
+ "@swc/register": "0.1.10",
87
+ "@types/jest": "29.5.1",
88
+ "@types/lodash": "4.14.194",
89
+ "@types/ms": "^0.7.31",
90
+ "@types/node": "18.16.0",
91
+ "@typescript-eslint/eslint-plugin": "5.59.0",
92
+ "@typescript-eslint/parser": "5.59.0",
93
+ "eslint": "8.39.0",
94
+ "eslint-config-standard": "17.0.0",
95
+ "eslint-plugin-import": "2.27.5",
96
+ "eslint-plugin-jest": "27.2.1",
97
+ "eslint-plugin-jest-formatting": "3.1.0",
98
+ "eslint-plugin-n": "15.7.0",
99
+ "eslint-plugin-node": "11.1.0",
100
+ "eslint-plugin-promise": "6.1.1",
101
+ "eslint-plugin-sonarjs": "0.19.0",
102
+ "jest": "29.5.0",
103
+ "merge": "2.1.1",
104
+ "mongoose": "6.10.5",
105
+ "open-cli": "7.2.0",
106
+ "ts-node": "10.9.1",
107
+ "typescript": "5.0.4"
108
+ },
109
+ "peerDependencies": {
110
+ "mongoose": ">=6.6.0 < 8"
111
+ }
112
+ }
@@ -0,0 +1,46 @@
1
+ import ms from 'ms'
2
+
3
+ import type ICacheEngine from '../interfaces/ICacheEngine'
4
+
5
+ import MemoryCacheEngine from './engine/MemoryCacheEngine'
6
+ import RedisCacheEngine from './engine/RedisCacheEngine'
7
+
8
+ export const engines = ['memory', 'redis'] as const
9
+
10
+ class Cache {
11
+ private engine: ICacheEngine
12
+ private defaultTTL: number
13
+
14
+ constructor (engineName: 'memory' | 'redis', defaultTTL: string) {
15
+ if (!engines.includes(engineName)) {
16
+ throw new Error(`Invalid engine name: ${engineName}`)
17
+ }
18
+
19
+ this.defaultTTL = ms(defaultTTL)
20
+
21
+ if (engineName === 'redis') {
22
+ this.engine = new RedisCacheEngine()
23
+ } else {
24
+ this.engine = new MemoryCacheEngine()
25
+ }
26
+ }
27
+
28
+ async get (key: string): Promise<Record<string, unknown> | Record<string, unknown>[] | undefined> {
29
+ return await this.engine.get(key)
30
+ }
31
+
32
+ async set (key: string, value: Record<string, unknown> | Record<string, unknown>[], ttl?: number): Promise<void> {
33
+ const actualTTL = ttl ?? this.defaultTTL
34
+ return await this.engine.set(key, value, actualTTL)
35
+ }
36
+
37
+ async del (key: string): Promise<void> {
38
+ return await this.engine.del(key)
39
+ }
40
+
41
+ async clear (): Promise<void> {
42
+ return await this.engine.clear()
43
+ }
44
+ }
45
+
46
+ export default Cache
@@ -0,0 +1,32 @@
1
+ import type ICacheEngine from '../../interfaces/ICacheEngine'
2
+
3
+ class MemoryCacheEngine implements ICacheEngine {
4
+ private cache: Record<string, { value: Record<string, unknown> | Record<string, unknown>[], expiresAt: number } | undefined>
5
+
6
+ constructor () {
7
+ this.cache = {}
8
+ }
9
+
10
+ get (key: string): Record<string, unknown> | Record<string, unknown>[] | undefined {
11
+ const item = this.cache[key]
12
+ if (!item || item.expiresAt < Date.now()) {
13
+ return undefined
14
+ }
15
+ return item.value
16
+ }
17
+
18
+ set (key: string, value: Record<string, unknown> | Record<string, unknown>[], ttl = Infinity): void {
19
+ this.cache[key] = { value, expiresAt: Date.now() + ttl }
20
+ }
21
+
22
+ del (key: string): void {
23
+ // eslint-disable-next-line @typescript-eslint/no-dynamic-delete
24
+ delete this.cache[key]
25
+ }
26
+
27
+ clear (): void {
28
+ this.cache = {}
29
+ }
30
+ }
31
+
32
+ export default MemoryCacheEngine
@@ -0,0 +1,36 @@
1
+ import IORedis from 'ioredis'
2
+
3
+ import type { Redis } from 'ioredis'
4
+
5
+ import type ICacheEngine from '../../interfaces/ICacheEngine'
6
+
7
+ class RedisCacheEngine implements ICacheEngine {
8
+ private client: Redis
9
+
10
+ constructor () {
11
+ this.client = new IORedis()
12
+ }
13
+
14
+ async get (key: string): Promise<Record<string, unknown> | Record<string, unknown>[] | undefined> {
15
+ const value = await this.client.get(key)
16
+ if (value === null) {
17
+ return undefined
18
+ }
19
+ return JSON.parse(value) as Promise<Record<string, unknown> | Record<string, unknown>[]>
20
+ }
21
+
22
+ async set (key: string, value: unknown, ttl = Infinity): Promise<void> {
23
+ const serializedValue = JSON.stringify(value)
24
+ await this.client.setex(key, Math.ceil(ttl / 1000), serializedValue)
25
+ }
26
+
27
+ async del (key: string): Promise<void> {
28
+ await this.client.del(key)
29
+ }
30
+
31
+ async clear (): Promise<void> {
32
+ await this.client.flushdb()
33
+ }
34
+ }
35
+
36
+ export default RedisCacheEngine
package/src/crypto.ts ADDED
@@ -0,0 +1,27 @@
1
+ import _ from 'lodash'
2
+ import { createHash } from 'crypto'
3
+
4
+ export function sortDeep (data: Record<string, unknown>[] | Record<string, unknown>): unknown {
5
+ if (_.isObjectLike(data) && _.isArray(data)) {
6
+ return data.map(sortDeep)
7
+ }
8
+
9
+ if (_.isObjectLike(data) && !_.isArray(data)) {
10
+ const sortedKeys = _.keys(data).sort((a, b) => a.localeCompare(b))
11
+ const sortedObj: Record<string, unknown> = {}
12
+
13
+ sortedKeys.forEach((key) => {
14
+ sortedObj[key] = sortDeep(data[key] as Record<string, unknown>[] | Record<string, unknown>)
15
+ })
16
+
17
+ return sortedObj
18
+ }
19
+
20
+ return data
21
+ }
22
+
23
+ export function getKey (data: Record<string, unknown>[] | Record<string, unknown>): string {
24
+ const sortedObj = sortDeep(data)
25
+ const sortedStr = JSON.stringify(sortedObj)
26
+ return createHash('sha256').update(sortedStr).digest('hex')
27
+ }
@@ -0,0 +1,8 @@
1
+ interface ICacheEngine {
2
+ get: (key: string) => Promise<Record<string, unknown> | Record<string, unknown>[] | undefined> | Record<string, unknown> | Record<string, unknown>[] | undefined
3
+ set: (key: string, value: Record<string, unknown> | Record<string, unknown>[], ttl?: number) => Promise<void> | void
4
+ del: (key: string) => Promise<void> | void
5
+ clear: () => Promise<void> | void
6
+ }
7
+
8
+ export default ICacheEngine
@@ -0,0 +1,10 @@
1
+ interface ICacheMongooseOptions {
2
+ engine: 'memory' | 'redis'
3
+ defaultTTL?: string
4
+ port?: number
5
+ host?: string
6
+ password?: string,
7
+ client?: unknown,
8
+ }
9
+
10
+ export default ICacheMongooseOptions