@web-ts-toolkit/moo 0.30.0 → 0.31.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.
- package/index.js +225 -7
- package/index.mjs +203 -3
- package/is.js +3 -1
- package/is.mjs +2 -1
- package/package.json +2 -2
- package/plugins/cascade-delete.js +35 -14
- package/plugins/cascade-delete.mjs +31 -11
- package/plugins/index.js +183 -5
- package/plugins/index.mjs +164 -2
- package/plugins/model-function.js +3 -1
- package/plugins/model-function.mjs +2 -1
- package/schema.js +4 -2
- package/schema.mjs +3 -2
- package/utils/index.js +6 -4
- package/utils/index.mjs +5 -4
package/index.js
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
6
12
|
var __copyProps = (to, from, except, desc) => {
|
|
7
13
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
14
|
for (let key of __getOwnPropNames(from))
|
|
@@ -11,16 +17,228 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
11
17
|
}
|
|
12
18
|
return to;
|
|
13
19
|
};
|
|
14
|
-
var
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
15
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/index.ts
|
|
16
31
|
var index_exports = {};
|
|
32
|
+
__export(index_exports, {
|
|
33
|
+
cascadeDeletePlugin: () => cascadeDeletePlugin,
|
|
34
|
+
isObjectId: () => isObjectId,
|
|
35
|
+
modelFunctionPlugin: () => modelFunctionPlugin,
|
|
36
|
+
uniqueEmptiableString: () => uniqueEmptiableString,
|
|
37
|
+
uniqueNullableString: () => uniqueNullableString
|
|
38
|
+
});
|
|
17
39
|
module.exports = __toCommonJS(index_exports);
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
40
|
+
|
|
41
|
+
// src/plugins/model-function.ts
|
|
42
|
+
var import_mongoose = __toESM(require("mongoose"));
|
|
43
|
+
var invokePluginFunction = (fn, document, args) => {
|
|
44
|
+
return fn.call(import_mongoose.default, document, ...args);
|
|
45
|
+
};
|
|
46
|
+
function modelFunctionPlugin(schema, options) {
|
|
47
|
+
const { fnName, fn } = options;
|
|
48
|
+
schema.static(fnName, function staticFn(doc, ...args) {
|
|
49
|
+
return invokePluginFunction(fn, doc, args);
|
|
50
|
+
});
|
|
51
|
+
schema.method(fnName, function methodFn(...args) {
|
|
52
|
+
return invokePluginFunction(fn, this, args);
|
|
53
|
+
});
|
|
54
|
+
schema.static(
|
|
55
|
+
`${fnName}ById`,
|
|
56
|
+
async function staticByIdFn(docId, ...args) {
|
|
57
|
+
const model = await this.findById(docId);
|
|
58
|
+
if (!model) return null;
|
|
59
|
+
return invokePluginFunction(fn, model, args);
|
|
60
|
+
}
|
|
61
|
+
);
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
// src/plugins/cascade-delete.ts
|
|
65
|
+
var import_mongoose3 = __toESM(require("mongoose"));
|
|
66
|
+
var import_utils2 = require("@web-ts-toolkit/utils");
|
|
67
|
+
|
|
68
|
+
// src/utils/index.ts
|
|
69
|
+
var import_mongoose2 = require("mongoose");
|
|
70
|
+
var import_utils = require("@web-ts-toolkit/utils");
|
|
71
|
+
var isObjectIdType = (value) => value === "ObjectId" || value === import_mongoose2.Schema.Types.ObjectId;
|
|
72
|
+
var hasReferenceTarget = (value) => (0, import_utils.isString)(value.ref) || (0, import_utils.isFunction)(value.ref) || (0, import_utils.isString)(value.refPath);
|
|
73
|
+
var isReference = (value, ref) => {
|
|
74
|
+
if (Array.isArray(value)) {
|
|
75
|
+
return value.length > 0 ? isReference(value[0], ref) : false;
|
|
76
|
+
}
|
|
77
|
+
if (!(0, import_utils.isPlainObject)(value) || !isObjectIdType(value.type)) {
|
|
78
|
+
return false;
|
|
79
|
+
}
|
|
80
|
+
if (ref === void 0) {
|
|
81
|
+
return hasReferenceTarget(value);
|
|
82
|
+
}
|
|
83
|
+
return value.ref === ref;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
// src/plugins/cascade-delete.ts
|
|
87
|
+
var resolveFilter = (value, document) => {
|
|
88
|
+
const result = (0, import_utils2.isFunction)(value) ? value(document) : value;
|
|
89
|
+
if (!(0, import_utils2.isPlainObject)(result)) {
|
|
90
|
+
return null;
|
|
91
|
+
}
|
|
92
|
+
return result;
|
|
93
|
+
};
|
|
94
|
+
var applyQueryOptions = (query, options) => {
|
|
95
|
+
let builder = query;
|
|
96
|
+
if (options.select) {
|
|
97
|
+
builder = builder.select(options.select);
|
|
98
|
+
}
|
|
99
|
+
if (options.sort) {
|
|
100
|
+
builder = builder.sort(options.sort);
|
|
101
|
+
}
|
|
102
|
+
if (options.populate) {
|
|
103
|
+
builder = builder.populate(options.populate);
|
|
104
|
+
}
|
|
105
|
+
if (options.lean) {
|
|
106
|
+
builder = builder.lean();
|
|
107
|
+
}
|
|
108
|
+
return builder;
|
|
109
|
+
};
|
|
110
|
+
var supportsDeleteOne = (value) => (0, import_utils2.isObject)(value) && typeof value.deleteOne === "function";
|
|
111
|
+
var supportsRemove = (value) => (0, import_utils2.isObject)(value) && typeof value.remove === "function";
|
|
112
|
+
var mergeResults = (previousResults, modelName, currentResults) => {
|
|
113
|
+
const resultMap = (0, import_utils2.isPlainObject)(previousResults) ? previousResults : {};
|
|
114
|
+
if (currentResults === null) {
|
|
115
|
+
return resultMap;
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
...resultMap,
|
|
119
|
+
[modelName]: currentResults
|
|
120
|
+
};
|
|
121
|
+
};
|
|
122
|
+
function cascadeDeletePlugin(schema, options) {
|
|
123
|
+
const { model, localField, foreignField, foreignFilter, extraForeignFilter } = options ?? {};
|
|
124
|
+
const findDependents = async function(queryOptions) {
|
|
125
|
+
const Target = import_mongoose3.default.model(model);
|
|
126
|
+
let query = null;
|
|
127
|
+
const document = this.toObject({ virtuals: false });
|
|
128
|
+
if (foreignFilter) {
|
|
129
|
+
query = resolveFilter(foreignFilter, document);
|
|
130
|
+
} else if (localField && foreignField) {
|
|
131
|
+
const localValue = this.get(localField);
|
|
132
|
+
const extraFilter = resolveFilter(extraForeignFilter, document) ?? {};
|
|
133
|
+
query = {
|
|
134
|
+
[foreignField]: Array.isArray(localValue) ? { $in: localValue } : localValue,
|
|
135
|
+
...extraFilter
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
if (!query || !(0, import_utils2.isPlainObject)(query)) {
|
|
139
|
+
return [];
|
|
140
|
+
}
|
|
141
|
+
return await applyQueryOptions(Target.find(query), queryOptions);
|
|
142
|
+
};
|
|
143
|
+
const findOrphans = async function(queryOptions) {
|
|
144
|
+
if (!localField || !foreignField) return null;
|
|
145
|
+
const Target = import_mongoose3.default.model(model);
|
|
146
|
+
const schemaValue = Target.schema.obj[foreignField];
|
|
147
|
+
if (!schemaValue) return null;
|
|
148
|
+
const isMyRef = isReference(schemaValue, this.modelName);
|
|
149
|
+
if (!isMyRef) return null;
|
|
150
|
+
const ids = await this.distinct("_id");
|
|
151
|
+
const extraFilter = resolveFilter(extraForeignFilter) ?? {};
|
|
152
|
+
const query = {
|
|
153
|
+
[foreignField]: { $not: { $in: ids } },
|
|
154
|
+
...extraFilter
|
|
155
|
+
};
|
|
156
|
+
return await applyQueryOptions(Target.find(query), queryOptions);
|
|
157
|
+
};
|
|
158
|
+
const deleteDependents = async function() {
|
|
159
|
+
const documents = await findDependents.call(this, { select: "_id" });
|
|
160
|
+
await Promise.all(
|
|
161
|
+
documents.map((document) => {
|
|
162
|
+
if (supportsDeleteOne(document)) {
|
|
163
|
+
return document.deleteOne();
|
|
164
|
+
}
|
|
165
|
+
if (supportsRemove(document)) {
|
|
166
|
+
return document.remove();
|
|
167
|
+
}
|
|
168
|
+
return Promise.resolve();
|
|
169
|
+
})
|
|
170
|
+
);
|
|
171
|
+
};
|
|
172
|
+
schema.post("deleteOne", { document: true, query: false }, async function postDeleteOne() {
|
|
173
|
+
await deleteDependents.call(this);
|
|
174
|
+
});
|
|
175
|
+
const methodFnName = "findDependents";
|
|
176
|
+
const prevMethodFn = schema.methods[methodFnName];
|
|
177
|
+
schema.method(methodFnName, async function methodFn(modelName) {
|
|
178
|
+
if (modelName) {
|
|
179
|
+
if (modelName === model) {
|
|
180
|
+
return findDependents.call(this, {});
|
|
181
|
+
}
|
|
182
|
+
return prevMethodFn ? prevMethodFn.call(this, modelName) : null;
|
|
183
|
+
}
|
|
184
|
+
const previousResults = prevMethodFn ? await prevMethodFn.call(this) : {};
|
|
185
|
+
return mergeResults(previousResults, model, await findDependents.call(this, {}));
|
|
186
|
+
});
|
|
187
|
+
const staticFnName = "findOrphans";
|
|
188
|
+
const prevStaticFn = schema.statics[staticFnName];
|
|
189
|
+
schema.static(staticFnName, async function methodFn(modelName) {
|
|
190
|
+
if (modelName) {
|
|
191
|
+
if (modelName === model) {
|
|
192
|
+
return findOrphans.call(this, {});
|
|
193
|
+
}
|
|
194
|
+
return prevStaticFn ? prevStaticFn.call(this, modelName) : null;
|
|
195
|
+
}
|
|
196
|
+
const previousResults = prevStaticFn ? await prevStaticFn.call(this) : {};
|
|
197
|
+
const currentResults = await findOrphans.call(this, {});
|
|
198
|
+
return mergeResults(previousResults, model, currentResults);
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
// src/schema.ts
|
|
203
|
+
var uniqueNullableString = (field, options = {}) => ({
|
|
204
|
+
type: String,
|
|
205
|
+
index: { unique: true, partialFilterExpression: { [field]: { $type: "string" } } },
|
|
206
|
+
trim: true,
|
|
207
|
+
default: null,
|
|
208
|
+
...options
|
|
209
|
+
});
|
|
210
|
+
var uniqueEmptiableString = (field, options = {}) => ({
|
|
211
|
+
type: String,
|
|
212
|
+
index: {
|
|
213
|
+
unique: true,
|
|
214
|
+
partialFilterExpression: { [field]: { $type: "string", $gt: "" } }
|
|
215
|
+
},
|
|
216
|
+
trim: true,
|
|
217
|
+
default: null,
|
|
218
|
+
...options
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
// src/is.ts
|
|
222
|
+
var import_mongoose4 = require("mongoose");
|
|
223
|
+
var { ObjectId } = import_mongoose4.Types;
|
|
224
|
+
function isObjectId(value) {
|
|
225
|
+
if (!ObjectId.isValid(value)) {
|
|
226
|
+
return false;
|
|
227
|
+
}
|
|
228
|
+
try {
|
|
229
|
+
const asString = String(value);
|
|
230
|
+
const asObjectId = new ObjectId(asString);
|
|
231
|
+
const asStringifiedObjectId = asObjectId.toString();
|
|
232
|
+
return asString === asStringifiedObjectId;
|
|
233
|
+
} catch {
|
|
234
|
+
return false;
|
|
235
|
+
}
|
|
236
|
+
}
|
|
21
237
|
// Annotate the CommonJS export names for ESM import in node:
|
|
22
238
|
0 && (module.exports = {
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
239
|
+
cascadeDeletePlugin,
|
|
240
|
+
isObjectId,
|
|
241
|
+
modelFunctionPlugin,
|
|
242
|
+
uniqueEmptiableString,
|
|
243
|
+
uniqueNullableString
|
|
26
244
|
});
|
package/index.mjs
CHANGED
|
@@ -1,3 +1,203 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
1
|
+
// src/plugins/model-function.ts
|
|
2
|
+
import mongoose from "mongoose";
|
|
3
|
+
var invokePluginFunction = (fn, document, args) => {
|
|
4
|
+
return fn.call(mongoose, document, ...args);
|
|
5
|
+
};
|
|
6
|
+
function modelFunctionPlugin(schema, options) {
|
|
7
|
+
const { fnName, fn } = options;
|
|
8
|
+
schema.static(fnName, function staticFn(doc, ...args) {
|
|
9
|
+
return invokePluginFunction(fn, doc, args);
|
|
10
|
+
});
|
|
11
|
+
schema.method(fnName, function methodFn(...args) {
|
|
12
|
+
return invokePluginFunction(fn, this, args);
|
|
13
|
+
});
|
|
14
|
+
schema.static(
|
|
15
|
+
`${fnName}ById`,
|
|
16
|
+
async function staticByIdFn(docId, ...args) {
|
|
17
|
+
const model = await this.findById(docId);
|
|
18
|
+
if (!model) return null;
|
|
19
|
+
return invokePluginFunction(fn, model, args);
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// src/plugins/cascade-delete.ts
|
|
25
|
+
import mongoose2 from "mongoose";
|
|
26
|
+
import { isFunction as isFunction2, isObject, isPlainObject as isPlainObject2 } from "@web-ts-toolkit/utils";
|
|
27
|
+
|
|
28
|
+
// src/utils/index.ts
|
|
29
|
+
import { Schema } from "mongoose";
|
|
30
|
+
import { isFunction, isPlainObject, isString } from "@web-ts-toolkit/utils";
|
|
31
|
+
var isObjectIdType = (value) => value === "ObjectId" || value === Schema.Types.ObjectId;
|
|
32
|
+
var hasReferenceTarget = (value) => isString(value.ref) || isFunction(value.ref) || isString(value.refPath);
|
|
33
|
+
var isReference = (value, ref) => {
|
|
34
|
+
if (Array.isArray(value)) {
|
|
35
|
+
return value.length > 0 ? isReference(value[0], ref) : false;
|
|
36
|
+
}
|
|
37
|
+
if (!isPlainObject(value) || !isObjectIdType(value.type)) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
if (ref === void 0) {
|
|
41
|
+
return hasReferenceTarget(value);
|
|
42
|
+
}
|
|
43
|
+
return value.ref === ref;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// src/plugins/cascade-delete.ts
|
|
47
|
+
var resolveFilter = (value, document) => {
|
|
48
|
+
const result = isFunction2(value) ? value(document) : value;
|
|
49
|
+
if (!isPlainObject2(result)) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
};
|
|
54
|
+
var applyQueryOptions = (query, options) => {
|
|
55
|
+
let builder = query;
|
|
56
|
+
if (options.select) {
|
|
57
|
+
builder = builder.select(options.select);
|
|
58
|
+
}
|
|
59
|
+
if (options.sort) {
|
|
60
|
+
builder = builder.sort(options.sort);
|
|
61
|
+
}
|
|
62
|
+
if (options.populate) {
|
|
63
|
+
builder = builder.populate(options.populate);
|
|
64
|
+
}
|
|
65
|
+
if (options.lean) {
|
|
66
|
+
builder = builder.lean();
|
|
67
|
+
}
|
|
68
|
+
return builder;
|
|
69
|
+
};
|
|
70
|
+
var supportsDeleteOne = (value) => isObject(value) && typeof value.deleteOne === "function";
|
|
71
|
+
var supportsRemove = (value) => isObject(value) && typeof value.remove === "function";
|
|
72
|
+
var mergeResults = (previousResults, modelName, currentResults) => {
|
|
73
|
+
const resultMap = isPlainObject2(previousResults) ? previousResults : {};
|
|
74
|
+
if (currentResults === null) {
|
|
75
|
+
return resultMap;
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
...resultMap,
|
|
79
|
+
[modelName]: currentResults
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
function cascadeDeletePlugin(schema, options) {
|
|
83
|
+
const { model, localField, foreignField, foreignFilter, extraForeignFilter } = options ?? {};
|
|
84
|
+
const findDependents = async function(queryOptions) {
|
|
85
|
+
const Target = mongoose2.model(model);
|
|
86
|
+
let query = null;
|
|
87
|
+
const document = this.toObject({ virtuals: false });
|
|
88
|
+
if (foreignFilter) {
|
|
89
|
+
query = resolveFilter(foreignFilter, document);
|
|
90
|
+
} else if (localField && foreignField) {
|
|
91
|
+
const localValue = this.get(localField);
|
|
92
|
+
const extraFilter = resolveFilter(extraForeignFilter, document) ?? {};
|
|
93
|
+
query = {
|
|
94
|
+
[foreignField]: Array.isArray(localValue) ? { $in: localValue } : localValue,
|
|
95
|
+
...extraFilter
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
if (!query || !isPlainObject2(query)) {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
return await applyQueryOptions(Target.find(query), queryOptions);
|
|
102
|
+
};
|
|
103
|
+
const findOrphans = async function(queryOptions) {
|
|
104
|
+
if (!localField || !foreignField) return null;
|
|
105
|
+
const Target = mongoose2.model(model);
|
|
106
|
+
const schemaValue = Target.schema.obj[foreignField];
|
|
107
|
+
if (!schemaValue) return null;
|
|
108
|
+
const isMyRef = isReference(schemaValue, this.modelName);
|
|
109
|
+
if (!isMyRef) return null;
|
|
110
|
+
const ids = await this.distinct("_id");
|
|
111
|
+
const extraFilter = resolveFilter(extraForeignFilter) ?? {};
|
|
112
|
+
const query = {
|
|
113
|
+
[foreignField]: { $not: { $in: ids } },
|
|
114
|
+
...extraFilter
|
|
115
|
+
};
|
|
116
|
+
return await applyQueryOptions(Target.find(query), queryOptions);
|
|
117
|
+
};
|
|
118
|
+
const deleteDependents = async function() {
|
|
119
|
+
const documents = await findDependents.call(this, { select: "_id" });
|
|
120
|
+
await Promise.all(
|
|
121
|
+
documents.map((document) => {
|
|
122
|
+
if (supportsDeleteOne(document)) {
|
|
123
|
+
return document.deleteOne();
|
|
124
|
+
}
|
|
125
|
+
if (supportsRemove(document)) {
|
|
126
|
+
return document.remove();
|
|
127
|
+
}
|
|
128
|
+
return Promise.resolve();
|
|
129
|
+
})
|
|
130
|
+
);
|
|
131
|
+
};
|
|
132
|
+
schema.post("deleteOne", { document: true, query: false }, async function postDeleteOne() {
|
|
133
|
+
await deleteDependents.call(this);
|
|
134
|
+
});
|
|
135
|
+
const methodFnName = "findDependents";
|
|
136
|
+
const prevMethodFn = schema.methods[methodFnName];
|
|
137
|
+
schema.method(methodFnName, async function methodFn(modelName) {
|
|
138
|
+
if (modelName) {
|
|
139
|
+
if (modelName === model) {
|
|
140
|
+
return findDependents.call(this, {});
|
|
141
|
+
}
|
|
142
|
+
return prevMethodFn ? prevMethodFn.call(this, modelName) : null;
|
|
143
|
+
}
|
|
144
|
+
const previousResults = prevMethodFn ? await prevMethodFn.call(this) : {};
|
|
145
|
+
return mergeResults(previousResults, model, await findDependents.call(this, {}));
|
|
146
|
+
});
|
|
147
|
+
const staticFnName = "findOrphans";
|
|
148
|
+
const prevStaticFn = schema.statics[staticFnName];
|
|
149
|
+
schema.static(staticFnName, async function methodFn(modelName) {
|
|
150
|
+
if (modelName) {
|
|
151
|
+
if (modelName === model) {
|
|
152
|
+
return findOrphans.call(this, {});
|
|
153
|
+
}
|
|
154
|
+
return prevStaticFn ? prevStaticFn.call(this, modelName) : null;
|
|
155
|
+
}
|
|
156
|
+
const previousResults = prevStaticFn ? await prevStaticFn.call(this) : {};
|
|
157
|
+
const currentResults = await findOrphans.call(this, {});
|
|
158
|
+
return mergeResults(previousResults, model, currentResults);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
// src/schema.ts
|
|
163
|
+
var uniqueNullableString = (field, options = {}) => ({
|
|
164
|
+
type: String,
|
|
165
|
+
index: { unique: true, partialFilterExpression: { [field]: { $type: "string" } } },
|
|
166
|
+
trim: true,
|
|
167
|
+
default: null,
|
|
168
|
+
...options
|
|
169
|
+
});
|
|
170
|
+
var uniqueEmptiableString = (field, options = {}) => ({
|
|
171
|
+
type: String,
|
|
172
|
+
index: {
|
|
173
|
+
unique: true,
|
|
174
|
+
partialFilterExpression: { [field]: { $type: "string", $gt: "" } }
|
|
175
|
+
},
|
|
176
|
+
trim: true,
|
|
177
|
+
default: null,
|
|
178
|
+
...options
|
|
179
|
+
});
|
|
180
|
+
|
|
181
|
+
// src/is.ts
|
|
182
|
+
import { Types } from "mongoose";
|
|
183
|
+
var { ObjectId } = Types;
|
|
184
|
+
function isObjectId(value) {
|
|
185
|
+
if (!ObjectId.isValid(value)) {
|
|
186
|
+
return false;
|
|
187
|
+
}
|
|
188
|
+
try {
|
|
189
|
+
const asString = String(value);
|
|
190
|
+
const asObjectId = new ObjectId(asString);
|
|
191
|
+
const asStringifiedObjectId = asObjectId.toString();
|
|
192
|
+
return asString === asStringifiedObjectId;
|
|
193
|
+
} catch {
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
export {
|
|
198
|
+
cascadeDeletePlugin,
|
|
199
|
+
isObjectId,
|
|
200
|
+
modelFunctionPlugin,
|
|
201
|
+
uniqueEmptiableString,
|
|
202
|
+
uniqueNullableString
|
|
203
|
+
};
|
package/is.js
CHANGED
|
@@ -16,13 +16,15 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
16
|
return to;
|
|
17
17
|
};
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/is.ts
|
|
19
21
|
var is_exports = {};
|
|
20
22
|
__export(is_exports, {
|
|
21
23
|
isObjectId: () => isObjectId
|
|
22
24
|
});
|
|
23
25
|
module.exports = __toCommonJS(is_exports);
|
|
24
26
|
var import_mongoose = require("mongoose");
|
|
25
|
-
|
|
27
|
+
var { ObjectId } = import_mongoose.Types;
|
|
26
28
|
function isObjectId(value) {
|
|
27
29
|
if (!ObjectId.isValid(value)) {
|
|
28
30
|
return false;
|
package/is.mjs
CHANGED
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "@web-ts-toolkit/moo",
|
|
3
3
|
"description": "Mongoose helpers for schema fields, ObjectId checks, and document plugins",
|
|
4
4
|
"homepage": "https://web-ts-toolkit.pages.dev/docs/packages/moo",
|
|
5
|
-
"version": "0.
|
|
5
|
+
"version": "0.31.1",
|
|
6
6
|
"sideEffects": false,
|
|
7
7
|
"keywords": [
|
|
8
8
|
"mongoose",
|
|
@@ -65,7 +65,7 @@
|
|
|
65
65
|
"mongoose": ">=8.0.0"
|
|
66
66
|
},
|
|
67
67
|
"dependencies": {
|
|
68
|
-
"@web-ts-toolkit/utils": "0.
|
|
68
|
+
"@web-ts-toolkit/utils": "0.31.1"
|
|
69
69
|
},
|
|
70
70
|
"files": [
|
|
71
71
|
"**/*",
|
|
@@ -26,22 +26,43 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
26
26
|
mod
|
|
27
27
|
));
|
|
28
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/plugins/cascade-delete.ts
|
|
29
31
|
var cascade_delete_exports = {};
|
|
30
32
|
__export(cascade_delete_exports, {
|
|
31
33
|
cascadeDeletePlugin: () => cascadeDeletePlugin
|
|
32
34
|
});
|
|
33
35
|
module.exports = __toCommonJS(cascade_delete_exports);
|
|
34
|
-
var
|
|
36
|
+
var import_mongoose2 = __toESM(require("mongoose"));
|
|
37
|
+
var import_utils2 = require("@web-ts-toolkit/utils");
|
|
38
|
+
|
|
39
|
+
// src/utils/index.ts
|
|
40
|
+
var import_mongoose = require("mongoose");
|
|
35
41
|
var import_utils = require("@web-ts-toolkit/utils");
|
|
36
|
-
var
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
if (
|
|
42
|
+
var isObjectIdType = (value) => value === "ObjectId" || value === import_mongoose.Schema.Types.ObjectId;
|
|
43
|
+
var hasReferenceTarget = (value) => (0, import_utils.isString)(value.ref) || (0, import_utils.isFunction)(value.ref) || (0, import_utils.isString)(value.refPath);
|
|
44
|
+
var isReference = (value, ref) => {
|
|
45
|
+
if (Array.isArray(value)) {
|
|
46
|
+
return value.length > 0 ? isReference(value[0], ref) : false;
|
|
47
|
+
}
|
|
48
|
+
if (!(0, import_utils.isPlainObject)(value) || !isObjectIdType(value.type)) {
|
|
49
|
+
return false;
|
|
50
|
+
}
|
|
51
|
+
if (ref === void 0) {
|
|
52
|
+
return hasReferenceTarget(value);
|
|
53
|
+
}
|
|
54
|
+
return value.ref === ref;
|
|
55
|
+
};
|
|
56
|
+
|
|
57
|
+
// src/plugins/cascade-delete.ts
|
|
58
|
+
var resolveFilter = (value, document) => {
|
|
59
|
+
const result = (0, import_utils2.isFunction)(value) ? value(document) : value;
|
|
60
|
+
if (!(0, import_utils2.isPlainObject)(result)) {
|
|
40
61
|
return null;
|
|
41
62
|
}
|
|
42
63
|
return result;
|
|
43
64
|
};
|
|
44
|
-
|
|
65
|
+
var applyQueryOptions = (query, options) => {
|
|
45
66
|
let builder = query;
|
|
46
67
|
if (options.select) {
|
|
47
68
|
builder = builder.select(options.select);
|
|
@@ -57,10 +78,10 @@ const applyQueryOptions = (query, options) => {
|
|
|
57
78
|
}
|
|
58
79
|
return builder;
|
|
59
80
|
};
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
const resultMap = (0,
|
|
81
|
+
var supportsDeleteOne = (value) => (0, import_utils2.isObject)(value) && typeof value.deleteOne === "function";
|
|
82
|
+
var supportsRemove = (value) => (0, import_utils2.isObject)(value) && typeof value.remove === "function";
|
|
83
|
+
var mergeResults = (previousResults, modelName, currentResults) => {
|
|
84
|
+
const resultMap = (0, import_utils2.isPlainObject)(previousResults) ? previousResults : {};
|
|
64
85
|
if (currentResults === null) {
|
|
65
86
|
return resultMap;
|
|
66
87
|
}
|
|
@@ -72,7 +93,7 @@ const mergeResults = (previousResults, modelName, currentResults) => {
|
|
|
72
93
|
function cascadeDeletePlugin(schema, options) {
|
|
73
94
|
const { model, localField, foreignField, foreignFilter, extraForeignFilter } = options ?? {};
|
|
74
95
|
const findDependents = async function(queryOptions) {
|
|
75
|
-
const Target =
|
|
96
|
+
const Target = import_mongoose2.default.model(model);
|
|
76
97
|
let query = null;
|
|
77
98
|
const document = this.toObject({ virtuals: false });
|
|
78
99
|
if (foreignFilter) {
|
|
@@ -85,17 +106,17 @@ function cascadeDeletePlugin(schema, options) {
|
|
|
85
106
|
...extraFilter
|
|
86
107
|
};
|
|
87
108
|
}
|
|
88
|
-
if (!query || !(0,
|
|
109
|
+
if (!query || !(0, import_utils2.isPlainObject)(query)) {
|
|
89
110
|
return [];
|
|
90
111
|
}
|
|
91
112
|
return await applyQueryOptions(Target.find(query), queryOptions);
|
|
92
113
|
};
|
|
93
114
|
const findOrphans = async function(queryOptions) {
|
|
94
115
|
if (!localField || !foreignField) return null;
|
|
95
|
-
const Target =
|
|
116
|
+
const Target = import_mongoose2.default.model(model);
|
|
96
117
|
const schemaValue = Target.schema.obj[foreignField];
|
|
97
118
|
if (!schemaValue) return null;
|
|
98
|
-
const isMyRef =
|
|
119
|
+
const isMyRef = isReference(schemaValue, this.modelName);
|
|
99
120
|
if (!isMyRef) return null;
|
|
100
121
|
const ids = await this.distinct("_id");
|
|
101
122
|
const extraFilter = resolveFilter(extraForeignFilter) ?? {};
|
|
@@ -1,14 +1,34 @@
|
|
|
1
|
+
// src/plugins/cascade-delete.ts
|
|
1
2
|
import mongoose from "mongoose";
|
|
2
|
-
import { isFunction, isObject, isPlainObject } from "@web-ts-toolkit/utils";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
import { isFunction as isFunction2, isObject, isPlainObject as isPlainObject2 } from "@web-ts-toolkit/utils";
|
|
4
|
+
|
|
5
|
+
// src/utils/index.ts
|
|
6
|
+
import { Schema } from "mongoose";
|
|
7
|
+
import { isFunction, isPlainObject, isString } from "@web-ts-toolkit/utils";
|
|
8
|
+
var isObjectIdType = (value) => value === "ObjectId" || value === Schema.Types.ObjectId;
|
|
9
|
+
var hasReferenceTarget = (value) => isString(value.ref) || isFunction(value.ref) || isString(value.refPath);
|
|
10
|
+
var isReference = (value, ref) => {
|
|
11
|
+
if (Array.isArray(value)) {
|
|
12
|
+
return value.length > 0 ? isReference(value[0], ref) : false;
|
|
13
|
+
}
|
|
14
|
+
if (!isPlainObject(value) || !isObjectIdType(value.type)) {
|
|
15
|
+
return false;
|
|
16
|
+
}
|
|
17
|
+
if (ref === void 0) {
|
|
18
|
+
return hasReferenceTarget(value);
|
|
19
|
+
}
|
|
20
|
+
return value.ref === ref;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
// src/plugins/cascade-delete.ts
|
|
24
|
+
var resolveFilter = (value, document) => {
|
|
25
|
+
const result = isFunction2(value) ? value(document) : value;
|
|
26
|
+
if (!isPlainObject2(result)) {
|
|
7
27
|
return null;
|
|
8
28
|
}
|
|
9
29
|
return result;
|
|
10
30
|
};
|
|
11
|
-
|
|
31
|
+
var applyQueryOptions = (query, options) => {
|
|
12
32
|
let builder = query;
|
|
13
33
|
if (options.select) {
|
|
14
34
|
builder = builder.select(options.select);
|
|
@@ -24,10 +44,10 @@ const applyQueryOptions = (query, options) => {
|
|
|
24
44
|
}
|
|
25
45
|
return builder;
|
|
26
46
|
};
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const resultMap =
|
|
47
|
+
var supportsDeleteOne = (value) => isObject(value) && typeof value.deleteOne === "function";
|
|
48
|
+
var supportsRemove = (value) => isObject(value) && typeof value.remove === "function";
|
|
49
|
+
var mergeResults = (previousResults, modelName, currentResults) => {
|
|
50
|
+
const resultMap = isPlainObject2(previousResults) ? previousResults : {};
|
|
31
51
|
if (currentResults === null) {
|
|
32
52
|
return resultMap;
|
|
33
53
|
}
|
|
@@ -52,7 +72,7 @@ function cascadeDeletePlugin(schema, options) {
|
|
|
52
72
|
...extraFilter
|
|
53
73
|
};
|
|
54
74
|
}
|
|
55
|
-
if (!query || !
|
|
75
|
+
if (!query || !isPlainObject2(query)) {
|
|
56
76
|
return [];
|
|
57
77
|
}
|
|
58
78
|
return await applyQueryOptions(Target.find(query), queryOptions);
|
package/plugins/index.js
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
2
3
|
var __defProp = Object.defineProperty;
|
|
3
4
|
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
4
5
|
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
5
7
|
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
6
12
|
var __copyProps = (to, from, except, desc) => {
|
|
7
13
|
if (from && typeof from === "object" || typeof from === "function") {
|
|
8
14
|
for (let key of __getOwnPropNames(from))
|
|
@@ -11,14 +17,186 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
11
17
|
}
|
|
12
18
|
return to;
|
|
13
19
|
};
|
|
14
|
-
var
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
15
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/plugins/index.ts
|
|
16
31
|
var plugins_exports = {};
|
|
32
|
+
__export(plugins_exports, {
|
|
33
|
+
cascadeDeletePlugin: () => cascadeDeletePlugin,
|
|
34
|
+
modelFunctionPlugin: () => modelFunctionPlugin
|
|
35
|
+
});
|
|
17
36
|
module.exports = __toCommonJS(plugins_exports);
|
|
18
|
-
|
|
19
|
-
|
|
37
|
+
|
|
38
|
+
// src/plugins/model-function.ts
|
|
39
|
+
var import_mongoose = __toESM(require("mongoose"));
|
|
40
|
+
var invokePluginFunction = (fn, document, args) => {
|
|
41
|
+
return fn.call(import_mongoose.default, document, ...args);
|
|
42
|
+
};
|
|
43
|
+
function modelFunctionPlugin(schema, options) {
|
|
44
|
+
const { fnName, fn } = options;
|
|
45
|
+
schema.static(fnName, function staticFn(doc, ...args) {
|
|
46
|
+
return invokePluginFunction(fn, doc, args);
|
|
47
|
+
});
|
|
48
|
+
schema.method(fnName, function methodFn(...args) {
|
|
49
|
+
return invokePluginFunction(fn, this, args);
|
|
50
|
+
});
|
|
51
|
+
schema.static(
|
|
52
|
+
`${fnName}ById`,
|
|
53
|
+
async function staticByIdFn(docId, ...args) {
|
|
54
|
+
const model = await this.findById(docId);
|
|
55
|
+
if (!model) return null;
|
|
56
|
+
return invokePluginFunction(fn, model, args);
|
|
57
|
+
}
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// src/plugins/cascade-delete.ts
|
|
62
|
+
var import_mongoose3 = __toESM(require("mongoose"));
|
|
63
|
+
var import_utils2 = require("@web-ts-toolkit/utils");
|
|
64
|
+
|
|
65
|
+
// src/utils/index.ts
|
|
66
|
+
var import_mongoose2 = require("mongoose");
|
|
67
|
+
var import_utils = require("@web-ts-toolkit/utils");
|
|
68
|
+
var isObjectIdType = (value) => value === "ObjectId" || value === import_mongoose2.Schema.Types.ObjectId;
|
|
69
|
+
var hasReferenceTarget = (value) => (0, import_utils.isString)(value.ref) || (0, import_utils.isFunction)(value.ref) || (0, import_utils.isString)(value.refPath);
|
|
70
|
+
var isReference = (value, ref) => {
|
|
71
|
+
if (Array.isArray(value)) {
|
|
72
|
+
return value.length > 0 ? isReference(value[0], ref) : false;
|
|
73
|
+
}
|
|
74
|
+
if (!(0, import_utils.isPlainObject)(value) || !isObjectIdType(value.type)) {
|
|
75
|
+
return false;
|
|
76
|
+
}
|
|
77
|
+
if (ref === void 0) {
|
|
78
|
+
return hasReferenceTarget(value);
|
|
79
|
+
}
|
|
80
|
+
return value.ref === ref;
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
// src/plugins/cascade-delete.ts
|
|
84
|
+
var resolveFilter = (value, document) => {
|
|
85
|
+
const result = (0, import_utils2.isFunction)(value) ? value(document) : value;
|
|
86
|
+
if (!(0, import_utils2.isPlainObject)(result)) {
|
|
87
|
+
return null;
|
|
88
|
+
}
|
|
89
|
+
return result;
|
|
90
|
+
};
|
|
91
|
+
var applyQueryOptions = (query, options) => {
|
|
92
|
+
let builder = query;
|
|
93
|
+
if (options.select) {
|
|
94
|
+
builder = builder.select(options.select);
|
|
95
|
+
}
|
|
96
|
+
if (options.sort) {
|
|
97
|
+
builder = builder.sort(options.sort);
|
|
98
|
+
}
|
|
99
|
+
if (options.populate) {
|
|
100
|
+
builder = builder.populate(options.populate);
|
|
101
|
+
}
|
|
102
|
+
if (options.lean) {
|
|
103
|
+
builder = builder.lean();
|
|
104
|
+
}
|
|
105
|
+
return builder;
|
|
106
|
+
};
|
|
107
|
+
var supportsDeleteOne = (value) => (0, import_utils2.isObject)(value) && typeof value.deleteOne === "function";
|
|
108
|
+
var supportsRemove = (value) => (0, import_utils2.isObject)(value) && typeof value.remove === "function";
|
|
109
|
+
var mergeResults = (previousResults, modelName, currentResults) => {
|
|
110
|
+
const resultMap = (0, import_utils2.isPlainObject)(previousResults) ? previousResults : {};
|
|
111
|
+
if (currentResults === null) {
|
|
112
|
+
return resultMap;
|
|
113
|
+
}
|
|
114
|
+
return {
|
|
115
|
+
...resultMap,
|
|
116
|
+
[modelName]: currentResults
|
|
117
|
+
};
|
|
118
|
+
};
|
|
119
|
+
function cascadeDeletePlugin(schema, options) {
|
|
120
|
+
const { model, localField, foreignField, foreignFilter, extraForeignFilter } = options ?? {};
|
|
121
|
+
const findDependents = async function(queryOptions) {
|
|
122
|
+
const Target = import_mongoose3.default.model(model);
|
|
123
|
+
let query = null;
|
|
124
|
+
const document = this.toObject({ virtuals: false });
|
|
125
|
+
if (foreignFilter) {
|
|
126
|
+
query = resolveFilter(foreignFilter, document);
|
|
127
|
+
} else if (localField && foreignField) {
|
|
128
|
+
const localValue = this.get(localField);
|
|
129
|
+
const extraFilter = resolveFilter(extraForeignFilter, document) ?? {};
|
|
130
|
+
query = {
|
|
131
|
+
[foreignField]: Array.isArray(localValue) ? { $in: localValue } : localValue,
|
|
132
|
+
...extraFilter
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
if (!query || !(0, import_utils2.isPlainObject)(query)) {
|
|
136
|
+
return [];
|
|
137
|
+
}
|
|
138
|
+
return await applyQueryOptions(Target.find(query), queryOptions);
|
|
139
|
+
};
|
|
140
|
+
const findOrphans = async function(queryOptions) {
|
|
141
|
+
if (!localField || !foreignField) return null;
|
|
142
|
+
const Target = import_mongoose3.default.model(model);
|
|
143
|
+
const schemaValue = Target.schema.obj[foreignField];
|
|
144
|
+
if (!schemaValue) return null;
|
|
145
|
+
const isMyRef = isReference(schemaValue, this.modelName);
|
|
146
|
+
if (!isMyRef) return null;
|
|
147
|
+
const ids = await this.distinct("_id");
|
|
148
|
+
const extraFilter = resolveFilter(extraForeignFilter) ?? {};
|
|
149
|
+
const query = {
|
|
150
|
+
[foreignField]: { $not: { $in: ids } },
|
|
151
|
+
...extraFilter
|
|
152
|
+
};
|
|
153
|
+
return await applyQueryOptions(Target.find(query), queryOptions);
|
|
154
|
+
};
|
|
155
|
+
const deleteDependents = async function() {
|
|
156
|
+
const documents = await findDependents.call(this, { select: "_id" });
|
|
157
|
+
await Promise.all(
|
|
158
|
+
documents.map((document) => {
|
|
159
|
+
if (supportsDeleteOne(document)) {
|
|
160
|
+
return document.deleteOne();
|
|
161
|
+
}
|
|
162
|
+
if (supportsRemove(document)) {
|
|
163
|
+
return document.remove();
|
|
164
|
+
}
|
|
165
|
+
return Promise.resolve();
|
|
166
|
+
})
|
|
167
|
+
);
|
|
168
|
+
};
|
|
169
|
+
schema.post("deleteOne", { document: true, query: false }, async function postDeleteOne() {
|
|
170
|
+
await deleteDependents.call(this);
|
|
171
|
+
});
|
|
172
|
+
const methodFnName = "findDependents";
|
|
173
|
+
const prevMethodFn = schema.methods[methodFnName];
|
|
174
|
+
schema.method(methodFnName, async function methodFn(modelName) {
|
|
175
|
+
if (modelName) {
|
|
176
|
+
if (modelName === model) {
|
|
177
|
+
return findDependents.call(this, {});
|
|
178
|
+
}
|
|
179
|
+
return prevMethodFn ? prevMethodFn.call(this, modelName) : null;
|
|
180
|
+
}
|
|
181
|
+
const previousResults = prevMethodFn ? await prevMethodFn.call(this) : {};
|
|
182
|
+
return mergeResults(previousResults, model, await findDependents.call(this, {}));
|
|
183
|
+
});
|
|
184
|
+
const staticFnName = "findOrphans";
|
|
185
|
+
const prevStaticFn = schema.statics[staticFnName];
|
|
186
|
+
schema.static(staticFnName, async function methodFn(modelName) {
|
|
187
|
+
if (modelName) {
|
|
188
|
+
if (modelName === model) {
|
|
189
|
+
return findOrphans.call(this, {});
|
|
190
|
+
}
|
|
191
|
+
return prevStaticFn ? prevStaticFn.call(this, modelName) : null;
|
|
192
|
+
}
|
|
193
|
+
const previousResults = prevStaticFn ? await prevStaticFn.call(this) : {};
|
|
194
|
+
const currentResults = await findOrphans.call(this, {});
|
|
195
|
+
return mergeResults(previousResults, model, currentResults);
|
|
196
|
+
});
|
|
197
|
+
}
|
|
20
198
|
// Annotate the CommonJS export names for ESM import in node:
|
|
21
199
|
0 && (module.exports = {
|
|
22
|
-
|
|
23
|
-
|
|
200
|
+
cascadeDeletePlugin,
|
|
201
|
+
modelFunctionPlugin
|
|
24
202
|
});
|
package/plugins/index.mjs
CHANGED
|
@@ -1,2 +1,164 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
// src/plugins/model-function.ts
|
|
2
|
+
import mongoose from "mongoose";
|
|
3
|
+
var invokePluginFunction = (fn, document, args) => {
|
|
4
|
+
return fn.call(mongoose, document, ...args);
|
|
5
|
+
};
|
|
6
|
+
function modelFunctionPlugin(schema, options) {
|
|
7
|
+
const { fnName, fn } = options;
|
|
8
|
+
schema.static(fnName, function staticFn(doc, ...args) {
|
|
9
|
+
return invokePluginFunction(fn, doc, args);
|
|
10
|
+
});
|
|
11
|
+
schema.method(fnName, function methodFn(...args) {
|
|
12
|
+
return invokePluginFunction(fn, this, args);
|
|
13
|
+
});
|
|
14
|
+
schema.static(
|
|
15
|
+
`${fnName}ById`,
|
|
16
|
+
async function staticByIdFn(docId, ...args) {
|
|
17
|
+
const model = await this.findById(docId);
|
|
18
|
+
if (!model) return null;
|
|
19
|
+
return invokePluginFunction(fn, model, args);
|
|
20
|
+
}
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// src/plugins/cascade-delete.ts
|
|
25
|
+
import mongoose2 from "mongoose";
|
|
26
|
+
import { isFunction as isFunction2, isObject, isPlainObject as isPlainObject2 } from "@web-ts-toolkit/utils";
|
|
27
|
+
|
|
28
|
+
// src/utils/index.ts
|
|
29
|
+
import { Schema } from "mongoose";
|
|
30
|
+
import { isFunction, isPlainObject, isString } from "@web-ts-toolkit/utils";
|
|
31
|
+
var isObjectIdType = (value) => value === "ObjectId" || value === Schema.Types.ObjectId;
|
|
32
|
+
var hasReferenceTarget = (value) => isString(value.ref) || isFunction(value.ref) || isString(value.refPath);
|
|
33
|
+
var isReference = (value, ref) => {
|
|
34
|
+
if (Array.isArray(value)) {
|
|
35
|
+
return value.length > 0 ? isReference(value[0], ref) : false;
|
|
36
|
+
}
|
|
37
|
+
if (!isPlainObject(value) || !isObjectIdType(value.type)) {
|
|
38
|
+
return false;
|
|
39
|
+
}
|
|
40
|
+
if (ref === void 0) {
|
|
41
|
+
return hasReferenceTarget(value);
|
|
42
|
+
}
|
|
43
|
+
return value.ref === ref;
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// src/plugins/cascade-delete.ts
|
|
47
|
+
var resolveFilter = (value, document) => {
|
|
48
|
+
const result = isFunction2(value) ? value(document) : value;
|
|
49
|
+
if (!isPlainObject2(result)) {
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
return result;
|
|
53
|
+
};
|
|
54
|
+
var applyQueryOptions = (query, options) => {
|
|
55
|
+
let builder = query;
|
|
56
|
+
if (options.select) {
|
|
57
|
+
builder = builder.select(options.select);
|
|
58
|
+
}
|
|
59
|
+
if (options.sort) {
|
|
60
|
+
builder = builder.sort(options.sort);
|
|
61
|
+
}
|
|
62
|
+
if (options.populate) {
|
|
63
|
+
builder = builder.populate(options.populate);
|
|
64
|
+
}
|
|
65
|
+
if (options.lean) {
|
|
66
|
+
builder = builder.lean();
|
|
67
|
+
}
|
|
68
|
+
return builder;
|
|
69
|
+
};
|
|
70
|
+
var supportsDeleteOne = (value) => isObject(value) && typeof value.deleteOne === "function";
|
|
71
|
+
var supportsRemove = (value) => isObject(value) && typeof value.remove === "function";
|
|
72
|
+
var mergeResults = (previousResults, modelName, currentResults) => {
|
|
73
|
+
const resultMap = isPlainObject2(previousResults) ? previousResults : {};
|
|
74
|
+
if (currentResults === null) {
|
|
75
|
+
return resultMap;
|
|
76
|
+
}
|
|
77
|
+
return {
|
|
78
|
+
...resultMap,
|
|
79
|
+
[modelName]: currentResults
|
|
80
|
+
};
|
|
81
|
+
};
|
|
82
|
+
function cascadeDeletePlugin(schema, options) {
|
|
83
|
+
const { model, localField, foreignField, foreignFilter, extraForeignFilter } = options ?? {};
|
|
84
|
+
const findDependents = async function(queryOptions) {
|
|
85
|
+
const Target = mongoose2.model(model);
|
|
86
|
+
let query = null;
|
|
87
|
+
const document = this.toObject({ virtuals: false });
|
|
88
|
+
if (foreignFilter) {
|
|
89
|
+
query = resolveFilter(foreignFilter, document);
|
|
90
|
+
} else if (localField && foreignField) {
|
|
91
|
+
const localValue = this.get(localField);
|
|
92
|
+
const extraFilter = resolveFilter(extraForeignFilter, document) ?? {};
|
|
93
|
+
query = {
|
|
94
|
+
[foreignField]: Array.isArray(localValue) ? { $in: localValue } : localValue,
|
|
95
|
+
...extraFilter
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
if (!query || !isPlainObject2(query)) {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
return await applyQueryOptions(Target.find(query), queryOptions);
|
|
102
|
+
};
|
|
103
|
+
const findOrphans = async function(queryOptions) {
|
|
104
|
+
if (!localField || !foreignField) return null;
|
|
105
|
+
const Target = mongoose2.model(model);
|
|
106
|
+
const schemaValue = Target.schema.obj[foreignField];
|
|
107
|
+
if (!schemaValue) return null;
|
|
108
|
+
const isMyRef = isReference(schemaValue, this.modelName);
|
|
109
|
+
if (!isMyRef) return null;
|
|
110
|
+
const ids = await this.distinct("_id");
|
|
111
|
+
const extraFilter = resolveFilter(extraForeignFilter) ?? {};
|
|
112
|
+
const query = {
|
|
113
|
+
[foreignField]: { $not: { $in: ids } },
|
|
114
|
+
...extraFilter
|
|
115
|
+
};
|
|
116
|
+
return await applyQueryOptions(Target.find(query), queryOptions);
|
|
117
|
+
};
|
|
118
|
+
const deleteDependents = async function() {
|
|
119
|
+
const documents = await findDependents.call(this, { select: "_id" });
|
|
120
|
+
await Promise.all(
|
|
121
|
+
documents.map((document) => {
|
|
122
|
+
if (supportsDeleteOne(document)) {
|
|
123
|
+
return document.deleteOne();
|
|
124
|
+
}
|
|
125
|
+
if (supportsRemove(document)) {
|
|
126
|
+
return document.remove();
|
|
127
|
+
}
|
|
128
|
+
return Promise.resolve();
|
|
129
|
+
})
|
|
130
|
+
);
|
|
131
|
+
};
|
|
132
|
+
schema.post("deleteOne", { document: true, query: false }, async function postDeleteOne() {
|
|
133
|
+
await deleteDependents.call(this);
|
|
134
|
+
});
|
|
135
|
+
const methodFnName = "findDependents";
|
|
136
|
+
const prevMethodFn = schema.methods[methodFnName];
|
|
137
|
+
schema.method(methodFnName, async function methodFn(modelName) {
|
|
138
|
+
if (modelName) {
|
|
139
|
+
if (modelName === model) {
|
|
140
|
+
return findDependents.call(this, {});
|
|
141
|
+
}
|
|
142
|
+
return prevMethodFn ? prevMethodFn.call(this, modelName) : null;
|
|
143
|
+
}
|
|
144
|
+
const previousResults = prevMethodFn ? await prevMethodFn.call(this) : {};
|
|
145
|
+
return mergeResults(previousResults, model, await findDependents.call(this, {}));
|
|
146
|
+
});
|
|
147
|
+
const staticFnName = "findOrphans";
|
|
148
|
+
const prevStaticFn = schema.statics[staticFnName];
|
|
149
|
+
schema.static(staticFnName, async function methodFn(modelName) {
|
|
150
|
+
if (modelName) {
|
|
151
|
+
if (modelName === model) {
|
|
152
|
+
return findOrphans.call(this, {});
|
|
153
|
+
}
|
|
154
|
+
return prevStaticFn ? prevStaticFn.call(this, modelName) : null;
|
|
155
|
+
}
|
|
156
|
+
const previousResults = prevStaticFn ? await prevStaticFn.call(this) : {};
|
|
157
|
+
const currentResults = await findOrphans.call(this, {});
|
|
158
|
+
return mergeResults(previousResults, model, currentResults);
|
|
159
|
+
});
|
|
160
|
+
}
|
|
161
|
+
export {
|
|
162
|
+
cascadeDeletePlugin,
|
|
163
|
+
modelFunctionPlugin
|
|
164
|
+
};
|
|
@@ -26,13 +26,15 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
26
26
|
mod
|
|
27
27
|
));
|
|
28
28
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/plugins/model-function.ts
|
|
29
31
|
var model_function_exports = {};
|
|
30
32
|
__export(model_function_exports, {
|
|
31
33
|
modelFunctionPlugin: () => modelFunctionPlugin
|
|
32
34
|
});
|
|
33
35
|
module.exports = __toCommonJS(model_function_exports);
|
|
34
36
|
var import_mongoose = __toESM(require("mongoose"));
|
|
35
|
-
|
|
37
|
+
var invokePluginFunction = (fn, document, args) => {
|
|
36
38
|
return fn.call(import_mongoose.default, document, ...args);
|
|
37
39
|
};
|
|
38
40
|
function modelFunctionPlugin(schema, options) {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
|
+
// src/plugins/model-function.ts
|
|
1
2
|
import mongoose from "mongoose";
|
|
2
|
-
|
|
3
|
+
var invokePluginFunction = (fn, document, args) => {
|
|
3
4
|
return fn.call(mongoose, document, ...args);
|
|
4
5
|
};
|
|
5
6
|
function modelFunctionPlugin(schema, options) {
|
package/schema.js
CHANGED
|
@@ -16,20 +16,22 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
16
|
return to;
|
|
17
17
|
};
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/schema.ts
|
|
19
21
|
var schema_exports = {};
|
|
20
22
|
__export(schema_exports, {
|
|
21
23
|
uniqueEmptiableString: () => uniqueEmptiableString,
|
|
22
24
|
uniqueNullableString: () => uniqueNullableString
|
|
23
25
|
});
|
|
24
26
|
module.exports = __toCommonJS(schema_exports);
|
|
25
|
-
|
|
27
|
+
var uniqueNullableString = (field, options = {}) => ({
|
|
26
28
|
type: String,
|
|
27
29
|
index: { unique: true, partialFilterExpression: { [field]: { $type: "string" } } },
|
|
28
30
|
trim: true,
|
|
29
31
|
default: null,
|
|
30
32
|
...options
|
|
31
33
|
});
|
|
32
|
-
|
|
34
|
+
var uniqueEmptiableString = (field, options = {}) => ({
|
|
33
35
|
type: String,
|
|
34
36
|
index: {
|
|
35
37
|
unique: true,
|
package/schema.mjs
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
|
-
|
|
1
|
+
// src/schema.ts
|
|
2
|
+
var uniqueNullableString = (field, options = {}) => ({
|
|
2
3
|
type: String,
|
|
3
4
|
index: { unique: true, partialFilterExpression: { [field]: { $type: "string" } } },
|
|
4
5
|
trim: true,
|
|
5
6
|
default: null,
|
|
6
7
|
...options
|
|
7
8
|
});
|
|
8
|
-
|
|
9
|
+
var uniqueEmptiableString = (field, options = {}) => ({
|
|
9
10
|
type: String,
|
|
10
11
|
index: {
|
|
11
12
|
unique: true,
|
package/utils/index.js
CHANGED
|
@@ -16,6 +16,8 @@ var __copyProps = (to, from, except, desc) => {
|
|
|
16
16
|
return to;
|
|
17
17
|
};
|
|
18
18
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
19
|
+
|
|
20
|
+
// src/utils/index.ts
|
|
19
21
|
var utils_exports = {};
|
|
20
22
|
__export(utils_exports, {
|
|
21
23
|
isObjectIdType: () => isObjectIdType,
|
|
@@ -25,10 +27,10 @@ __export(utils_exports, {
|
|
|
25
27
|
module.exports = __toCommonJS(utils_exports);
|
|
26
28
|
var import_mongoose = require("mongoose");
|
|
27
29
|
var import_utils = require("@web-ts-toolkit/utils");
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
30
|
+
var isSchema = (value) => value instanceof import_mongoose.Schema;
|
|
31
|
+
var isObjectIdType = (value) => value === "ObjectId" || value === import_mongoose.Schema.Types.ObjectId;
|
|
32
|
+
var hasReferenceTarget = (value) => (0, import_utils.isString)(value.ref) || (0, import_utils.isFunction)(value.ref) || (0, import_utils.isString)(value.refPath);
|
|
33
|
+
var isReference = (value, ref) => {
|
|
32
34
|
if (Array.isArray(value)) {
|
|
33
35
|
return value.length > 0 ? isReference(value[0], ref) : false;
|
|
34
36
|
}
|
package/utils/index.mjs
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
// src/utils/index.ts
|
|
1
2
|
import { Schema } from "mongoose";
|
|
2
3
|
import { isFunction, isPlainObject, isString } from "@web-ts-toolkit/utils";
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
4
|
+
var isSchema = (value) => value instanceof Schema;
|
|
5
|
+
var isObjectIdType = (value) => value === "ObjectId" || value === Schema.Types.ObjectId;
|
|
6
|
+
var hasReferenceTarget = (value) => isString(value.ref) || isFunction(value.ref) || isString(value.refPath);
|
|
7
|
+
var isReference = (value, ref) => {
|
|
7
8
|
if (Array.isArray(value)) {
|
|
8
9
|
return value.length > 0 ? isReference(value[0], ref) : false;
|
|
9
10
|
}
|