@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.
Files changed (53) hide show
  1. package/README.md +94 -0
  2. package/dist/compileSchemaValidators.cjs +110 -0
  3. package/dist/compileSchemaValidators.cjs.map +7 -0
  4. package/dist/compileSchemaValidators.esm.js +79 -0
  5. package/dist/compileSchemaValidators.esm.js.map +7 -0
  6. package/dist/index.cjs +461 -0
  7. package/dist/index.cjs.map +7 -0
  8. package/dist/validator-ata.esm.js +450 -0
  9. package/dist/validator-ata.esm.js.map +7 -0
  10. package/dist/validator-ata.umd.js +422 -0
  11. package/lib/compileSchemaValidators.d.ts +16 -0
  12. package/lib/compileSchemaValidators.js +21 -0
  13. package/lib/compileSchemaValidators.js.map +1 -0
  14. package/lib/compileSchemaValidatorsCode.d.ts +13 -0
  15. package/lib/compileSchemaValidatorsCode.js +80 -0
  16. package/lib/compileSchemaValidatorsCode.js.map +1 -0
  17. package/lib/createAtaInstance.d.ts +27 -0
  18. package/lib/createAtaInstance.js +68 -0
  19. package/lib/createAtaInstance.js.map +1 -0
  20. package/lib/createPrecompiledValidator.d.ts +15 -0
  21. package/lib/createPrecompiledValidator.js +17 -0
  22. package/lib/createPrecompiledValidator.js.map +1 -0
  23. package/lib/customizeValidator.d.ts +8 -0
  24. package/lib/customizeValidator.js +9 -0
  25. package/lib/customizeValidator.js.map +1 -0
  26. package/lib/index.d.ts +7 -0
  27. package/lib/index.js +7 -0
  28. package/lib/index.js.map +1 -0
  29. package/lib/precompiledValidator.d.ts +89 -0
  30. package/lib/precompiledValidator.js +107 -0
  31. package/lib/precompiledValidator.js.map +1 -0
  32. package/lib/processRawValidationErrors.d.ts +28 -0
  33. package/lib/processRawValidationErrors.js +137 -0
  34. package/lib/processRawValidationErrors.js.map +1 -0
  35. package/lib/tsconfig.tsbuildinfo +1 -0
  36. package/lib/types.d.ts +63 -0
  37. package/lib/types.js +2 -0
  38. package/lib/types.js.map +1 -0
  39. package/lib/validator.d.ts +85 -0
  40. package/lib/validator.js +154 -0
  41. package/lib/validator.js.map +1 -0
  42. package/package.json +113 -0
  43. package/src/compileSchemaValidators.ts +30 -0
  44. package/src/compileSchemaValidatorsCode.ts +92 -0
  45. package/src/createAtaInstance.ts +81 -0
  46. package/src/createPrecompiledValidator.ts +29 -0
  47. package/src/customizeValidator.ts +16 -0
  48. package/src/index.ts +8 -0
  49. package/src/precompiledValidator.ts +188 -0
  50. package/src/processRawValidationErrors.ts +197 -0
  51. package/src/tsconfig.json +15 -0
  52. package/src/types.ts +71 -0
  53. package/src/validator.ts +231 -0
@@ -0,0 +1,30 @@
1
+ import { RJSFSchema, StrictRJSFSchema } from '@rjsf/utils';
2
+ import fs from 'fs';
3
+
4
+ import { compileSchemaValidatorsCode } from './compileSchemaValidatorsCode';
5
+ import { CustomValidatorOptionsType } from './types';
6
+
7
+ export { compileSchemaValidatorsCode };
8
+
9
+ /** The function used to compile a schema into an output file in the form that allows it to be used as a precompiled
10
+ * validator. The main reasons for using a precompiled validator is reducing code size, improving validation speed and,
11
+ * most importantly, avoiding dynamic code compilation when prohibited by a browser's Content Security Policy. For more
12
+ * information about ata standalone compilation see the ata-validator documentation.
13
+ *
14
+ * @param schema - The schema to be compiled into a set of precompiled validators functions
15
+ * @param output - The name of the file into which the precompiled validator functions will be generated
16
+ * @param [options={}] - The set of `CustomValidatorOptionsType` information used to alter the ata validator used for
17
+ * compiling the schema. They are the same options that are passed to the `customizeValidator()` function in
18
+ * order to modify the behavior of the regular ata-based validator.
19
+ */
20
+ export default function compileSchemaValidators<S extends StrictRJSFSchema = RJSFSchema>(
21
+ schema: S,
22
+ output: string,
23
+ options: CustomValidatorOptionsType = {},
24
+ ) {
25
+ console.log('parsing the schema');
26
+
27
+ const moduleCode = compileSchemaValidatorsCode(schema, options);
28
+ console.log(`writing ${output}`);
29
+ fs.writeFileSync(output, moduleCode);
30
+ }
@@ -0,0 +1,92 @@
1
+ import { RJSFSchema, StrictRJSFSchema, schemaParser } from '@rjsf/utils';
2
+ import { Validator } from 'ata-validator';
3
+
4
+ import { COLOR_FORMAT_REGEX, DATA_URL_FORMAT_REGEX } from './createAtaInstance';
5
+ import { CustomValidatorOptionsType } from './types';
6
+
7
+ /** Compiles a schema into a precompiled validator module. ata's
8
+ * `bundleStandalone` emits `module.exports = [fn, ...]`, one validator per
9
+ * schema in order, where each `fn(data)` returns `{ valid, errors }`. This wraps
10
+ * that array into the `{ [id]: (data) => boolean }`-with-`errors` map that
11
+ * `createPrecompiledValidator` consumes, keyed the same way `schemaParser` keys
12
+ * the schemas so the precompiled validator can look each one up.
13
+ *
14
+ * @param schema - The schema to compile
15
+ * @param [options={}] - The `CustomValidatorOptionsType` used to build the validator
16
+ */
17
+ export function compileSchemaValidatorsCode<S extends StrictRJSFSchema = RJSFSchema>(
18
+ schema: S,
19
+ options: CustomValidatorOptionsType = {},
20
+ ) {
21
+ const schemaMaps = schemaParser(schema);
22
+ const keys = Object.keys(schemaMaps);
23
+ const schemas = Object.values(schemaMaps);
24
+
25
+ const { customFormats, ataOptionsOverrides = {} } = options;
26
+
27
+ // Format checkers are serialized into the standalone module via Function#toString,
28
+ // so they must carry no closure references or build-time instrumentation (a plain
29
+ // arrow over the source file picks up coverage counters). Building each from its
30
+ // regex via `new Function` produces a function whose source is exactly its body.
31
+ const regexFormats: Record<string, RegExp> = {
32
+ color: COLOR_FORMAT_REGEX,
33
+ 'data-url': DATA_URL_FORMAT_REGEX,
34
+ };
35
+ if (customFormats) {
36
+ for (const [name, check] of Object.entries(customFormats)) {
37
+ if (typeof check === 'function') {
38
+ // A function checker would be serialized via toString, which is unsafe in a
39
+ // precompiled bundle: transpilers, minifiers, and coverage tools rewrite the
40
+ // body and leave references that do not exist in the generated module. Only
41
+ // RegExp/string patterns can be embedded reliably here.
42
+ throw new Error(
43
+ `@rjsf/validator-ata: custom format "${name}" is a function, which is not supported by the precompiled validator; provide a RegExp or string pattern instead`,
44
+ );
45
+ }
46
+ regexFormats[name] = typeof check === 'string' ? new RegExp(check) : check;
47
+ }
48
+ }
49
+ const formats: Record<string, (value: string) => boolean> = {};
50
+ for (const [name, re] of Object.entries(regexFormats)) {
51
+ // `new Function` rather than a fat-arrow on purpose: ata serializes these format
52
+ // checkers into the standalone bundle via Function#toString. A fat-arrow would
53
+ // close over `re` and emit `re.test(value)`, leaving `re` undefined in the
54
+ // generated module. Building it from a string inlines the regex literal into the
55
+ // body, so the bundled output stays self-contained.
56
+ // eslint-disable-next-line no-new-func
57
+ formats[name] = new Function('value', `return ${re.toString()}.test(value)`) as (value: string) => boolean;
58
+ }
59
+
60
+ const bundle = Validator.bundleStandalone(schemas, {
61
+ format: 'cjs',
62
+ verbose: ataOptionsOverrides.verbose !== false,
63
+ formats,
64
+ });
65
+
66
+ const entries = keys
67
+ .map((key, index) => ` ${JSON.stringify(key)}: wrap(validators[${index}], ${JSON.stringify(key)}),`)
68
+ .join('\n');
69
+
70
+ return [
71
+ 'const validators = (function () {',
72
+ ' const module = { exports: {} };',
73
+ bundle,
74
+ ' return module.exports;',
75
+ '})();',
76
+ 'function wrap(fn, key) {',
77
+ ' function validate(data) {',
78
+ " if (typeof fn !== 'function') {",
79
+ " throw new Error('@rjsf/validator-ata: schema \"' + key + '\" was not compiled to a standalone validator');",
80
+ ' }',
81
+ ' const result = fn(data);',
82
+ ' validate.errors = result.valid ? null : result.errors;',
83
+ ' return result.valid;',
84
+ ' }',
85
+ ' return validate;',
86
+ '}',
87
+ 'module.exports = {',
88
+ entries,
89
+ '};',
90
+ '',
91
+ ].join('\n');
92
+ }
@@ -0,0 +1,81 @@
1
+ import { Validator, type ValidatorOptions } from 'ata-validator';
2
+ import isObject from 'lodash/isObject';
3
+
4
+ import type { AtaFormatChecker, CustomValidatorOptionsType } from './types';
5
+
6
+ /** Default options applied to every ata `Validator` constructed for RJSF.
7
+ * `verbose: true` keeps `parentSchema` available on errors, which the RJSF
8
+ * error transformer uses to recover field titles. The remainder match
9
+ * `@rjsf/validator-ajv8`'s defaults (`allErrors`-equivalent behavior is
10
+ * ata's default).
11
+ */
12
+ export const ATA_CONFIG: ValidatorOptions = {
13
+ verbose: true,
14
+ } as const;
15
+
16
+ /** Color names + hex + `rgb()` regex source. Mirrors the pattern shipped by
17
+ * `@rjsf/validator-ajv8` so RJSF schemas using `format: 'color'` keep
18
+ * working when swapped to validator-ata.
19
+ */
20
+ export const COLOR_FORMAT_REGEX =
21
+ /^(#?([0-9A-Fa-f]{3}){1,2}\b|aqua|black|blue|fuchsia|gray|green|lime|maroon|navy|olive|orange|purple|red|silver|teal|white|yellow|(rgb\(\s*\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b\s*,\s*\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b\s*,\s*\b([0-9]|[1-9][0-9]|1[0-9][0-9]|2[0-4][0-9]|25[0-5])\b\s*\))|(rgb\(\s*(\d?\d%|100%)+\s*,\s*(\d?\d%|100%)+\s*,\s*(\d?\d%|100%)+\s*\)))$/;
22
+
23
+ /** RFC 2397 data URL with required mime type and base64 marker, matching
24
+ * the shape used by `@rjsf/validator-ajv8`.
25
+ */
26
+ export const DATA_URL_FORMAT_REGEX = /^data:([a-z]+\/[a-z0-9-+.]+)?;(?:name=(.*);)?base64,(.*)$/;
27
+
28
+ /** Coerces the user-supplied custom format value into the `(value: string)
29
+ * => boolean` shape expected by ata. Strings are treated as RegExp source
30
+ * (anchored at both ends if not already), RegExps are converted to a `.test`
31
+ * call, functions pass through.
32
+ */
33
+ function asFormatChecker(spec: string | RegExp | AtaFormatChecker): AtaFormatChecker {
34
+ if (typeof spec === 'function') {
35
+ return spec;
36
+ }
37
+ const re = spec instanceof RegExp ? spec : new RegExp(spec);
38
+ return (value: string) => re.test(value);
39
+ }
40
+
41
+ /** Builds a fresh `ata-validator` `Validator` for a given schema with all
42
+ * RJSF-aware defaults and user customizations applied.
43
+ *
44
+ * The factory exists so that the validator class can construct one
45
+ * `Validator` per schema-id (ata is schema-bound, unlike AJV's single
46
+ * instance with a schema registry) while keeping the option translation
47
+ * in one place.
48
+ */
49
+ export default function createAtaInstance(schema: object, options: CustomValidatorOptionsType = {}): Validator {
50
+ const { customFormats, ataOptionsOverrides, additionalMetaSchemas, extenderFn } = options;
51
+
52
+ const formats: Record<string, AtaFormatChecker> = {
53
+ color: asFormatChecker(COLOR_FORMAT_REGEX),
54
+ 'data-url': asFormatChecker(DATA_URL_FORMAT_REGEX),
55
+ };
56
+ if (isObject(customFormats)) {
57
+ for (const [name, spec] of Object.entries(customFormats!)) {
58
+ formats[name] = asFormatChecker(spec);
59
+ }
60
+ }
61
+
62
+ const validatorOptions: ValidatorOptions = {
63
+ ...ATA_CONFIG,
64
+ ...ataOptionsOverrides,
65
+ formats,
66
+ };
67
+
68
+ let validator = new Validator(schema, validatorOptions);
69
+
70
+ if (Array.isArray(additionalMetaSchemas)) {
71
+ for (const meta of additionalMetaSchemas) {
72
+ validator.addSchema(meta);
73
+ }
74
+ }
75
+
76
+ if (typeof extenderFn === 'function') {
77
+ validator = extenderFn(validator);
78
+ }
79
+
80
+ return validator;
81
+ }
@@ -0,0 +1,29 @@
1
+ import { FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '@rjsf/utils';
2
+
3
+ import ATAPrecompiledValidator from './precompiledValidator';
4
+ import { Localizer, SuppressDuplicateFilteringType, ValidatorFunctions } from './types';
5
+
6
+ /** Creates and returns a `ValidatorType` interface that is implemented with a precompiled validator. If a `localizer`
7
+ * is provided, it is used to translate the messages generated by the underlying ata validation.
8
+ *
9
+ * NOTE: The `validateFns` parameter is an object obtained by importing from a precompiled validation file created via
10
+ * the `compileSchemaValidators()` function.
11
+ *
12
+ * @param validateFns - The map of the validation functions that are created by the `compileSchemaValidators()` function
13
+ * @param rootSchema - The root schema that was used with the `compileSchemaValidators()` function
14
+ * @param [localizer] - If provided, is used to localize a list of ata `ValidationError`s
15
+ * @param [suppressDuplicateFiltering] - Controls which duplicate filtering is suppressed; see `filterDuplicateErrors`
16
+ * @returns - The precompiled validator implementation resulting from the set of parameters provided
17
+ */
18
+ export default function createPrecompiledValidator<
19
+ T = any,
20
+ S extends StrictRJSFSchema = RJSFSchema,
21
+ F extends FormContextType = any,
22
+ >(
23
+ validateFns: ValidatorFunctions,
24
+ rootSchema: S,
25
+ localizer?: Localizer,
26
+ suppressDuplicateFiltering?: SuppressDuplicateFilteringType,
27
+ ): ValidatorType<T, S, F> {
28
+ return new ATAPrecompiledValidator<T, S, F>(validateFns, rootSchema, localizer, suppressDuplicateFiltering);
29
+ }
@@ -0,0 +1,16 @@
1
+ import { FormContextType, RJSFSchema, StrictRJSFSchema } from '@rjsf/utils';
2
+
3
+ import type { CustomValidatorOptionsType, Localizer } from './types';
4
+ import ATAValidator from './validator';
5
+
6
+ /** Build an `ATAValidator` instance, optionally customized with format
7
+ * checkers, validator overrides, an extender hook, or a localizer. Mirrors
8
+ * `@rjsf/validator-ajv8`'s `customizeValidator`.
9
+ */
10
+ export default function customizeValidator<
11
+ T = any,
12
+ S extends StrictRJSFSchema = RJSFSchema,
13
+ F extends FormContextType = any,
14
+ >(options: CustomValidatorOptionsType = {}, localizer?: Localizer) {
15
+ return new ATAValidator<T, S, F>(options, localizer);
16
+ }
package/src/index.ts ADDED
@@ -0,0 +1,8 @@
1
+ import createPrecompiledValidator from './createPrecompiledValidator';
2
+ import customizeValidator from './customizeValidator';
3
+
4
+ export { customizeValidator, createPrecompiledValidator };
5
+ export { default as ATAValidator } from './validator';
6
+ export * from './types';
7
+
8
+ export default customizeValidator();
@@ -0,0 +1,188 @@
1
+ import {
2
+ CustomValidator,
3
+ deepEquals,
4
+ ErrorTransformer,
5
+ FormContextType,
6
+ hashForSchema,
7
+ ID_KEY,
8
+ JUNK_OPTION_ID,
9
+ retrieveSchema,
10
+ RJSFSchema,
11
+ StrictRJSFSchema,
12
+ UiSchema,
13
+ ValidationData,
14
+ ValidatorType,
15
+ } from '@rjsf/utils';
16
+ import type { ValidationError } from 'ata-validator';
17
+ import get from 'lodash/get';
18
+
19
+ import processRawValidationErrors, { RawValidationErrorsType } from './processRawValidationErrors';
20
+ import { CompiledValidateFunction, Localizer, SuppressDuplicateFilteringType, ValidatorFunctions } from './types';
21
+
22
+ /** `ValidatorType` implementation that uses an ata precompiled validator as created by the
23
+ * `compileSchemaValidators()` function provided by the `@rjsf/validator-ata` library.
24
+ */
25
+ export default class ATAPrecompiledValidator<
26
+ T = any,
27
+ S extends StrictRJSFSchema = RJSFSchema,
28
+ F extends FormContextType = any,
29
+ > implements ValidatorType<T, S, F> {
30
+ /** The root schema object used to construct this validator
31
+ *
32
+ * @private
33
+ */
34
+ readonly rootSchema: S;
35
+
36
+ /** The `ValidatorFunctions` map used to construct this validator
37
+ *
38
+ * @private
39
+ */
40
+ readonly validateFns: ValidatorFunctions;
41
+
42
+ /** The main validator function associated with the base schema in the `precompiledValidator`
43
+ *
44
+ * @private
45
+ */
46
+ readonly mainValidator: CompiledValidateFunction;
47
+
48
+ /** The Localizer function to use for localizing ata errors
49
+ *
50
+ * @private
51
+ */
52
+ readonly localizer?: Localizer;
53
+
54
+ /** Controls which duplicate error filtering is suppressed; see `filterDuplicateErrors`
55
+ *
56
+ * @private
57
+ */
58
+ readonly suppressDuplicateFiltering?: SuppressDuplicateFilteringType;
59
+
60
+ /** Constructs an `ATAPrecompiledValidator` instance using the `validateFns` and `rootSchema`
61
+ *
62
+ * @param validateFns - The map of the validation functions that are generated by the `schemaCompile()` function
63
+ * @param rootSchema - The root schema that was used with the `compileSchema()` function
64
+ * @param [localizer] - If provided, is used to localize a list of ata `ValidationError`s
65
+ * @param [suppressDuplicateFiltering] - Controls which duplicate filtering is suppressed; see `filterDuplicateErrors`
66
+ * @throws - Error when the base schema of the precompiled validator does not have a matching validator function
67
+ */
68
+ constructor(
69
+ validateFns: ValidatorFunctions,
70
+ rootSchema: S,
71
+ localizer?: Localizer,
72
+ suppressDuplicateFiltering?: SuppressDuplicateFilteringType,
73
+ ) {
74
+ this.rootSchema = rootSchema;
75
+ this.validateFns = validateFns;
76
+ this.localizer = localizer;
77
+ this.suppressDuplicateFiltering = suppressDuplicateFiltering;
78
+ this.mainValidator = this.getValidator(rootSchema);
79
+ }
80
+
81
+ /** Returns the precompiled validator associated with the given `schema` from the map of precompiled validator
82
+ * functions.
83
+ *
84
+ * @param schema - The schema for which a precompiled validator function is desired
85
+ * @returns - The precompiled validator function associated with this schema
86
+ */
87
+ getValidator(schema: S) {
88
+ const key = get(schema, ID_KEY) || hashForSchema(schema);
89
+ const validator = this.validateFns[key];
90
+ if (!validator) {
91
+ throw new Error(`No precompiled validator function was found for the given schema for "${key}"`);
92
+ }
93
+ return validator;
94
+ }
95
+
96
+ /** Ensures that the validator is using the same schema as the root schema used to construct the precompiled
97
+ * validator. It first compares the given `schema` against the root schema and if they aren't the same, then it
98
+ * checks against the resolved root schema, on the chance that a resolved version of the root schema was passed in
99
+ * instead of the raw root schema.
100
+ *
101
+ * @param schema - The schema against which to validate the form data
102
+ * @param [formData] - The form data to validate if any
103
+ */
104
+ ensureSameRootSchema(schema: S, formData?: T) {
105
+ if (!deepEquals(schema, this.rootSchema)) {
106
+ // Resolve the root schema with the passed in form data since that may affect the resolution
107
+ const resolvedRootSchema = retrieveSchema(this, this.rootSchema, this.rootSchema, formData);
108
+ if (!deepEquals(schema, resolvedRootSchema)) {
109
+ throw new Error(
110
+ 'The schema associated with the precompiled validator differs from the rootSchema provided for validation',
111
+ );
112
+ }
113
+ }
114
+ return true;
115
+ }
116
+
117
+ /** Runs the pure validation of the `schema` and `formData` without any of the RJSF functionality. Provided for use
118
+ * by the playground. Returns the `errors` from the validation
119
+ *
120
+ * @param schema - The schema against which to validate the form data
121
+ * @param [formData] - The form data to validate, if any
122
+ * @throws - Error when the schema provided does not match the base schema of the precompiled validator
123
+ */
124
+ rawValidation<Result = any>(schema: S, formData?: T): RawValidationErrorsType<Result> {
125
+ this.ensureSameRootSchema(schema, formData);
126
+ this.mainValidator(formData);
127
+
128
+ if (typeof this.localizer === 'function') {
129
+ this.localizer(this.mainValidator.errors);
130
+ }
131
+ const errors = this.mainValidator.errors || undefined;
132
+
133
+ // Clear errors to prevent persistent errors, see #1104
134
+ this.mainValidator.errors = null;
135
+
136
+ return { errors: errors as unknown as Result[] };
137
+ }
138
+
139
+ /** This function processes the `formData` with an optional user contributed `customValidate` function, which receives
140
+ * the form data and a `errorHandler` function that will be used to add custom validation errors for each field. Also
141
+ * supports a `transformErrors` function that will take the raw ata validation errors, prior to custom validation and
142
+ * transform them in what ever way it chooses.
143
+ *
144
+ * @param formData - The form data to validate
145
+ * @param schema - The schema against which to validate the form data
146
+ * @param [customValidate] - An optional function that is used to perform custom validation
147
+ * @param [transformErrors] - An optional function that is used to transform errors after ata validation
148
+ * @param [uiSchema] - An optional uiSchema that is passed to `transformErrors` and `customValidate`
149
+ */
150
+ validateFormData(
151
+ formData: T | undefined,
152
+ schema: S,
153
+ customValidate?: CustomValidator<T, S, F>,
154
+ transformErrors?: ErrorTransformer<T, S, F>,
155
+ uiSchema?: UiSchema<T, S, F>,
156
+ ): ValidationData<T> {
157
+ const rawErrors = this.rawValidation<ValidationError>(schema, formData);
158
+ return processRawValidationErrors(
159
+ this,
160
+ rawErrors,
161
+ formData,
162
+ schema,
163
+ customValidate,
164
+ transformErrors,
165
+ uiSchema,
166
+ this.suppressDuplicateFiltering,
167
+ );
168
+ }
169
+
170
+ /** Validates data against a schema, returning true if the data is valid, or false otherwise. If the schema is
171
+ * invalid, then this function will return false.
172
+ *
173
+ * @param schema - The schema against which to validate the form data
174
+ * @param formData - The form data to validate
175
+ * @param rootSchema - The root schema used to provide $ref resolutions
176
+ * @returns - true if the formData validates against the schema, false otherwise
177
+ * @throws - Error when the schema provided does not match the base schema of the precompiled validator OR if there
178
+ * isn't a precompiled validator function associated with the schema
179
+ */
180
+ isValid(schema: S, formData: T | undefined, rootSchema: S) {
181
+ this.ensureSameRootSchema(rootSchema, formData);
182
+ if (get(schema, ID_KEY) === JUNK_OPTION_ID) {
183
+ return false;
184
+ }
185
+ const validator = this.getValidator(schema);
186
+ return validator(formData);
187
+ }
188
+ }
@@ -0,0 +1,197 @@
1
+ import {
2
+ ANY_OF_KEY,
3
+ createErrorHandler,
4
+ CustomValidator,
5
+ ErrorTransformer,
6
+ FormContextType,
7
+ getDefaultFormState,
8
+ getUiOptions,
9
+ ONE_OF_KEY,
10
+ PROPERTIES_KEY,
11
+ RJSFSchema,
12
+ RJSFValidationError,
13
+ StrictRJSFSchema,
14
+ toErrorSchema,
15
+ UiSchema,
16
+ unwrapErrorHandler,
17
+ validationDataMerge,
18
+ ValidatorType,
19
+ } from '@rjsf/utils';
20
+ import type { ValidationError } from 'ata-validator';
21
+ import get from 'lodash/get';
22
+
23
+ import type { SuppressDuplicateFilteringType } from './types';
24
+
25
+ export type RawValidationErrorsType<Result = any> = {
26
+ errors?: Result[];
27
+ validationError?: Error;
28
+ };
29
+
30
+ /** Filters duplicate errors from `anyOf`/`oneOf` schema paths according to
31
+ * the `suppressDuplicateFiltering` flag. Mirrors the `@rjsf/validator-ajv8`
32
+ * implementation: under any non-`'all'` setting, duplicate messages that
33
+ * share a common prefix before the `/anyOf/` or `/oneOf/` segment are
34
+ * collapsed into a single entry.
35
+ */
36
+ export function filterDuplicateErrors(
37
+ errorList: RJSFValidationError[],
38
+ suppressDuplicateFiltering: SuppressDuplicateFilteringType = 'none',
39
+ ): RJSFValidationError[] {
40
+ if (suppressDuplicateFiltering === 'all') {
41
+ return errorList;
42
+ }
43
+ return errorList.reduce((acc: RJSFValidationError[], err: RJSFValidationError) => {
44
+ const { message, schemaPath } = err;
45
+ const anyOfIndex = suppressDuplicateFiltering !== 'anyOf' ? schemaPath?.indexOf(`/${ANY_OF_KEY}/`) : undefined;
46
+ const oneOfIndex = suppressDuplicateFiltering !== 'oneOf' ? schemaPath?.indexOf(`/${ONE_OF_KEY}/`) : undefined;
47
+ let schemaPrefix: string | undefined;
48
+ if (anyOfIndex && anyOfIndex >= 0) {
49
+ schemaPrefix = schemaPath?.substring(0, anyOfIndex);
50
+ } else if (oneOfIndex && oneOfIndex >= 0) {
51
+ schemaPrefix = schemaPath?.substring(0, oneOfIndex);
52
+ }
53
+ const dup = schemaPrefix
54
+ ? acc.find((e: RJSFValidationError) => e.message === message && e.schemaPath?.startsWith(schemaPrefix))
55
+ : undefined;
56
+ if (!dup) {
57
+ acc.push(err);
58
+ }
59
+ return acc;
60
+ }, [] as RJSFValidationError[]);
61
+ }
62
+
63
+ /** Transforms ata-validator errors into the RJSF-internal `RJSFValidationError`
64
+ * shape. ata's error objects already use the same field names AJV does
65
+ * (`instancePath`, `keyword`, `params`, `schemaPath`, `parentSchema`,
66
+ * `message`), so the conversion is structural only.
67
+ */
68
+ export function transformRJSFValidationErrors<
69
+ T = any,
70
+ S extends StrictRJSFSchema = RJSFSchema,
71
+ F extends FormContextType = any,
72
+ >(
73
+ errors: ValidationError[] = [],
74
+ uiSchema?: UiSchema<T, S, F>,
75
+ suppressDuplicateFiltering?: SuppressDuplicateFilteringType,
76
+ ): RJSFValidationError[] {
77
+ const errorList = errors.map((e: ValidationError) => {
78
+ const { instancePath, keyword, params, schemaPath, parentSchema } = e;
79
+ let { message = '' } = e;
80
+ let property = instancePath.replace(/\//g, '.');
81
+ let stack = `${property} ${message}`.trim();
82
+ let uiTitle = '';
83
+
84
+ const p = params as Record<string, any>;
85
+ const rawPropertyNames: string[] = [
86
+ ...((p?.deps as string | undefined)?.split(', ') || []),
87
+ p?.missingProperty,
88
+ p?.property,
89
+ ].filter((item) => Boolean(item));
90
+
91
+ if (rawPropertyNames.length > 0) {
92
+ rawPropertyNames.forEach((currentProperty) => {
93
+ const path = property ? `${property}.${currentProperty}` : currentProperty;
94
+ let uiSchemaTitle = getUiOptions(get(uiSchema, `${path.replace(/^\./, '')}`)).title;
95
+ if (uiSchemaTitle === undefined) {
96
+ const uiSchemaPath = schemaPath
97
+ .replace(/\/properties\//g, '/')
98
+ .split('/')
99
+ .slice(1, -1)
100
+ .concat([currentProperty]);
101
+ uiSchemaTitle = getUiOptions(get(uiSchema, uiSchemaPath)).title;
102
+ }
103
+ if (uiSchemaTitle) {
104
+ message = message.replace(`'${currentProperty}'`, `'${uiSchemaTitle}'`);
105
+ uiTitle = uiSchemaTitle;
106
+ } else {
107
+ const parentSchemaTitle = get(parentSchema, [PROPERTIES_KEY, currentProperty, 'title']);
108
+ if (parentSchemaTitle) {
109
+ message = message.replace(`'${currentProperty}'`, `'${parentSchemaTitle}'`);
110
+ uiTitle = parentSchemaTitle;
111
+ }
112
+ }
113
+ });
114
+
115
+ stack = message;
116
+ } else {
117
+ const uiSchemaTitle = getUiOptions<T, S, F>(get(uiSchema, `${property.replace(/^\./, '')}`)).title;
118
+
119
+ if (uiSchemaTitle) {
120
+ stack = `'${uiSchemaTitle}' ${message}`.trim();
121
+ uiTitle = uiSchemaTitle;
122
+ } else {
123
+ const parentSchemaTitle = (parentSchema as { title?: string } | undefined)?.title;
124
+
125
+ if (parentSchemaTitle) {
126
+ stack = `'${parentSchemaTitle}' ${message}`.trim();
127
+ uiTitle = parentSchemaTitle;
128
+ }
129
+ }
130
+ }
131
+
132
+ if (p && 'missingProperty' in p) {
133
+ property = property ? `${property}.${p.missingProperty}` : p.missingProperty;
134
+ }
135
+
136
+ return {
137
+ name: keyword,
138
+ property,
139
+ message,
140
+ params,
141
+ stack,
142
+ schemaPath,
143
+ title: uiTitle,
144
+ };
145
+ });
146
+ return filterDuplicateErrors(errorList, suppressDuplicateFiltering);
147
+ }
148
+
149
+ /** Processes raw ata validation errors into the `ValidationData<T>` shape
150
+ * RJSF consumes. Mirrors the AJV-validator's `processRawValidationErrors`,
151
+ * including the optional `customValidate` and `transformErrors` hooks.
152
+ */
153
+ export default function processRawValidationErrors<
154
+ T = any,
155
+ S extends StrictRJSFSchema = RJSFSchema,
156
+ F extends FormContextType = any,
157
+ >(
158
+ validator: ValidatorType<T, S, F>,
159
+ rawErrors: RawValidationErrorsType<ValidationError>,
160
+ formData: T | undefined,
161
+ schema: S,
162
+ customValidate?: CustomValidator<T, S, F>,
163
+ transformErrors?: ErrorTransformer<T, S, F>,
164
+ uiSchema?: UiSchema<T, S, F>,
165
+ suppressDuplicateFiltering?: SuppressDuplicateFilteringType,
166
+ ) {
167
+ const { validationError: invalidSchemaError } = rawErrors;
168
+ let errors = transformRJSFValidationErrors<T, S, F>(rawErrors.errors, uiSchema, suppressDuplicateFiltering);
169
+
170
+ if (invalidSchemaError) {
171
+ errors = [...errors, { stack: invalidSchemaError!.message }];
172
+ }
173
+ if (typeof transformErrors === 'function') {
174
+ errors = transformErrors(errors, uiSchema);
175
+ }
176
+
177
+ let errorSchema = toErrorSchema<T>(errors);
178
+
179
+ if (invalidSchemaError) {
180
+ errorSchema = {
181
+ ...errorSchema,
182
+ $schema: {
183
+ __errors: [invalidSchemaError!.message],
184
+ },
185
+ };
186
+ }
187
+
188
+ if (typeof customValidate !== 'function') {
189
+ return { errors, errorSchema };
190
+ }
191
+
192
+ const newFormData = getDefaultFormState<T, S, F>(validator, schema, formData, schema, true) as T;
193
+
194
+ const errorHandler = customValidate(newFormData, createErrorHandler<T>(newFormData), uiSchema, errorSchema);
195
+ const userErrorSchema = unwrapErrorHandler<T>(errorHandler);
196
+ return validationDataMerge<T>({ errors, errorSchema }, userErrorSchema);
197
+ }
@@ -0,0 +1,15 @@
1
+ {
2
+ "extends": "../../../tsconfig.base.json",
3
+ "include": ["./"],
4
+ "compilerOptions": {
5
+ "rootDir": "./",
6
+ "outDir": "../lib",
7
+ "jsx": "react-jsx",
8
+ "types": ["node"]
9
+ },
10
+ "references": [
11
+ {
12
+ "path": "../../utils"
13
+ }
14
+ ]
15
+ }