@bedrockio/model 0.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 (54) hide show
  1. package/CHANGELOG.md +60 -0
  2. package/README.md +932 -0
  3. package/babel.config.cjs +41 -0
  4. package/dist/cjs/access.js +66 -0
  5. package/dist/cjs/assign.js +50 -0
  6. package/dist/cjs/const.js +16 -0
  7. package/dist/cjs/errors.js +17 -0
  8. package/dist/cjs/include.js +222 -0
  9. package/dist/cjs/index.js +62 -0
  10. package/dist/cjs/load.js +40 -0
  11. package/dist/cjs/package.json +1 -0
  12. package/dist/cjs/references.js +104 -0
  13. package/dist/cjs/schema.js +277 -0
  14. package/dist/cjs/search.js +266 -0
  15. package/dist/cjs/serialization.js +55 -0
  16. package/dist/cjs/slug.js +47 -0
  17. package/dist/cjs/soft-delete.js +192 -0
  18. package/dist/cjs/testing.js +33 -0
  19. package/dist/cjs/utils.js +73 -0
  20. package/dist/cjs/validation.js +313 -0
  21. package/dist/cjs/warn.js +13 -0
  22. package/jest-mongodb-config.js +10 -0
  23. package/jest.config.js +8 -0
  24. package/package.json +53 -0
  25. package/src/access.js +60 -0
  26. package/src/assign.js +45 -0
  27. package/src/const.js +9 -0
  28. package/src/errors.js +9 -0
  29. package/src/include.js +209 -0
  30. package/src/index.js +5 -0
  31. package/src/load.js +37 -0
  32. package/src/references.js +101 -0
  33. package/src/schema.js +286 -0
  34. package/src/search.js +263 -0
  35. package/src/serialization.js +49 -0
  36. package/src/slug.js +45 -0
  37. package/src/soft-delete.js +234 -0
  38. package/src/testing.js +29 -0
  39. package/src/utils.js +63 -0
  40. package/src/validation.js +329 -0
  41. package/src/warn.js +7 -0
  42. package/test/assign.test.js +225 -0
  43. package/test/definitions/custom-model.json +9 -0
  44. package/test/definitions/special-category.json +18 -0
  45. package/test/include.test.js +896 -0
  46. package/test/load.test.js +47 -0
  47. package/test/references.test.js +71 -0
  48. package/test/schema.test.js +919 -0
  49. package/test/search.test.js +652 -0
  50. package/test/serialization.test.js +748 -0
  51. package/test/setup.js +27 -0
  52. package/test/slug.test.js +112 -0
  53. package/test/soft-delete.test.js +333 -0
  54. package/test/validation.test.js +1925 -0
@@ -0,0 +1,192 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.applySoftDelete = applySoftDelete;
7
+ var _warn = _interopRequireDefault(require("./warn"));
8
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
9
+ function applySoftDelete(schema) {
10
+ // Implementation
11
+
12
+ schema.pre(/^find|count|exists/, function (next) {
13
+ const filter = this.getFilter();
14
+ if (filter.deleted === undefined) {
15
+ // Search non-deleted docs by default
16
+ filter.deleted = false;
17
+ }
18
+ return next();
19
+ });
20
+
21
+ // Instance Methods
22
+
23
+ schema.method('delete', function () {
24
+ this.deleted = true;
25
+ this.deletedAt = new Date();
26
+ return this.save();
27
+ });
28
+ schema.method('restore', function restore() {
29
+ this.deleted = false;
30
+ this.deletedAt = undefined;
31
+ return this.save();
32
+ });
33
+ schema.method('destroy', function destroy(...args) {
34
+ const filter = {
35
+ _id: this._id
36
+ };
37
+ return this.constructor.destroyOne(filter, ...args);
38
+ });
39
+
40
+ // Static Methods
41
+
42
+ schema.static('deleteOne', async function deleteOne(filter, ...rest) {
43
+ const update = getDelete();
44
+ const res = await this.updateOne(filter, update, ...rest);
45
+ return {
46
+ acknowledged: res.acknowledged,
47
+ deletedCount: res.modifiedCount
48
+ };
49
+ });
50
+ schema.static('deleteMany', async function deleteMany(filter, ...rest) {
51
+ const update = getDelete();
52
+ const res = await this.updateMany(filter, update, ...rest);
53
+ return {
54
+ acknowledged: res.acknowledged,
55
+ deletedCount: res.modifiedCount
56
+ };
57
+ });
58
+ schema.static('findOneAndDelete', async function findOneAndDelete(filter, ...args) {
59
+ return await this.findOneAndUpdate(filter, getDelete(), ...args);
60
+ });
61
+ schema.static('restoreOne', async function restoreOne(filter, ...rest) {
62
+ const update = getRestore();
63
+ const res = await this.updateOne(filter, update, ...rest);
64
+ return {
65
+ acknowledged: res.acknowledged,
66
+ restoredCount: res.modifiedCount
67
+ };
68
+ });
69
+ schema.static('restoreMany', async function restoreMany(filter, ...rest) {
70
+ const update = getRestore();
71
+ const res = await this.updateMany(filter, update, ...rest);
72
+ return {
73
+ acknowledged: res.acknowledged,
74
+ restoredCount: res.modifiedCount
75
+ };
76
+ });
77
+ schema.static('destroyOne', async function destroyOne(...args) {
78
+ const res = await this.collection.deleteOne(...args);
79
+ return {
80
+ acknowledged: res.acknowledged,
81
+ destroyedCount: res.deletedCount
82
+ };
83
+ });
84
+ schema.static('destroyMany', async function destroyMany(...args) {
85
+ const res = await this.collection.deleteMany(...args);
86
+ return {
87
+ acknowledged: res.acknowledged,
88
+ destroyedCount: res.deletedCount
89
+ };
90
+ });
91
+ schema.static('findDeleted', function findDeleted(filter) {
92
+ return this.find({
93
+ ...filter,
94
+ deleted: true
95
+ });
96
+ });
97
+ schema.static('findOneDeleted', function findOneDeleted(filter) {
98
+ return this.findOne({
99
+ ...filter,
100
+ deleted: true
101
+ });
102
+ });
103
+ schema.static('findByIdDeleted', function findByIdDeleted(id) {
104
+ return this.findOne({
105
+ _id: id,
106
+ deleted: true
107
+ });
108
+ });
109
+ schema.static('existsDeleted', function existsDeleted() {
110
+ return this.exists({
111
+ deleted: true
112
+ });
113
+ });
114
+ schema.static('countDocumentsDeleted', function countDocumentsDeleted(filter) {
115
+ return this.countDocuments({
116
+ ...filter,
117
+ deleted: true
118
+ });
119
+ });
120
+ schema.static('findWithDeleted', function findWithDeleted(filter) {
121
+ return this.find({
122
+ ...filter,
123
+ ...getWithDeletedQuery()
124
+ });
125
+ });
126
+ schema.static('findOneWithDeleted', function findOneWithDeleted(filter) {
127
+ return this.findOne({
128
+ ...filter,
129
+ ...getWithDeletedQuery()
130
+ });
131
+ });
132
+ schema.static('findByIdWithDeleted', function findByIdWithDeleted(id) {
133
+ return this.findOne({
134
+ _id: id,
135
+ ...getWithDeletedQuery()
136
+ });
137
+ });
138
+ schema.static('existsWithDeleted', function existsWithDeleted() {
139
+ return this.exists({
140
+ ...getWithDeletedQuery()
141
+ });
142
+ });
143
+ schema.static('countDocumentsWithDeleted', function countDocumentsWithDeleted(filter) {
144
+ return this.countDocuments({
145
+ ...filter,
146
+ ...getWithDeletedQuery()
147
+ });
148
+ });
149
+ schema.method('remove', function () {
150
+ (0, _warn.default)('The "remove" method on documents is disallowed due to ambiguity.', 'To permanently delete a document use "destroy", otherwise "delete".');
151
+ throw new Error('Method not allowed.');
152
+ }, {
153
+ suppressWarning: true
154
+ });
155
+ schema.method('deleteOne', function () {
156
+ (0, _warn.default)('The "deleteOne" method on documents is disallowed due to ambiguity', 'Use either "delete" or "deleteOne" on the model.');
157
+ throw new Error('Method not allowed.');
158
+ });
159
+ schema.static('remove', function () {
160
+ (0, _warn.default)('The "remove" method on models is disallowed due to ambiguity.', 'To permanently delete a document use "destroyMany", otherwise "deleteMany".');
161
+ throw new Error('Method not allowed.');
162
+ });
163
+ schema.static('findOneAndRemove', function () {
164
+ (0, _warn.default)('The "findOneAndRemove" method on models is disallowed due to ambiguity.', 'To permanently delete a document use "findOneAndDestroy", otherwise "findOneAndDelete".');
165
+ throw new Error('Method not allowed.');
166
+ });
167
+ schema.static('findByIdAndRemove', function () {
168
+ (0, _warn.default)('The "findByIdAndRemove" method on models is disallowed due to ambiguity.', 'To permanently delete a document use "findByIdAndDestroy", otherwise "findByIdAndDelete".');
169
+ throw new Error('Method not allowed.');
170
+ });
171
+ }
172
+ function getDelete() {
173
+ return {
174
+ deleted: true,
175
+ deletedAt: new Date()
176
+ };
177
+ }
178
+ function getRestore() {
179
+ return {
180
+ deleted: false,
181
+ $unset: {
182
+ deletedAt: true
183
+ }
184
+ };
185
+ }
186
+ function getWithDeletedQuery() {
187
+ return {
188
+ deleted: {
189
+ $in: [true, false]
190
+ }
191
+ };
192
+ }
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.createTestModel = createTestModel;
7
+ exports.getTestModelName = getTestModelName;
8
+ var _mongoose = _interopRequireDefault(require("mongoose"));
9
+ var _schema = require("./schema");
10
+ var _utils = require("./utils");
11
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
12
+ let counter = 0;
13
+ function createTestModel(...args) {
14
+ let modelName, attributes, schema;
15
+ if (typeof args[0] === 'string') {
16
+ modelName = args[0];
17
+ attributes = args[1];
18
+ } else {
19
+ attributes = args[0];
20
+ }
21
+ if ((0, _utils.isMongooseSchema)(attributes)) {
22
+ schema = attributes;
23
+ } else {
24
+ schema = (0, _schema.createSchema)({
25
+ attributes
26
+ });
27
+ }
28
+ modelName ||= getTestModelName();
29
+ return _mongoose.default.model(modelName, schema);
30
+ }
31
+ function getTestModelName() {
32
+ return `TestModel${counter++}`;
33
+ }
@@ -0,0 +1,73 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.isDateField = isDateField;
7
+ exports.isMongooseSchema = isMongooseSchema;
8
+ exports.isNumberField = isNumberField;
9
+ exports.isReferenceField = isReferenceField;
10
+ exports.isSchemaTypedef = isSchemaTypedef;
11
+ exports.resolveField = resolveField;
12
+ exports.resolveInnerField = resolveInnerField;
13
+ var _mongoose = _interopRequireDefault(require("mongoose"));
14
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
15
+ function isMongooseSchema(obj) {
16
+ return obj instanceof _mongoose.default.Schema;
17
+ }
18
+ function isReferenceField(obj, path) {
19
+ return isType(obj, path, 'ObjectId');
20
+ }
21
+ function isDateField(obj, path) {
22
+ return isType(obj, path, 'Date');
23
+ }
24
+ function isNumberField(obj, path) {
25
+ return isType(obj, path, 'Number');
26
+ }
27
+ function isType(obj, path, test) {
28
+ const {
29
+ type
30
+ } = resolveInnerField(obj, path);
31
+ return type === test || type === _mongoose.default.Schema.Types[test];
32
+ }
33
+ function isSchemaTypedef(arg) {
34
+ // Has a type defined and is not a literal type field.
35
+ return arg?.type && !arg.type?.type;
36
+ }
37
+
38
+ // Note: Resolved field may be an object or a function
39
+ // from mongoose.Schema.Types that is resolved from the
40
+ // shorthand: field: 'String'.
41
+ function resolveField(obj, path) {
42
+ let typedef = obj;
43
+ for (let key of path.split('.')) {
44
+ typedef = resolveFieldForKey(typedef, key);
45
+ }
46
+ return typedef;
47
+ }
48
+
49
+ // The same as resolveField but gets the element
50
+ // typedef in the case of arrays.
51
+ function resolveInnerField(obj, path) {
52
+ let typedef = resolveField(obj, path);
53
+ if (Array.isArray(typedef.type)) {
54
+ typedef = typedef.type[0];
55
+ }
56
+ return typedef;
57
+ }
58
+ function resolveFieldForKey(obj, key) {
59
+ let typedef;
60
+ if (isSchemaTypedef(obj)) {
61
+ const {
62
+ type
63
+ } = obj;
64
+ if (Array.isArray(type)) {
65
+ typedef = type[0][key];
66
+ } else {
67
+ typedef = type[key];
68
+ }
69
+ } else {
70
+ typedef = obj[key];
71
+ }
72
+ return typedef || {};
73
+ }
@@ -0,0 +1,313 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.addValidators = addValidators;
7
+ exports.applyValidation = applyValidation;
8
+ exports.getNamedValidator = getNamedValidator;
9
+ exports.getTupleValidator = getTupleValidator;
10
+ exports.getValidationSchema = getValidationSchema;
11
+ var _lowerFirst2 = _interopRequireDefault(require("lodash/lowerFirst"));
12
+ var _omit2 = _interopRequireDefault(require("lodash/omit"));
13
+ var _get2 = _interopRequireDefault(require("lodash/get"));
14
+ var _mongoose = _interopRequireDefault(require("mongoose"));
15
+ var _yada = _interopRequireDefault(require("@bedrockio/yada"));
16
+ var _access = require("./access");
17
+ var _search = require("./search");
18
+ var _errors = require("./errors");
19
+ var _utils = require("./utils");
20
+ var _schema = require("./schema");
21
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
22
+ const namedSchemas = {
23
+ // Email is special as we are assuming that in
24
+ // all cases lowercase should be allowed but coerced.
25
+ email: _yada.default.string().lowercase().email(),
26
+ // Force "ObjectId" to have parity with refs.
27
+ // "mongo" is notably excluded here for this reason.
28
+ ObjectId: _yada.default.string().mongo(),
29
+ ascii: _yada.default.string().ascii(),
30
+ base64: _yada.default.string().base64(),
31
+ btc: _yada.default.string().btc(),
32
+ country: _yada.default.string().country(),
33
+ creditCard: _yada.default.string().creditCard(),
34
+ domain: _yada.default.string().domain(),
35
+ eth: _yada.default.string().eth(),
36
+ hex: _yada.default.string().hex(),
37
+ ip: _yada.default.string().ip(),
38
+ jwt: _yada.default.string().jwt(),
39
+ latlng: _yada.default.string().latlng(),
40
+ locale: _yada.default.string().locale(),
41
+ md5: _yada.default.string().md5(),
42
+ phone: _yada.default.string().phone(),
43
+ postalCode: _yada.default.string().postalCode(),
44
+ sha1: _yada.default.string().sha1(),
45
+ slug: _yada.default.string().slug(),
46
+ swift: _yada.default.string().swift(),
47
+ url: _yada.default.string().url(),
48
+ uuid: _yada.default.string().uuid()
49
+ };
50
+ function addValidators(schemas) {
51
+ Object.assign(namedSchemas, schemas);
52
+ }
53
+ function applyValidation(schema, definition) {
54
+ schema.static('getCreateValidation', function getCreateValidation(appendSchema) {
55
+ return getSchemaFromMongoose(schema, {
56
+ appendSchema,
57
+ stripReserved: true,
58
+ requireWriteAccess: true,
59
+ modelName: this.modelName
60
+ });
61
+ });
62
+ schema.static('getUpdateValidation', function getUpdateValidation(appendSchema) {
63
+ return getSchemaFromMongoose(schema, {
64
+ appendSchema,
65
+ skipRequired: true,
66
+ stripReserved: true,
67
+ stripUnknown: true,
68
+ requireWriteAccess: true,
69
+ modelName: this.modelName
70
+ });
71
+ });
72
+ schema.static('getSearchValidation', function getSearchValidation(searchOptions) {
73
+ return getSchemaFromMongoose(schema, {
74
+ allowRanges: true,
75
+ skipRequired: true,
76
+ allowMultiple: true,
77
+ unwindArrayFields: true,
78
+ requireReadAccess: true,
79
+ appendSchema: (0, _search.searchValidation)(definition, searchOptions),
80
+ modelName: this.modelName
81
+ });
82
+ });
83
+ }
84
+
85
+ // Yada schemas
86
+
87
+ function getSchemaFromMongoose(schema, options) {
88
+ let {
89
+ obj
90
+ } = schema;
91
+ if (options.stripReserved) {
92
+ obj = (0, _omit2.default)(obj, _schema.RESERVED_FIELDS);
93
+ }
94
+ return getValidationSchema(obj, options);
95
+ }
96
+
97
+ // Exported for testing
98
+ function getValidationSchema(attributes, options = {}) {
99
+ const {
100
+ appendSchema
101
+ } = options;
102
+ let schema = getObjectSchema(attributes, options);
103
+ if (appendSchema) {
104
+ schema = schema.append(appendSchema);
105
+ }
106
+ return schema;
107
+ }
108
+ function getObjectSchema(arg, options) {
109
+ const {
110
+ stripUnknown
111
+ } = options;
112
+ if ((0, _utils.isSchemaTypedef)(arg)) {
113
+ return getSchemaForTypedef(arg, options);
114
+ } else if (arg instanceof _mongoose.default.Schema) {
115
+ return getObjectSchema(arg.obj, options);
116
+ } else if (Array.isArray(arg)) {
117
+ return getArraySchema(arg, options);
118
+ } else if (typeof arg === 'object') {
119
+ const map = {};
120
+ for (let [key, field] of Object.entries(arg)) {
121
+ if (!isExcludedField(field, options)) {
122
+ map[key] = getObjectSchema(field, options);
123
+ }
124
+ }
125
+ let schema = _yada.default.object(map);
126
+ if (stripUnknown) {
127
+ schema = schema.options({
128
+ stripUnknown: true
129
+ });
130
+ }
131
+ return schema;
132
+ } else {
133
+ return getSchemaForType(arg);
134
+ }
135
+ }
136
+ function getArraySchema(obj, options) {
137
+ // Nested array fields may not skip required
138
+ // validations as they are a new context.
139
+ let schema = getObjectSchema(obj[0], {
140
+ ...options,
141
+ skipRequired: false
142
+ });
143
+ if (!options.unwindArrayFields) {
144
+ schema = _yada.default.array(schema);
145
+ }
146
+ return schema;
147
+ }
148
+ function getSchemaForTypedef(typedef, options = {}) {
149
+ let {
150
+ type
151
+ } = typedef;
152
+ let schema;
153
+ if ((0, _utils.isMongooseSchema)(type)) {
154
+ schema = getSchemaFromMongoose(type, options);
155
+ } else if (Array.isArray(type)) {
156
+ schema = getArraySchema(type, options);
157
+ } else if (typeof type === 'object') {
158
+ schema = getObjectSchema(type, options);
159
+ } else {
160
+ schema = getSchemaForType(type);
161
+ }
162
+ if (isRequired(typedef, options)) {
163
+ schema = schema.required();
164
+ }
165
+ if (typedef.validate?.schema) {
166
+ schema = schema.append(typedef.validate.schema);
167
+ }
168
+ if (typedef.enum) {
169
+ schema = schema.allow(...typedef.enum);
170
+ }
171
+ if (typedef.match) {
172
+ schema = schema.match(RegExp(typedef.match));
173
+ }
174
+ if (typedef.min != null || typedef.minLength != null) {
175
+ schema = schema.min(typedef.min ?? typedef.minLength);
176
+ }
177
+ if (typedef.max != null || typedef.maxLength != null) {
178
+ schema = schema.max(typedef.max ?? typedef.maxLength);
179
+ }
180
+ if (options.allowRanges) {
181
+ schema = getRangeSchema(schema, type);
182
+ }
183
+ if (options.allowMultiple) {
184
+ schema = _yada.default.allow(schema, _yada.default.array(schema));
185
+ }
186
+ if (typedef.readAccess && options.requireReadAccess) {
187
+ schema = validateReadAccess(schema, typedef.readAccess, options);
188
+ }
189
+ if (typedef.writeAccess && options.requireWriteAccess) {
190
+ schema = validateWriteAccess(schema, typedef.writeAccess, options);
191
+ }
192
+ return schema;
193
+ }
194
+ function getSchemaForType(type) {
195
+ switch (type) {
196
+ case 'String':
197
+ return _yada.default.string();
198
+ case 'Number':
199
+ return _yada.default.number();
200
+ case 'Boolean':
201
+ return _yada.default.boolean();
202
+ case 'Date':
203
+ return _yada.default.date().iso();
204
+ case 'Mixed':
205
+ case 'Object':
206
+ return _yada.default.object();
207
+ case 'Array':
208
+ return _yada.default.array();
209
+ case 'ObjectId':
210
+ return _yada.default.custom(async val => {
211
+ const id = String(val.id || val);
212
+ await namedSchemas['ObjectId'].validate(id);
213
+ return id;
214
+ });
215
+ default:
216
+ throw new TypeError(`Unknown schema type ${type}`);
217
+ }
218
+ }
219
+ function getRangeSchema(schema, type) {
220
+ if (type === 'Number') {
221
+ schema = _yada.default.allow(schema, _yada.default.object({
222
+ lt: _yada.default.number(),
223
+ gt: _yada.default.number(),
224
+ lte: _yada.default.number(),
225
+ gte: _yada.default.number()
226
+ }));
227
+ } else if (type === 'Date') {
228
+ return _yada.default.allow(schema, _yada.default.object({
229
+ lt: _yada.default.date().iso(),
230
+ gt: _yada.default.date().iso(),
231
+ lte: _yada.default.date().iso(),
232
+ gte: _yada.default.date().iso()
233
+ }));
234
+ }
235
+ return schema;
236
+ }
237
+ function isRequired(typedef, options) {
238
+ return typedef.required && !typedef.default && !options.skipRequired;
239
+ }
240
+ function isExcludedField(field, options) {
241
+ if ((0, _utils.isSchemaTypedef)(field)) {
242
+ const {
243
+ requireWriteAccess
244
+ } = options;
245
+ return requireWriteAccess && field.writeAccess === 'none';
246
+ } else {
247
+ return false;
248
+ }
249
+ }
250
+ function validateReadAccess(schema, allowed, options) {
251
+ return validateAccess('read', schema, allowed, options);
252
+ }
253
+ function validateWriteAccess(schema, allowed, options) {
254
+ return validateAccess('write', schema, allowed, options);
255
+ }
256
+ function validateAccess(type, schema, allowed, options) {
257
+ const {
258
+ modelName
259
+ } = options;
260
+ return schema.custom((val, options) => {
261
+ const document = options[(0, _lowerFirst2.default)(modelName)] || options['document'];
262
+ const isAllowed = (0, _access.hasAccess)(type, allowed, {
263
+ ...options,
264
+ document
265
+ });
266
+ if (!isAllowed) {
267
+ const currentValue = (0, _get2.default)(document, options.path);
268
+ if (val !== currentValue) {
269
+ throw new _errors.PermissionsError('requires write permissions.');
270
+ }
271
+ }
272
+ });
273
+ }
274
+
275
+ // Mongoose Validators
276
+
277
+ function getNamedValidator(name) {
278
+ return wrapMongooseValidator(getNamedSchema(name));
279
+ }
280
+ function getTupleValidator(types) {
281
+ types = types.map(type => {
282
+ return getSchemaForTypedef(type);
283
+ });
284
+ return wrapMongooseValidator(_yada.default.array(types).length(types.length));
285
+ }
286
+
287
+ // Returns an async function that will error on failure.
288
+ //
289
+ // Note that mongoose validator functions will not be called
290
+ // if the field is optional and not set or unset with undefined.
291
+ // If the field is not optional the "required" field will also
292
+ // perform valdation so no additional checks are necessary.
293
+ //
294
+ // Also note that throwing an error inside a validator and passing
295
+ // the "message" field result in an identical error message. In this
296
+ // case we want the schema error messages to trickle down so using
297
+ // the first style here.
298
+ //
299
+ // https://mongoosejs.com/docs/api/schematype.html#schematype_SchemaType-validate
300
+ function wrapMongooseValidator(schema) {
301
+ const validator = async val => {
302
+ await schema.validate(val);
303
+ };
304
+ validator.schema = schema;
305
+ return validator;
306
+ }
307
+ function getNamedSchema(name) {
308
+ const schema = namedSchemas[name];
309
+ if (!schema) {
310
+ throw new Error(`Cannot find schema for "${name}".`);
311
+ }
312
+ return schema;
313
+ }
@@ -0,0 +1,13 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.default = warn;
7
+ var _logger = _interopRequireDefault(require("@bedrockio/logger"));
8
+ function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
9
+ function warn(...lines) {
10
+ if (process.env.ENV_NAME !== 'test') {
11
+ _logger.default.warn(lines.join('\n'));
12
+ }
13
+ }
@@ -0,0 +1,10 @@
1
+ module.exports = {
2
+ mongodbMemoryServerOptions: {
3
+ binary: {
4
+ version: '6.0.2',
5
+ skipMD5: true,
6
+ },
7
+ autoStart: false,
8
+ instance: {},
9
+ },
10
+ };
package/jest.config.js ADDED
@@ -0,0 +1,8 @@
1
+ process.env.ENV_NAME = 'test';
2
+
3
+ export default {
4
+ preset: '@shelf/jest-mongodb',
5
+ setupFilesAfterEnv: ['<rootDir>/test/setup'],
6
+ // https://github.com/shelfio/jest-mongodb#6-jest-watch-mode-gotcha
7
+ watchPathIgnorePatterns: ['globalConfig'],
8
+ };
package/package.json ADDED
@@ -0,0 +1,53 @@
1
+ {
2
+ "name": "@bedrockio/model",
3
+ "version": "0.1.0",
4
+ "description": "Bedrock utilities for model creation.",
5
+ "type": "module",
6
+ "scripts": {
7
+ "test": "jest",
8
+ "lint": "eslint",
9
+ "build": "scripts/build",
10
+ "prepublishOnly": "npm run build"
11
+ },
12
+ "main": "dist/cjs/index.js",
13
+ "module": "src/index.js",
14
+ "contributors": [
15
+ {
16
+ "name": "Andrew Plummer",
17
+ "email": "andrew@rekall.ai"
18
+ }
19
+ ],
20
+ "license": "MIT",
21
+ "repository": {
22
+ "type": "git",
23
+ "url": "https://github.com/bedrockio/model"
24
+ },
25
+ "dependencies": {
26
+ "@bedrockio/logger": "^1.0.3",
27
+ "@bedrockio/yada": "^1.0.9",
28
+ "lodash": "^4.17.21"
29
+ },
30
+ "peerDependencies": {
31
+ "mongoose": "^6.9.0"
32
+ },
33
+ "devDependencies": {
34
+ "@babel/cli": "^7.20.7",
35
+ "@babel/core": "^7.20.12",
36
+ "@babel/preset-env": "^7.20.2",
37
+ "@bedrockio/prettier-config": "^1.0.2",
38
+ "@shelf/jest-mongodb": "^4.1.6",
39
+ "babel-plugin-import-replacement": "^1.0.1",
40
+ "babel-plugin-lodash": "^3.3.4",
41
+ "eslint": "^8.33.0",
42
+ "eslint-plugin-bedrock": "^1.0.21",
43
+ "jest": "^29.4.1",
44
+ "jest-environment-node": "^29.4.1",
45
+ "mongodb": "4.13.0",
46
+ "mongoose": "^6.9.0",
47
+ "prettier-eslint": "^15.0.1"
48
+ },
49
+ "volta": {
50
+ "node": "18.14.0",
51
+ "yarn": "1.22.19"
52
+ }
53
+ }