@bedrockio/model 0.17.0 → 0.18.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.
@@ -0,0 +1,15 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(yarn test:*)",
5
+ "Bash(yarn list:*)",
6
+ "WebSearch",
7
+ "WebFetch(domain:www.npmjs.com)",
8
+ "WebFetch(domain:gist.github.com)",
9
+ "WebFetch(domain:mongoosejs.com)",
10
+ "WebFetch(domain:github.com)"
11
+ ],
12
+ "deny": [],
13
+ "ask": []
14
+ }
15
+ }
package/CHANGELOG.md CHANGED
@@ -1,3 +1,13 @@
1
+ ## 0.18.1
2
+
3
+ - Added `export` for dumping documents and to support reload.
4
+ - Fix for `include` not working on nested virtuals.
5
+ - Fixes for `reload`.
6
+
7
+ ## 0.18.0
8
+
9
+ - Added `reload`.
10
+
1
11
  ## 0.17.0
2
12
 
3
13
  - Added `time` validation.
package/README.md CHANGED
@@ -23,6 +23,7 @@ Bedrock utilities for model creation.
23
23
  - [Access Control](#access-control)
24
24
  - [Assign](#assign)
25
25
  - [Upsert](#upsert)
26
+ - [Reload](#reload)
26
27
  - [Clone](#clone)
27
28
  - [Slugs](#slugs)
28
29
  - [Testing](#testing)
@@ -1752,6 +1753,32 @@ if (!shop) {
1752
1753
  }
1753
1754
  ```
1754
1755
 
1756
+ ### Reload
1757
+
1758
+ Adds a single `reload` method that reloads a document in place. This is useful
1759
+ for testing purposes, etc:
1760
+
1761
+ ```js
1762
+ const shop = await Shop.create({
1763
+ name: 'My Shop',
1764
+ });
1765
+
1766
+ await Shop.updateOne(
1767
+ {
1768
+ _id: shop._id,
1769
+ },
1770
+ {
1771
+ $set: {
1772
+ name: 'My New Shop',
1773
+ },
1774
+ },
1775
+ );
1776
+
1777
+ await shop.reload();
1778
+
1779
+ shop.name; // Now "My New Shop"
1780
+ ```
1781
+
1755
1782
  ### Clone
1756
1783
 
1757
1784
  Adds a single `clone` method on documents. This is an async method mostly for
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.applyExport = applyExport;
7
+ var _lodash = require("lodash");
8
+ function applyExport(schema) {
9
+ schema.method('export', function () {
10
+ const result = {};
11
+ this.constructor.schema.eachPath(schemaPath => {
12
+ const value = this.get(schemaPath);
13
+ if (value !== undefined) {
14
+ (0, _lodash.set)(result, schemaPath, value);
15
+ }
16
+ });
17
+ return result;
18
+ });
19
+ }
@@ -162,16 +162,21 @@ function getDocumentParams(doc, arg, options = {}) {
162
162
  const params = getParams(doc.constructor.modelName, arg);
163
163
  if (!options.force) {
164
164
  params.populate = params.populate.filter(p => {
165
- return !isDocumentPopulated(doc, p);
165
+ return !isPopulated(doc, p);
166
166
  });
167
167
  }
168
168
  return params;
169
169
  }
170
- function isDocumentPopulated(doc, params) {
171
- if (doc.populated(params.path)) {
172
- const sub = doc.get(params.path);
170
+ function isPopulated(arg, params) {
171
+ if (Array.isArray(arg)) {
172
+ return arg.every(el => {
173
+ return isPopulated(el, params);
174
+ });
175
+ }
176
+ if (arg.populated(params.path)) {
177
+ const sub = arg.get(params.path);
173
178
  return params.populate.every(p => {
174
- return isDocumentPopulated(sub, p);
179
+ return isPopulated(sub, p);
175
180
  });
176
181
  } else {
177
182
  return false;
@@ -360,7 +365,7 @@ function resolvePaths(schema, str) {
360
365
  source = source.replaceAll('\\*', '[^.]+');
361
366
  source = `^${source}$`;
362
367
  const reg = RegExp(source);
363
- paths = getSchemaPaths(schema).filter(path => {
368
+ paths = (0, _utils.getSchemaPaths)(schema).filter(path => {
364
369
  return reg.test(path);
365
370
  });
366
371
  } else {
@@ -369,17 +374,4 @@ function resolvePaths(schema, str) {
369
374
  return paths.map(path => {
370
375
  return [path, schema.pathType(path)];
371
376
  });
372
- }
373
- function getSchemaPaths(schema) {
374
- return Object.entries(schema.paths || {}).flatMap(([key, schema]) => {
375
- if (key.startsWith('_')) {
376
- return [];
377
- } else if (schema.schema) {
378
- return getSchemaPaths(schema.schema).map(path => {
379
- return [key, path].join('.');
380
- });
381
- } else {
382
- return [key];
383
- }
384
- });
385
377
  }
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+
3
+ Object.defineProperty(exports, "__esModule", {
4
+ value: true
5
+ });
6
+ exports.applyReload = applyReload;
7
+ var _mongoose = _interopRequireDefault(require("mongoose"));
8
+ var _utils = require("./utils");
9
+ function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; }
10
+ function applyReload(schema) {
11
+ schema.method('reload', async function reload() {
12
+ const paths = getPopulatedPaths(this);
13
+ const doc = await this.constructor.findById(this.id).include(paths);
14
+ if (!doc) {
15
+ throw new Error('Document does not exist');
16
+ }
17
+ this.overwrite(doc.export());
18
+
19
+ // Include on the query above will not work
20
+ // for virtuals so handle separately here.
21
+ for (const path of getVirtualReferencePaths(doc)) {
22
+ await doc.include(path);
23
+ this.set(path, doc[path]);
24
+ }
25
+
26
+ // All data reloaded so mark as unmodified.
27
+ for (const path of this.modifiedPaths()) {
28
+ this.unmarkModified(path);
29
+ }
30
+ });
31
+ }
32
+ function getPopulatedPaths(doc, base = []) {
33
+ const schema = doc.constructor.schema;
34
+ return getReferencePaths(schema).filter(name => {
35
+ return doc.populated(name);
36
+ }).flatMap(name => {
37
+ const path = [...base, name];
38
+ const value = doc.get(name);
39
+ const inner = Array.isArray(value) ? value[0] : value;
40
+ return [path.join('.'), ...getPopulatedPaths(inner, path)];
41
+ });
42
+ }
43
+ function getReferencePaths(schema) {
44
+ return [...getRealReferencePaths(schema), ...getVirtualReferencePaths(schema)];
45
+ }
46
+ function getRealReferencePaths(schema) {
47
+ return (0, _utils.getSchemaPaths)(schema).filter(path => {
48
+ return (0, _utils.isReferenceField)(schema, path);
49
+ });
50
+ }
51
+ function getVirtualReferencePaths(arg) {
52
+ const schema = resolveSchema(arg);
53
+ return Object.keys(schema.virtuals).filter(key => {
54
+ return schema.virtuals[key].options?.ref;
55
+ });
56
+ }
57
+ function resolveSchema(arg) {
58
+ if (arg instanceof _mongoose.default.Document) {
59
+ // @ts-ignore
60
+ return arg.constructor.schema;
61
+ } else if (arg instanceof _mongoose.default.Schema) {
62
+ return arg;
63
+ }
64
+ }
@@ -12,8 +12,10 @@ var _cache = require("./cache");
12
12
  var _clone = require("./clone");
13
13
  var _deleteHooks = require("./delete-hooks");
14
14
  var _disallowed = require("./disallowed");
15
+ var _export = require("./export");
15
16
  var _hydrate = require("./hydrate");
16
17
  var _include = require("./include");
18
+ var _reload = require("./reload");
17
19
  var _search = require("./search");
18
20
  var _serialization = require("./serialization");
19
21
  var _slug = require("./slug");
@@ -58,6 +60,8 @@ function createSchema(definition, options = {}) {
58
60
  (0, _search.applySearch)(schema, definition);
59
61
  (0, _cache.applyCache)(schema, definition);
60
62
  (0, _clone.applyClone)(schema);
63
+ (0, _reload.applyReload)(schema);
64
+ (0, _export.applyExport)(schema);
61
65
  (0, _disallowed.applyDisallowed)(schema);
62
66
  (0, _include.applyInclude)(schema);
63
67
  (0, _hydrate.applyHydrate)(schema);
package/dist/cjs/utils.js CHANGED
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
5
5
  });
6
6
  exports.getField = getField;
7
7
  exports.getInnerField = getInnerField;
8
+ exports.getSchemaPaths = getSchemaPaths;
8
9
  exports.isArrayField = isArrayField;
9
10
  exports.isDateField = isDateField;
10
11
  exports.isEqual = isEqual;
@@ -149,4 +150,17 @@ function resolveInnerField(field) {
149
150
  field = field.obj;
150
151
  }
151
152
  return field;
153
+ }
154
+ function getSchemaPaths(schema) {
155
+ return Object.entries(schema.paths || {}).flatMap(([key, schema]) => {
156
+ if (key.startsWith('_')) {
157
+ return [];
158
+ } else if (schema.schema) {
159
+ return getSchemaPaths(schema.schema).map(path => {
160
+ return [key, path].join('.');
161
+ });
162
+ } else {
163
+ return [key];
164
+ }
165
+ });
152
166
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bedrockio/model",
3
- "version": "0.17.0",
3
+ "version": "0.18.1",
4
4
  "description": "Bedrock utilities for model creation.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -45,7 +45,7 @@
45
45
  "jest": "^30.2.0",
46
46
  "jest-environment-node": "^30.2.0",
47
47
  "mongodb": "^6.20.0",
48
- "mongoose": "^8.18.2",
48
+ "mongoose": "^8.19.2",
49
49
  "prettier": "^3.6.2",
50
50
  "typescript": "^5.9.2"
51
51
  },
package/src/export.js ADDED
@@ -0,0 +1,14 @@
1
+ import { set } from 'lodash';
2
+
3
+ export function applyExport(schema) {
4
+ schema.method('export', function () {
5
+ const result = {};
6
+ this.constructor.schema.eachPath((schemaPath) => {
7
+ const value = this.get(schemaPath);
8
+ if (value !== undefined) {
9
+ set(result, schemaPath, value);
10
+ }
11
+ });
12
+ return result;
13
+ });
14
+ }
package/src/include.js CHANGED
@@ -2,6 +2,7 @@ import { escapeRegExp } from 'lodash';
2
2
  import mongoose from 'mongoose';
3
3
 
4
4
  import { POPULATE_MAX_DEPTH } from './const';
5
+ import { getSchemaPaths } from './utils';
5
6
  import { getInnerField, isSchemaTypedef } from './utils';
6
7
 
7
8
  // @ts-ignore
@@ -145,18 +146,23 @@ export function getDocumentParams(doc, arg, options = {}) {
145
146
 
146
147
  if (!options.force) {
147
148
  params.populate = params.populate.filter((p) => {
148
- return !isDocumentPopulated(doc, p);
149
+ return !isPopulated(doc, p);
149
150
  });
150
151
  }
151
152
 
152
153
  return params;
153
154
  }
154
155
 
155
- function isDocumentPopulated(doc, params) {
156
- if (doc.populated(params.path)) {
157
- const sub = doc.get(params.path);
156
+ function isPopulated(arg, params) {
157
+ if (Array.isArray(arg)) {
158
+ return arg.every((el) => {
159
+ return isPopulated(el, params);
160
+ });
161
+ }
162
+ if (arg.populated(params.path)) {
163
+ const sub = arg.get(params.path);
158
164
  return params.populate.every((p) => {
159
- return isDocumentPopulated(sub, p);
165
+ return isPopulated(sub, p);
160
166
  });
161
167
  } else {
162
168
  return false;
@@ -363,17 +369,3 @@ function resolvePaths(schema, str) {
363
369
  return [path, schema.pathType(path)];
364
370
  });
365
371
  }
366
-
367
- function getSchemaPaths(schema) {
368
- return Object.entries(schema.paths || {}).flatMap(([key, schema]) => {
369
- if (key.startsWith('_')) {
370
- return [];
371
- } else if (schema.schema) {
372
- return getSchemaPaths(schema.schema).map((path) => {
373
- return [key, path].join('.');
374
- });
375
- } else {
376
- return [key];
377
- }
378
- });
379
- }
package/src/reload.js ADDED
@@ -0,0 +1,75 @@
1
+ import mongoose from 'mongoose';
2
+
3
+ import { getSchemaPaths } from './utils';
4
+ import { isReferenceField } from './utils';
5
+
6
+ export function applyReload(schema) {
7
+ schema.method('reload', async function reload() {
8
+ const paths = getPopulatedPaths(this);
9
+
10
+ const doc = await this.constructor.findById(this.id).include(paths);
11
+
12
+ if (!doc) {
13
+ throw new Error('Document does not exist');
14
+ }
15
+
16
+ this.overwrite(doc.export());
17
+
18
+ // Include on the query above will not work
19
+ // for virtuals so handle separately here.
20
+ for (const path of getVirtualReferencePaths(doc)) {
21
+ await doc.include(path);
22
+ this.set(path, doc[path]);
23
+ }
24
+
25
+ // All data reloaded so mark as unmodified.
26
+ for (const path of this.modifiedPaths()) {
27
+ this.unmarkModified(path);
28
+ }
29
+ });
30
+ }
31
+
32
+ function getPopulatedPaths(doc, base = []) {
33
+ const schema = doc.constructor.schema;
34
+ return getReferencePaths(schema)
35
+ .filter((name) => {
36
+ return doc.populated(name);
37
+ })
38
+ .flatMap((name) => {
39
+ const path = [...base, name];
40
+
41
+ const value = doc.get(name);
42
+ const inner = Array.isArray(value) ? value[0] : value;
43
+
44
+ return [path.join('.'), ...getPopulatedPaths(inner, path)];
45
+ });
46
+ }
47
+
48
+ function getReferencePaths(schema) {
49
+ return [
50
+ ...getRealReferencePaths(schema),
51
+ ...getVirtualReferencePaths(schema),
52
+ ];
53
+ }
54
+
55
+ function getRealReferencePaths(schema) {
56
+ return getSchemaPaths(schema).filter((path) => {
57
+ return isReferenceField(schema, path);
58
+ });
59
+ }
60
+
61
+ function getVirtualReferencePaths(arg) {
62
+ const schema = resolveSchema(arg);
63
+ return Object.keys(schema.virtuals).filter((key) => {
64
+ return schema.virtuals[key].options?.ref;
65
+ });
66
+ }
67
+
68
+ function resolveSchema(arg) {
69
+ if (arg instanceof mongoose.Document) {
70
+ // @ts-ignore
71
+ return arg.constructor.schema;
72
+ } else if (arg instanceof mongoose.Schema) {
73
+ return arg;
74
+ }
75
+ }
package/src/schema.js CHANGED
@@ -6,8 +6,10 @@ import { applyCache } from './cache';
6
6
  import { applyClone } from './clone';
7
7
  import { applyDeleteHooks } from './delete-hooks';
8
8
  import { applyDisallowed } from './disallowed';
9
+ import { applyExport } from './export';
9
10
  import { applyHydrate } from './hydrate';
10
11
  import { applyInclude } from './include';
12
+ import { applyReload } from './reload';
11
13
  import { applySearch } from './search';
12
14
  import { serializeOptions } from './serialization';
13
15
  import { applySlug } from './slug';
@@ -61,6 +63,8 @@ export function createSchema(definition, options = {}) {
61
63
  applySearch(schema, definition);
62
64
  applyCache(schema, definition);
63
65
  applyClone(schema);
66
+ applyReload(schema);
67
+ applyExport(schema);
64
68
  applyDisallowed(schema);
65
69
  applyInclude(schema);
66
70
  applyHydrate(schema);
package/src/utils.js CHANGED
@@ -139,3 +139,17 @@ function resolveInnerField(field) {
139
139
  }
140
140
  return field;
141
141
  }
142
+
143
+ export function getSchemaPaths(schema) {
144
+ return Object.entries(schema.paths || {}).flatMap(([key, schema]) => {
145
+ if (key.startsWith('_')) {
146
+ return [];
147
+ } else if (schema.schema) {
148
+ return getSchemaPaths(schema.schema).map((path) => {
149
+ return [key, path].join('.');
150
+ });
151
+ } else {
152
+ return [key];
153
+ }
154
+ });
155
+ }
@@ -0,0 +1,2 @@
1
+ export function applyExport(schema: any): void;
2
+ //# sourceMappingURL=export.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"export.d.ts","sourceRoot":"","sources":["../src/export.js"],"names":[],"mappings":"AAEA,+CAWC"}
@@ -1 +1 @@
1
- {"version":3,"file":"include.d.ts","sourceRoot":"","sources":["../src/include.js"],"names":[],"mappings":"AAiBA,gDAuEC;AAMD,uDA4BC;AAGD,yDAIC;AAaD,yEAUC"}
1
+ {"version":3,"file":"include.d.ts","sourceRoot":"","sources":["../src/include.js"],"names":[],"mappings":"AAkBA,gDAuEC;AAMD,uDA4BC;AAGD,yDAIC;AAaD,yEAUC"}
@@ -0,0 +1,2 @@
1
+ export function applyReload(schema: any): void;
2
+ //# sourceMappingURL=reload.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"reload.d.ts","sourceRoot":"","sources":["../src/reload.js"],"names":[],"mappings":"AAKA,+CAwBC"}
package/types/schema.d.ts CHANGED
@@ -19,6 +19,7 @@ export function createSchema(definition: object, options?: mongoose.SchemaOption
19
19
  };
20
20
  collation?: mongoose.mongo.CollationOptions;
21
21
  collectionOptions?: mongoose.mongo.CreateCollectionOptions;
22
+ lean?: boolean | mongoose.LeanOptions;
22
23
  timeseries?: mongoose.mongo.TimeSeriesCollectionOptions;
23
24
  expireAfterSeconds?: number;
24
25
  expires?: number | string;
@@ -1 +1 @@
1
- {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.js"],"names":[],"mappings":"AAuBA;;;;;;;GAOG;AACH,yCAJW,MAAM,YACN,QAAQ,CAAC,aAAa;;;;;;;YAgDJ,CAAC;WAC1B,CAAF;mBAAqB,CAAC;;;;;;;;;;;;;;;;;;;;;;;SA2HlB,CAAC;gBACL,CAAC;SAAW,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAlId;AAED,iEAsBC;qBA9FoB,UAAU"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.js"],"names":[],"mappings":"AAyBA;;;;;;;GAOG;AACH,yCAJW,MAAM,YACN,QAAQ,CAAC,aAAa;;;;;;;YA+CpB,CAAC;WAAa,CAAC;mBAAqB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;SA4Hf,CAAC;gBAA2B,CAAC;SAGhE,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAjIC;AAED,iEAsBC;qBAlGoB,UAAU"}
package/types/utils.d.ts CHANGED
@@ -13,5 +13,6 @@ export function resolveRefPath(schema: any, path: any): {
13
13
  foreign: any;
14
14
  };
15
15
  export function getInnerField(obj: any, path: any): any;
16
+ export function getSchemaPaths(schema: any): any;
16
17
  import mongoose from 'mongoose';
17
18
  //# sourceMappingURL=utils.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.js"],"names":[],"mappings":"AAQA,iDAcC;AAED,gHAEC;AAED,+DAEC;AAED,0DAEC;AAED,4DAEC;AAED,4DAEC;AAED,2DAGC;AAOD,mDAGC;AAuBD,mDAgBC;AAKD;;;;EAoBC;AAKD,wDAEC;qBAhIoB,UAAU"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.js"],"names":[],"mappings":"AAQA,iDAcC;AAED,gHAEC;AAED,+DAEC;AAED,0DAEC;AAED,4DAEC;AAED,4DAEC;AAED,2DAGC;AAOD,mDAGC;AAuBD,mDAgBC;AAKD;;;;EAoBC;AAKD,wDAEC;AAcD,iDAYC;qBA1JoB,UAAU"}