equipped 4.0.2-alpha.3 → 4.1.0

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 (55) hide show
  1. package/CHANGELOG.md +27 -0
  2. package/README.md +68 -0
  3. package/lib/cache/cache.d.ts +1 -2
  4. package/lib/cache/types/redis-cache.d.ts +2 -3
  5. package/lib/cache/types/redis-cache.js +4 -9
  6. package/lib/db/_instance.d.ts +10 -6
  7. package/lib/db/_instance.js +38 -8
  8. package/lib/db/debezium.d.ts +10 -0
  9. package/lib/db/debezium.js +11 -0
  10. package/lib/db/index.d.ts +0 -2
  11. package/lib/db/index.js +0 -6
  12. package/lib/db/mongoose/changes.d.ts +8 -0
  13. package/lib/db/mongoose/changes.js +99 -0
  14. package/lib/db/mongoose/index.d.ts +9 -10
  15. package/lib/db/mongoose/index.js +46 -98
  16. package/lib/db/mongoose/query.d.ts +2 -2
  17. package/lib/db/mongoose/query.js +5 -5
  18. package/lib/errors/index.d.ts +4 -4
  19. package/lib/errors/index.js +4 -4
  20. package/lib/events/index.d.ts +22 -0
  21. package/lib/events/index.js +15 -0
  22. package/lib/events/kafka.d.ts +11 -0
  23. package/lib/events/kafka.js +85 -0
  24. package/lib/events/rabbit.d.ts +11 -4
  25. package/lib/events/rabbit.js +72 -37
  26. package/lib/exit.js +1 -1
  27. package/lib/index.d.ts +2 -1
  28. package/lib/index.js +1 -0
  29. package/lib/instance/index.d.ts +4 -2
  30. package/lib/instance/index.js +19 -27
  31. package/lib/instance/settings.d.ts +3 -1
  32. package/lib/instance/settings.js +3 -1
  33. package/lib/listeners/emitter.d.ts +1 -0
  34. package/lib/listeners/emitter.js +13 -5
  35. package/lib/server/app.d.ts +3 -8
  36. package/lib/server/app.js +12 -28
  37. package/lib/server/controllers/index.js +3 -40
  38. package/lib/server/controllers/request.d.ts +7 -3
  39. package/lib/server/controllers/request.js +42 -16
  40. package/lib/server/index.d.ts +2 -1
  41. package/lib/server/index.js +3 -0
  42. package/lib/server/middlewares/index.d.ts +2 -2
  43. package/lib/server/middlewares/index.js +2 -2
  44. package/lib/server/middlewares/notFoundHandler.js +1 -1
  45. package/lib/server/middlewares/parseAuthUser.js +2 -2
  46. package/lib/server/routes.d.ts +10 -0
  47. package/lib/server/routes.js +36 -0
  48. package/lib/utils/json.d.ts +1 -0
  49. package/lib/utils/json.js +12 -0
  50. package/lib/utils/retry.d.ts +2 -0
  51. package/lib/utils/retry.js +16 -0
  52. package/lib/validations/index.d.ts +26 -25
  53. package/package.json +24 -16
  54. package/lib/events/events.d.ts +0 -17
  55. package/lib/events/events.js +0 -26
package/CHANGELOG.md CHANGED
@@ -2,6 +2,33 @@
2
2
 
3
3
  All notable changes to this project will be documented in this file. See [standard-version](https://github.com/conventional-changelog/standard-version) for commit guidelines.
4
4
 
5
+ ## [4.1.0](https://github.com/kevinand11/equipped/compare/v4.0.2-alpha.3...v4.1.0) (2023-03-07)
6
+
7
+
8
+ ### Features
9
+
10
+ * access db name from mongoose ([febdee0](https://github.com/kevinand11/equipped/commit/febdee0264fe0f7d2de02fcd08a380a8c93df68c))
11
+ * add fanout option for event subscribers ([4f0c060](https://github.com/kevinand11/equipped/commit/4f0c060aa2d99fd6128499d49e1f163d1e93a721))
12
+ * add helmet and supertest ([71ddcad](https://github.com/kevinand11/equipped/commit/71ddcad4e2b191913a890e7480619f1df527fde1))
13
+ * add Id to mongo db ([55a8728](https://github.com/kevinand11/equipped/commit/55a872827a427a65a9be25ce464468935a4c5674))
14
+ * add kafka eventbus ([22f79d4](https://github.com/kevinand11/equipped/commit/22f79d48e48cc7c5695a43089f6a724f479ed81c))
15
+ * automatically start subscribers in Instance.startConnections ([96a07c9](https://github.com/kevinand11/equipped/commit/96a07c923a90b9203920ab490c4554098dfab5e8))
16
+ * enable defaults, getters and virtuals on lean docs ([69f9d2b](https://github.com/kevinand11/equipped/commit/69f9d2b5f26072e91d411cc1dd5e676264a6a586))
17
+ * enable lean documents for query ([ce04a8a](https://github.com/kevinand11/equipped/commit/ce04a8a326025916a32ce017634328c37c09a7e0))
18
+ * enable pre images for all collections ([710457d](https://github.com/kevinand11/equipped/commit/710457d1132cd380a0890b191f98fece4473729f))
19
+ * hydrate cs docs with getters and virtuals ([c93809a](https://github.com/kevinand11/equipped/commit/c93809ab8f2f4597fb310c68dc8d95b9f901aedd))
20
+ * new changestream api ([923660b](https://github.com/kevinand11/equipped/commit/923660b11c1ab6f799c030b37dc6ce06c3b90ebb))
21
+ * server request ip ([371dac1](https://github.com/kevinand11/equipped/commit/371dac18ef729c88da0e8885e1e63e581e8b979b))
22
+ * socket emitters fanout ([65a36db](https://github.com/kevinand11/equipped/commit/65a36db9671205a30fe8aabd3b1763714e94f886))
23
+
24
+
25
+ ### Bug Fixes
26
+
27
+ * hydrate _id from changes ([65e6539](https://github.com/kevinand11/equipped/commit/65e6539bfc9644421152af3ad5c0436a2b4ed3dc))
28
+ * loop over db connections ([eb3f548](https://github.com/kevinand11/equipped/commit/eb3f548f2e7a2f172cb4ead8ff655df0fc8f3b95))
29
+ * remove cache setInTransaction ([4f1b3c0](https://github.com/kevinand11/equipped/commit/4f1b3c0db37621162065c69fc24eab744675da27))
30
+ * setup debezium connection to mongo ([3d36283](https://github.com/kevinand11/equipped/commit/3d3628349910d8ba0633578b6578f09e682b8145))
31
+
5
32
  ### [4.0.2-alpha.3](https://github.com/kevinand11/equipped/compare/v4.0.2-alpha.2...v4.0.2-alpha.3) (2023-02-19)
6
33
 
7
34
  ### [4.0.2-alpha.2](https://github.com/kevinand11/equipped/compare/v4.0.2-alpha.1...v4.0.2-alpha.2) (2023-02-19)
package/README.md ADDED
@@ -0,0 +1,68 @@
1
+ # Equipped
2
+
3
+ <br>
4
+
5
+ ## Installation
6
+
7
+ This is a [Node.js](https://nodejs.org/en/) module available through the [npm registry](https://www.npmjs.com/package/equipped).
8
+ Before installing, [download and install Node.js](https://nodejs.org/en/download/). Node.js 4.2.4 or higher is required.
9
+ If this is a brand new project, make sure to create a `package.json` first with the [`npm init` command](https://docs.npmjs.com/creating-a-package-json-file).
10
+ Installation is done using the [`npm install` command](https://docs.npmjs.com/getting-started/installing-npm-packages-locally):
11
+
12
+ ### Using npm:
13
+ npm install equipped
14
+
15
+ ### Using yarn:
16
+ yarn add equipped
17
+
18
+ ### Using CDN:
19
+
20
+ [Equipped jsDelivr CDN](https://www.jsdelivr.com/package/npm/equipped)
21
+
22
+ <br>
23
+
24
+ ## Basic Usage
25
+ Before use, the package must be initialized in the entry point into your application, with the required settings.
26
+ ```ts
27
+ import { Instance } from 'equipped'
28
+
29
+ Instance.initialize({
30
+ isDev: true, // Are you running in a dev environment
31
+ appId: 'testing', // An id to identify your application
32
+ mongoDbURI: 'mongodb://...', // A mongodb url if you want to use a database
33
+ redisURI: 'redis://localhost:6379', // A redis url if you want to use a redis cache. Confirm all other functionality you intend to use do not depend on the cache, even if you are not using a cache directly
34
+ ...other settings
35
+ })
36
+ ```
37
+
38
+ <br>
39
+
40
+ After the app has been initialized, the Instance class returns a singleton that provides access to most functionality provided by Equipped
41
+ ```ts
42
+ import { Instance } from 'equipped'
43
+
44
+ const appInstance = Instance.get()
45
+ ```
46
+
47
+ <br>
48
+
49
+ To start a server
50
+ ```ts
51
+ import { makeController, StatusCodes } from 'equipped'
52
+
53
+ // Aggregate all your routes into an array. This can be defined in the file or imported from your controllers etc
54
+ appInstance.server.routes = [
55
+ {
56
+ path: '/',
57
+ method: 'get',
58
+ controllers: [makeController(async () => {
59
+ return {
60
+ result: 'Hello world',
61
+ status: StatusCodes.Ok
62
+ }
63
+ })]
64
+ }
65
+ ]
66
+
67
+ await appInstance.server.start(8080)
68
+ ```
@@ -1,8 +1,7 @@
1
1
  export declare abstract class Cache {
2
- abstract connect(): Promise<void>;
2
+ abstract start(): Promise<void>;
3
3
  abstract close(): Promise<void>;
4
4
  abstract set(key: string, data: string, ttlInSecs: number): Promise<void>;
5
- abstract setInTransaction(key: string, data: string, ttlInSecs: number): Promise<[string, string]>;
6
5
  abstract get(key: string): Promise<string | null>;
7
6
  abstract delete(key: string): Promise<void>;
8
7
  }
@@ -2,11 +2,10 @@ import { createClient } from 'redis';
2
2
  import { Cache } from '../cache';
3
3
  export declare class RedisCache extends Cache {
4
4
  client: ReturnType<typeof createClient>;
5
- constructor(connection: string);
6
- connect(): Promise<void>;
5
+ constructor();
6
+ start(): Promise<void>;
7
7
  close(): Promise<void>;
8
8
  delete(key: string): Promise<void>;
9
9
  get(key: string): Promise<string | null>;
10
10
  set(key: string, data: string, ttlInSecs: number): Promise<void>;
11
- setInTransaction(key: string, data: string, ttlInSecs: number): Promise<[string, string]>;
12
11
  }
@@ -3,16 +3,17 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.RedisCache = void 0;
4
4
  const redis_1 = require("redis");
5
5
  const exit_1 = require("../../exit");
6
+ const instance_1 = require("../../instance");
6
7
  const cache_1 = require("../cache");
7
8
  class RedisCache extends cache_1.Cache {
8
- constructor(connection) {
9
+ constructor() {
9
10
  super();
10
- this.client = (0, redis_1.createClient)({ url: connection });
11
+ this.client = (0, redis_1.createClient)({ url: instance_1.Instance.get().settings.redisURI });
11
12
  this.client.on('error', async (error) => {
12
13
  (0, exit_1.exit)(`Redis failed with error: ${error}`);
13
14
  });
14
15
  }
15
- async connect() {
16
+ async start() {
16
17
  await this.client.connect();
17
18
  }
18
19
  async close() {
@@ -30,11 +31,5 @@ class RedisCache extends cache_1.Cache {
30
31
  else
31
32
  this.client.set(key, data);
32
33
  }
33
- async setInTransaction(key, data, ttlInSecs) {
34
- return await this.client.multi()
35
- .get(key)
36
- .setEx(key, ttlInSecs, data)
37
- .exec();
38
- }
39
34
  }
40
35
  exports.RedisCache = RedisCache;
@@ -1,18 +1,22 @@
1
1
  import { BaseEntity } from '../structure';
2
+ import { DebeziumSetup } from './debezium';
2
3
  import { QueryParams, QueryResults } from './query';
3
4
  export declare abstract class Db {
4
5
  #private;
5
- abstract generateDbChange<Model, Entity extends BaseEntity>(collection: any, callbacks: DbChangeCallbacks<Model, Entity>, mapper: (model: Model | null) => Entity | null): DbChange<Model, Entity>;
6
- abstract parseQueryParams<Model>(collection: any, params: QueryParams): Promise<QueryResults<Model>>;
6
+ abstract change<Model, Entity extends BaseEntity>(collection: any, callbacks: DbChangeCallbacks<Model, Entity>, mapper: (model: Model | null) => Entity | null): DbChange<Model, Entity>;
7
+ abstract query<Model>(collection: any, params: QueryParams): Promise<QueryResults<Model>>;
7
8
  protected _addToDbChanges(dbChange: DbChange<any, any>): this;
8
9
  startAllDbChanges(): Promise<void>;
9
- abstract start(url: string): Promise<void>;
10
+ abstract start(): Promise<void>;
10
11
  abstract close(): Promise<void>;
11
12
  }
12
13
  export declare abstract class DbChange<Model, Entity extends BaseEntity> {
13
- _cbs: DbChangeCallbacks<Model, Entity>;
14
- abstract start(...args: any[]): Promise<void>;
15
- setCallbacks(callbacks: DbChangeCallbacks<Model, Entity>): this;
14
+ #private;
15
+ constructor(callbacks: DbChangeCallbacks<Model, Entity>, mapper: (model: Model | null) => Entity | null);
16
+ abstract start(): Promise<void>;
17
+ get callbacks(): Readonly<DbChangeCallbacks<Model, Entity>>;
18
+ get mapper(): (model: Model | null) => Entity | null;
19
+ protected _setup(key: string, data: DebeziumSetup): Promise<boolean>;
16
20
  }
17
21
  type DeepPartial<T> = {
18
22
  [P in keyof T]?: DeepPartial<T[P]>;
@@ -4,9 +4,22 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
4
4
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
5
5
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
6
6
  };
7
- var _Db_dbChanges;
7
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
8
+ if (kind === "m") throw new TypeError("Private method is not writable");
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
11
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
12
+ };
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
16
+ var _Db_dbChanges, _DbChange_callbacks, _DbChange_mapper;
8
17
  Object.defineProperty(exports, "__esModule", { value: true });
9
18
  exports.DbChange = exports.Db = void 0;
19
+ const axios_1 = __importDefault(require("axios"));
20
+ const exit_1 = require("../exit");
21
+ const instance_1 = require("../instance");
22
+ const debezium_1 = require("./debezium");
10
23
  class Db {
11
24
  constructor() {
12
25
  _Db_dbChanges.set(this, []);
@@ -22,14 +35,31 @@ class Db {
22
35
  exports.Db = Db;
23
36
  _Db_dbChanges = new WeakMap();
24
37
  class DbChange {
25
- constructor() {
26
- this._cbs = {};
38
+ constructor(callbacks, mapper) {
39
+ _DbChange_callbacks.set(this, {});
40
+ _DbChange_mapper.set(this, void 0);
41
+ __classPrivateFieldSet(this, _DbChange_callbacks, callbacks, "f");
42
+ __classPrivateFieldSet(this, _DbChange_mapper, mapper, "f");
27
43
  }
28
- setCallbacks(callbacks) {
29
- this._cbs.created = callbacks.created;
30
- this._cbs.updated = callbacks.updated;
31
- this._cbs.deleted = callbacks.deleted;
32
- return this;
44
+ get callbacks() {
45
+ return Object.freeze(__classPrivateFieldGet(this, _DbChange_callbacks, "f"));
46
+ }
47
+ get mapper() {
48
+ return __classPrivateFieldGet(this, _DbChange_mapper, "f");
49
+ }
50
+ async _setup(key, data) {
51
+ data = { ...debezium_1.DefaultDebeziumSetup, ...data };
52
+ const topics = await axios_1.default.put(`/connectors/${key}/config`, data, { baseURL: instance_1.Instance.get().settings.debeziumUrl })
53
+ .then(async () => {
54
+ const res = await axios_1.default.get(`/connectors/${key}/topics`, { baseURL: instance_1.Instance.get().settings.debeziumUrl });
55
+ return res.data[key]?.topics ?? [];
56
+ })
57
+ .catch((err) => {
58
+ (0, exit_1.exit)(`Failed to setup debezium for ${key}: ${err.message}`);
59
+ return [];
60
+ });
61
+ return topics[0] === key;
33
62
  }
34
63
  }
35
64
  exports.DbChange = DbChange;
65
+ _DbChange_callbacks = new WeakMap(), _DbChange_mapper = new WeakMap();
@@ -0,0 +1,10 @@
1
+ export type DebeziumSetup = Partial<{
2
+ 'connector.class': string;
3
+ 'topic.prefix': string;
4
+ 'key.converter': string;
5
+ 'key.converter.schemas.enable': string;
6
+ 'value.converter': string;
7
+ 'value.converter.schemas.enable': string;
8
+ }> & Record<string, string>;
9
+ export declare const TopicPrefix = "equipped";
10
+ export declare const DefaultDebeziumSetup: DebeziumSetup;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.DefaultDebeziumSetup = exports.TopicPrefix = void 0;
4
+ exports.TopicPrefix = 'equipped';
5
+ exports.DefaultDebeziumSetup = {
6
+ 'topic.prefix': exports.TopicPrefix,
7
+ 'key.converter': 'org.apache.kafka.connect.json.JsonConverter',
8
+ 'key.converter.schemas.enable': 'false',
9
+ 'value.converter': 'org.apache.kafka.connect.json.JsonConverter',
10
+ 'value.converter.schemas.enable': 'false'
11
+ };
package/lib/db/index.d.ts CHANGED
@@ -1,4 +1,2 @@
1
- import mongoose from 'mongoose';
2
1
  export * from './query';
3
2
  export { DbChangeCallbacks } from './_instance';
4
- export { mongoose };
package/lib/db/index.js CHANGED
@@ -13,11 +13,5 @@ var __createBinding = (this && this.__createBinding) || (Object.create ? (functi
13
13
  var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
14
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
15
  };
16
- var __importDefault = (this && this.__importDefault) || function (mod) {
17
- return (mod && mod.__esModule) ? mod : { "default": mod };
18
- };
19
16
  Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.mongoose = void 0;
21
- const mongoose_1 = __importDefault(require("mongoose"));
22
- exports.mongoose = mongoose_1.default;
23
17
  __exportStar(require("./query"), exports);
@@ -0,0 +1,8 @@
1
+ import mongoose from 'mongoose';
2
+ import { BaseEntity } from '../../structure';
3
+ import { DbChange, DbChangeCallbacks } from '../_instance';
4
+ export declare class MongoDbChange<Model, Entity extends BaseEntity> extends DbChange<Model, Entity> {
5
+ #private;
6
+ constructor(model: mongoose.Model<Model>, callbacks: DbChangeCallbacks<Model, Entity>, mapper: (model: Model | null) => Entity | null);
7
+ start(): Promise<void>;
8
+ }
@@ -0,0 +1,99 @@
1
+ "use strict";
2
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
+ if (kind === "m") throw new TypeError("Private method is not writable");
4
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
6
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
+ };
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var __importDefault = (this && this.__importDefault) || function (mod) {
14
+ return (mod && mod.__esModule) ? mod : { "default": mod };
15
+ };
16
+ var _MongoDbChange_started, _MongoDbChange_model;
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.MongoDbChange = void 0;
19
+ const mongoose_1 = __importDefault(require("mongoose"));
20
+ const exit_1 = require("../../exit");
21
+ const instance_1 = require("../../instance");
22
+ const retry_1 = require("../../utils/retry");
23
+ const validations_1 = require("../../validations");
24
+ const debezium_1 = require("../debezium");
25
+ const _instance_1 = require("../_instance");
26
+ class MongoDbChange extends _instance_1.DbChange {
27
+ constructor(model, callbacks, mapper) {
28
+ super(callbacks, mapper);
29
+ _MongoDbChange_started.set(this, false);
30
+ _MongoDbChange_model.set(this, void 0);
31
+ __classPrivateFieldSet(this, _MongoDbChange_model, model, "f");
32
+ }
33
+ async start() {
34
+ if (__classPrivateFieldGet(this, _MongoDbChange_started, "f"))
35
+ return;
36
+ __classPrivateFieldSet(this, _MongoDbChange_started, true, "f");
37
+ const model = __classPrivateFieldGet(this, _MongoDbChange_model, "f");
38
+ const dbName = model.db.name;
39
+ const colName = model.collection.name;
40
+ const dbColName = `${dbName}.${colName}`;
41
+ const topic = `${debezium_1.TopicPrefix}.${dbColName}`;
42
+ (0, retry_1.retry)(async () => {
43
+ const { hosts, replicaSet, credentials } = model.collection.conn.getClient().options;
44
+ const started = await this._setup(topic, {
45
+ 'connector.class': 'io.debezium.connector.mongodb.MongoDbConnector',
46
+ 'capture.mode': 'change_streams_update_full_with_pre_image',
47
+ 'mongodb.hosts': `${replicaSet}/` + hosts.map((h) => `${h.host}:${h.port}`).join(','),
48
+ ...(credentials ? {
49
+ 'mongodb.user': credentials.username,
50
+ 'mongodb.pass': credentials.password,
51
+ 'mongodb.auth-source': credentials.source
52
+ } : {}),
53
+ 'collection.include.list': dbColName
54
+ });
55
+ const TestId = new mongoose_1.default.Types.ObjectId('__equipped__');
56
+ if (!started) {
57
+ await model.findByIdAndUpdate(TestId, { $set: { colName } }, { upsert: true });
58
+ await model.findByIdAndDelete(TestId);
59
+ throw new Error(`Wait a few minutes for db changes for ${colName} to initialize...`);
60
+ }
61
+ const hydrate = (data) => model.hydrate({
62
+ ...data, _id: new mongoose_1.default.Types.ObjectId(data._id['$oid'])
63
+ }).toObject({ getters: true, virtuals: true });
64
+ if (started)
65
+ await instance_1.Instance.get().eventBus
66
+ .createSubscriber(topic, async (data) => {
67
+ const op = data.op;
68
+ let before = JSON.parse(data.before ?? 'null');
69
+ let after = JSON.parse(data.after ?? 'null');
70
+ if (before?.__id === TestId || after?.__id === TestId)
71
+ return;
72
+ if (before)
73
+ before = hydrate(before);
74
+ if (after)
75
+ after = hydrate(after);
76
+ if (op === 'c' && this.callbacks.created && after)
77
+ await this.callbacks.created({
78
+ before: null,
79
+ after: this.mapper(after)
80
+ });
81
+ else if (op === 'u' && this.callbacks.updated && before)
82
+ await this.callbacks.updated({
83
+ before: this.mapper(before),
84
+ after: this.mapper(after),
85
+ changes: validations_1.Validation.Differ.from(validations_1.Validation.Differ.diff(before, after))
86
+ });
87
+ else if (op === 'd' && this.callbacks.deleted && before)
88
+ await this.callbacks.deleted({
89
+ before: this.mapper(before),
90
+ after: null
91
+ });
92
+ })
93
+ .subscribe();
94
+ }, 10, 60000)
95
+ .catch((err) => (0, exit_1.exit)(err.message));
96
+ }
97
+ }
98
+ exports.MongoDbChange = MongoDbChange;
99
+ _MongoDbChange_started = new WeakMap(), _MongoDbChange_model = new WeakMap();
@@ -1,16 +1,15 @@
1
1
  import mongoose from 'mongoose';
2
2
  import { BaseEntity } from '../../structure';
3
3
  import { QueryParams, QueryResults } from '../query';
4
- import { Db, DbChange, DbChangeCallbacks } from '../_instance';
4
+ import { Db, DbChangeCallbacks } from '../_instance';
5
+ import { MongoDbChange } from './changes';
5
6
  export declare class MongoDb extends Db {
6
- generateDbChange<Model, Entity extends BaseEntity>(collection: mongoose.Model<Model | any>, callbacks: DbChangeCallbacks<Model, Entity>, mapper: (model: Model | null) => Entity | null): MongoDbChange<Model, Entity>;
7
- parseQueryParams<Model>(collection: mongoose.Model<Model | any>, params: QueryParams): Promise<QueryResults<Model>>;
8
- start(url: string): Promise<void>;
9
- close(): Promise<void>;
10
- }
11
- declare class MongoDbChange<Model, Entity extends BaseEntity> extends DbChange<Model, Entity> {
12
7
  #private;
13
- constructor(collection: mongoose.Model<Model | any>, mapper: (model: Model | null) => Entity | null);
14
- start(skipResume?: boolean): Promise<void>;
8
+ get Schema(): typeof mongoose.Schema;
9
+ get Id(): mongoose.Types.ObjectId;
10
+ use(dbName?: string): mongoose.Connection;
11
+ change<Model, Entity extends BaseEntity>(model: mongoose.Model<Model>, callbacks: DbChangeCallbacks<Model, Entity>, mapper: (model: Model | null) => Entity | null): MongoDbChange<Model, Entity>;
12
+ query<Model>(model: mongoose.Model<Model>, params: QueryParams): Promise<QueryResults<Model>>;
13
+ start(): Promise<void>;
14
+ close(): Promise<void>;
15
15
  }
16
- export {};
@@ -1,125 +1,73 @@
1
1
  "use strict";
2
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
3
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
4
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
5
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
6
+ };
2
7
  var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
8
  if (kind === "m") throw new TypeError("Private method is not writable");
4
9
  if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
10
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
6
11
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
12
  };
8
- var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
- if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
10
- if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
- return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
- };
13
13
  var __importDefault = (this && this.__importDefault) || function (mod) {
14
14
  return (mod && mod.__esModule) ? mod : { "default": mod };
15
15
  };
16
- var _MongoDbChange_started, _MongoDbChange_col, _MongoDbChange_mapper;
16
+ var _MongoDb_instances, _MongoDb_started, _MongoDb_connections_get;
17
17
  Object.defineProperty(exports, "__esModule", { value: true });
18
18
  exports.MongoDb = void 0;
19
19
  const mongoose_1 = __importDefault(require("mongoose"));
20
- const exit_1 = require("../../exit");
20
+ const mongoose_lean_defaults_1 = __importDefault(require("mongoose-lean-defaults"));
21
+ const mongoose_lean_getters_1 = __importDefault(require("mongoose-lean-getters"));
22
+ const mongoose_lean_virtuals_1 = __importDefault(require("mongoose-lean-virtuals"));
21
23
  const instance_1 = require("../../instance");
22
- const validations_1 = require("../../validations");
23
24
  const _instance_1 = require("../_instance");
25
+ const changes_1 = require("./changes");
24
26
  const query_1 = require("./query");
25
27
  class MongoDb extends _instance_1.Db {
26
- generateDbChange(collection, callbacks, mapper) {
27
- const change = new MongoDbChange(collection, mapper)
28
- .setCallbacks(callbacks);
28
+ constructor() {
29
+ super(...arguments);
30
+ _MongoDb_instances.add(this);
31
+ _MongoDb_started.set(this, false);
32
+ }
33
+ get Schema() {
34
+ return mongoose_1.default.Schema;
35
+ }
36
+ get Id() {
37
+ return new mongoose_1.default.Types.ObjectId();
38
+ }
39
+ use(dbName = 'default') {
40
+ const conn = dbName === 'default' ? mongoose_1.default.connection : mongoose_1.default.connection.useDb(dbName, { useCache: true });
41
+ conn.plugin(mongoose_lean_defaults_1.default).plugin(mongoose_lean_virtuals_1.default).plugin(mongoose_lean_getters_1.default);
42
+ return conn;
43
+ }
44
+ change(model, callbacks, mapper) {
45
+ const change = new changes_1.MongoDbChange(model, callbacks, mapper);
29
46
  this._addToDbChanges(change);
30
47
  return change;
31
48
  }
32
- async parseQueryParams(collection, params) {
33
- return (0, query_1.parseMongodbQueryParams)(collection, params);
49
+ async query(model, params) {
50
+ return await (0, query_1.parseMongodbQueryParams)(model, params);
34
51
  }
35
- async start(url) {
36
- await mongoose_1.default.connect(url);
52
+ async start() {
53
+ if (__classPrivateFieldGet(this, _MongoDb_started, "f"))
54
+ return;
55
+ __classPrivateFieldSet(this, _MongoDb_started, true, "f");
56
+ mongoose_1.default.set('strictQuery', true);
57
+ await mongoose_1.default.connect(instance_1.Instance.get().settings.mongoDbURI);
58
+ await Promise.all([mongoose_1.default.connection, ...__classPrivateFieldGet(this, _MongoDb_instances, "a", _MongoDb_connections_get)].map((conn) => {
59
+ return Object.values(conn.models)
60
+ .map(async (model) => {
61
+ await conn.db.command({ collMod: model.collection.name, changeStreamPreAndPostImages: { enabled: true } });
62
+ });
63
+ }).flat());
37
64
  }
38
65
  async close() {
66
+ await Promise.all(__classPrivateFieldGet(this, _MongoDb_instances, "a", _MongoDb_connections_get).map(async (conn) => conn.close()));
39
67
  await mongoose_1.default.disconnect();
40
68
  }
41
69
  }
42
70
  exports.MongoDb = MongoDb;
43
- class MongoDbChange extends _instance_1.DbChange {
44
- constructor(collection, mapper) {
45
- super();
46
- _MongoDbChange_started.set(this, false);
47
- _MongoDbChange_col.set(this, void 0);
48
- _MongoDbChange_mapper.set(this, void 0);
49
- __classPrivateFieldSet(this, _MongoDbChange_col, collection, "f");
50
- __classPrivateFieldSet(this, _MongoDbChange_mapper, mapper, "f");
51
- this._cbs = {};
52
- }
53
- async start(skipResume = false) {
54
- if (__classPrivateFieldGet(this, _MongoDbChange_started, "f"))
55
- return;
56
- __classPrivateFieldSet(this, _MongoDbChange_started, true, "f");
57
- const dbName = __classPrivateFieldGet(this, _MongoDbChange_col, "f").collection.collectionName;
58
- const cloneName = dbName + '_streams_clone';
59
- const getClone = () => __classPrivateFieldGet(this, _MongoDbChange_col, "f").collection.conn.db.collection(cloneName);
60
- const getStreamTokens = () => __classPrivateFieldGet(this, _MongoDbChange_col, "f").collection.conn.db.collection('stream-tokens');
61
- const res = await getStreamTokens().findOne({ _id: dbName });
62
- const resumeToken = skipResume ? undefined : res?.resumeToken;
63
- const changeStream = __classPrivateFieldGet(this, _MongoDbChange_col, "f")
64
- .watch([], { fullDocument: 'updateLookup', startAfter: resumeToken })
65
- .on('change', async (data) => {
66
- const streamId = data._id._data;
67
- const cacheName = `streams-${streamId}`;
68
- const cached = await instance_1.Instance.get().cache.setInTransaction(cacheName, streamId, 15);
69
- if (cached[0])
70
- return;
71
- (0, exit_1.addWaitBeforeExit)((async () => {
72
- await getStreamTokens().findOneAndUpdate({ _id: dbName }, { $set: { resumeToken: data._id } }, { upsert: true });
73
- if (data.operationType === 'insert') {
74
- const _id = data.documentKey._id;
75
- const after = data.fullDocument;
76
- const { value } = await getClone().findOneAndUpdate({ _id }, { $set: { ...after, _id } }, {
77
- upsert: true,
78
- returnDocument: 'after'
79
- });
80
- if (value)
81
- this._cbs.created?.({
82
- before: null,
83
- after: __classPrivateFieldGet(this, _MongoDbChange_mapper, "f").call(this, new (__classPrivateFieldGet(this, _MongoDbChange_col, "f"))(after))
84
- });
85
- }
86
- if (data.operationType === 'delete') {
87
- const _id = data.documentKey._id;
88
- const { value: before } = await getClone().findOneAndDelete({ _id });
89
- if (before)
90
- this._cbs.deleted?.({
91
- before: __classPrivateFieldGet(this, _MongoDbChange_mapper, "f").call(this, new (__classPrivateFieldGet(this, _MongoDbChange_col, "f"))(before)),
92
- after: null
93
- });
94
- }
95
- if (data.operationType === 'update') {
96
- const _id = data.documentKey._id;
97
- const after = data.fullDocument;
98
- const { value: before } = await getClone().findOneAndUpdate({ _id }, { $set: after }, { returnDocument: 'before' });
99
- const { updatedFields = {}, removedFields = [], truncatedArrays = [] } = data.updateDescription ?? {};
100
- const changed = removedFields
101
- .map((f) => f.toString())
102
- .concat(truncatedArrays.map((a) => a.field))
103
- .concat(Object.keys(updatedFields));
104
- const changes = validations_1.Validation.Differ.from(changed);
105
- if (before)
106
- this._cbs.updated?.({
107
- before: __classPrivateFieldGet(this, _MongoDbChange_mapper, "f").call(this, new (__classPrivateFieldGet(this, _MongoDbChange_col, "f"))(before)),
108
- after: __classPrivateFieldGet(this, _MongoDbChange_mapper, "f").call(this, new (__classPrivateFieldGet(this, _MongoDbChange_col, "f"))(after)),
109
- changes
110
- });
111
- }
112
- })());
113
- })
114
- .on('error', async (err) => {
115
- await instance_1.Instance.get().logger.error(`Change Stream errored out: ${dbName}: ${err.message}`);
116
- changeStream.close();
117
- return this.start(true);
118
- });
119
- (0, exit_1.addWaitBeforeExit)(async () => {
120
- changeStream.close();
121
- __classPrivateFieldSet(this, _MongoDbChange_started, false, "f");
122
- });
123
- }
124
- }
125
- _MongoDbChange_started = new WeakMap(), _MongoDbChange_col = new WeakMap(), _MongoDbChange_mapper = new WeakMap();
71
+ _MongoDb_started = new WeakMap(), _MongoDb_instances = new WeakSet(), _MongoDb_connections_get = function _MongoDb_connections_get() {
72
+ return mongoose_1.default.connection.otherDbs ?? [];
73
+ };
@@ -1,3 +1,3 @@
1
- import { QueryParams, QueryResults } from '../query';
2
1
  import mongoose from 'mongoose';
3
- export declare const parseMongodbQueryParams: <Model>(collection: mongoose.Model<any, {}, {}, {}, any>, params: QueryParams) => Promise<QueryResults<Model>>;
2
+ import { QueryParams, QueryResults } from '../query';
3
+ export declare const parseMongodbQueryParams: <Model>(model: mongoose.Model<Model, {}, {}, {}, mongoose.IfAny<Model, any, mongoose.Document<unknown, {}, Model> & Omit<mongoose.Require_id<Model>, never>>, any>, params: QueryParams) => Promise<QueryResults<Model>>;