@rjsf/validator-ata 6.6.1
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/README.md +94 -0
- package/dist/compileSchemaValidators.cjs +110 -0
- package/dist/compileSchemaValidators.cjs.map +7 -0
- package/dist/compileSchemaValidators.esm.js +79 -0
- package/dist/compileSchemaValidators.esm.js.map +7 -0
- package/dist/index.cjs +461 -0
- package/dist/index.cjs.map +7 -0
- package/dist/validator-ata.esm.js +450 -0
- package/dist/validator-ata.esm.js.map +7 -0
- package/dist/validator-ata.umd.js +422 -0
- package/lib/compileSchemaValidators.d.ts +16 -0
- package/lib/compileSchemaValidators.js +21 -0
- package/lib/compileSchemaValidators.js.map +1 -0
- package/lib/compileSchemaValidatorsCode.d.ts +13 -0
- package/lib/compileSchemaValidatorsCode.js +80 -0
- package/lib/compileSchemaValidatorsCode.js.map +1 -0
- package/lib/createAtaInstance.d.ts +27 -0
- package/lib/createAtaInstance.js +68 -0
- package/lib/createAtaInstance.js.map +1 -0
- package/lib/createPrecompiledValidator.d.ts +15 -0
- package/lib/createPrecompiledValidator.js +17 -0
- package/lib/createPrecompiledValidator.js.map +1 -0
- package/lib/customizeValidator.d.ts +8 -0
- package/lib/customizeValidator.js +9 -0
- package/lib/customizeValidator.js.map +1 -0
- package/lib/index.d.ts +7 -0
- package/lib/index.js +7 -0
- package/lib/index.js.map +1 -0
- package/lib/precompiledValidator.d.ts +89 -0
- package/lib/precompiledValidator.js +107 -0
- package/lib/precompiledValidator.js.map +1 -0
- package/lib/processRawValidationErrors.d.ts +28 -0
- package/lib/processRawValidationErrors.js +137 -0
- package/lib/processRawValidationErrors.js.map +1 -0
- package/lib/tsconfig.tsbuildinfo +1 -0
- package/lib/types.d.ts +63 -0
- package/lib/types.js +2 -0
- package/lib/types.js.map +1 -0
- package/lib/validator.d.ts +85 -0
- package/lib/validator.js +154 -0
- package/lib/validator.js.map +1 -0
- package/package.json +113 -0
- package/src/compileSchemaValidators.ts +30 -0
- package/src/compileSchemaValidatorsCode.ts +92 -0
- package/src/createAtaInstance.ts +81 -0
- package/src/createPrecompiledValidator.ts +29 -0
- package/src/customizeValidator.ts +16 -0
- package/src/index.ts +8 -0
- package/src/precompiledValidator.ts +188 -0
- package/src/processRawValidationErrors.ts +197 -0
- package/src/tsconfig.json +15 -0
- package/src/types.ts +71 -0
- package/src/validator.ts +231 -0
package/src/types.ts
ADDED
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import type { ValidationError, Validator, ValidatorOptions } from 'ata-validator';
|
|
2
|
+
|
|
3
|
+
/** Custom format checker. Receives a string and returns true when the value
|
|
4
|
+
* is considered valid for this format. Mirrors the function shape used by
|
|
5
|
+
* `ata-validator`'s `formats` option.
|
|
6
|
+
*/
|
|
7
|
+
export type AtaFormatChecker = (value: string) => boolean;
|
|
8
|
+
|
|
9
|
+
/** Controls which duplicate error filtering is suppressed; see
|
|
10
|
+
* `processRawValidationErrors#filterDuplicateErrors` for the full semantics.
|
|
11
|
+
* Mirrors `@rjsf/validator-ajv8`'s option of the same name.
|
|
12
|
+
*/
|
|
13
|
+
export type SuppressDuplicateFilteringType = 'anyOf' | 'oneOf' | 'all' | 'none';
|
|
14
|
+
|
|
15
|
+
/** A function that takes a list of `ata-validator` `ValidationError`s and
|
|
16
|
+
* mutates them in place (or replaces messages) to produce localized output.
|
|
17
|
+
* Same shape as the AJV-validator `Localizer`, except the input type is
|
|
18
|
+
* `ata-validator`'s `ValidationError` rather than ajv's `ErrorObject`.
|
|
19
|
+
*/
|
|
20
|
+
export type Localizer = (errors?: null | ValidationError[]) => void;
|
|
21
|
+
|
|
22
|
+
/** Options used to customize the underlying `ata-validator` instance.
|
|
23
|
+
*
|
|
24
|
+
* The shape mirrors `@rjsf/validator-ajv8`'s `CustomValidatorOptionsType`
|
|
25
|
+
* where there is a direct correspondence; AJV-only knobs (`AjvClass`,
|
|
26
|
+
* `ajvFormatOptions`, `ajvOptionsOverrides`) are intentionally omitted
|
|
27
|
+
* because they don't map to ata's API. `extenderFn` is preserved so users
|
|
28
|
+
* can run arbitrary setup against the constructed `Validator`.
|
|
29
|
+
*/
|
|
30
|
+
export interface CustomValidatorOptionsType {
|
|
31
|
+
/** Additional schemas registered for cross-schema `$ref` resolution.
|
|
32
|
+
* Mirrors `ajv.addMetaSchema` / `ajv.addSchema` semantics; ata resolves
|
|
33
|
+
* `$ref` against the registered set.
|
|
34
|
+
*/
|
|
35
|
+
additionalMetaSchemas?: ReadonlyArray<object>;
|
|
36
|
+
|
|
37
|
+
/** Custom format checkers. Keys are format names referenced from `format`
|
|
38
|
+
* in the schema. Values are validation functions, RegExps, or pre-anchored
|
|
39
|
+
* regex source strings (compiled into a function for ata).
|
|
40
|
+
*/
|
|
41
|
+
customFormats?: {
|
|
42
|
+
[k: string]: string | RegExp | AtaFormatChecker;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/** Overrides spread on top of the default `ata-validator` options. Use this
|
|
46
|
+
* to flip `coerceTypes`, `removeAdditional`, `verbose`, or `abortEarly`.
|
|
47
|
+
*/
|
|
48
|
+
ataOptionsOverrides?: ValidatorOptions;
|
|
49
|
+
|
|
50
|
+
/** Hook for applying additional setup against a freshly-built `Validator`.
|
|
51
|
+
* Returning a different instance is supported.
|
|
52
|
+
*/
|
|
53
|
+
extenderFn?: (validator: Validator) => Validator;
|
|
54
|
+
|
|
55
|
+
/** Suppress duplicate error filtering for the specified keyword(s). See
|
|
56
|
+
* `processRawValidationErrors#filterDuplicateErrors`.
|
|
57
|
+
*/
|
|
58
|
+
suppressDuplicateFiltering?: SuppressDuplicateFilteringType;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** The simplified `ValidateFunction` shape produced by ata's compiled
|
|
62
|
+
* standalone modules. Kept for symmetry with the AJV validator package and
|
|
63
|
+
* for the upcoming precompiled-validator support; not consumed by the
|
|
64
|
+
* runtime path in this initial release.
|
|
65
|
+
*/
|
|
66
|
+
export interface CompiledValidateFunction {
|
|
67
|
+
errors?: null | ValidationError[];
|
|
68
|
+
(data: unknown): boolean;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
export type ValidatorFunctions = { [key: string]: CompiledValidateFunction };
|
package/src/validator.ts
ADDED
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
import {
|
|
2
|
+
CustomValidator,
|
|
3
|
+
deepEquals,
|
|
4
|
+
ErrorTransformer,
|
|
5
|
+
FormContextType,
|
|
6
|
+
hashForSchema,
|
|
7
|
+
ID_KEY,
|
|
8
|
+
RJSFSchema,
|
|
9
|
+
ROOT_SCHEMA_PREFIX,
|
|
10
|
+
StrictRJSFSchema,
|
|
11
|
+
UiSchema,
|
|
12
|
+
ValidationData,
|
|
13
|
+
ValidatorType,
|
|
14
|
+
withIdRefPrefix,
|
|
15
|
+
} from '@rjsf/utils';
|
|
16
|
+
import type { ValidationError, Validator } from 'ata-validator';
|
|
17
|
+
import cloneDeep from 'lodash/cloneDeep';
|
|
18
|
+
|
|
19
|
+
import createAtaInstance from './createAtaInstance';
|
|
20
|
+
import processRawValidationErrors, { type RawValidationErrorsType } from './processRawValidationErrors';
|
|
21
|
+
import type { CustomValidatorOptionsType, Localizer, SuppressDuplicateFilteringType } from './types';
|
|
22
|
+
|
|
23
|
+
/** `ValidatorType` implementation backed by `ata-validator`.
|
|
24
|
+
*
|
|
25
|
+
* ata is schema-bound (one `Validator` per schema) rather than instance-with-
|
|
26
|
+
* registry like AJV, so this class maintains its own per-schema-id cache and
|
|
27
|
+
* registers cross-schema dependencies through ata's `addSchema` so `$ref`
|
|
28
|
+
* still resolves between schemas RJSF passes in.
|
|
29
|
+
*/
|
|
30
|
+
export default class ATAValidator<
|
|
31
|
+
T = any,
|
|
32
|
+
S extends StrictRJSFSchema = RJSFSchema,
|
|
33
|
+
F extends FormContextType = any,
|
|
34
|
+
> implements ValidatorType<T, S, F> {
|
|
35
|
+
/** Stable copy of the constructor options, used when (re)building per-schema
|
|
36
|
+
* `Validator` instances on demand.
|
|
37
|
+
*
|
|
38
|
+
* @private
|
|
39
|
+
*/
|
|
40
|
+
readonly options: CustomValidatorOptionsType;
|
|
41
|
+
|
|
42
|
+
/** The Localizer function applied to ata errors before they are returned.
|
|
43
|
+
*
|
|
44
|
+
* @private
|
|
45
|
+
*/
|
|
46
|
+
readonly localizer?: Localizer;
|
|
47
|
+
|
|
48
|
+
/** Controls which duplicate error filtering is suppressed; see
|
|
49
|
+
* `processRawValidationErrors#filterDuplicateErrors`.
|
|
50
|
+
*
|
|
51
|
+
* @private
|
|
52
|
+
*/
|
|
53
|
+
readonly suppressDuplicateFiltering?: SuppressDuplicateFilteringType;
|
|
54
|
+
|
|
55
|
+
/** Per-schema-id cache of constructed ata `Validator` instances. AJV uses a
|
|
56
|
+
* single instance with a schema registry; ata is schema-bound, so we
|
|
57
|
+
* maintain the registry ourselves.
|
|
58
|
+
*/
|
|
59
|
+
private readonly validators = new Map<string, { validator: Validator; schema: object }>();
|
|
60
|
+
|
|
61
|
+
/** Most recent `rootSchema` reference seen by `handleSchemaUpdate`, used as
|
|
62
|
+
* a fast-path to skip a deep-equality check when the same root is passed
|
|
63
|
+
* back-to-back.
|
|
64
|
+
*/
|
|
65
|
+
private lastSeenRootSchema?: S;
|
|
66
|
+
|
|
67
|
+
/** True once a `rootSchema` has been registered in this lifecycle. */
|
|
68
|
+
private hasRegisteredRootSchema = false;
|
|
69
|
+
|
|
70
|
+
constructor(options: CustomValidatorOptionsType, localizer?: Localizer) {
|
|
71
|
+
this.options = options;
|
|
72
|
+
this.localizer = localizer;
|
|
73
|
+
this.suppressDuplicateFiltering = options.suppressDuplicateFiltering;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Resets the cached validators and root-schema bookkeeping. Mirrors
|
|
77
|
+
* `AJV8Validator#reset` so RJSF's test harness can flush state between runs.
|
|
78
|
+
*/
|
|
79
|
+
reset() {
|
|
80
|
+
this.validators.clear();
|
|
81
|
+
this.lastSeenRootSchema = undefined;
|
|
82
|
+
this.hasRegisteredRootSchema = false;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/** The most recently registered rootSchema, kept so we can pass it to ata's
|
|
86
|
+
* `schemas` constructor option when building validators for sub-schemas.
|
|
87
|
+
* This is how `$ref` to root-level definitions resolves under ata's
|
|
88
|
+
* per-schema model (vs AJV's single-instance registry).
|
|
89
|
+
*/
|
|
90
|
+
private cachedRootSchema?: object;
|
|
91
|
+
|
|
92
|
+
/** Returns a structural copy of `formData` so ata's default-applier
|
|
93
|
+
* (which writes `default` values into the input object during validation)
|
|
94
|
+
* doesn't leak a mutation back to the caller. RJSF probes the same data
|
|
95
|
+
* through `isValid` repeatedly while resolving oneOf/anyOf options, and
|
|
96
|
+
* a mutated probe changes subsequent answers, so the wrapper has to
|
|
97
|
+
* preserve referential purity that AJV provides by default.
|
|
98
|
+
*/
|
|
99
|
+
private cloneForValidation<D>(data: D): D {
|
|
100
|
+
if (data === null || typeof data !== 'object') {
|
|
101
|
+
return data;
|
|
102
|
+
}
|
|
103
|
+
if (typeof globalThis.structuredClone === 'function') {
|
|
104
|
+
return globalThis.structuredClone(data);
|
|
105
|
+
}
|
|
106
|
+
return cloneDeep(data);
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
/** Returns the cached ata `Validator` for the given id, or builds and
|
|
110
|
+
* caches a new one. When a rootSchema has been registered via
|
|
111
|
+
* `handleSchemaUpdate`, it is supplied as a sibling schema so `$ref` to
|
|
112
|
+
* the root resolves regardless of which sub-schema is being validated.
|
|
113
|
+
*/
|
|
114
|
+
private getOrBuild(id: string, schema: object): Validator {
|
|
115
|
+
const existing = this.validators.get(id);
|
|
116
|
+
if (existing && deepEquals(existing.schema, schema)) {
|
|
117
|
+
return existing.validator;
|
|
118
|
+
}
|
|
119
|
+
// Pass the rootSchema as a sibling so `$ref` to its definitions resolves.
|
|
120
|
+
// Skip when the schema itself is the root, otherwise ata sees a
|
|
121
|
+
// self-referential addition.
|
|
122
|
+
const siblingRoots = this.cachedRootSchema && this.cachedRootSchema !== schema ? [this.cachedRootSchema] : [];
|
|
123
|
+
const optionsWithSchemas = {
|
|
124
|
+
...this.options,
|
|
125
|
+
ataOptionsOverrides: {
|
|
126
|
+
...(this.options.ataOptionsOverrides || {}),
|
|
127
|
+
...(siblingRoots.length ? { schemas: siblingRoots } : {}),
|
|
128
|
+
},
|
|
129
|
+
};
|
|
130
|
+
const validator = createAtaInstance(schema, optionsWithSchemas);
|
|
131
|
+
this.validators.set(id, { validator, schema });
|
|
132
|
+
return validator;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/** Runs raw validation against the given schema. Equivalent to
|
|
136
|
+
* `AJV8Validator#rawValidation`: returns ata's error array (already in
|
|
137
|
+
* AJV-compatible shape) plus any compilation error encountered.
|
|
138
|
+
*/
|
|
139
|
+
rawValidation<Result = any>(schema: S, formData?: T): RawValidationErrorsType<Result> {
|
|
140
|
+
let compilationError: Error | undefined;
|
|
141
|
+
let errors: ValidationError[] | undefined;
|
|
142
|
+
|
|
143
|
+
try {
|
|
144
|
+
const id = (schema[ID_KEY] as string | undefined) ?? hashForSchema(schema);
|
|
145
|
+
const validator = this.getOrBuild(id, schema);
|
|
146
|
+
const result = validator.validate(this.cloneForValidation(formData));
|
|
147
|
+
errors = result.valid ? undefined : result.errors;
|
|
148
|
+
|
|
149
|
+
if (errors && typeof this.localizer === 'function') {
|
|
150
|
+
// ata freezes `error.params`, so the AJV-validator's pre-quote dance
|
|
151
|
+
// (used to coax `ajv-i18n` into producing quoted property names)
|
|
152
|
+
// can't be applied verbatim. The hook is invoked with the raw
|
|
153
|
+
// ata errors; localizers that mutate `message` in place still work.
|
|
154
|
+
this.localizer(errors);
|
|
155
|
+
}
|
|
156
|
+
} catch (err) {
|
|
157
|
+
compilationError = err as Error;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return {
|
|
161
|
+
errors: errors as unknown as Result[] | undefined,
|
|
162
|
+
validationError: compilationError,
|
|
163
|
+
};
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Validates `formData` and returns RJSF's `ValidationData<T>`. See
|
|
167
|
+
* `processRawValidationErrors` for the shape of the post-processing
|
|
168
|
+
* pipeline (custom validation, transform hook, ui-title resolution).
|
|
169
|
+
*/
|
|
170
|
+
validateFormData(
|
|
171
|
+
formData: T | undefined,
|
|
172
|
+
schema: S,
|
|
173
|
+
customValidate?: CustomValidator<T, S, F>,
|
|
174
|
+
transformErrors?: ErrorTransformer<T, S, F>,
|
|
175
|
+
uiSchema?: UiSchema<T, S, F>,
|
|
176
|
+
): ValidationData<T> {
|
|
177
|
+
const rawErrors = this.rawValidation<ValidationError>(schema, formData);
|
|
178
|
+
return processRawValidationErrors(
|
|
179
|
+
this,
|
|
180
|
+
rawErrors,
|
|
181
|
+
formData,
|
|
182
|
+
schema,
|
|
183
|
+
customValidate,
|
|
184
|
+
transformErrors,
|
|
185
|
+
uiSchema,
|
|
186
|
+
this.suppressDuplicateFiltering,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Registers (or refreshes) the rootSchema in the per-schema registry so
|
|
191
|
+
* subsequent `$ref`-resolving validators can see it. Mirrors AJV's
|
|
192
|
+
* `addSchema(rootSchema, rootSchemaId)` flow.
|
|
193
|
+
*/
|
|
194
|
+
handleSchemaUpdate(rootSchema: S): void {
|
|
195
|
+
if (this.lastSeenRootSchema === rootSchema && this.hasRegisteredRootSchema) {
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
const rootSchemaId = (rootSchema[ID_KEY] as string | undefined) ?? ROOT_SCHEMA_PREFIX;
|
|
199
|
+
// Inject $id into a copy of the rootSchema so ata's schema registry can
|
|
200
|
+
// resolve `<rootSchemaId>#/...` refs produced by `withIdRefPrefix`.
|
|
201
|
+
// The original user-supplied schema is left untouched.
|
|
202
|
+
const rootWithId =
|
|
203
|
+
rootSchema[ID_KEY] === rootSchemaId
|
|
204
|
+
? (rootSchema as object)
|
|
205
|
+
: { ...(rootSchema as object), [ID_KEY]: rootSchemaId };
|
|
206
|
+
this.cachedRootSchema = rootWithId;
|
|
207
|
+
const cached = this.validators.get(rootSchemaId);
|
|
208
|
+
if (!cached || !deepEquals(cached.schema, rootWithId)) {
|
|
209
|
+
this.getOrBuild(rootSchemaId, rootWithId);
|
|
210
|
+
}
|
|
211
|
+
this.lastSeenRootSchema = rootSchema;
|
|
212
|
+
this.hasRegisteredRootSchema = true;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
/** Boolean validation entrypoint. Returns false on validation failure or
|
|
216
|
+
* compilation error. Mirrors `AJV8Validator#isValid` semantics.
|
|
217
|
+
*/
|
|
218
|
+
isValid(schema: S, formData: T | undefined, rootSchema: S) {
|
|
219
|
+
try {
|
|
220
|
+
this.handleSchemaUpdate(rootSchema);
|
|
221
|
+
const schemaWithIdRefPrefix = withIdRefPrefix<S>(schema) as S;
|
|
222
|
+
const id = (schemaWithIdRefPrefix[ID_KEY] as string | undefined) ?? hashForSchema(schemaWithIdRefPrefix);
|
|
223
|
+
const validator = this.getOrBuild(id, schemaWithIdRefPrefix);
|
|
224
|
+
return validator.validate(this.cloneForValidation(formData)).valid;
|
|
225
|
+
} catch (e) {
|
|
226
|
+
// eslint-disable-next-line no-console
|
|
227
|
+
console.warn('Error encountered compiling schema:', e);
|
|
228
|
+
return false;
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
}
|