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