@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.
Files changed (48) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +266 -0
  3. package/dist/index.d.ts +2715 -0
  4. package/dist/index.es.js +1715 -0
  5. package/dist/index.es.js.map +1 -0
  6. package/package.json +70 -0
  7. package/src/index.ts +90 -0
  8. package/src/reform/ArrayHelper.ts +164 -0
  9. package/src/reform/Form.tsx +81 -0
  10. package/src/reform/FormManager.ts +494 -0
  11. package/src/reform/Reform.ts +15 -0
  12. package/src/reform/components/BaseCheckboxField.tsx +72 -0
  13. package/src/reform/components/BaseDateField.tsx +84 -0
  14. package/src/reform/components/BaseRadioField.tsx +72 -0
  15. package/src/reform/components/BaseSelectField.tsx +103 -0
  16. package/src/reform/components/BaseTextAreaField.tsx +87 -0
  17. package/src/reform/components/BaseTextField.tsx +135 -0
  18. package/src/reform/components/InputHTMLProps.tsx +89 -0
  19. package/src/reform/observers/observer.ts +131 -0
  20. package/src/reform/observers/observerPath.ts +327 -0
  21. package/src/reform/observers/useObservers.ts +232 -0
  22. package/src/reform/useForm.ts +246 -0
  23. package/src/reform/useFormContext.tsx +37 -0
  24. package/src/reform/useFormField.ts +75 -0
  25. package/src/reform/useRender.ts +12 -0
  26. package/src/yop/MessageProvider.ts +204 -0
  27. package/src/yop/Metadata.ts +304 -0
  28. package/src/yop/ObjectsUtil.ts +811 -0
  29. package/src/yop/TypesUtil.ts +148 -0
  30. package/src/yop/ValidationContext.ts +207 -0
  31. package/src/yop/Yop.ts +430 -0
  32. package/src/yop/constraints/CommonConstraints.ts +124 -0
  33. package/src/yop/constraints/Constraint.ts +135 -0
  34. package/src/yop/constraints/MinMaxConstraints.ts +53 -0
  35. package/src/yop/constraints/OneOfConstraint.ts +40 -0
  36. package/src/yop/constraints/TestConstraint.ts +176 -0
  37. package/src/yop/decorators/array.ts +157 -0
  38. package/src/yop/decorators/boolean.ts +69 -0
  39. package/src/yop/decorators/date.ts +73 -0
  40. package/src/yop/decorators/email.ts +66 -0
  41. package/src/yop/decorators/file.ts +69 -0
  42. package/src/yop/decorators/id.ts +35 -0
  43. package/src/yop/decorators/ignored.ts +40 -0
  44. package/src/yop/decorators/instance.ts +110 -0
  45. package/src/yop/decorators/number.ts +73 -0
  46. package/src/yop/decorators/string.ts +90 -0
  47. package/src/yop/decorators/test.ts +41 -0
  48. 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
+ }