@oscarpalmer/jhunal 0.18.0 → 0.20.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.
@@ -1,20 +1,24 @@
1
1
  import {isPlainObject} from '@oscarpalmer/atoms/is';
2
2
  import type {GenericCallback} from '@oscarpalmer/atoms/models';
3
+ import {join} from '@oscarpalmer/atoms/string';
4
+ import {TYPE_OBJECT} from '../constants';
3
5
  import {
4
6
  getInvalidInputMessage,
5
7
  getInvalidMissingMessage,
6
8
  getInvalidTypeMessage,
7
9
  getInvalidValidatorMessage,
10
+ getUnknownKeysMessage,
8
11
  isSchematic,
9
12
  } from '../helpers';
10
13
  import type {ValueName} from '../models/misc.model';
11
14
  import {
12
15
  ValidationError,
13
- type ReportingInformation,
14
16
  type ValidatedProperty,
15
17
  type ValidatedPropertyType,
16
18
  type ValidationInformation,
19
+ type ValidationOptionsExtended,
17
20
  } from '../models/validation.model';
21
+ import {schematicProperties, type Schematic} from '../schematic';
18
22
 
19
23
  function validateNamed(
20
24
  property: ValidatedProperty,
@@ -39,6 +43,7 @@ function validateNamed(
39
43
 
40
44
  if (!validator(value)) {
41
45
  validation.push({
46
+ value,
42
47
  key: {...property.key},
43
48
  message: getInvalidValidatorMessage(property, name, index, length),
44
49
  validator: validator as GenericCallback,
@@ -54,26 +59,77 @@ function validateNamed(
54
59
  export function validateObject(
55
60
  obj: unknown,
56
61
  properties: ValidatedProperty[],
57
- reporting: ReportingInformation,
62
+ options: ValidationOptionsExtended,
63
+ origin?: ValidatedProperty,
58
64
  validation?: ValidationInformation[],
59
- ): boolean {
65
+ ): boolean | ValidationInformation[] {
60
66
  if (!isPlainObject(obj)) {
61
- if (reporting.throw && validation == null) {
62
- throw new ValidationError([
63
- {
64
- key: {full: '', short: ''},
65
- message: getInvalidInputMessage(obj),
66
- },
67
- ]);
67
+ const key = origin == null ? {full: '', short: ''} : {...origin.key};
68
+
69
+ const information = {
70
+ key,
71
+ message:
72
+ origin == null
73
+ ? getInvalidInputMessage(obj)
74
+ : getInvalidTypeMessage(
75
+ {
76
+ ...origin,
77
+ key,
78
+ },
79
+ obj,
80
+ ),
81
+ value: obj,
82
+ };
83
+
84
+ if (options.reporting.throw) {
85
+ throw new ValidationError([information]);
68
86
  }
69
87
 
70
- return false;
88
+ validation?.push(information);
89
+
90
+ return options.reporting.none ? false : [information];
71
91
  }
72
92
 
93
+ if (options.strict) {
94
+ const objKeys = Object.keys(obj);
95
+
96
+ const propertiesKeys = new Set(properties.map(property => property.key.short));
97
+
98
+ const unknownKeys = objKeys.filter(key => !propertiesKeys.has(key));
99
+
100
+ if (unknownKeys.length > 0) {
101
+ const key = origin == null ? {full: '', short: ''} : {...origin.key};
102
+
103
+ const information: ValidationInformation = {
104
+ key,
105
+ message: getUnknownKeysMessage(unknownKeys.map(key => join([origin?.key.full, key], '.'))),
106
+ value: obj,
107
+ };
108
+
109
+ if (options.reporting.throw) {
110
+ throw new ValidationError([information]);
111
+ }
112
+
113
+ validation?.push(information);
114
+
115
+ return options.reporting.none ? false : [information];
116
+ }
117
+ }
118
+
119
+ const allInformation: ValidationInformation[] = [];
120
+
73
121
  const propertiesLength = properties.length;
74
122
 
75
123
  outer: for (let propertyIndex = 0; propertyIndex < propertiesLength; propertyIndex += 1) {
76
- const property = properties[propertyIndex];
124
+ let property = properties[propertyIndex];
125
+
126
+ property = {
127
+ ...property,
128
+ key: {
129
+ full: join([origin?.key.full, property.key.short], '.'),
130
+ short: property.key.short,
131
+ },
132
+ };
77
133
 
78
134
  const {key, required, types} = property;
79
135
 
@@ -81,11 +137,12 @@ export function validateObject(
81
137
 
82
138
  if (value === undefined && required) {
83
139
  const information: ValidationInformation = {
140
+ value,
84
141
  key: {...key},
85
142
  message: getInvalidMissingMessage(property),
86
143
  };
87
144
 
88
- if (reporting.throw && validation == null) {
145
+ if (options.reporting.throw && validation == null) {
89
146
  throw new ValidationError([information]);
90
147
  }
91
148
 
@@ -93,7 +150,13 @@ export function validateObject(
93
150
  validation.push(information);
94
151
  }
95
152
 
96
- return false;
153
+ if (options.reporting.all) {
154
+ allInformation.push(information);
155
+
156
+ continue;
157
+ }
158
+
159
+ return options.reporting.none ? false : [information];
97
160
  }
98
161
 
99
162
  const typesLength = types.length;
@@ -103,60 +166,74 @@ export function validateObject(
103
166
  for (let typeIndex = 0; typeIndex < typesLength; typeIndex += 1) {
104
167
  const type = types[typeIndex];
105
168
 
106
- if (validateValue(type, property, value, reporting, information)) {
169
+ if (validateValue(type, property, value, options, information)) {
107
170
  continue outer;
108
171
  }
109
172
  }
110
173
 
111
- 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
- );
174
+ if (information.length === 0) {
175
+ information.push({
176
+ value,
177
+ key: {...key},
178
+ message: getInvalidTypeMessage(property, value),
179
+ });
180
+ }
181
+
182
+ if (options.reporting.throw && validation == null) {
183
+ throw new ValidationError(information);
122
184
  }
123
185
 
124
186
  validation?.push(...information);
125
187
 
126
- return false;
188
+ if (options.reporting.all) {
189
+ allInformation.push(...information);
190
+
191
+ continue;
192
+ }
193
+
194
+ return options.reporting.none ? false : information;
127
195
  }
128
196
 
129
- return true;
197
+ return options.reporting.none || allInformation.length === 0 ? true : allInformation;
198
+ }
199
+
200
+ function validateSchematic(
201
+ property: ValidatedProperty,
202
+ schematic: Schematic<unknown>,
203
+ value: unknown,
204
+ options: ValidationOptionsExtended,
205
+ validation: ValidationInformation[],
206
+ ): boolean {
207
+ const properties = schematicProperties.get(schematic)!;
208
+
209
+ const result = validateObject(value, properties, options, property, validation);
210
+
211
+ return typeof result === 'boolean' ? result : result.length === 0;
130
212
  }
131
213
 
132
214
  function validateValue(
133
215
  type: ValidatedPropertyType,
134
216
  property: ValidatedProperty,
135
217
  value: unknown,
136
- reporting: ReportingInformation,
218
+ options: ValidationOptionsExtended,
137
219
  validation: ValidationInformation[],
138
220
  ): boolean {
139
- let result: boolean;
140
-
141
221
  switch (true) {
142
222
  case typeof type === 'function':
143
- result = (type as GenericCallback)(value);
144
- break;
223
+ return (type as GenericCallback)(value);
145
224
 
146
- case Array.isArray(type):
147
- result = validateObject(value, type, reporting, validation);
148
- break;
225
+ case Array.isArray(type): {
226
+ const validated = validateObject(value, type, options, property, validation);
227
+
228
+ return typeof validated === 'boolean' ? validated : false;
229
+ }
149
230
 
150
231
  case isSchematic(type):
151
- result = type.is(value, reporting as never) as unknown as boolean;
152
- break;
232
+ return validateSchematic(property, type, value, options, validation);
153
233
 
154
234
  default:
155
- result = validateNamed(property, type as ValueName, value, validation);
156
- break;
235
+ return validateNamed(property, type as ValueName, value, validation);
157
236
  }
158
-
159
- return result;
160
237
  }
161
238
 
162
239
  //
@@ -169,7 +246,7 @@ const validators: Record<ValueName, (value: unknown) => boolean> = {
169
246
  function: value => typeof value === 'function',
170
247
  null: value => value === null,
171
248
  number: value => typeof value === 'number',
172
- object: value => typeof value === 'object' && value !== null,
249
+ object: value => typeof value === TYPE_OBJECT && value !== null,
173
250
  string: value => typeof value === 'string',
174
251
  symbol: value => typeof value === 'symbol',
175
252
  undefined: value => value === undefined,