Browse Source

feat: Add ts-patch-mongoose package and integrate events

Owen Diffey 1 year ago
parent
commit
b653691a20

+ 312 - 0
backend/package-lock.json

@@ -23,6 +23,7 @@
 				"redis": "^4.6.6",
 				"retry-axios": "^3.0.0",
 				"sha256": "^0.2.0",
+				"ts-patch-mongoose": "^2.0.5",
 				"ws": "^8.13.0"
 			},
 			"devDependencies": {
@@ -1484,6 +1485,14 @@
 			"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
 			"dev": true
 		},
+		"node_modules/deepmerge": {
+			"version": "2.2.1",
+			"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
+			"integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA==",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
 		"node_modules/define-properties": {
 			"version": "1.2.0",
 			"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
@@ -2219,6 +2228,11 @@
 				"node": ">= 6"
 			}
 		},
+		"node_modules/fast-json-patch": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz",
+			"integrity": "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ=="
+		},
 		"node_modules/fast-json-stable-stringify": {
 			"version": "2.1.0",
 			"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@@ -2541,6 +2555,14 @@
 				"url": "https://github.com/sponsors/ljharb"
 			}
 		},
+		"node_modules/get-value": {
+			"version": "2.0.6",
+			"resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+			"integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA==",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
 		"node_modules/glob": {
 			"version": "7.2.0",
 			"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
@@ -2724,6 +2746,43 @@
 			"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
 			"integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
 		},
+		"node_modules/has-value": {
+			"version": "0.3.1",
+			"resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+			"integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==",
+			"dependencies": {
+				"get-value": "^2.0.3",
+				"has-values": "^0.1.4",
+				"isobject": "^2.0.0"
+			},
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/has-value/node_modules/isarray": {
+			"version": "1.0.0",
+			"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+			"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+		},
+		"node_modules/has-value/node_modules/isobject": {
+			"version": "2.1.0",
+			"resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+			"integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==",
+			"dependencies": {
+				"isarray": "1.0.0"
+			},
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
+		"node_modules/has-values": {
+			"version": "0.1.4",
+			"resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+			"integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ==",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
 		"node_modules/haxec": {
 			"version": "2.0.1",
 			"resolved": "https://registry.npmjs.org/haxec/-/haxec-2.0.1.tgz",
@@ -3035,6 +3094,17 @@
 				"node": ">=8"
 			}
 		},
+		"node_modules/is-plain-object": {
+			"version": "2.0.4",
+			"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+			"integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+			"dependencies": {
+				"isobject": "^3.0.1"
+			},
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
 		"node_modules/is-regex": {
 			"version": "1.1.4",
 			"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
@@ -3157,6 +3227,14 @@
 			"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
 			"dev": true
 		},
+		"node_modules/isobject": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+			"integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg==",
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
 		"node_modules/jju": {
 			"version": "1.4.0",
 			"resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz",
@@ -3250,6 +3328,11 @@
 				"url": "https://github.com/sponsors/sindresorhus"
 			}
 		},
+		"node_modules/lodash": {
+			"version": "4.17.21",
+			"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+			"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+		},
 		"node_modules/lodash.get": {
 			"version": "4.4.2",
 			"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
@@ -3598,6 +3681,11 @@
 				"whatwg-url": "^11.0.0"
 			}
 		},
+		"node_modules/mongolike-operations": {
+			"version": "0.1.5",
+			"resolved": "https://registry.npmjs.org/mongolike-operations/-/mongolike-operations-0.1.5.tgz",
+			"integrity": "sha512-sYFNudINVxHuC68pJp4HWi4IXL3GPLeg4s/wSEWPyNKAd62cpXli9FYwex3d1eLo718o2znXlINfjRK+LZOoeA=="
+		},
 		"node_modules/mongoose": {
 			"version": "7.2.0",
 			"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-7.2.0.tgz",
@@ -3796,6 +3884,15 @@
 				"set-blocking": "^2.0.0"
 			}
 		},
+		"node_modules/oad-utils": {
+			"version": "0.3.0",
+			"resolved": "https://registry.npmjs.org/oad-utils/-/oad-utils-0.3.0.tgz",
+			"integrity": "sha512-qjVeIGscc9DBzdpMiVws7ymrcinOcpIaqRYfJHjXhsJeg7/ex+pZ6kfMu98EVBmR7+vulzVRO7L9CrJNyUDvLw==",
+			"dependencies": {
+				"deepmerge": "^2.0.0",
+				"mongolike-operations": "~0.1.5"
+			}
+		},
 		"node_modules/oauth": {
 			"version": "0.10.0",
 			"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.10.0.tgz",
@@ -3875,6 +3972,18 @@
 				"url": "https://github.com/sponsors/ljharb"
 			}
 		},
+		"node_modules/omit-deep": {
+			"version": "0.3.0",
+			"resolved": "https://registry.npmjs.org/omit-deep/-/omit-deep-0.3.0.tgz",
+			"integrity": "sha512-Lbl/Ma59sss2b15DpnWnGmECBRL8cRl/PjPbPMVW+Y8zIQzRrwMaI65Oy6HvxyhYeILVKBJb2LWeG81bj5zbMg==",
+			"dependencies": {
+				"is-plain-object": "^2.0.1",
+				"unset-value": "^0.1.1"
+			},
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
 		"node_modules/on-finished": {
 			"version": "2.4.1",
 			"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
@@ -4028,6 +4137,37 @@
 				"url": "https://github.com/sponsors/jonschlinkert"
 			}
 		},
+		"node_modules/power-assign": {
+			"version": "0.2.10",
+			"resolved": "https://registry.npmjs.org/power-assign/-/power-assign-0.2.10.tgz",
+			"integrity": "sha512-UNSXkOYJj8MVnpDdg7skBWRWbpPR7ukXTxo6ypcXQQwOaqa6v0kXbMM5SzvExozufvPfrDlsjyQvTd8Ub3pEGA==",
+			"dependencies": {
+				"fast-deep-equal": "^1.0.0",
+				"mongolike-operations": "~0.1.5",
+				"oad-utils": "~0.3.0",
+				"power-filter": "~0.1.9"
+			}
+		},
+		"node_modules/power-assign/node_modules/fast-deep-equal": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
+			"integrity": "sha512-fueX787WZKCV0Is4/T2cyAdM4+x1S3MXXOAhavE1ys/W42SHAPacLTQhucja22QBYrfGw50M2sRiXPtTGv9Ymw=="
+		},
+		"node_modules/power-filter": {
+			"version": "0.1.9",
+			"resolved": "https://registry.npmjs.org/power-filter/-/power-filter-0.1.9.tgz",
+			"integrity": "sha512-sVZyGCwnopn9Wb9YlyfkZOXxoaXVC4z7hLXkSK48aF5iEH+ocGxUyKATR7Kf7s+vb2mDd8IFWiMY77+r/8ORmA==",
+			"dependencies": {
+				"fast-deep-equal": "^1.0.0",
+				"mongolike-operations": "~0.1.5",
+				"oad-utils": "~0.3.0"
+			}
+		},
+		"node_modules/power-filter/node_modules/fast-deep-equal": {
+			"version": "1.1.0",
+			"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
+			"integrity": "sha512-fueX787WZKCV0Is4/T2cyAdM4+x1S3MXXOAhavE1ys/W42SHAPacLTQhucja22QBYrfGw50M2sRiXPtTGv9Ymw=="
+		},
 		"node_modules/prelude-ls": {
 			"version": "1.2.1",
 			"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -4911,6 +5051,24 @@
 				"node": ">=0.3.1"
 			}
 		},
+		"node_modules/ts-patch-mongoose": {
+			"version": "2.0.5",
+			"resolved": "https://registry.npmjs.org/ts-patch-mongoose/-/ts-patch-mongoose-2.0.5.tgz",
+			"integrity": "sha512-P9voVoKGHhetpfLc3Z3cXrkritN0qMMpxNYfolUieI7LyJuZnksZRMMnQgS02/fzlH9+tp+0Nu3w+WbrXu8mkw==",
+			"dependencies": {
+				"fast-json-patch": "3.1.1",
+				"lodash": "4.17.21",
+				"omit-deep": "0.3.0",
+				"power-assign": "0.2.10",
+				"semver": "7.5.1"
+			},
+			"engines": {
+				"node": "^14.15.0 || ^16.10.0 || >=18.0.0"
+			},
+			"peerDependencies": {
+				"mongoose": ">=6.6.0 < 8"
+			}
+		},
 		"node_modules/tsconfig": {
 			"version": "7.0.0",
 			"resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz",
@@ -5072,6 +5230,18 @@
 				"node": ">= 0.8"
 			}
 		},
+		"node_modules/unset-value": {
+			"version": "0.1.2",
+			"resolved": "https://registry.npmjs.org/unset-value/-/unset-value-0.1.2.tgz",
+			"integrity": "sha512-yhv5I4TsldLdE3UcVQn0hD2T5sNCPv4+qm/CTUpRKIpwthYRIipsAPdsrNpOI79hPQa0rTTeW22Fq6JWRcTgNg==",
+			"dependencies": {
+				"has-value": "^0.3.1",
+				"isobject": "^3.0.0"
+			},
+			"engines": {
+				"node": ">=0.10.0"
+			}
+		},
 		"node_modules/uri-js": {
 			"version": "4.4.1",
 			"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
@@ -6433,6 +6603,11 @@
 			"integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
 			"dev": true
 		},
+		"deepmerge": {
+			"version": "2.2.1",
+			"resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-2.2.1.tgz",
+			"integrity": "sha512-R9hc1Xa/NOBi9WRVUWg19rl1UB7Tt4kuPd+thNJgFZoxXsTz7ncaPaeIm+40oSGuP33DfMb4sZt1QIGiJzC4EA=="
+		},
 		"define-properties": {
 			"version": "1.2.0",
 			"resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.2.0.tgz",
@@ -7011,6 +7186,11 @@
 				}
 			}
 		},
+		"fast-json-patch": {
+			"version": "3.1.1",
+			"resolved": "https://registry.npmjs.org/fast-json-patch/-/fast-json-patch-3.1.1.tgz",
+			"integrity": "sha512-vf6IHUX2SBcA+5/+4883dsIjpBTqmfBjmYiWK1savxQmFk4JfBMLa7ynTYOs1Rolp/T1betJxHiGD3g1Mn8lUQ=="
+		},
 		"fast-json-stable-stringify": {
 			"version": "2.1.0",
 			"resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
@@ -7254,6 +7434,11 @@
 				"get-intrinsic": "^1.1.1"
 			}
 		},
+		"get-value": {
+			"version": "2.0.6",
+			"resolved": "https://registry.npmjs.org/get-value/-/get-value-2.0.6.tgz",
+			"integrity": "sha512-Ln0UQDlxH1BapMu3GPtf7CuYNwRZf2gwCuPqbyG6pB8WfmFpzqcy4xtAaAMUhnNqjMKTiCPZG2oMT3YSx8U2NA=="
+		},
 		"glob": {
 			"version": "7.2.0",
 			"resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz",
@@ -7377,6 +7562,36 @@
 			"resolved": "https://registry.npmjs.org/has-unicode/-/has-unicode-2.0.1.tgz",
 			"integrity": "sha512-8Rf9Y83NBReMnx0gFzA8JImQACstCYWUplepDa9xprwwtmgEZUF0h/i5xSA625zB/I37EtrswSST6OXxwaaIJQ=="
 		},
+		"has-value": {
+			"version": "0.3.1",
+			"resolved": "https://registry.npmjs.org/has-value/-/has-value-0.3.1.tgz",
+			"integrity": "sha512-gpG936j8/MzaeID5Yif+577c17TxaDmhuyVgSwtnL/q8UUTySg8Mecb+8Cf1otgLoD7DDH75axp86ER7LFsf3Q==",
+			"requires": {
+				"get-value": "^2.0.3",
+				"has-values": "^0.1.4",
+				"isobject": "^2.0.0"
+			},
+			"dependencies": {
+				"isarray": {
+					"version": "1.0.0",
+					"resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+					"integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+				},
+				"isobject": {
+					"version": "2.1.0",
+					"resolved": "https://registry.npmjs.org/isobject/-/isobject-2.1.0.tgz",
+					"integrity": "sha512-+OUdGJlgjOBZDfxnDjYYG6zp487z0JGNQq3cYQYg5f5hKR+syHMsaztzGeml/4kGG55CSpKSpWTY+jYGgsHLgA==",
+					"requires": {
+						"isarray": "1.0.0"
+					}
+				}
+			}
+		},
+		"has-values": {
+			"version": "0.1.4",
+			"resolved": "https://registry.npmjs.org/has-values/-/has-values-0.1.4.tgz",
+			"integrity": "sha512-J8S0cEdWuQbqD9//tlZxiMuMNmxB8PlEwvYwuxsTmR1G5RXUePEX/SJn7aD0GMLieuZYSwNH0cQuJGwnYunXRQ=="
+		},
 		"haxec": {
 			"version": "2.0.1",
 			"resolved": "https://registry.npmjs.org/haxec/-/haxec-2.0.1.tgz",
@@ -7595,6 +7810,14 @@
 			"integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
 			"dev": true
 		},
+		"is-plain-object": {
+			"version": "2.0.4",
+			"resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-2.0.4.tgz",
+			"integrity": "sha512-h5PpgXkWitc38BBMYawTYMWJHFZJVnBquFE57xFpjB8pJFiF6gZ+bU+WyI/yqXiFR5mdLsgYNaPe8uao6Uv9Og==",
+			"requires": {
+				"isobject": "^3.0.1"
+			}
+		},
 		"is-regex": {
 			"version": "1.1.4",
 			"resolved": "https://registry.npmjs.org/is-regex/-/is-regex-1.1.4.tgz",
@@ -7678,6 +7901,11 @@
 			"integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
 			"dev": true
 		},
+		"isobject": {
+			"version": "3.0.1",
+			"resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz",
+			"integrity": "sha512-WhB9zCku7EGTj/HQQRz5aUQEUeoQZH2bWcltRErOpymJ4boYE6wL9Tbr23krRPSZ+C5zqNSrSw+Cc7sZZ4b7vg=="
+		},
 		"jju": {
 			"version": "1.4.0",
 			"resolved": "https://registry.npmjs.org/jju/-/jju-1.4.0.tgz",
@@ -7746,6 +7974,11 @@
 				"p-locate": "^5.0.0"
 			}
 		},
+		"lodash": {
+			"version": "4.17.21",
+			"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+			"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
+		},
 		"lodash.get": {
 			"version": "4.4.2",
 			"resolved": "https://registry.npmjs.org/lodash.get/-/lodash.get-4.4.2.tgz",
@@ -7993,6 +8226,11 @@
 				"whatwg-url": "^11.0.0"
 			}
 		},
+		"mongolike-operations": {
+			"version": "0.1.5",
+			"resolved": "https://registry.npmjs.org/mongolike-operations/-/mongolike-operations-0.1.5.tgz",
+			"integrity": "sha512-sYFNudINVxHuC68pJp4HWi4IXL3GPLeg4s/wSEWPyNKAd62cpXli9FYwex3d1eLo718o2znXlINfjRK+LZOoeA=="
+		},
 		"mongoose": {
 			"version": "7.2.0",
 			"resolved": "https://registry.npmjs.org/mongoose/-/mongoose-7.2.0.tgz",
@@ -8152,6 +8390,15 @@
 				"set-blocking": "^2.0.0"
 			}
 		},
+		"oad-utils": {
+			"version": "0.3.0",
+			"resolved": "https://registry.npmjs.org/oad-utils/-/oad-utils-0.3.0.tgz",
+			"integrity": "sha512-qjVeIGscc9DBzdpMiVws7ymrcinOcpIaqRYfJHjXhsJeg7/ex+pZ6kfMu98EVBmR7+vulzVRO7L9CrJNyUDvLw==",
+			"requires": {
+				"deepmerge": "^2.0.0",
+				"mongolike-operations": "~0.1.5"
+			}
+		},
 		"oauth": {
 			"version": "0.10.0",
 			"resolved": "https://registry.npmjs.org/oauth/-/oauth-0.10.0.tgz",
@@ -8207,6 +8454,15 @@
 				"es-abstract": "^1.20.4"
 			}
 		},
+		"omit-deep": {
+			"version": "0.3.0",
+			"resolved": "https://registry.npmjs.org/omit-deep/-/omit-deep-0.3.0.tgz",
+			"integrity": "sha512-Lbl/Ma59sss2b15DpnWnGmECBRL8cRl/PjPbPMVW+Y8zIQzRrwMaI65Oy6HvxyhYeILVKBJb2LWeG81bj5zbMg==",
+			"requires": {
+				"is-plain-object": "^2.0.1",
+				"unset-value": "^0.1.1"
+			}
+		},
 		"on-finished": {
 			"version": "2.4.1",
 			"resolved": "https://registry.npmjs.org/on-finished/-/on-finished-2.4.1.tgz",
@@ -8315,6 +8571,41 @@
 			"integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
 			"dev": true
 		},
+		"power-assign": {
+			"version": "0.2.10",
+			"resolved": "https://registry.npmjs.org/power-assign/-/power-assign-0.2.10.tgz",
+			"integrity": "sha512-UNSXkOYJj8MVnpDdg7skBWRWbpPR7ukXTxo6ypcXQQwOaqa6v0kXbMM5SzvExozufvPfrDlsjyQvTd8Ub3pEGA==",
+			"requires": {
+				"fast-deep-equal": "^1.0.0",
+				"mongolike-operations": "~0.1.5",
+				"oad-utils": "~0.3.0",
+				"power-filter": "~0.1.9"
+			},
+			"dependencies": {
+				"fast-deep-equal": {
+					"version": "1.1.0",
+					"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
+					"integrity": "sha512-fueX787WZKCV0Is4/T2cyAdM4+x1S3MXXOAhavE1ys/W42SHAPacLTQhucja22QBYrfGw50M2sRiXPtTGv9Ymw=="
+				}
+			}
+		},
+		"power-filter": {
+			"version": "0.1.9",
+			"resolved": "https://registry.npmjs.org/power-filter/-/power-filter-0.1.9.tgz",
+			"integrity": "sha512-sVZyGCwnopn9Wb9YlyfkZOXxoaXVC4z7hLXkSK48aF5iEH+ocGxUyKATR7Kf7s+vb2mDd8IFWiMY77+r/8ORmA==",
+			"requires": {
+				"fast-deep-equal": "^1.0.0",
+				"mongolike-operations": "~0.1.5",
+				"oad-utils": "~0.3.0"
+			},
+			"dependencies": {
+				"fast-deep-equal": {
+					"version": "1.1.0",
+					"resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-1.1.0.tgz",
+					"integrity": "sha512-fueX787WZKCV0Is4/T2cyAdM4+x1S3MXXOAhavE1ys/W42SHAPacLTQhucja22QBYrfGw50M2sRiXPtTGv9Ymw=="
+				}
+			}
+		},
 		"prelude-ls": {
 			"version": "1.2.1",
 			"resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
@@ -8936,6 +9227,18 @@
 				}
 			}
 		},
+		"ts-patch-mongoose": {
+			"version": "2.0.5",
+			"resolved": "https://registry.npmjs.org/ts-patch-mongoose/-/ts-patch-mongoose-2.0.5.tgz",
+			"integrity": "sha512-P9voVoKGHhetpfLc3Z3cXrkritN0qMMpxNYfolUieI7LyJuZnksZRMMnQgS02/fzlH9+tp+0Nu3w+WbrXu8mkw==",
+			"requires": {
+				"fast-json-patch": "3.1.1",
+				"lodash": "4.17.21",
+				"omit-deep": "0.3.0",
+				"power-assign": "0.2.10",
+				"semver": "7.5.1"
+			}
+		},
 		"tsconfig": {
 			"version": "7.0.0",
 			"resolved": "https://registry.npmjs.org/tsconfig/-/tsconfig-7.0.0.tgz",
@@ -9058,6 +9361,15 @@
 			"resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
 			"integrity": "sha512-pjy2bYhSsufwWlKwPc+l3cN7+wuJlK6uz0YdJEOlQDbl6jo/YlPi4mb8agUkVC8BF7V8NuzeyPNqRksA3hztKQ=="
 		},
+		"unset-value": {
+			"version": "0.1.2",
+			"resolved": "https://registry.npmjs.org/unset-value/-/unset-value-0.1.2.tgz",
+			"integrity": "sha512-yhv5I4TsldLdE3UcVQn0hD2T5sNCPv4+qm/CTUpRKIpwthYRIipsAPdsrNpOI79hPQa0rTTeW22Fq6JWRcTgNg==",
+			"requires": {
+				"has-value": "^0.3.1",
+				"isobject": "^3.0.0"
+			}
+		},
 		"uri-js": {
 			"version": "4.4.1",
 			"resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",

+ 1 - 0
backend/package.json

@@ -31,6 +31,7 @@
 		"redis": "^4.6.6",
 		"retry-axios": "^3.0.0",
 		"sha256": "^0.2.0",
+		"ts-patch-mongoose": "^2.0.5",
 		"ws": "^8.13.0"
 	},
 	"devDependencies": {

+ 2 - 2
backend/src/main.ts

@@ -89,7 +89,7 @@ setTimeout(async () => {
 	await jobQueue.runJob("events", "subscribe", {
 		channel: "test",
 		type: "schedule",
-		callback: () => {
+		callback: async () => {
 			console.log(`SCHEDULED: ${now} :: ${Date.now()}`);
 		}
 	});
@@ -97,7 +97,7 @@ setTimeout(async () => {
 	// Events (was cache pub/sub)
 	await jobQueue.runJob("events", "subscribe", {
 		channel: "test",
-		callback: value => {
+		callback: async value => {
 			console.log(`PUBLISHED: ${value}`);
 		}
 	});

+ 115 - 54
backend/src/modules/DataModule.ts

@@ -6,6 +6,7 @@ import mongoose, {
 	MongooseDistinctQueryMiddleware,
 	MongooseQueryOrDocumentMiddleware
 } from "mongoose";
+import { patchHistoryPlugin, patchEventEmitter } from "ts-patch-mongoose";
 import { readdir } from "fs/promises";
 import path from "path";
 import JobContext from "../JobContext";
@@ -16,6 +17,7 @@ import { Schemas } from "../types/Schemas";
 import documentVersionPlugin from "../schemas/plugins/documentVersion";
 import getDataPlugin from "../schemas/plugins/getData";
 import Migration from "../Migration";
+import JobQueue from "../JobQueue";
 
 export default class DataModule extends BaseModule {
 	private models?: Models;
@@ -24,11 +26,15 @@ export default class DataModule extends BaseModule {
 
 	//	private redisClient?: RedisClientType;
 
+	private jobQueue: JobQueue;
+
 	/**
 	 * Data Module
 	 */
 	public constructor() {
 		super("data");
+
+		this.jobQueue = JobQueue.getPrimaryInstance();
 	}
 
 	/**
@@ -37,30 +43,7 @@ export default class DataModule extends BaseModule {
 	public override async startup() {
 		await super.startup();
 
-		const { user, password, host, port, database } = config.get<{
-			user: string;
-			password: string;
-			host: string;
-			port: number;
-			database: string;
-		}>("mongo");
-		const mongoUrl = `mongodb://${user}:${password}@${host}:${port}/${database}`;
-
-		this.mongoConnection = await mongoose
-			.createConnection(mongoUrl)
-			.asPromise();
-
-		this.mongoConnection.set("runValidators", true);
-		this.mongoConnection.set("sanitizeFilter", true);
-		this.mongoConnection.set("strict", "throw");
-		this.mongoConnection.set("strictQuery", "throw");
-
-		mongoose.SchemaTypes.String.set("trim", true);
-
-		this.mongoConnection.plugin(documentVersionPlugin);
-		this.mongoConnection.plugin(getDataPlugin, {
-			tags: ["useGetDataPlugin"]
-		});
+		await this.createMongoConnection();
 
 		await this.runMigrations();
 
@@ -102,9 +85,92 @@ export default class DataModule extends BaseModule {
 	public override async shutdown() {
 		await super.shutdown();
 		//		if (this.redisClient) await this.redisClient.quit();
+		patchEventEmitter.removeAllListeners();
 		if (this.mongoConnection) await this.mongoConnection.close();
 	}
 
+	/**
+	 * createMongoConnection - Create mongo connection
+	 */
+	private async createMongoConnection() {
+		const { user, password, host, port, database } = config.get<{
+			user: string;
+			password: string;
+			host: string;
+			port: number;
+			database: string;
+		}>("mongo");
+		const mongoUrl = `mongodb://${user}:${password}@${host}:${port}/${database}`;
+
+		this.mongoConnection = await mongoose
+			.createConnection(mongoUrl)
+			.asPromise();
+
+		this.mongoConnection.set("runValidators", true);
+		this.mongoConnection.set("sanitizeFilter", true);
+		this.mongoConnection.set("strict", "throw");
+		this.mongoConnection.set("strictQuery", "throw");
+	}
+
+	/**
+	 * registerEvents - Register events for schema with event module
+	 */
+	private async registerEvents<
+		ModelName extends keyof Models,
+		SchemaType extends Schemas[keyof ModelName]
+	>(modelName: ModelName, schema: SchemaType) {
+		// const preMethods: string[] = [
+		// 	"aggregate",
+		// 	"count",
+		// 	"countDocuments",
+		// 	"deleteOne",
+		// 	"deleteMany",
+		// 	"estimatedDocumentCount",
+		// 	"find",
+		// 	"findOne",
+		// 	"findOneAndDelete",
+		// 	"findOneAndRemove",
+		// 	"findOneAndReplace",
+		// 	"findOneAndUpdate",
+		// 	"init",
+		// 	"insertMany",
+		// 	"remove",
+		// 	"replaceOne",
+		// 	"save",
+		// 	"update",
+		// 	"updateOne",
+		// 	"updateMany",
+		// 	"validate"
+		// ];
+
+		// preMethods.forEach(preMethod => {
+		// 	// @ts-ignore
+		// 	schema.pre(preMethods, () => {
+		// 		console.log(`Pre-${preMethod}!`);
+		// 	});
+		// });
+
+		const { enabled, eventCreated, eventUpdated, eventDeleted } =
+			schema.get("patchHistory") ?? {};
+
+		if (!enabled) return;
+
+		Object.entries({
+			created: eventCreated,
+			updated: eventUpdated,
+			deleted: eventDeleted
+		})
+			.filter(([, event]) => !!event)
+			.forEach(([action, event]) => {
+				patchEventEmitter.on(event, async ({ doc }) => {
+					await this.jobQueue.runJob("events", "publish", {
+						channel: `model.${modelName}.${doc._id}.${action}`,
+						value: doc
+					});
+				});
+			});
+	}
+
 	/**
 	 * loadModel - Import and load model schema
 	 *
@@ -120,36 +186,29 @@ export default class DataModule extends BaseModule {
 			`../schemas/${modelName.toString()}`
 		);
 
-		const preMethods: string[] = [
-			"aggregate",
-			"count",
-			"countDocuments",
-			"deleteOne",
-			"deleteMany",
-			"estimatedDocumentCount",
-			"find",
-			"findOne",
-			"findOneAndDelete",
-			"findOneAndRemove",
-			"findOneAndReplace",
-			"findOneAndUpdate",
-			"init",
-			"insertMany",
-			"remove",
-			"replaceOne",
-			"save",
-			"update",
-			"updateOne",
-			"updateMany",
-			"validate"
-		];
-
-		preMethods.forEach(preMethod => {
-			// @ts-ignore
-			schema.pre(preMethods, () => {
-				console.log(`Pre-${preMethod}!`);
-			});
-		});
+		schema.plugin(documentVersionPlugin);
+
+		schema.set("timestamps", schema.get("timestamps") ?? true);
+
+		const patchHistoryConfig = {
+			enabled: true,
+			patchHistoryDisabled: true,
+			eventCreated: `${modelName}.created`,
+			eventUpdated: `${modelName}.updated`,
+			eventDeleted: `${modelName}.deleted`,
+			...(schema.get("patchHistory") ?? {})
+		};
+		schema.set("patchHistory", patchHistoryConfig);
+
+		if (patchHistoryConfig.enabled) {
+			schema.plugin(patchHistoryPlugin, patchHistoryConfig);
+		}
+
+		const { enabled: getDataEnabled = false } = schema.get("getData") ?? {};
+
+		if (getDataEnabled) schema.plugin(getDataPlugin);
+
+		await this.registerEvents(modelName, schema);
 
 		return this.mongoConnection.model(modelName.toString(), schema);
 	}
@@ -160,6 +219,8 @@ export default class DataModule extends BaseModule {
 	 * @returns Promise
 	 */
 	private async loadModels() {
+		mongoose.SchemaTypes.String.set("trim", true);
+
 		this.models = {
 			abc: await this.loadModel("abc"),
 			news: await this.loadModel("news"),

+ 4 - 4
backend/src/modules/EventsModule.ts

@@ -10,9 +10,9 @@ export default class EventsModule extends BaseModule {
 
 	private subClient?: RedisClientType;
 
-	private subscriptions: Record<string, ((message: any) => void)[]>;
+	private subscriptions: Record<string, ((message: any) => Promise<void>)[]>;
 
-	private scheduleCallbacks: Record<string, (() => void)[]>;
+	private scheduleCallbacks: Record<string, (() => Promise<void>)[]>;
 
 	/**
 	 * Events Module
@@ -152,7 +152,7 @@ export default class EventsModule extends BaseModule {
 		payload: {
 			type?: "event" | "schedule";
 			channel: string;
-			callback: (message?: any) => void;
+			callback: (message?: any) => Promise<void>;
 			unique?: boolean;
 		}
 	) {
@@ -204,7 +204,7 @@ export default class EventsModule extends BaseModule {
 		payload: {
 			type?: "event" | "schedule";
 			channel: string;
-			callback: (message?: any) => void;
+			callback: (message?: any) => Promise<void>;
 		}
 	) {
 		if (!this.subClient) throw new Error("Redis subClient unavailable.");

+ 27 - 24
backend/src/schemas/abc.ts

@@ -1,7 +1,7 @@
 import { Model, Schema, SchemaTypes, Types } from "mongoose";
-import { BaseSchema } from "../types/Schemas";
+import { BaseSchema, TimestampsSchema } from "../types/Schemas";
 
-export interface AbcSchema extends BaseSchema {
+export interface AbcSchema extends Omit<BaseSchema, keyof TimestampsSchema> {
 	name: string;
 	autofill?: {
 		enabled?: boolean;
@@ -14,28 +14,31 @@ export interface AbcSchema extends BaseSchema {
 
 export type AbcModel = Model<AbcSchema>;
 
-export const schema = new Schema<AbcSchema, AbcModel>({
-	name: {
-		type: SchemaTypes.String,
-		required: true
+export const schema = new Schema<AbcSchema, AbcModel>(
+	{
+		name: {
+			type: SchemaTypes.String,
+			required: true
+		},
+		autofill: {
+			enabled: {
+				type: SchemaTypes.Boolean,
+				required: false
+			}
+		},
+		someNumbers: [{ type: SchemaTypes.Number }],
+		songs: [
+			{
+				_id: { type: SchemaTypes.ObjectId, required: true }
+			}
+		],
+		restrictedName: {
+			type: SchemaTypes.String,
+			restricted: true
+		},
+		aNumber: { type: SchemaTypes.Number, required: true }
 	},
-	autofill: {
-		enabled: {
-			type: SchemaTypes.Boolean,
-			required: false
-		}
-	},
-	someNumbers: [{ type: SchemaTypes.Number }],
-	songs: [
-		{
-			_id: { type: SchemaTypes.ObjectId, required: true }
-		}
-	],
-	restrictedName: {
-		type: SchemaTypes.String,
-		restricted: true
-	},
-	aNumber: { type: SchemaTypes.Number, required: true }
-});
+	{ timestamps: false }
+);
 
 export type AbcSchemaType = typeof schema;

+ 3 - 4
backend/src/schemas/news.ts

@@ -7,7 +7,7 @@ import {
 	Types
 } from "mongoose";
 import { GetData } from "./plugins/getData";
-import { BaseSchema, TimestampsSchema } from "../types/Schemas";
+import { BaseSchema } from "../types/Schemas";
 
 export enum NewsStatus {
 	DRAFT = "draft",
@@ -15,7 +15,7 @@ export enum NewsStatus {
 	ARCHIVED = "archived"
 }
 
-export interface NewsSchema extends BaseSchema, TimestampsSchema {
+export interface NewsSchema extends BaseSchema {
 	title: string;
 	markdown: string;
 	status: NewsStatus;
@@ -83,7 +83,6 @@ export const schema = new Schema<NewsSchema, NewsModel, {}, NewsQueryHelpers>(
 	{
 		// @ts-ignore
 		documentVersion: 3,
-		timestamps: true,
 		query: {
 			published() {
 				return this.where({ status: NewsStatus.PUBLISHED });
@@ -94,9 +93,9 @@ export const schema = new Schema<NewsSchema, NewsModel, {}, NewsQueryHelpers>(
 				return query;
 			}
 		},
-		pluginTags: ["useGetDataPlugin"],
 		// @ts-ignore need to somehow use GetDataSchemaOptions
 		getData: {
+			enabled: true,
 			specialProperties: {
 				createdBy: [
 					{

+ 3 - 4
backend/src/schemas/station.ts

@@ -1,6 +1,6 @@
 import { Model, Schema, SchemaTypes, Types } from "mongoose";
 import { GetData } from "./plugins/getData";
-import { BaseSchema, TimestampsSchema } from "../types/Schemas";
+import { BaseSchema } from "../types/Schemas";
 
 export enum StationType {
 	OFFICIAL = "official",
@@ -31,7 +31,7 @@ export enum StationAutofillMode {
 	SEQUENTIAL = "sequential"
 }
 
-export interface StationSchema extends BaseSchema, TimestampsSchema {
+export interface StationSchema extends BaseSchema {
 	type: StationType;
 	name: string;
 	displayName: string;
@@ -176,10 +176,9 @@ export const schema = new Schema<StationSchema, StationModel>(
 	{
 		// @ts-ignore
 		documentVersion: 10,
-		timestamps: true,
-		pluginTags: ["useGetDataPlugin"],
 		// @ts-ignore
 		getData: {
+			enabled: true,
 			specialProperties: {
 				owner: [
 					{

+ 1 - 1
backend/src/types/Schemas.ts

@@ -4,7 +4,7 @@ import { NewsSchemaType } from "../schemas/news";
 import { StationSchemaType } from "../schemas/station";
 
 // eslint-disable-next-line
-export interface BaseSchema extends DocumentVersion {}
+export interface BaseSchema extends DocumentVersion, TimestampsSchema {}
 
 export interface TimestampsSchema {
 	createdAt: number;