@oscarpalmer/jhunal 0.25.0 → 0.27.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.
Files changed (75) hide show
  1. package/dist/constants.d.mts +15 -7
  2. package/dist/constants.mjs +15 -6
  3. package/dist/handler/base.handler.d.mts +6 -0
  4. package/dist/{validator/base.validator.mjs → handler/base.handler.mjs} +5 -5
  5. package/dist/handler/function.handler.d.mts +6 -0
  6. package/dist/handler/function.handler.mjs +9 -0
  7. package/dist/handler/object.handler.d.mts +7 -0
  8. package/dist/handler/object.handler.mjs +130 -0
  9. package/dist/handler/schema.handler.d.mts +7 -0
  10. package/dist/handler/schema.handler.mjs +16 -0
  11. package/dist/handler/type.handler.d.mts +9 -0
  12. package/dist/handler/type.handler.mjs +71 -0
  13. package/dist/handler/validator.handler.d.mts +10 -0
  14. package/dist/handler/validator.handler.mjs +34 -0
  15. package/dist/handler/value.handler.d.mts +14 -0
  16. package/dist/handler/value.handler.mjs +98 -0
  17. package/dist/helpers/message.helper.d.mts +9 -7
  18. package/dist/helpers/message.helper.mjs +34 -16
  19. package/dist/helpers/misc.helper.d.mts +13 -6
  20. package/dist/helpers/misc.helper.mjs +12 -4
  21. package/dist/helpers/report.helper.d.mts +23 -0
  22. package/dist/helpers/report.helper.mjs +19 -0
  23. package/dist/helpers/result.helper.d.mts +7 -0
  24. package/dist/helpers/result.helper.mjs +17 -0
  25. package/dist/index.d.mts +172 -73
  26. package/dist/index.mjs +396 -235
  27. package/dist/models/infer.model.d.mts +11 -8
  28. package/dist/models/misc.model.d.mts +8 -8
  29. package/dist/models/schematic.plain.model.d.mts +10 -9
  30. package/dist/models/schematic.typed.model.d.mts +1 -1
  31. package/dist/models/transform.model.d.mts +2 -2
  32. package/dist/models/validation.model.d.mts +56 -25
  33. package/dist/models/validation.model.mjs +11 -2
  34. package/dist/schema.d.mts +34 -34
  35. package/dist/schema.mjs +11 -19
  36. package/dist/validator.d.mts +83 -0
  37. package/dist/validator.mjs +25 -0
  38. package/package.json +2 -2
  39. package/src/constants.ts +30 -8
  40. package/src/{validator/base.validator.ts → handler/base.handler.ts} +6 -6
  41. package/src/handler/function.handler.ts +9 -0
  42. package/src/handler/object.handler.ts +245 -0
  43. package/src/handler/schema.handler.ts +25 -0
  44. package/src/handler/type.handler.ts +160 -0
  45. package/src/handler/validator.handler.ts +49 -0
  46. package/src/handler/value.handler.ts +202 -0
  47. package/src/helpers/message.helper.ts +72 -30
  48. package/src/helpers/misc.helper.ts +23 -6
  49. package/src/helpers/report.helper.ts +72 -0
  50. package/src/helpers/result.helper.ts +33 -0
  51. package/src/index.ts +1 -0
  52. package/src/models/infer.model.ts +31 -13
  53. package/src/models/misc.model.ts +9 -9
  54. package/src/models/schematic.plain.model.ts +12 -9
  55. package/src/models/schematic.typed.model.ts +3 -3
  56. package/src/models/transform.model.ts +2 -2
  57. package/src/models/validation.model.ts +75 -37
  58. package/src/schema.ts +44 -70
  59. package/src/validator.ts +135 -0
  60. package/dist/validator/base.validator.d.mts +0 -6
  61. package/dist/validator/function.validator.d.mts +0 -6
  62. package/dist/validator/function.validator.mjs +0 -9
  63. package/dist/validator/named.handler.d.mts +0 -6
  64. package/dist/validator/named.handler.mjs +0 -23
  65. package/dist/validator/named.validator.d.mts +0 -7
  66. package/dist/validator/named.validator.mjs +0 -38
  67. package/dist/validator/object.validator.d.mts +0 -7
  68. package/dist/validator/object.validator.mjs +0 -185
  69. package/dist/validator/schematic.validator.d.mts +0 -7
  70. package/dist/validator/schematic.validator.mjs +0 -16
  71. package/src/validator/function.validator.ts +0 -9
  72. package/src/validator/named.handler.ts +0 -65
  73. package/src/validator/named.validator.ts +0 -61
  74. package/src/validator/object.validator.ts +0 -366
  75. package/src/validator/schematic.validator.ts +0 -25
@@ -0,0 +1,202 @@
1
+ import {isPlainObject} from '@oscarpalmer/atoms/is';
2
+ import type {PlainObject} from '@oscarpalmer/atoms/models';
3
+ import {join} from '@oscarpalmer/atoms/string';
4
+ import {
5
+ PROPERTY_DEFAULT,
6
+ PROPERTY_REQUIRED,
7
+ PROPERTY_TYPE,
8
+ PROPERTY_VALIDATORS,
9
+ TYPE_UNDEFINED,
10
+ TYPES_ALL,
11
+ VALIDATOR_MESSAGE_INVALID_PROPERTY_NULLABLE,
12
+ VALIDATOR_MESSAGE_INVALID_PROPERTY_TYPE,
13
+ } from '../constants';
14
+ import {
15
+ getDefaultRequiredMessage,
16
+ getDefaultTypeMessage,
17
+ getDisallowedMessage,
18
+ getRequiredMessage,
19
+ getSchematicPropertyNullableMessage,
20
+ getSchematicPropertyTypeMessage,
21
+ } from '../helpers/message.helper';
22
+ import {getParameters, isSchema, isValidator} from '../helpers/misc.helper';
23
+ import type {ValueType} from '../models/misc.model';
24
+ import {
25
+ type PropertyValidationKey,
26
+ SchematicError,
27
+ type ValidationHandler,
28
+ type ValidationHandlerDefaults,
29
+ type ValidationHandlerItem,
30
+ type ValidationHandlerType,
31
+ ValidatorError,
32
+ type Validators,
33
+ } from '../models/validation.model';
34
+ import {getBaseHandler} from './base.handler';
35
+ import {getFunctionHandler} from './function.handler';
36
+ import {getObjectHandler} from './object.handler';
37
+ import {getSchemaHandler} from './schema.handler';
38
+ import {getTypeHandler, getTypeValidators, getValidators} from './type.handler';
39
+ import {validatorHandlers} from '../validator';
40
+
41
+ type Input = {
42
+ validators?: unknown;
43
+ value: unknown;
44
+ };
45
+
46
+ type PropertyInformation = {
47
+ key: string;
48
+ origin?: PropertyValidationKey;
49
+ };
50
+
51
+ function getDefaults(
52
+ obj: PlainObject,
53
+ key: string,
54
+ allowed: boolean,
55
+ ): ValidationHandlerDefaults | undefined {
56
+ if (!(PROPERTY_DEFAULT in obj)) {
57
+ return;
58
+ }
59
+
60
+ if (!allowed) {
61
+ throw new SchematicError(getDisallowedMessage(key, PROPERTY_DEFAULT));
62
+ }
63
+
64
+ return {
65
+ value: obj[PROPERTY_DEFAULT],
66
+ };
67
+ }
68
+
69
+ function getRequired(obj: PlainObject, key: string, allowed: boolean): boolean | undefined {
70
+ if (!(PROPERTY_REQUIRED in obj)) {
71
+ return;
72
+ }
73
+
74
+ if (!allowed) {
75
+ throw new SchematicError(getDisallowedMessage(key, PROPERTY_REQUIRED));
76
+ }
77
+
78
+ if (typeof obj[PROPERTY_REQUIRED] !== 'boolean') {
79
+ throw new SchematicError(getRequiredMessage(key));
80
+ }
81
+
82
+ return obj[PROPERTY_REQUIRED];
83
+ }
84
+
85
+ export function getValueHandler(
86
+ input: Input,
87
+ property?: PropertyInformation,
88
+ ): ValidationHandlerItem {
89
+ const isProperty = property != null;
90
+
91
+ const prefixedKey = isProperty
92
+ ? property.origin == null
93
+ ? property.key
94
+ : join([property.origin.full, property.key], '.')
95
+ : '';
96
+
97
+ const {value} = input;
98
+
99
+ if (value == null) {
100
+ if (isProperty) {
101
+ throw new SchematicError(getSchematicPropertyNullableMessage(prefixedKey));
102
+ }
103
+
104
+ throw new ValidatorError(VALIDATOR_MESSAGE_INVALID_PROPERTY_NULLABLE);
105
+ }
106
+
107
+ const fullKey: PropertyValidationKey = {
108
+ full: prefixedKey,
109
+ short: property?.key ?? '',
110
+ };
111
+
112
+ let required = true;
113
+ let typed = false;
114
+ let validators: Validators = {};
115
+
116
+ let defaults: ValidationHandlerDefaults | undefined;
117
+
118
+ let types: ValidationHandlerType[];
119
+
120
+ const handlers: ValidationHandler[] = [];
121
+
122
+ if (isProperty && isPlainObject(value)) {
123
+ typed = PROPERTY_TYPE in value;
124
+
125
+ const type = typed ? value[PROPERTY_TYPE] : value;
126
+
127
+ defaults = getDefaults(value, prefixedKey, typed);
128
+ required = getRequired(value, prefixedKey, typed) ?? required;
129
+ validators = getValidators(value[PROPERTY_VALIDATORS], typed, prefixedKey);
130
+
131
+ types = Array.isArray(type) ? type : [type];
132
+ } else {
133
+ types = Array.isArray(value) ? value : [value];
134
+
135
+ if (input.validators != null) {
136
+ validators = getTypeValidators(types, input.validators);
137
+ }
138
+ }
139
+
140
+ const typesLength = types.length;
141
+
142
+ let invalid = false;
143
+
144
+ typeLoop: for (let typeIndex = 0; typeIndex < typesLength; typeIndex += 1) {
145
+ const type = types[typeIndex];
146
+
147
+ let handler: ValidationHandler;
148
+
149
+ switch (true) {
150
+ case typeof type === 'function':
151
+ handler = getFunctionHandler(type);
152
+ break;
153
+
154
+ case isProperty && isPlainObject(type):
155
+ handler = getObjectHandler(type, fullKey, typed);
156
+ break;
157
+
158
+ case isProperty && isSchema(type):
159
+ handler = getSchemaHandler(type);
160
+ break;
161
+
162
+ case isProperty && isValidator(type):
163
+ handler = validatorHandlers.get(type)!;
164
+ break;
165
+
166
+ case TYPES_ALL.has(type as ValueType):
167
+ handler = getTypeHandler(type as ValueType, validators, isProperty ? fullKey : undefined);
168
+ break;
169
+
170
+ default:
171
+ invalid = true;
172
+ break typeLoop;
173
+ }
174
+
175
+ handlers.push(handler);
176
+ }
177
+
178
+ if (invalid || handlers.length === 0) {
179
+ if (isProperty) {
180
+ throw new SchematicError(getSchematicPropertyTypeMessage(prefixedKey));
181
+ }
182
+
183
+ throw new ValidatorError(VALIDATOR_MESSAGE_INVALID_PROPERTY_TYPE);
184
+ }
185
+
186
+ required = required && !types.includes(TYPE_UNDEFINED);
187
+
188
+ if (defaults != null && !required) {
189
+ throw new SchematicError(getDefaultRequiredMessage(prefixedKey));
190
+ }
191
+
192
+ const handler = getBaseHandler(handlers);
193
+
194
+ if (
195
+ defaults != null &&
196
+ Array.isArray(handler(defaults.value, getParameters(isProperty), false))
197
+ ) {
198
+ throw new SchematicError(getDefaultTypeMessage(prefixedKey, types));
199
+ }
200
+
201
+ return {defaults, handler, required, types, key: fullKey};
202
+ }
@@ -5,7 +5,6 @@ import {
5
5
  CONJUNCTION_AND_COMMA,
6
6
  CONJUNCTION_OR,
7
7
  CONJUNCTION_OR_COMMA,
8
- PREFIXED_TYPES,
9
8
  SCHEMATIC_MESSAGE_SCHEMA_INVALID_DEFAULT_REQUIRED,
10
9
  SCHEMATIC_MESSAGE_SCHEMA_INVALID_DEFAULT_TYPE,
11
10
  SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_DISALLOWED,
@@ -13,20 +12,25 @@ import {
13
12
  SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_REQUIRED,
14
13
  SCHEMATIC_MESSAGE_SCHEMA_INVALID_PROPERTY_TYPE,
15
14
  TEMPLATE_PATTERN,
16
- TYPE_ALL,
15
+ TYPES_ALL,
16
+ TYPES_PREFIXED,
17
17
  TYPE_ARRAY,
18
18
  TYPE_FUNCTION_RESULT,
19
19
  TYPE_NULL,
20
20
  TYPE_OBJECT,
21
21
  VALIDATION_MESSAGE_INVALID_INPUT,
22
+ VALIDATION_MESSAGE_INVALID_PROPERTY_TYPE,
23
+ VALIDATION_MESSAGE_INVALID_PROPERTY_VALIDATOR,
22
24
  VALIDATION_MESSAGE_INVALID_REQUIRED,
23
- VALIDATION_MESSAGE_INVALID_TYPE,
24
- VALIDATION_MESSAGE_INVALID_VALUE,
25
- VALIDATION_MESSAGE_INVALID_VALUE_SUFFIX,
25
+ VALIDATION_MESSAGE_INVALID_VALIDATOR_SUFFIX,
26
+ VALIDATION_MESSAGE_INVALID_VALUE_TYPE,
27
+ VALIDATION_MESSAGE_INVALID_VALUE_VALIDATOR,
26
28
  VALIDATION_MESSAGE_UNKNOWN_KEYS,
27
29
  } from '../constants';
28
- import type {ValueName} from '../models/misc.model';
29
- import type {ValidatorType} from '../models/validation.model';
30
+ import type {ValueType} from '../models/misc.model';
31
+ import type {ValidationHandlerType} from '../models/validation.model';
32
+ import {isValidator} from './misc.helper';
33
+ import {validatorTypes} from '../validator';
30
34
 
31
35
  // #region Defaults
32
36
 
@@ -34,7 +38,7 @@ export function getDefaultRequiredMessage(key: string): string {
34
38
  return SCHEMATIC_MESSAGE_SCHEMA_INVALID_DEFAULT_REQUIRED.replace(TEMPLATE_PATTERN, key);
35
39
  }
36
40
 
37
- export function getDefaultTypeMessage(key: string, types: ValidatorType[]): string {
41
+ export function getDefaultTypeMessage(key: string, types: ValidationHandlerType[]): string {
38
42
  let message = SCHEMATIC_MESSAGE_SCHEMA_INVALID_DEFAULT_TYPE.replace(TEMPLATE_PATTERN, key);
39
43
 
40
44
  message = message.replace(TEMPLATE_PATTERN, renderTypes(types));
@@ -62,7 +66,10 @@ export function getInputTypeMessage(actual: unknown): string {
62
66
  return VALIDATION_MESSAGE_INVALID_INPUT.replace(TEMPLATE_PATTERN, getValueType(actual));
63
67
  }
64
68
 
65
- export function getInputPropertyMissingMessage(key: string, types: ValidatorType[]): string {
69
+ export function getInputPropertyMissingMessage(
70
+ key: string,
71
+ types: ValidationHandlerType[],
72
+ ): string {
66
73
  let message = VALIDATION_MESSAGE_INVALID_REQUIRED.replace(TEMPLATE_PATTERN, renderTypes(types));
67
74
 
68
75
  message = message.replace(TEMPLATE_PATTERN, key);
@@ -72,10 +79,13 @@ export function getInputPropertyMissingMessage(key: string, types: ValidatorType
72
79
 
73
80
  export function getInputPropertyTypeMessage(
74
81
  key: string,
75
- types: ValidatorType[],
82
+ types: ValidationHandlerType[],
76
83
  actual: unknown,
77
84
  ): string {
78
- let message = VALIDATION_MESSAGE_INVALID_TYPE.replace(TEMPLATE_PATTERN, renderTypes(types));
85
+ let message = VALIDATION_MESSAGE_INVALID_PROPERTY_TYPE.replace(
86
+ TEMPLATE_PATTERN,
87
+ renderTypes(types),
88
+ );
79
89
 
80
90
  message = message.replace(TEMPLATE_PATTERN, key);
81
91
  message = message.replace(TEMPLATE_PATTERN, getValueType(actual));
@@ -85,16 +95,38 @@ export function getInputPropertyTypeMessage(
85
95
 
86
96
  export function getInputPropertyValidatorMessage(
87
97
  key: string,
88
- type: ValueName,
98
+ type: ValueType,
89
99
  index: number,
90
100
  length: number,
91
101
  ): string {
92
- let message = VALIDATION_MESSAGE_INVALID_VALUE.replace(TEMPLATE_PATTERN, key);
102
+ let message = VALIDATION_MESSAGE_INVALID_PROPERTY_VALIDATOR.replace(TEMPLATE_PATTERN, key);
93
103
 
94
104
  message = message.replace(TEMPLATE_PATTERN, type);
95
105
 
96
106
  if (length > 1) {
97
- message += VALIDATION_MESSAGE_INVALID_VALUE_SUFFIX.replace(TEMPLATE_PATTERN, String(index));
107
+ message += VALIDATION_MESSAGE_INVALID_VALIDATOR_SUFFIX.replace(TEMPLATE_PATTERN, String(index));
108
+ }
109
+
110
+ return message;
111
+ }
112
+
113
+ export function getInputValueTypeMessage(types: ValidationHandlerType[], actual: unknown): string {
114
+ let message = VALIDATION_MESSAGE_INVALID_VALUE_TYPE.replace(TEMPLATE_PATTERN, renderTypes(types));
115
+
116
+ message = message.replace(TEMPLATE_PATTERN, getValueType(actual));
117
+
118
+ return message;
119
+ }
120
+
121
+ export function getInputValueValidatorMessage(
122
+ type: ValueType,
123
+ index: number,
124
+ length: number,
125
+ ): string {
126
+ let message = VALIDATION_MESSAGE_INVALID_VALUE_VALIDATOR.replace(TEMPLATE_PATTERN, type);
127
+
128
+ if (length > 1) {
129
+ message += VALIDATION_MESSAGE_INVALID_VALIDATOR_SUFFIX.replace(TEMPLATE_PATTERN, String(index));
98
130
  }
99
131
 
100
132
  return message;
@@ -116,16 +148,19 @@ export function getSchematicPropertyTypeMessage(key: string): string {
116
148
 
117
149
  // #region Misc.
118
150
 
119
- function getPropertyType(type: ValidatorType): string {
151
+ function getPropertyType(type: ValidationHandlerType): string[] {
120
152
  switch (true) {
121
153
  case typeof type === 'function':
122
- return isConstructor(type) ? type.name : TYPE_FUNCTION_RESULT;
154
+ return [isConstructor(type) ? type.name : TYPE_FUNCTION_RESULT];
155
+
156
+ case isValidator(type):
157
+ return validatorTypes.get(type)!.flatMap(getPropertyType);
123
158
 
124
- case TYPE_ALL.has(type as ValueName):
125
- return PREFIXED_TYPES[type as ValueName];
159
+ case TYPES_ALL.has(type as ValueType):
160
+ return [TYPES_PREFIXED[type as ValueType]];
126
161
 
127
162
  default:
128
- return PREFIXED_TYPES[TYPE_OBJECT];
163
+ return [TYPES_PREFIXED[TYPE_OBJECT]];
129
164
  }
130
165
  }
131
166
 
@@ -137,13 +172,13 @@ function getValueType(value: unknown): string {
137
172
  return TYPE_NULL;
138
173
 
139
174
  case Array.isArray(value):
140
- return PREFIXED_TYPES[TYPE_ARRAY];
175
+ return TYPES_PREFIXED[TYPE_ARRAY];
141
176
 
142
177
  case isPlainObject(value):
143
- return PREFIXED_TYPES[TYPE_OBJECT];
178
+ return TYPES_PREFIXED[TYPE_OBJECT];
144
179
 
145
180
  case valueType !== TYPE_OBJECT:
146
- return PREFIXED_TYPES[valueType as ValueName];
181
+ return TYPES_PREFIXED[valueType as ValueType];
147
182
 
148
183
  default:
149
184
  return (value as object).constructor.name;
@@ -180,19 +215,26 @@ function renderParts(parts: string[], delimiterShort: string, delimiterLong: str
180
215
  return rendered;
181
216
  }
182
217
 
183
- function renderTypes(types: ValidatorType[]): string {
218
+ function renderTypes(types: ValidationHandlerType[]): string {
184
219
  const unique = new Set<string>();
185
220
  const parts: string[] = [];
186
221
 
187
- for (let index = 0; index < types.length; index += 1) {
188
- const rendered = getPropertyType(types[index]);
222
+ const typesLength = types.length;
189
223
 
190
- if (unique.has(rendered)) {
191
- continue;
192
- }
224
+ for (let typeIndex = 0; typeIndex < typesLength; typeIndex += 1) {
225
+ const properties = getPropertyType(types[typeIndex]);
226
+ const propertiesLength = properties.length;
193
227
 
194
- unique.add(rendered);
195
- parts.push(rendered);
228
+ for (let propertyIndex = 0; propertyIndex < propertiesLength; propertyIndex += 1) {
229
+ const property = properties[propertyIndex];
230
+
231
+ if (unique.has(property)) {
232
+ continue;
233
+ }
234
+
235
+ unique.add(property);
236
+ parts.push(property);
237
+ }
196
238
  }
197
239
 
198
240
  return renderParts(parts, CONJUNCTION_OR, CONJUNCTION_OR_COMMA);
@@ -3,20 +3,23 @@ import type {Constructor} from '@oscarpalmer/atoms/models';
3
3
  import {
4
4
  MESSAGE_CONSTRUCTOR,
5
5
  PROPERTY_SCHEMA,
6
+ PROPERTY_VALIDATOR,
6
7
  REPORTING_ALL,
7
8
  REPORTING_FIRST,
8
9
  REPORTING_NONE,
10
+ REPORTING_RESULT,
9
11
  REPORTING_THROW,
10
12
  REPORTING_TYPES,
11
13
  } from '../constants';
12
14
  import type {
13
15
  ReportingInformation,
14
16
  ReportingType,
15
- ValidatorParameters,
17
+ ValidationHandlerParameters,
16
18
  } from '../models/validation.model';
17
19
  import type {Schema} from '../schema';
20
+ import type {Validator} from '../validator';
18
21
 
19
- export function getParameters(input?: unknown): ValidatorParameters {
22
+ export function getParameters(input?: unknown): ValidationHandlerParameters {
20
23
  if (typeof input === 'boolean') {
21
24
  return {
22
25
  clone: true,
@@ -53,7 +56,7 @@ export function getReporting(value?: unknown): ReportingInformation {
53
56
  return {
54
57
  type,
55
58
  [REPORTING_ALL]: type === REPORTING_ALL,
56
- [REPORTING_FIRST]: type === REPORTING_FIRST,
59
+ [REPORTING_FIRST]: type === REPORTING_FIRST || type === REPORTING_RESULT,
57
60
  [REPORTING_NONE]: type === REPORTING_NONE,
58
61
  [REPORTING_THROW]: type === REPORTING_THROW,
59
62
  } as ReportingInformation;
@@ -78,11 +81,11 @@ export function instanceOf<Instance>(
78
81
  }
79
82
 
80
83
  /**
81
- * Is the value a schematic?
84
+ * Is the value a schema?
82
85
  * @param value Value to check
83
- * @returns `true` if the value is a schematic, `false` otherwise
86
+ * @returns `true` if the value is a schema, `false` otherwise
84
87
  */
85
- export function isSchema(value: unknown): value is Schema<never> {
88
+ export function isSchema(value: unknown): value is Schema<unknown> {
86
89
  return (
87
90
  typeof value === 'object' &&
88
91
  value !== null &&
@@ -90,3 +93,17 @@ export function isSchema(value: unknown): value is Schema<never> {
90
93
  value[PROPERTY_SCHEMA] === true
91
94
  );
92
95
  }
96
+
97
+ /**
98
+ * Is the value a validator?
99
+ * @param value Value to check
100
+ * @returns `true` if the value is a validator, `false` otherwise
101
+ */
102
+ export function isValidator(value: unknown): value is Validator<unknown> {
103
+ return (
104
+ typeof value === 'object' &&
105
+ value !== null &&
106
+ PROPERTY_VALIDATOR in value &&
107
+ value[PROPERTY_VALIDATOR] === true
108
+ );
109
+ }
@@ -0,0 +1,72 @@
1
+ import {
2
+ ValidationError,
3
+ type ValidationHandlerParameters,
4
+ type PropertyValidation,
5
+ type PropertyValidationKey,
6
+ } from '../models/validation.model';
7
+
8
+ type ReportParameters<Callback extends (...args: any[]) => string> = {
9
+ extract?: boolean;
10
+ information?: ReportParametersInformation;
11
+ key?: PropertyValidationKey;
12
+ message: ReportParametersMessage<Callback>;
13
+ original: ValidationHandlerParameters;
14
+ value: unknown;
15
+ };
16
+
17
+ type ReportParametersMessage<Callback extends (...args: any[]) => string> = {
18
+ arguments: Parameters<Callback>;
19
+ callback: Callback;
20
+ };
21
+
22
+ type ReportParametersInformation = {
23
+ all: PropertyValidation[];
24
+ existing?: PropertyValidation[];
25
+ };
26
+
27
+ export function report<Callback extends (...args: any[]) => string>(
28
+ parameters: ReportParameters<Callback>,
29
+ getReports: true,
30
+ ): PropertyValidation[];
31
+
32
+ export function report<Callback extends (...args: any[]) => string>(
33
+ parameters: ReportParameters<Callback>,
34
+ ): PropertyValidation[] | undefined;
35
+
36
+ export function report<Callback extends (...args: any[]) => string>(
37
+ parameters: ReportParameters<Callback>,
38
+ getReports?: boolean,
39
+ ): PropertyValidation[] | undefined {
40
+ const {information, message, original} = parameters;
41
+
42
+ let reported: PropertyValidation[];
43
+
44
+ if (information?.existing == null) {
45
+ reported = [
46
+ {
47
+ value: parameters.value,
48
+ message: message.callback(...message.arguments),
49
+ },
50
+ ];
51
+
52
+ if (parameters.key != null) {
53
+ reported[0].key = parameters.key;
54
+ }
55
+ } else {
56
+ reported = information.existing;
57
+ }
58
+
59
+ if (original.reporting.throw) {
60
+ throw new ValidationError(reported);
61
+ }
62
+
63
+ information?.all.push(...reported);
64
+
65
+ if (parameters.extract ?? true) {
66
+ original.information?.push(...reported);
67
+ }
68
+
69
+ if ((getReports ?? false) || !original.reporting.all) {
70
+ return reported;
71
+ }
72
+ }
@@ -0,0 +1,33 @@
1
+ import {error, ok} from '@oscarpalmer/atoms/result/misc';
2
+ import type {ValidationHandler} from '../models/validation.model';
3
+ import {getParameters} from './misc.helper';
4
+
5
+ export function getResult(handler: ValidationHandler, value: unknown, options?: unknown): unknown {
6
+ const parameters = getParameters(options);
7
+
8
+ const result = handler(value, parameters, true);
9
+
10
+ if (result === true) {
11
+ return parameters.reporting.none || parameters.reporting.throw
12
+ ? parameters.clone
13
+ ? parameters.output
14
+ : value
15
+ : ok(parameters.clone ? parameters.output : value);
16
+ }
17
+
18
+ if (!parameters.reporting.none) {
19
+ return error(parameters.reporting.all ? result : result[0]);
20
+ }
21
+ }
22
+
23
+ export function isResult(handler: ValidationHandler, value: unknown, options?: unknown): unknown {
24
+ const parameters = getParameters(options);
25
+
26
+ const result = handler(value, parameters, false);
27
+
28
+ if (result === true) {
29
+ return parameters.reporting.none || parameters.reporting.throw ? result : ok(result);
30
+ }
31
+
32
+ return parameters.reporting.none ? false : error(parameters.reporting.all ? result : result[0]);
33
+ }
package/src/index.ts CHANGED
@@ -8,3 +8,4 @@ export {
8
8
  type IsOptions,
9
9
  } from './models/validation.model';
10
10
  export {schema, type Schema} from './schema';
11
+ export {validator, type Validator} from './validator';
@@ -1,7 +1,8 @@
1
1
  import type {Constructor, Simplify} from '@oscarpalmer/atoms/models';
2
2
  import type {Schema} from '../schema';
3
- import type {IsOptionalProperty, ValueName, Values} from './misc.model';
3
+ import type {IsOptionalProperty, ValueType, Values} from './misc.model';
4
4
  import type {PlainSchematic, Schematic, SchematicProperty} from './schematic.plain.model';
5
+ import type {Validator} from '../validator';
5
6
 
6
7
  /**
7
8
  * Infers the TypeScript type from a {@link Schematic} definition
@@ -49,8 +50,6 @@ export type InferPropertyType<Value> = Value extends (infer Item)[]
49
50
  /**
50
51
  * Maps a single `$type` definition to its TypeScript equivalent
51
52
  *
52
- * Resolves, in order: {@link Constructor}s, {@link Schema} instances, {@link ValueName} values, and nested {@link PlainSchematic} objects
53
- *
54
53
  * @template Value single type definition
55
54
  */
56
55
  export type InferPropertyValue<Value> =
@@ -58,11 +57,13 @@ export type InferPropertyValue<Value> =
58
57
  ? Instance
59
58
  : Value extends Schema<infer Model>
60
59
  ? Model
61
- : Value extends ValueName
62
- ? Values[Value & ValueName]
63
- : Value extends PlainSchematic
64
- ? Infer<Value>
65
- : never;
60
+ : Value extends Validator<infer Type>
61
+ ? Type
62
+ : Value extends ValueType
63
+ ? Values[Value & ValueType]
64
+ : Value extends PlainSchematic
65
+ ? Infer<Value>
66
+ : never;
66
67
 
67
68
  /**
68
69
  * Extracts keys from a {@link Schematic} whose entries are required _(i.e., `$required` is not `false`)_
@@ -85,8 +86,6 @@ export type InferSchemaEntry<Value> = Value extends (infer Item)[]
85
86
  /**
86
87
  * Maps a single top-level schema entry to its TypeScript type
87
88
  *
88
- * Resolves, in order: {@link Constructor}s, {@link Schema} instances, {@link SchemaProperty} objects, {@link PlainSchematic} objects, and {@link ValueName} values
89
- *
90
89
  * @template Value single schema entry
91
90
  */
92
91
  export type InferSchemaEntryValue<Value> =
@@ -98,6 +97,25 @@ export type InferSchemaEntryValue<Value> =
98
97
  ? InferPropertyType<Value['$type']>
99
98
  : Value extends PlainSchematic
100
99
  ? Infer<Value & Schematic>
101
- : Value extends ValueName
102
- ? Values[Value & ValueName]
103
- : never;
100
+ : Value extends Validator<infer Type>
101
+ ? Type
102
+ : Value extends ValueType
103
+ ? Values[Value & ValueType]
104
+ : never;
105
+
106
+ /**
107
+ * Infers the TypeScript type from a {@link Validator} definition
108
+ *
109
+ * @template Value Validator to infer type from
110
+ */
111
+ export type InferValidatorValue<Value> = Value extends (infer Item)[]
112
+ ? InferValidatorValue<Item>
113
+ : Value extends Constructor<infer Instance>
114
+ ? Instance
115
+ : Value extends ((value: unknown) => value is infer Type)
116
+ ? Type
117
+ : Value extends (value: unknown) => boolean
118
+ ? 'xyz'
119
+ : Value extends ValueType
120
+ ? Values[Value & ValueType]
121
+ : never;
@@ -22,22 +22,22 @@ export type DeduplicateTuple<Value extends unknown[], Seen extends unknown[] = [
22
22
  : Seen;
23
23
 
24
24
  /**
25
- * Recursively extracts {@link ValueName} strings from a type, unwrapping arrays and readonly arrays
25
+ * Recursively extracts {@link ValueType} strings from a type, unwrapping arrays and readonly arrays
26
26
  *
27
- * @template Value Type to extract value names from
27
+ * @template Value Type to extract value types from
28
28
  *
29
29
  * @example
30
30
  * ```ts
31
- * // ExtractValueNames<'string'> => 'string'
32
- * // ExtractValueNames<['string', 'number']> => 'string' | 'number'
31
+ * // ExtractValueTypes<'string'> => 'string'
32
+ * // ExtractValueTypes<['string', 'number']> => 'string' | 'number'
33
33
  * ```
34
34
  */
35
- export type ExtractValueNames<Value> = Value extends ValueName
35
+ export type ExtractValueTypes<Value> = Value extends ValueType
36
36
  ? Value
37
37
  : Value extends (infer Item)[]
38
- ? ExtractValueNames<Item>
38
+ ? ExtractValueTypes<Item>
39
39
  : Value extends readonly (infer Item)[]
40
- ? ExtractValueNames<Item>
40
+ ? ExtractValueTypes<Item>
41
41
  : never;
42
42
 
43
43
  /**
@@ -183,10 +183,10 @@ export type UnwrapSingle<Value extends unknown[]> = Value extends [infer Only]
183
183
  /**
184
184
  * A union of valid type name strings, e.g. `'string'`, `'number'`, `'date'`
185
185
  */
186
- export type ValueName = keyof Values;
186
+ export type ValueType = keyof Values;
187
187
 
188
188
  /**
189
- * Maps {@link ValueName} strings to their TypeScript equivalents
189
+ * Maps {@link ValueType} strings to their TypeScript equivalents
190
190
  */
191
191
  export type Values = {
192
192
  array: unknown[];