@4riders/reform 3.0.24
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/LICENSE +21 -0
- package/README.md +266 -0
- package/dist/index.d.ts +2715 -0
- package/dist/index.es.js +1715 -0
- package/dist/index.es.js.map +1 -0
- package/package.json +70 -0
- package/src/index.ts +90 -0
- package/src/reform/ArrayHelper.ts +164 -0
- package/src/reform/Form.tsx +81 -0
- package/src/reform/FormManager.ts +494 -0
- package/src/reform/Reform.ts +15 -0
- package/src/reform/components/BaseCheckboxField.tsx +72 -0
- package/src/reform/components/BaseDateField.tsx +84 -0
- package/src/reform/components/BaseRadioField.tsx +72 -0
- package/src/reform/components/BaseSelectField.tsx +103 -0
- package/src/reform/components/BaseTextAreaField.tsx +87 -0
- package/src/reform/components/BaseTextField.tsx +135 -0
- package/src/reform/components/InputHTMLProps.tsx +89 -0
- package/src/reform/observers/observer.ts +131 -0
- package/src/reform/observers/observerPath.ts +327 -0
- package/src/reform/observers/useObservers.ts +232 -0
- package/src/reform/useForm.ts +246 -0
- package/src/reform/useFormContext.tsx +37 -0
- package/src/reform/useFormField.ts +75 -0
- package/src/reform/useRender.ts +12 -0
- package/src/yop/MessageProvider.ts +204 -0
- package/src/yop/Metadata.ts +304 -0
- package/src/yop/ObjectsUtil.ts +811 -0
- package/src/yop/TypesUtil.ts +148 -0
- package/src/yop/ValidationContext.ts +207 -0
- package/src/yop/Yop.ts +430 -0
- package/src/yop/constraints/CommonConstraints.ts +124 -0
- package/src/yop/constraints/Constraint.ts +135 -0
- package/src/yop/constraints/MinMaxConstraints.ts +53 -0
- package/src/yop/constraints/OneOfConstraint.ts +40 -0
- package/src/yop/constraints/TestConstraint.ts +176 -0
- package/src/yop/decorators/array.ts +157 -0
- package/src/yop/decorators/boolean.ts +69 -0
- package/src/yop/decorators/date.ts +73 -0
- package/src/yop/decorators/email.ts +66 -0
- package/src/yop/decorators/file.ts +69 -0
- package/src/yop/decorators/id.ts +35 -0
- package/src/yop/decorators/ignored.ts +40 -0
- package/src/yop/decorators/instance.ts +110 -0
- package/src/yop/decorators/number.ts +73 -0
- package/src/yop/decorators/string.ts +90 -0
- package/src/yop/decorators/test.ts +41 -0
- package/src/yop/decorators/time.ts +112 -0
|
@@ -0,0 +1,304 @@
|
|
|
1
|
+
import { assign, clone } from "./ObjectsUtil"
|
|
2
|
+
import { ClassConstructor, isBoolean, isObject } from "./TypesUtil"
|
|
3
|
+
import { InternalValidationContext } from "./ValidationContext"
|
|
4
|
+
import { validationSymbol } from "./Yop"
|
|
5
|
+
import { CommonConstraints, ContraintsParent, ContraintsValue, InternalCommonConstraints, InternalConstraints, Traverser, validateCommonConstraints, validateTypeConstraint, Validator } from "./constraints/CommonConstraints"
|
|
6
|
+
import { validateConstraint } from "./constraints/Constraint"
|
|
7
|
+
import { TestConstraintFunction, validateTestConstraint } from "./constraints/TestConstraint"
|
|
8
|
+
import { ArrayConstraints, arrayKind } from "./decorators/array"
|
|
9
|
+
import { InstanceConstraints, instanceKind } from "./decorators/instance"
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Internal constraints for a class, including test and field constraints.
|
|
13
|
+
* @template Class - The type of the class.
|
|
14
|
+
* @ignore
|
|
15
|
+
*/
|
|
16
|
+
export interface InternalClassConstraints<Class = any> extends InternalConstraints {
|
|
17
|
+
/**
|
|
18
|
+
* Optional test constraint function for the class.
|
|
19
|
+
*/
|
|
20
|
+
test?: TestConstraintFunction<Class>
|
|
21
|
+
/**
|
|
22
|
+
* Optional map of field names to their internal constraints.
|
|
23
|
+
*/
|
|
24
|
+
fields?: { [name: string]: InternalCommonConstraints }
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Traverses a class field to retrieve its constraints and value.
|
|
29
|
+
* @param context - The validation context for the class.
|
|
30
|
+
* @param constraints - The class constraints.
|
|
31
|
+
* @param key - The field name or index to traverse.
|
|
32
|
+
* @param traverseNullish - If true, traverses only if value is not nullish; otherwise, returns undefined for nullish values.
|
|
33
|
+
* @returns A tuple of the field constraints (if any) and the value at the given key.
|
|
34
|
+
* @ignore
|
|
35
|
+
*/
|
|
36
|
+
export function traverseClass(
|
|
37
|
+
context: InternalValidationContext<unknown>,
|
|
38
|
+
constraints: InternalClassConstraints,
|
|
39
|
+
key: string | number,
|
|
40
|
+
traverseNullish?: boolean
|
|
41
|
+
): readonly [InternalCommonConstraints | undefined, any] {
|
|
42
|
+
if (traverseNullish ? context.value != null && (typeof context.value !== "object" || typeof key !== "string") : context.value == null)
|
|
43
|
+
return [undefined, undefined]
|
|
44
|
+
return [constraints.fields?.[key], (context.value as { [x: string]: any })?.[key]]
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Validates a class object against its constraints, including all fields and test constraints.
|
|
49
|
+
* @param context - The validation context for the class object.
|
|
50
|
+
* @param constraints - The class constraints to validate.
|
|
51
|
+
* @returns True if all constraints pass, false otherwise.
|
|
52
|
+
* @ignore
|
|
53
|
+
*/
|
|
54
|
+
export function validateClass(context: InternalValidationContext<{ [x: string]: any }>, constraints: InternalClassConstraints) {
|
|
55
|
+
if (context.value == null || !validateTypeConstraint(context, isObject, "object"))
|
|
56
|
+
return false
|
|
57
|
+
|
|
58
|
+
let valid = true
|
|
59
|
+
|
|
60
|
+
const parent = context.value
|
|
61
|
+
for (const [fieldName, fieldConstraints] of Object.entries(constraints.fields!)) {
|
|
62
|
+
if (fieldConstraints.validate == null)
|
|
63
|
+
continue
|
|
64
|
+
|
|
65
|
+
const fieldContext = context.createChildContext({
|
|
66
|
+
kind: fieldConstraints.kind,
|
|
67
|
+
value: parent[fieldName],
|
|
68
|
+
key: fieldName,
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
valid = (
|
|
72
|
+
validateConstraint(fieldContext, fieldConstraints, "exists", isBoolean, (_, constraint) => constraint !== true || fieldName in parent) &&
|
|
73
|
+
fieldConstraints.validate(fieldContext, fieldConstraints) &&
|
|
74
|
+
valid
|
|
75
|
+
)
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
return valid && validateTestConstraint(context, constraints)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Initializes and returns the internal class constraints for a given decorator metadata object.
|
|
83
|
+
* @param decoratorMetadata - The metadata object from a class decorator.
|
|
84
|
+
* @returns The initialized internal class constraints.
|
|
85
|
+
* @ignore
|
|
86
|
+
*/
|
|
87
|
+
export function initClassConstraints(decoratorMetadata: DecoratorMetadata) {
|
|
88
|
+
const metadata = decoratorMetadata as unknown as { [validationSymbol]: InternalClassConstraints }
|
|
89
|
+
if (!Object.hasOwnProperty.bind(metadata)(validationSymbol))
|
|
90
|
+
metadata[validationSymbol] = clone(metadata[validationSymbol] ?? {})
|
|
91
|
+
|
|
92
|
+
const validation = metadata[validationSymbol]
|
|
93
|
+
validation.validate ??= validateClass
|
|
94
|
+
validation.traverse ??= traverseClass
|
|
95
|
+
validation.kind ??= "class"
|
|
96
|
+
return validation
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Type for a class field decorator function.
|
|
101
|
+
* @template Value - The type of the field value.
|
|
102
|
+
* @template Parent - The type of the parent object.
|
|
103
|
+
* @ignore
|
|
104
|
+
*/
|
|
105
|
+
export type ClassFieldDecorator<Value, Parent = unknown> = (_: unknown, context: ClassFieldDecoratorContext<Parent, Value>) => void
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Retrieves field metadata from a class field decorator.
|
|
109
|
+
* @template Value - The type of the field value.
|
|
110
|
+
* @template Parent - The type of the parent object.
|
|
111
|
+
* @param decorator - The class field decorator function.
|
|
112
|
+
* @returns The field constraints metadata for a placeholder field.
|
|
113
|
+
* @ignore
|
|
114
|
+
*/
|
|
115
|
+
export function getMetadataFromDecorator<Value, Parent>(decorator: ClassFieldDecorator<Value, Parent>) {
|
|
116
|
+
const metadata = { [validationSymbol]: {} as InternalClassConstraints }
|
|
117
|
+
decorator(null, { metadata, name: "placeholder" } as any)
|
|
118
|
+
return metadata[validationSymbol]?.fields?.placeholder
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Retrieves the validation metadata for a given class constructor.
|
|
123
|
+
* @template T - The type of the class instance.
|
|
124
|
+
* @param model - The class constructor.
|
|
125
|
+
* @returns The common constraints metadata, if any.
|
|
126
|
+
* @ignore
|
|
127
|
+
*/
|
|
128
|
+
export function getMetadata<T>(model: ClassConstructor<T>) {
|
|
129
|
+
return model?.[Symbol.metadata]?.[validationSymbol] as CommonConstraints<any, T> | undefined
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
/**
|
|
133
|
+
* Retrieves the field constraints metadata for all fields of a class constructor.
|
|
134
|
+
* @template T - The type of the class instance.
|
|
135
|
+
* @param model - The class constructor.
|
|
136
|
+
* @returns An object mapping field names to their constraints, if any.
|
|
137
|
+
* @ignore
|
|
138
|
+
*/
|
|
139
|
+
export function getMetadataFields<T>(model: ClassConstructor<T>) {
|
|
140
|
+
const metadata = model?.[Symbol.metadata]?.[validationSymbol] as InternalClassConstraints | undefined
|
|
141
|
+
return metadata?.fields as { [K in keyof T]: CommonConstraints<any, T> } | undefined
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Retrieves the class constructor from validation metadata, handling array and instance kinds.
|
|
146
|
+
* @template T - The type of the class instance.
|
|
147
|
+
* @param metadata - The validation metadata object.
|
|
148
|
+
* @returns The class constructor, if found.
|
|
149
|
+
* @ignore
|
|
150
|
+
*/
|
|
151
|
+
export function getClassConstructor<T>(metadata: any): ClassConstructor<T> | undefined {
|
|
152
|
+
if (metadata?.kind === arrayKind) {
|
|
153
|
+
const of = (metadata as unknown as ArrayConstraints<any, any>).of
|
|
154
|
+
if (typeof of === "function")
|
|
155
|
+
return of as ClassConstructor<T>
|
|
156
|
+
metadata = (of as any)?.[Symbol.metadata]?.[validationSymbol] as unknown as InternalConstraints
|
|
157
|
+
}
|
|
158
|
+
return (
|
|
159
|
+
metadata?.kind === instanceKind ?
|
|
160
|
+
(metadata as unknown as InstanceConstraints<any, any>).of as ClassConstructor<T> :
|
|
161
|
+
undefined
|
|
162
|
+
)
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* Symbol used to tag validation decorators with their kind.
|
|
167
|
+
* @ignore
|
|
168
|
+
*/
|
|
169
|
+
const decoratorSymbol = Symbol("YopValidationDecorator")
|
|
170
|
+
|
|
171
|
+
/**
|
|
172
|
+
* Retrieves the kind of a validation decorator from a value.
|
|
173
|
+
* @param value - The value to inspect.
|
|
174
|
+
* @returns The decorator kind, if present.
|
|
175
|
+
* @ignore
|
|
176
|
+
*/
|
|
177
|
+
export function getValidationDecoratorKind(value: any): string | undefined {
|
|
178
|
+
return value?.[decoratorSymbol]
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Creates a class field decorator that assigns or modifies field constraints.
|
|
183
|
+
* @template Parent - The type of the parent object.
|
|
184
|
+
* @template Value - The type of the field value.
|
|
185
|
+
* @param properties - An object or function to assign/modify field constraints.
|
|
186
|
+
* @param reset - If true, resets the field constraints before applying properties.
|
|
187
|
+
* @returns A class field decorator function.
|
|
188
|
+
* @ignore
|
|
189
|
+
*/
|
|
190
|
+
export function fieldDecorator<Parent, Value>(properties: object | ((field: InternalCommonConstraints) => void), reset = false) {
|
|
191
|
+
return (_: unknown, context: ClassFieldDecoratorContext<Parent, Value>) => {
|
|
192
|
+
const classConstraints = initClassConstraints(context.metadata)
|
|
193
|
+
if (!Object.hasOwnProperty.bind(classConstraints)("fields"))
|
|
194
|
+
classConstraints.fields = clone(classConstraints.fields ?? {})
|
|
195
|
+
|
|
196
|
+
const fieldName = context.name as string
|
|
197
|
+
const fields = classConstraints.fields!
|
|
198
|
+
if (reset || !Object.hasOwnProperty.bind(fields)(fieldName))
|
|
199
|
+
fields[fieldName] = {} as InternalCommonConstraints
|
|
200
|
+
|
|
201
|
+
if (typeof properties === "function")
|
|
202
|
+
properties(fields[fieldName])
|
|
203
|
+
else
|
|
204
|
+
assign(fields[fieldName], properties)
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Type for a map of group names to constraints.
|
|
210
|
+
* @template Constraints - The type of the constraints.
|
|
211
|
+
* @ignore
|
|
212
|
+
*/
|
|
213
|
+
export type Groups<Constraints> = { [group: string]: Constraints }
|
|
214
|
+
|
|
215
|
+
/**
|
|
216
|
+
* Merges constraints from props and groups using a merger function.
|
|
217
|
+
* @template Constraints - The type of the constraints.
|
|
218
|
+
* @param props - The main constraints object.
|
|
219
|
+
* @param groups - Optional groups of constraints.
|
|
220
|
+
* @param merger - Function to merge each constraints object.
|
|
221
|
+
* @ignore
|
|
222
|
+
*/
|
|
223
|
+
export function mergeMetadata<Constraints>(props?: Constraints, groups?: Groups<Constraints>, merger: (params: Constraints) => void = () => {}) {
|
|
224
|
+
Object.values(groups ?? {}).concat(props != null ? [props] : []).forEach(params => {
|
|
225
|
+
merger(params)
|
|
226
|
+
})
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Merges default constraint properties into field metadata, applying to all groups if not already set.
|
|
232
|
+
* @template Constraints - The type of the constraints.
|
|
233
|
+
* @template Parent - The type of the parent object.
|
|
234
|
+
* @template Value - The type of the field value.
|
|
235
|
+
* @param decorator - The field decorator function.
|
|
236
|
+
* @param defaultProps - The default constraint properties to merge.
|
|
237
|
+
* @returns A new field decorator function with merged defaults.
|
|
238
|
+
* @ignore
|
|
239
|
+
*/
|
|
240
|
+
export function mergeDefaultMetadata<Constraints extends {}, Parent, Value>(
|
|
241
|
+
decorator: (_: unknown, context: ClassFieldDecoratorContext<Parent, Value>) => void,
|
|
242
|
+
defaultProps: Constraints
|
|
243
|
+
) {
|
|
244
|
+
return (_: unknown, context: ClassFieldDecoratorContext<Parent, Value>) => {
|
|
245
|
+
decorator(_, context)
|
|
246
|
+
const fields = (context.metadata?.[validationSymbol] as InternalClassConstraints)?.fields
|
|
247
|
+
if (fields?.[context.name as string] != null) {
|
|
248
|
+
const metadata = fields[context.name as string]
|
|
249
|
+
Object.entries(defaultProps).forEach(([key, value]) => {
|
|
250
|
+
const constraintName = key as keyof InternalCommonConstraints
|
|
251
|
+
if (metadata[constraintName] === undefined)
|
|
252
|
+
metadata[constraintName] = value as any
|
|
253
|
+
if (metadata.groups != null) {
|
|
254
|
+
Object.keys(metadata.groups).forEach(groupKey => {
|
|
255
|
+
const groupName = groupKey as keyof Groups<Constraints>
|
|
256
|
+
if (metadata.groups![groupName][constraintName] === undefined)
|
|
257
|
+
metadata.groups![groupName][constraintName] = value as any
|
|
258
|
+
})
|
|
259
|
+
}
|
|
260
|
+
})
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
/**
|
|
266
|
+
* Creates a field validation decorator with the specified kind, constraints, groups, and validator.
|
|
267
|
+
* @template Constraints - The type of the field constraints.
|
|
268
|
+
* @template Value - The type of the field value.
|
|
269
|
+
* @template Parent - The type of the parent object.
|
|
270
|
+
* @param kind - The kind of the field (e.g., 'string', 'number').
|
|
271
|
+
* @param constraints - The field constraints to apply.
|
|
272
|
+
* @param groups - Optional groups of constraints.
|
|
273
|
+
* @param validator - The validation function for the field.
|
|
274
|
+
* @param isMinMaxType - Optional function to check min/max type.
|
|
275
|
+
* @param traverse - Optional traverser function for the field.
|
|
276
|
+
* @returns A class field decorator function with validation.
|
|
277
|
+
* @ignore
|
|
278
|
+
*/
|
|
279
|
+
export function fieldValidationDecorator<
|
|
280
|
+
Constraints extends CommonConstraints<any, any>,
|
|
281
|
+
Value = ContraintsValue<Constraints>,
|
|
282
|
+
Parent = ContraintsParent<Constraints>
|
|
283
|
+
>(
|
|
284
|
+
kind: string,
|
|
285
|
+
constraints: Constraints,
|
|
286
|
+
groups: Groups<Constraints> | undefined,
|
|
287
|
+
validator: Validator<Constraints>,
|
|
288
|
+
isMinMaxType?: (value: any) => boolean,
|
|
289
|
+
traverse?: Traverser<Constraints>,
|
|
290
|
+
) {
|
|
291
|
+
const validate = (context: InternalValidationContext<any, any>, constraints: Constraints) => {
|
|
292
|
+
if (context.ignored() || !validateConstraint(context, constraints, "ignored", isBoolean, (_, constraint) => constraint === false, undefined, undefined, false))
|
|
293
|
+
return true
|
|
294
|
+
if (!validateCommonConstraints(context, constraints))
|
|
295
|
+
return false
|
|
296
|
+
if (context.value == null)
|
|
297
|
+
return true
|
|
298
|
+
return validator(context, constraints)
|
|
299
|
+
}
|
|
300
|
+
constraints = assign(clone(constraints), { groups, kind, validate, traverse, isMinMaxType })
|
|
301
|
+
const decorator = fieldDecorator<Parent, Value>(constraints)
|
|
302
|
+
Object.defineProperty(decorator, decoratorSymbol, { value: kind })
|
|
303
|
+
return decorator
|
|
304
|
+
}
|