@bedrockio/model 0.8.1 → 0.8.3

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/CHANGELOG.md CHANGED
@@ -1,3 +1,11 @@
1
+ ## 0.8.3
2
+
3
+ - Bumped yada version
4
+
5
+ ## 0.8.2
6
+
7
+ - Trim strings by default.
8
+
1
9
  ## 0.8.0
2
10
 
3
11
  - Moved "cache" field in "search" to definition root.
package/README.md CHANGED
@@ -11,6 +11,7 @@ Bedrock utilities for model creation.
11
11
  - [Scopes](#scopes)
12
12
  - [Tuples](#tuples)
13
13
  - [Array Extensions](#array-extensions)
14
+ - [String Trimming](#string-trimming)
14
15
  - [Modules](#modules)
15
16
  - [Soft Delete](#soft-delete)
16
17
  - [Validation](#validation)
@@ -288,19 +289,37 @@ so the field will always exist. It also suffers from being ambiguous (is the
288
289
  array required or the elements inside?). An extension is provided here for
289
290
  explicit handling of this case:
290
291
 
291
- ```js
292
+ ```json
292
293
  {
293
294
  "tokens": {
294
295
  "type": ["String"],
295
296
  "minLength": 1,
296
297
  "maxLength": 2
297
298
  }
298
- };
299
+ }
299
300
  ```
300
301
 
301
302
  A custom validator will be created to enforce the array length, bringing parity
302
303
  with `minLength` and `maxLength` on strings.
303
304
 
305
+ #### String Trimming
306
+
307
+ Note that strings are always trimmed by default. To disable this pass `false`
308
+ explicitly for the `trim` flag.
309
+
310
+ ```jsonc
311
+ {
312
+ // Implies trim: true
313
+ "name": "String",
314
+
315
+ // Preserves surrounding whitespace
316
+ "nameLong": {
317
+ "type": "String",
318
+ "trim": false,
319
+ },
320
+ }
321
+ ```
322
+
304
323
  ### Gotchas
305
324
 
306
325
  #### The `type` field is special:
@@ -462,11 +481,11 @@ router.post(
462
481
  validateBody(
463
482
  User.getCreateValidation({
464
483
  password: yd.string().password().required(),
465
- })
484
+ }),
466
485
  ),
467
486
  async (ctx) => {
468
487
  // ....
469
- }
488
+ },
470
489
  );
471
490
  ```
472
491
 
@@ -904,9 +923,9 @@ await Product.findById(id).include('shop.^user.name').
904
923
  // etc
905
924
  "shop": {
906
925
  "user": {
907
- "name": "User Name"
908
- }
909
- }
926
+ "name": "User Name",
927
+ },
928
+ },
910
929
  }
911
930
  ```
912
931
 
@@ -929,9 +948,9 @@ await Product.findById(id).include('shop.user.^name').
929
948
  "rating": 5,
930
949
  // etc
931
950
  "user": {
932
- "name": "User Name"
933
- }
934
- }
951
+ "name": "User Name",
952
+ },
953
+ },
935
954
  }
936
955
  ```
937
956
 
@@ -969,7 +988,7 @@ const user = await User.findById(id).include('*Name');
969
988
  ```jsonc
970
989
  {
971
990
  "firstName": "Frank",
972
- "lastName": "Reynolds"
991
+ "lastName": "Reynolds",
973
992
  // Other fields excluded.
974
993
  }
975
994
  ```
@@ -1239,13 +1258,13 @@ However, only admins can set the owner of the shop:
1239
1258
  {
1240
1259
  "name": {
1241
1260
  "type": "String",
1242
- "writeAccess": ["owner", "admin"]
1261
+ "writeAccess": ["owner", "admin"],
1243
1262
  },
1244
1263
  "owner": {
1245
1264
  "type": "ObjectId",
1246
1265
  "ref": "User",
1247
- "writeAccess": "admin"
1248
- }
1266
+ "writeAccess": "admin",
1267
+ },
1249
1268
  }
1250
1269
  ```
1251
1270
 
@@ -1304,8 +1323,8 @@ of the model definition:
1304
1323
  // A user may update themselves or an admin.
1305
1324
  "update": ["self", "admin"],
1306
1325
  // Only an admin may delete a user.
1307
- "delete": ["admin"]
1308
- }
1326
+ "delete": ["admin"],
1327
+ },
1309
1328
  }
1310
1329
  ```
1311
1330
 
@@ -1319,15 +1338,15 @@ The same options can be used as
1319
1338
  "attributes": {
1320
1339
  "owner": {
1321
1340
  "type": "ObjectId",
1322
- "ref": "User"
1323
- }
1341
+ "ref": "User",
1342
+ },
1324
1343
  },
1325
1344
  "access": {
1326
1345
  // An owner may update their own shop.
1327
1346
  "update": ["owner", "admin"],
1328
1347
  // Only an admin may delete a shop.
1329
- "delete": ["admin"]
1330
- }
1348
+ "delete": ["admin"],
1349
+ },
1331
1350
  }
1332
1351
  ```
1333
1352
 
@@ -1343,23 +1362,23 @@ deletion. They are defined in the `onDelete` field of the model definition file:
1343
1362
  "name": "String",
1344
1363
  "profile": {
1345
1364
  "type": "ObjectId",
1346
- "ref": "UserProfile"
1347
- }
1365
+ "ref": "UserProfile",
1366
+ },
1348
1367
  },
1349
1368
  "onDelete": {
1350
1369
  "clean": [
1351
1370
  {
1352
- "path": "profile"
1371
+ "path": "profile",
1353
1372
  },
1354
1373
  {
1355
1374
  "ref": "Shop",
1356
- "path": "owner"
1357
- }
1375
+ "path": "owner",
1376
+ },
1358
1377
  ],
1359
1378
  "errorOnReferenced": {
1360
- "except": ["AuditEntry"]
1361
- }
1362
- }
1379
+ "except": ["AuditEntry"],
1380
+ },
1381
+ },
1363
1382
  }
1364
1383
  ```
1365
1384
 
@@ -1412,11 +1431,11 @@ Operations may filter on additional fields with `query`:
1412
1431
  "ref": "Shop",
1413
1432
  "path": "owner",
1414
1433
  "query": {
1415
- "status": "active"
1416
- }
1417
- }
1418
- ]
1419
- }
1434
+ "status": "active",
1435
+ },
1436
+ },
1437
+ ],
1438
+ },
1420
1439
  }
1421
1440
  ```
1422
1441
 
@@ -1450,10 +1469,10 @@ query:
1450
1469
  "clean": [
1451
1470
  {
1452
1471
  "ref": "Shop",
1453
- "path": ["owner", "administrator"]
1454
- }
1455
- ]
1456
- }
1472
+ "path": ["owner", "administrator"],
1473
+ },
1474
+ ],
1475
+ },
1457
1476
  }
1458
1477
  ```
1459
1478
 
@@ -1563,7 +1582,7 @@ const shop = await Shop.findOrCreate(
1563
1582
  {
1564
1583
  name: 'My Shop',
1565
1584
  slug: 'my-shop',
1566
- }
1585
+ },
1567
1586
  );
1568
1587
 
1569
1588
  // This is equivalent to running:
@@ -100,6 +100,9 @@ function normalizeSchemaTypedef(typedef, path) {
100
100
  } else {
101
101
  assertSchemaType(type, path);
102
102
  }
103
+ if (typedef.type === 'String') {
104
+ typedef.trim ??= true;
105
+ }
103
106
  return typedef;
104
107
  }
105
108
  function normalizeArrayAttributes(arr, path) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bedrockio/model",
3
- "version": "0.8.1",
3
+ "version": "0.8.3",
4
4
  "description": "Bedrock utilities for model creation.",
5
5
  "type": "module",
6
6
  "scripts": {
@@ -30,7 +30,7 @@
30
30
  "lodash": "^4.17.21"
31
31
  },
32
32
  "peerDependencies": {
33
- "@bedrockio/yada": "^1.2.7",
33
+ "@bedrockio/yada": "^1.2.8",
34
34
  "mongoose": "^8.6.2"
35
35
  },
36
36
  "devDependencies": {
@@ -38,7 +38,7 @@
38
38
  "@babel/core": "^7.26.0",
39
39
  "@babel/preset-env": "^7.26.0",
40
40
  "@bedrockio/prettier-config": "^1.0.2",
41
- "@bedrockio/yada": "^1.2.7",
41
+ "@bedrockio/yada": "^1.2.8",
42
42
  "@shelf/jest-mongodb": "^4.3.2",
43
43
  "eslint": "^8.33.0",
44
44
  "eslint-plugin-bedrock": "^1.0.26",
@@ -49,8 +49,11 @@
49
49
  "prettier-eslint": "^16.3.0",
50
50
  "typescript": "^5.7.2"
51
51
  },
52
+ "resolutions": {
53
+ "whatwg-url": "14.1.0"
54
+ },
52
55
  "volta": {
53
- "node": "22.12.0",
56
+ "node": "22.13.0",
54
57
  "yarn": "1.22.22"
55
58
  }
56
59
  }
package/src/schema.js CHANGED
@@ -50,7 +50,7 @@ export function createSchema(definition, options = {}) {
50
50
  toJSON: serializeOptions,
51
51
  toObject: serializeOptions,
52
52
  ...options,
53
- }
53
+ },
54
54
  );
55
55
 
56
56
  // Soft Delete needs to be applied
@@ -105,6 +105,10 @@ function normalizeSchemaTypedef(typedef, path) {
105
105
  assertSchemaType(type, path);
106
106
  }
107
107
 
108
+ if (typedef.type === 'String') {
109
+ typedef.trim ??= true;
110
+ }
111
+
108
112
  return typedef;
109
113
  }
110
114
 
@@ -15,6 +15,7 @@ export const INCLUDE_FIELD_SCHEMA: {
15
15
  required(): /*elided*/ any;
16
16
  default(arg: any): /*elided*/ any;
17
17
  custom(fn: Function): /*elided*/ any;
18
+ missing(fn: Function): /*elided*/ any;
18
19
  strip(strip: any): /*elided*/ any;
19
20
  allow(...set: any[]): /*elided*/ any;
20
21
  reject(...set: any[]): /*elided*/ any;
@@ -42,9 +43,10 @@ export const INCLUDE_FIELD_SCHEMA: {
42
43
  assertEnum(set: any, allow: any): /*elided*/ any;
43
44
  assert(type: any, fn: any): /*elided*/ any;
44
45
  pushAssertion(assertion: any): void;
46
+ canSkipAssertion(value: any, assertion: any, options: any): any;
45
47
  transform(fn: any): /*elided*/ any;
46
48
  getSortIndex(type: any): number;
47
- runAssertion(assertion: any, value: any, options?: {}): Promise<any>;
49
+ runAssertion(value: any, assertion: any, options?: {}): Promise<any>;
48
50
  enumToOpenApi(): any;
49
51
  };
50
52
  //# sourceMappingURL=include.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"include.d.ts","sourceRoot":"","sources":["../src/include.js"],"names":[],"mappings":"AA2BA,gDAuEC;AAMD,uDA4BC;AAGD,yDAIC;AAaD,yEAUC;AA9ID;;;;;;;;;;;;;;;;;;;;;;;;;;;;eA+CK,CAAC;;;;;;;;;;;;;;;;EA1CH"}
1
+ {"version":3,"file":"include.d.ts","sourceRoot":"","sources":["../src/include.js"],"names":[],"mappings":"AA2BA,gDAuEC;AAMD,uDA4BC;AAGD,yDAIC;AAaD,yEAUC;AA9ID;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAsDoB,CAAC;;;;;;;;;;;;;;;;;EAjDlB"}
@@ -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;;;;;;;YA8C3B,CAAC;WACH,CAAC;mBAAqB,CAAC;;;;;;;;;;;;;;;;;;;;;SA8G3B,CAAC;gBAEwB,CAAC;SAChB,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aAtHV;AAED,iEAsBC;qBA9FoB,UAAU"}
1
+ {"version":3,"file":"schema.d.ts","sourceRoot":"","sources":["../src/schema.js"],"names":[],"mappings":"AAuBA;;;;;;;GAOG;AACH,yCAJW,MAAM,YACN,QAAQ,CAAC,aAAa;;;;;;;YA8C5B,CAAC;WACF,CAAA;mBAAqB,CAAC;;;;;;;;;;;;;;;;;;;;;SAgHtB,CAAD;gBAA4B,CAAA;SAAY,CAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;aArH1C;AAED,iEAsBC;qBA9FoB,UAAU"}
package/types/search.d.ts CHANGED
@@ -12,6 +12,7 @@ export function searchValidation(options?: {}): {
12
12
  required(): /*elided*/ any;
13
13
  default(arg: any): /*elided*/ any;
14
14
  custom(fn: Function): /*elided*/ any;
15
+ missing(fn: Function): /*elided*/ any;
15
16
  strip(strip: any): /*elided*/ any;
16
17
  allow(...set: any[]): /*elided*/ any;
17
18
  reject(...set: any[]): /*elided*/ any;
@@ -39,9 +40,10 @@ export function searchValidation(options?: {}): {
39
40
  assertEnum(set: any, allow: any): /*elided*/ any;
40
41
  assert(type: any, fn: any): /*elided*/ any;
41
42
  pushAssertion(assertion: any): void;
43
+ canSkipAssertion(value: any, assertion: any, options: any): any;
42
44
  transform(fn: any): /*elided*/ any;
43
45
  getSortIndex(type: any): number;
44
- runAssertion(assertion: any, value: any, options?: {}): Promise<any>;
46
+ runAssertion(value: any, assertion: any, options?: {}): Promise<any>;
45
47
  enumToOpenApi(): any;
46
48
  };
47
49
  //# sourceMappingURL=search.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.js"],"names":[],"mappings":"AAsBA,gEAwDC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAA2C,CAAA;;;;;;;;;;;;;;;;EAyB1C"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../src/search.js"],"names":[],"mappings":"AAsBA,gEAwDC;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eASsB,CAAA;;;;;;;;;;;;;;;;;EAgBrB"}
@@ -73,6 +73,7 @@ export const OBJECT_ID_SCHEMA: {
73
73
  meta: {};
74
74
  default(arg: any): /*elided*/ any;
75
75
  custom(fn: Function): /*elided*/ any;
76
+ missing(fn: Function): /*elided*/ any;
76
77
  strip(strip: any): /*elided*/ any;
77
78
  allow(...set: any[]): /*elided*/ any;
78
79
  reject(...set: any[]): /*elided*/ any;
@@ -101,9 +102,10 @@ export const OBJECT_ID_SCHEMA: {
101
102
  assertEnum(set: any, allow: any): /*elided*/ any;
102
103
  assert(type: any, fn: any): /*elided*/ any;
103
104
  pushAssertion(assertion: any): void;
105
+ canSkipAssertion(value: any, assertion: any, options: any): any;
104
106
  transform(fn: any): /*elided*/ any;
105
107
  getSortIndex(type: any): number;
106
- runAssertion(assertion: any, value: any, options?: {}): Promise<any>;
108
+ runAssertion(value: any, assertion: any, options?: {}): Promise<any>;
107
109
  enumToOpenApi(): any;
108
110
  };
109
111
  //# sourceMappingURL=validation.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.js"],"names":[],"mappings":"AAoFA,kDAEC;AAED,oEAgGC;AAsBD,wEA2BC;AAkVD;;;EAEC;AAED;;;EAOC;AApjBD;;;;;;;;;;;;;;;;eAwBE,CAAF;;;;;;;;;;;;iBAcmC,CAAC;kBAC3B,CAAC;kBAEH,CAAC;oBACA,CAAC;oBACF,CAAC;;;wBAkBkB,CAAC;8BAEf,CAAC;oBAGD,CAAC;oBACV,CAAC;oCAGG,CAAC;uBAAkC,CAAC;8BACb,CAAC;uBAEjB,CAAC;iBAEX,CAAJ;;;mBAa6B,CAAC;yBAEjB,CAAC;0BAEN,CAAF;yBAKE,CAAC;sBACgB,CAAC;yBACS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAvDzB,CAAC;;;;;;;;;;;;;;;;EApCP"}
1
+ {"version":3,"file":"validation.d.ts","sourceRoot":"","sources":["../src/validation.js"],"names":[],"mappings":"AAoFA,kDAEC;AAED,oEAgGC;AAsBD,wEA2BC;AAkVD;;;EAEC;AAED;;;EAOC;AApjBD;;;;;;;;;;;;;;;;eAwBE,CAAF;;;;;;;;;;;;iBAcmC,CAAC;kBAC3B,CAAC;kBAEH,CAAC;oBACA,CAAC;oBACF,CAAC;;;wBAkBkB,CAAC;8BAEf,CAAC;oBAGD,CAAC;oBACV,CAAC;oCAGG,CAAC;uBAAkC,CAAC;8BACb,CAAC;uBAEjB,CAAC;iBAEX,CAAJ;;;mBAa6B,CAAC;yBAEjB,CAAC;0BAEN,CAAF;yBAKE,CAAC;sBACgB,CAAC;yBACS,CAAC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;eAhDxB,CAAC;;;;;;;;;;;;;;;;;EA3CR"}