@oscarpalmer/jhunal 0.18.0 → 0.19.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.
package/dist/index.d.mts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { Constructor, GenericCallback, PlainObject, Simplify } from "@oscarpalmer/atoms/models";
2
+ import { Result } from "@oscarpalmer/atoms/result/models";
2
3
 
3
4
  //#region src/models/infer.model.d.ts
4
5
  /**
@@ -273,6 +274,7 @@ type ValidationInformation = {
273
274
  key: ValidationInformationKey;
274
275
  message: string;
275
276
  validator?: GenericCallback;
277
+ value: unknown;
276
278
  };
277
279
  type ValidationInformationKey = ValidatedPropertyKey;
278
280
  //#endregion
@@ -293,6 +295,24 @@ declare class Schematic<Model> {
293
295
  * @returns `true` if the value matches the schema, otherwise throws an error
294
296
  */
295
297
  is(value: unknown, errors: 'throw'): asserts value is Model;
298
+ /**
299
+ * Does the value match the schema?
300
+ *
301
+ * Will validate that the value matches the schema and return a result of `true` or all validation information for validation failures from the same depth in the object.
302
+ * @param value Value to validate
303
+ * @param errors All
304
+ * @returns `true` if the value matches the schema, otherwise `false`
305
+ */
306
+ is(value: unknown, errors: 'all'): Result<true, ValidationInformation[]>;
307
+ /**
308
+ * Does the value match the schema?
309
+ *
310
+ * Will validate that the value matches the schema and return a result of `true` or all validation information for the failing property.
311
+ * @param value Value to validate
312
+ * @param errors First
313
+ * @returns `true` if the value matches the schema, otherwise `false`
314
+ */
315
+ is(value: unknown, errors: 'first'): Result<true, ValidationInformation>;
296
316
  /**
297
317
  * Does the value match the schema?
298
318
  *
package/dist/index.mjs CHANGED
@@ -1,5 +1,6 @@
1
1
  import { isConstructor, isPlainObject } from "@oscarpalmer/atoms/is";
2
2
  import { join } from "@oscarpalmer/atoms/string";
3
+ import { error } from "@oscarpalmer/atoms/result/misc";
3
4
  //#region src/constants.ts
4
5
  const MESSAGE_CONSTRUCTOR = "Expected a constructor function";
5
6
  const NAME_SCHEMATIC = "Schematic";
@@ -258,6 +259,7 @@ function validateNamed(property, name, value, validation) {
258
259
  const validator = propertyValidators[index];
259
260
  if (!validator(value)) {
260
261
  validation.push({
262
+ value,
261
263
  key: { ...property.key },
262
264
  message: getInvalidValidatorMessage(property, name, index, length),
263
265
  validator
@@ -267,17 +269,20 @@ function validateNamed(property, name, value, validation) {
267
269
  }
268
270
  return true;
269
271
  }
270
- function validateObject(obj, properties, reporting, validation) {
272
+ function validateObject(obj, properties, reporting, property, validation) {
271
273
  if (!isPlainObject(obj)) {
272
- if (reporting.throw && validation == null) throw new ValidationError([{
274
+ const information = {
273
275
  key: {
274
276
  full: "",
275
277
  short: ""
276
278
  },
277
- message: getInvalidInputMessage(obj)
278
- }]);
279
- return false;
279
+ message: property == null ? getInvalidInputMessage(obj) : getInvalidTypeMessage(property, obj),
280
+ value: obj
281
+ };
282
+ if (reporting.throw) throw new ValidationError([information]);
283
+ return reporting.none ? false : [information];
280
284
  }
285
+ const allInformation = [];
281
286
  const propertiesLength = properties.length;
282
287
  outer: for (let propertyIndex = 0; propertyIndex < propertiesLength; propertyIndex += 1) {
283
288
  const property = properties[propertyIndex];
@@ -285,12 +290,17 @@ function validateObject(obj, properties, reporting, validation) {
285
290
  const value = obj[key.short];
286
291
  if (value === void 0 && required) {
287
292
  const information = {
293
+ value,
288
294
  key: { ...key },
289
295
  message: getInvalidMissingMessage(property)
290
296
  };
291
297
  if (reporting.throw && validation == null) throw new ValidationError([information]);
292
298
  if (validation != null) validation.push(information);
293
- return false;
299
+ if (reporting.all) {
300
+ allInformation.push(information);
301
+ continue;
302
+ }
303
+ return reporting.none ? false : [information];
294
304
  }
295
305
  const typesLength = types.length;
296
306
  const information = [];
@@ -298,32 +308,31 @@ function validateObject(obj, properties, reporting, validation) {
298
308
  const type = types[typeIndex];
299
309
  if (validateValue(type, property, value, reporting, information)) continue outer;
300
310
  }
301
- if (reporting.throw && validation == null) throw new ValidationError(information.length === 0 ? [{
311
+ if (information.length === 0) information.push({
312
+ value,
302
313
  key: { ...key },
303
314
  message: getInvalidTypeMessage(property, value)
304
- }] : information);
315
+ });
316
+ if (reporting.throw && validation == null) throw new ValidationError(information);
305
317
  validation?.push(...information);
306
- return false;
318
+ if (reporting.all) {
319
+ allInformation.push(...information);
320
+ continue;
321
+ }
322
+ return reporting.none ? false : information;
307
323
  }
308
- return true;
324
+ return reporting.none ? true : allInformation.length === 0 ? true : allInformation;
309
325
  }
310
326
  function validateValue(type, property, value, reporting, validation) {
311
- let result;
312
327
  switch (true) {
313
- case typeof type === "function":
314
- result = type(value);
315
- break;
316
- case Array.isArray(type):
317
- result = validateObject(value, type, reporting, validation);
318
- break;
319
- case isSchematic(type):
320
- result = type.is(value, reporting);
321
- break;
322
- default:
323
- result = validateNamed(property, type, value, validation);
324
- break;
328
+ case typeof type === "function": return type(value);
329
+ case Array.isArray(type): {
330
+ const nested = validateObject(value, type, reporting, property, validation);
331
+ return typeof nested !== "boolean" ? false : nested;
332
+ }
333
+ case isSchematic(type): return type.is(value, reporting);
334
+ default: return validateNamed(property, type, value, validation);
325
335
  }
326
- return result;
327
336
  }
328
337
  const validators = {
329
338
  array: Array.isArray,
@@ -350,7 +359,10 @@ var Schematic = class {
350
359
  this.#properties = properties;
351
360
  }
352
361
  is(value, errors) {
353
- return validateObject(value, this.#properties, getReporting(errors));
362
+ const reporting = getReporting(errors);
363
+ const result = validateObject(value, this.#properties, reporting);
364
+ if (typeof result === "boolean") return result;
365
+ return error(reporting.all ? result : result[0]);
354
366
  }
355
367
  };
356
368
  function schematic(schema) {
@@ -75,6 +75,7 @@ type ValidationInformation = {
75
75
  key: ValidationInformationKey;
76
76
  message: string;
77
77
  validator?: GenericCallback;
78
+ value: unknown;
78
79
  };
79
80
  type ValidationInformationKey = ValidatedPropertyKey;
80
81
  //#endregion
@@ -1,8 +1,9 @@
1
1
  import { Infer } from "./models/infer.model.mjs";
2
2
  import { TypedSchema } from "./models/schema.typed.model.mjs";
3
- import { ValidatedProperty } from "./models/validation.model.mjs";
3
+ import { ValidatedProperty, ValidationInformation } from "./models/validation.model.mjs";
4
4
  import { Schema } from "./models/schema.plain.model.mjs";
5
5
  import { PlainObject } from "@oscarpalmer/atoms/models";
6
+ import { Result } from "@oscarpalmer/atoms/result/models";
6
7
 
7
8
  //#region src/schematic.d.ts
8
9
  /**
@@ -21,6 +22,24 @@ declare class Schematic<Model> {
21
22
  * @returns `true` if the value matches the schema, otherwise throws an error
22
23
  */
23
24
  is(value: unknown, errors: 'throw'): asserts value is Model;
25
+ /**
26
+ * Does the value match the schema?
27
+ *
28
+ * Will validate that the value matches the schema and return a result of `true` or all validation information for validation failures from the same depth in the object.
29
+ * @param value Value to validate
30
+ * @param errors All
31
+ * @returns `true` if the value matches the schema, otherwise `false`
32
+ */
33
+ is(value: unknown, errors: 'all'): Result<true, ValidationInformation[]>;
34
+ /**
35
+ * Does the value match the schema?
36
+ *
37
+ * Will validate that the value matches the schema and return a result of `true` or all validation information for the failing property.
38
+ * @param value Value to validate
39
+ * @param errors First
40
+ * @returns `true` if the value matches the schema, otherwise `false`
41
+ */
42
+ is(value: unknown, errors: 'first'): Result<true, ValidationInformation>;
24
43
  /**
25
44
  * Does the value match the schema?
26
45
  *
@@ -4,6 +4,7 @@ import { SchematicError } from "./models/validation.model.mjs";
4
4
  import { getProperties } from "./validation/property.validation.mjs";
5
5
  import { validateObject } from "./validation/value.validation.mjs";
6
6
  import { isPlainObject } from "@oscarpalmer/atoms/is";
7
+ import { error } from "@oscarpalmer/atoms/result/misc";
7
8
  //#region src/schematic.ts
8
9
  /**
9
10
  * A schematic for validating objects
@@ -15,7 +16,10 @@ var Schematic = class {
15
16
  this.#properties = properties;
16
17
  }
17
18
  is(value, errors) {
18
- return validateObject(value, this.#properties, getReporting(errors));
19
+ const reporting = getReporting(errors);
20
+ const result = validateObject(value, this.#properties, reporting);
21
+ if (typeof result === "boolean") return result;
22
+ return error(reporting.all ? result : result[0]);
19
23
  }
20
24
  };
21
25
  function schematic(schema) {
@@ -1,6 +1,6 @@
1
1
  import { ReportingInformation, ValidatedProperty, ValidationInformation } from "../models/validation.model.mjs";
2
2
 
3
3
  //#region src/validation/value.validation.d.ts
4
- declare function validateObject(obj: unknown, properties: ValidatedProperty[], reporting: ReportingInformation, validation?: ValidationInformation[]): boolean;
4
+ declare function validateObject(obj: unknown, properties: ValidatedProperty[], reporting: ReportingInformation, property?: ValidatedProperty, validation?: ValidationInformation[]): boolean | ValidationInformation[];
5
5
  //#endregion
6
6
  export { validateObject };
@@ -1,3 +1,4 @@
1
+ import "../constants.mjs";
1
2
  import { getInvalidInputMessage, getInvalidMissingMessage, getInvalidTypeMessage, getInvalidValidatorMessage, isSchematic } from "../helpers.mjs";
2
3
  import { ValidationError } from "../models/validation.model.mjs";
3
4
  import { isPlainObject } from "@oscarpalmer/atoms/is";
@@ -11,6 +12,7 @@ function validateNamed(property, name, value, validation) {
11
12
  const validator = propertyValidators[index];
12
13
  if (!validator(value)) {
13
14
  validation.push({
15
+ value,
14
16
  key: { ...property.key },
15
17
  message: getInvalidValidatorMessage(property, name, index, length),
16
18
  validator
@@ -20,17 +22,20 @@ function validateNamed(property, name, value, validation) {
20
22
  }
21
23
  return true;
22
24
  }
23
- function validateObject(obj, properties, reporting, validation) {
25
+ function validateObject(obj, properties, reporting, property, validation) {
24
26
  if (!isPlainObject(obj)) {
25
- if (reporting.throw && validation == null) throw new ValidationError([{
27
+ const information = {
26
28
  key: {
27
29
  full: "",
28
30
  short: ""
29
31
  },
30
- message: getInvalidInputMessage(obj)
31
- }]);
32
- return false;
32
+ message: property == null ? getInvalidInputMessage(obj) : getInvalidTypeMessage(property, obj),
33
+ value: obj
34
+ };
35
+ if (reporting.throw) throw new ValidationError([information]);
36
+ return reporting.none ? false : [information];
33
37
  }
38
+ const allInformation = [];
34
39
  const propertiesLength = properties.length;
35
40
  outer: for (let propertyIndex = 0; propertyIndex < propertiesLength; propertyIndex += 1) {
36
41
  const property = properties[propertyIndex];
@@ -38,12 +43,17 @@ function validateObject(obj, properties, reporting, validation) {
38
43
  const value = obj[key.short];
39
44
  if (value === void 0 && required) {
40
45
  const information = {
46
+ value,
41
47
  key: { ...key },
42
48
  message: getInvalidMissingMessage(property)
43
49
  };
44
50
  if (reporting.throw && validation == null) throw new ValidationError([information]);
45
51
  if (validation != null) validation.push(information);
46
- return false;
52
+ if (reporting.all) {
53
+ allInformation.push(information);
54
+ continue;
55
+ }
56
+ return reporting.none ? false : [information];
47
57
  }
48
58
  const typesLength = types.length;
49
59
  const information = [];
@@ -51,32 +61,31 @@ function validateObject(obj, properties, reporting, validation) {
51
61
  const type = types[typeIndex];
52
62
  if (validateValue(type, property, value, reporting, information)) continue outer;
53
63
  }
54
- if (reporting.throw && validation == null) throw new ValidationError(information.length === 0 ? [{
64
+ if (information.length === 0) information.push({
65
+ value,
55
66
  key: { ...key },
56
67
  message: getInvalidTypeMessage(property, value)
57
- }] : information);
68
+ });
69
+ if (reporting.throw && validation == null) throw new ValidationError(information);
58
70
  validation?.push(...information);
59
- return false;
71
+ if (reporting.all) {
72
+ allInformation.push(...information);
73
+ continue;
74
+ }
75
+ return reporting.none ? false : information;
60
76
  }
61
- return true;
77
+ return reporting.none ? true : allInformation.length === 0 ? true : allInformation;
62
78
  }
63
79
  function validateValue(type, property, value, reporting, validation) {
64
- let result;
65
80
  switch (true) {
66
- case typeof type === "function":
67
- result = type(value);
68
- break;
69
- case Array.isArray(type):
70
- result = validateObject(value, type, reporting, validation);
71
- break;
72
- case isSchematic(type):
73
- result = type.is(value, reporting);
74
- break;
75
- default:
76
- result = validateNamed(property, type, value, validation);
77
- break;
81
+ case typeof type === "function": return type(value);
82
+ case Array.isArray(type): {
83
+ const nested = validateObject(value, type, reporting, property, validation);
84
+ return typeof nested !== "boolean" ? false : nested;
85
+ }
86
+ case isSchematic(type): return type.is(value, reporting);
87
+ default: return validateNamed(property, type, value, validation);
78
88
  }
79
- return result;
80
89
  }
81
90
  const validators = {
82
91
  array: Array.isArray,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oscarpalmer/jhunal",
3
- "version": "0.18.0",
3
+ "version": "0.19.0",
4
4
  "description": "Flies free beneath the glistening moons…",
5
5
  "keywords": [
6
6
  "schema",
@@ -116,6 +116,7 @@ export type ValidationInformation = {
116
116
  key: ValidationInformationKey;
117
117
  message: string;
118
118
  validator?: GenericCallback;
119
+ value: unknown;
119
120
  };
120
121
 
121
122
  export type ValidationInformationKey = ValidatedPropertyKey;
package/src/schematic.ts CHANGED
@@ -1,13 +1,19 @@
1
1
  import {isPlainObject} from '@oscarpalmer/atoms/is';
2
2
  import type {PlainObject} from '@oscarpalmer/atoms/models';
3
- import {SCHEMATIC_MESSAGE_SCHEMA_INVALID_TYPE, PROPERTY_SCHEMATIC} from './constants';
3
+ import type {Result} from '@oscarpalmer/atoms/result/models';
4
+ import {PROPERTY_SCHEMATIC, SCHEMATIC_MESSAGE_SCHEMA_INVALID_TYPE} from './constants';
4
5
  import {getReporting, isSchematic} from './helpers';
5
6
  import type {Infer} from './models/infer.model';
6
7
  import type {Schema} from './models/schema.plain.model';
7
8
  import type {TypedSchema} from './models/schema.typed.model';
8
- import {SchematicError, type ValidatedProperty} from './models/validation.model';
9
+ import {
10
+ SchematicError,
11
+ type ValidatedProperty,
12
+ type ValidationInformation,
13
+ } from './models/validation.model';
9
14
  import {getProperties} from './validation/property.validation';
10
15
  import {validateObject} from './validation/value.validation';
16
+ import {error} from '@oscarpalmer/atoms/result/misc';
11
17
 
12
18
  /**
13
19
  * A schematic for validating objects
@@ -27,7 +33,7 @@ export class Schematic<Model> {
27
33
 
28
34
  /**
29
35
  * Does the value match the schema?
30
- *
36
+ *
31
37
  * Will assert that the values matches the schema and throw an error if it does not. The error will contain all validation information for the first property that fails validation.
32
38
  * @param value Value to validate
33
39
  * @param errors Throws an error for the first validation failure
@@ -37,15 +43,43 @@ export class Schematic<Model> {
37
43
 
38
44
  /**
39
45
  * Does the value match the schema?
40
- *
46
+ *
47
+ * Will validate that the value matches the schema and return a result of `true` or all validation information for validation failures from the same depth in the object.
48
+ * @param value Value to validate
49
+ * @param errors All
50
+ * @returns `true` if the value matches the schema, otherwise `false`
51
+ */
52
+ is(value: unknown, errors: 'all'): Result<true, ValidationInformation[]>;
53
+
54
+ /**
55
+ * Does the value match the schema?
56
+ *
57
+ * Will validate that the value matches the schema and return a result of `true` or all validation information for the failing property.
58
+ * @param value Value to validate
59
+ * @param errors First
60
+ * @returns `true` if the value matches the schema, otherwise `false`
61
+ */
62
+ is(value: unknown, errors: 'first'): Result<true, ValidationInformation>;
63
+
64
+ /**
65
+ * Does the value match the schema?
66
+ *
41
67
  * Will validate that the value matches the schema and return `true` or `false`, without any validation information for validation failures.
42
68
  * @param value Value to validate
43
69
  * @returns `true` if the value matches the schema, otherwise `false`
44
70
  */
45
71
  is(value: unknown): value is Model;
46
72
 
47
- is(value: unknown, errors?: unknown): boolean {
48
- return validateObject(value, this.#properties, getReporting(errors));
73
+ is(value: unknown, errors?: unknown): unknown {
74
+ const reporting = getReporting(errors);
75
+
76
+ const result = validateObject(value, this.#properties, reporting);
77
+
78
+ if (typeof result === 'boolean') {
79
+ return result;
80
+ }
81
+
82
+ return error(reporting.all ? result : result[0]);
49
83
  }
50
84
  }
51
85
 
@@ -1,5 +1,6 @@
1
1
  import {isPlainObject} from '@oscarpalmer/atoms/is';
2
2
  import type {GenericCallback} from '@oscarpalmer/atoms/models';
3
+ import {TYPE_OBJECT} from '../constants';
3
4
  import {
4
5
  getInvalidInputMessage,
5
6
  getInvalidMissingMessage,
@@ -39,6 +40,7 @@ function validateNamed(
39
40
 
40
41
  if (!validator(value)) {
41
42
  validation.push({
43
+ value,
42
44
  key: {...property.key},
43
45
  message: getInvalidValidatorMessage(property, name, index, length),
44
46
  validator: validator as GenericCallback,
@@ -55,21 +57,26 @@ export function validateObject(
55
57
  obj: unknown,
56
58
  properties: ValidatedProperty[],
57
59
  reporting: ReportingInformation,
60
+ property?: ValidatedProperty,
58
61
  validation?: ValidationInformation[],
59
- ): boolean {
62
+ ): boolean | ValidationInformation[] {
60
63
  if (!isPlainObject(obj)) {
61
- if (reporting.throw && validation == null) {
62
- throw new ValidationError([
63
- {
64
- key: {full: '', short: ''},
65
- message: getInvalidInputMessage(obj),
66
- },
67
- ]);
64
+ const information = {
65
+ key: {full: '', short: ''},
66
+ message:
67
+ property == null ? getInvalidInputMessage(obj) : getInvalidTypeMessage(property, obj),
68
+ value: obj,
69
+ };
70
+
71
+ if (reporting.throw) {
72
+ throw new ValidationError([information]);
68
73
  }
69
74
 
70
- return false;
75
+ return reporting.none ? false : [information];
71
76
  }
72
77
 
78
+ const allInformation: ValidationInformation[] = [];
79
+
73
80
  const propertiesLength = properties.length;
74
81
 
75
82
  outer: for (let propertyIndex = 0; propertyIndex < propertiesLength; propertyIndex += 1) {
@@ -81,6 +88,7 @@ export function validateObject(
81
88
 
82
89
  if (value === undefined && required) {
83
90
  const information: ValidationInformation = {
91
+ value,
84
92
  key: {...key},
85
93
  message: getInvalidMissingMessage(property),
86
94
  };
@@ -93,7 +101,13 @@ export function validateObject(
93
101
  validation.push(information);
94
102
  }
95
103
 
96
- return false;
104
+ if (reporting.all) {
105
+ allInformation.push(information);
106
+
107
+ continue;
108
+ }
109
+
110
+ return reporting.none ? false : [information];
97
111
  }
98
112
 
99
113
  const typesLength = types.length;
@@ -108,25 +122,30 @@ export function validateObject(
108
122
  }
109
123
  }
110
124
 
125
+ if (information.length === 0) {
126
+ information.push({
127
+ value,
128
+ key: {...key},
129
+ message: getInvalidTypeMessage(property, value),
130
+ });
131
+ }
132
+
111
133
  if (reporting.throw && validation == null) {
112
- throw new ValidationError(
113
- information.length === 0
114
- ? [
115
- {
116
- key: {...key},
117
- message: getInvalidTypeMessage(property, value),
118
- },
119
- ]
120
- : information,
121
- );
134
+ throw new ValidationError(information);
122
135
  }
123
136
 
124
137
  validation?.push(...information);
125
138
 
126
- return false;
139
+ if (reporting.all) {
140
+ allInformation.push(...information);
141
+
142
+ continue;
143
+ }
144
+
145
+ return reporting.none ? false : information;
127
146
  }
128
147
 
129
- return true;
148
+ return reporting.none ? true : allInformation.length === 0 ? true : allInformation;
130
149
  }
131
150
 
132
151
  function validateValue(
@@ -136,27 +155,21 @@ function validateValue(
136
155
  reporting: ReportingInformation,
137
156
  validation: ValidationInformation[],
138
157
  ): boolean {
139
- let result: boolean;
140
-
141
158
  switch (true) {
142
159
  case typeof type === 'function':
143
- result = (type as GenericCallback)(value);
144
- break;
160
+ return (type as GenericCallback)(value);
145
161
 
146
- case Array.isArray(type):
147
- result = validateObject(value, type, reporting, validation);
148
- break;
162
+ case Array.isArray(type): {
163
+ const nested = validateObject(value, type, reporting, property, validation);
164
+ return typeof nested !== 'boolean' ? false : nested;
165
+ }
149
166
 
150
167
  case isSchematic(type):
151
- result = type.is(value, reporting as never) as unknown as boolean;
152
- break;
168
+ return type.is(value, reporting as never) as unknown as boolean;
153
169
 
154
170
  default:
155
- result = validateNamed(property, type as ValueName, value, validation);
156
- break;
171
+ return validateNamed(property, type as ValueName, value, validation);
157
172
  }
158
-
159
- return result;
160
173
  }
161
174
 
162
175
  //
@@ -169,7 +182,7 @@ const validators: Record<ValueName, (value: unknown) => boolean> = {
169
182
  function: value => typeof value === 'function',
170
183
  null: value => value === null,
171
184
  number: value => typeof value === 'number',
172
- object: value => typeof value === 'object' && value !== null,
185
+ object: value => typeof value === TYPE_OBJECT && value !== null,
173
186
  string: value => typeof value === 'string',
174
187
  symbol: value => typeof value === 'symbol',
175
188
  undefined: value => value === undefined,