@rjsf/utils 6.5.3 → 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/dist/index.cjs +618 -433
- package/dist/index.cjs.map +4 -4
- package/dist/utils.esm.js +614 -429
- package/dist/utils.esm.js.map +4 -4
- package/dist/utils.umd.js +623 -438
- package/lib/ErrorSchemaBuilder.js.map +1 -1
- package/lib/canExpand.js.map +1 -1
- package/lib/constIsAjvDataReference.js +1 -1
- package/lib/constIsAjvDataReference.js.map +1 -1
- package/lib/createSchemaUtils.js +3 -3
- package/lib/createSchemaUtils.js.map +1 -1
- package/lib/enumOptionSelectedValue.js.map +1 -1
- package/lib/enumOptionValueDecoder.js.map +1 -1
- package/lib/enumOptionsDeselectValue.js +1 -1
- package/lib/enumOptionsDeselectValue.js.map +1 -1
- package/lib/enumOptionsIndexForValue.js.map +1 -1
- package/lib/enumOptionsIsSelected.js.map +1 -1
- package/lib/enumOptionsSelectValue.js +1 -1
- package/lib/enumOptionsSelectValue.js.map +1 -1
- package/lib/enumOptionsValueForIndex.js +2 -2
- package/lib/enumOptionsValueForIndex.js.map +1 -1
- package/lib/findSchemaDefinition.js +4 -4
- package/lib/findSchemaDefinition.js.map +1 -1
- package/lib/getChangedFields.js +3 -3
- package/lib/getChangedFields.js.map +1 -1
- package/lib/getDiscriminatorFieldFromSchema.js.map +1 -1
- package/lib/getOptionMatchingSimpleDiscriminator.js.map +1 -1
- package/lib/getTestIds.d.ts +1 -1
- package/lib/getTestIds.js +4 -3
- package/lib/getTestIds.js.map +1 -1
- package/lib/getWidget.js +1 -1
- package/lib/getWidget.js.map +1 -1
- package/lib/idGenerators.js.map +1 -1
- package/lib/index.d.ts +6 -6
- package/lib/index.js +6 -6
- package/lib/index.js.map +1 -1
- package/lib/isFormDataAvailable.js +1 -1
- package/lib/isFormDataAvailable.js.map +1 -1
- package/lib/isRootSchema.js +1 -1
- package/lib/isRootSchema.js.map +1 -1
- package/lib/mergeDefaultsWithFormData.js +1 -1
- package/lib/mergeDefaultsWithFormData.js.map +1 -1
- package/lib/parser/ParserValidator.js +1 -1
- package/lib/parser/ParserValidator.js.map +1 -1
- package/lib/parser/index.d.ts +1 -1
- package/lib/parser/index.js.map +1 -1
- package/lib/parser/schemaParser.js +2 -2
- package/lib/parser/schemaParser.js.map +1 -1
- package/lib/removeOptionalEmptyObjects.d.ts +2 -0
- package/lib/removeOptionalEmptyObjects.js +8 -29
- package/lib/removeOptionalEmptyObjects.js.map +1 -1
- package/lib/schema/findFieldInSchema.js +1 -1
- package/lib/schema/findFieldInSchema.js.map +1 -1
- package/lib/schema/findSelectedOptionInXxxOf.js +1 -1
- package/lib/schema/findSelectedOptionInXxxOf.js.map +1 -1
- package/lib/schema/getClosestMatchingOption.js +3 -3
- package/lib/schema/getClosestMatchingOption.js.map +1 -1
- package/lib/schema/getDefaultFormState.d.ts +4 -4
- package/lib/schema/getDefaultFormState.js +17 -22
- package/lib/schema/getDefaultFormState.js.map +1 -1
- package/lib/schema/getFromSchema.js +1 -1
- package/lib/schema/getFromSchema.js.map +1 -1
- package/lib/schema/index.d.ts +4 -4
- package/lib/schema/index.js +4 -4
- package/lib/schema/index.js.map +1 -1
- package/lib/schema/isMultiSelect.js.map +1 -1
- package/lib/schema/omitExtraData.d.ts +18 -8
- package/lib/schema/omitExtraData.js +352 -16
- package/lib/schema/omitExtraData.js.map +1 -1
- package/lib/schema/retrieveSchema.d.ts +15 -0
- package/lib/schema/retrieveSchema.js +36 -13
- package/lib/schema/retrieveSchema.js.map +1 -1
- package/lib/schema/sanitizeDataForNewSchema.js.map +1 -1
- package/lib/schema/shallowAllOfMerge.d.ts +11 -0
- package/lib/schema/shallowAllOfMerge.js +18 -0
- package/lib/schema/shallowAllOfMerge.js.map +1 -0
- package/lib/schema/toPathSchema.d.ts +1 -0
- package/lib/schema/toPathSchema.js +2 -1
- package/lib/schema/toPathSchema.js.map +1 -1
- package/lib/shouldRenderOptionalField.js +1 -1
- package/lib/shouldRenderOptionalField.js.map +1 -1
- package/lib/toErrorSchema.js.map +1 -1
- package/lib/tsconfig.tsbuildinfo +1 -1
- package/lib/types.d.ts +4 -1
- package/lib/useFileWidgetProps.d.ts +1 -1
- package/lib/useFileWidgetProps.js +9 -10
- package/lib/useFileWidgetProps.js.map +1 -1
- package/lib/withIdRefPrefix.js +1 -1
- package/lib/withIdRefPrefix.js.map +1 -1
- package/package.json +10 -10
- package/src/ErrorSchemaBuilder.ts +1 -1
- package/src/canExpand.ts +1 -1
- package/src/constIsAjvDataReference.ts +3 -2
- package/src/createSchemaUtils.ts +18 -17
- package/src/enumOptionSelectedValue.ts +1 -1
- package/src/enumOptionValueDecoder.ts +1 -1
- package/src/enumOptionsDeselectValue.ts +2 -2
- package/src/enumOptionsIndexForValue.ts +1 -1
- package/src/enumOptionsIsSelected.ts +1 -1
- package/src/enumOptionsSelectValue.ts +3 -2
- package/src/enumOptionsValueForIndex.ts +2 -2
- package/src/findSchemaDefinition.ts +4 -4
- package/src/getChangedFields.ts +4 -3
- package/src/getDiscriminatorFieldFromSchema.ts +1 -1
- package/src/getOptionMatchingSimpleDiscriminator.ts +1 -0
- package/src/getTestIds.ts +4 -3
- package/src/getWidget.tsx +2 -2
- package/src/idGenerators.ts +1 -1
- package/src/index.ts +6 -6
- package/src/isFormDataAvailable.ts +1 -1
- package/src/isRootSchema.ts +1 -1
- package/src/mergeDefaultsWithFormData.ts +2 -2
- package/src/parser/ParserValidator.ts +1 -1
- package/src/parser/index.ts +1 -1
- package/src/parser/schemaParser.ts +3 -3
- package/src/removeOptionalEmptyObjects.ts +8 -30
- package/src/schema/findFieldInSchema.ts +2 -2
- package/src/schema/findSelectedOptionInXxxOf.ts +1 -1
- package/src/schema/getClosestMatchingOption.ts +4 -4
- package/src/schema/getDefaultFormState.ts +18 -24
- package/src/schema/getFromSchema.ts +2 -2
- package/src/schema/index.ts +5 -3
- package/src/schema/isMultiSelect.ts +0 -1
- package/src/schema/omitExtraData.ts +398 -19
- package/src/schema/retrieveSchema.ts +41 -15
- package/src/schema/sanitizeDataForNewSchema.ts +1 -1
- package/src/schema/shallowAllOfMerge.ts +19 -0
- package/src/schema/toPathSchema.ts +2 -1
- package/src/shouldRenderOptionalField.ts +2 -2
- package/src/toErrorSchema.ts +1 -1
- package/src/tsconfig.json +0 -1
- package/src/types.ts +4 -1
- package/src/useFileWidgetProps.ts +9 -10
- package/src/withIdRefPrefix.ts +2 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { JSONSchema7Object } from 'json-schema';
|
|
1
2
|
import get from 'lodash/get';
|
|
2
3
|
import isEmpty from 'lodash/isEmpty';
|
|
3
|
-
import { JSONSchema7Object } from 'json-schema';
|
|
4
4
|
|
|
5
5
|
import {
|
|
6
6
|
ALL_OF_KEY,
|
|
@@ -13,15 +13,18 @@ import {
|
|
|
13
13
|
PROPERTIES_KEY,
|
|
14
14
|
REF_KEY,
|
|
15
15
|
} from '../constants';
|
|
16
|
+
import constIsAjvDataReference from '../constIsAjvDataReference';
|
|
17
|
+
import deepEquals from '../deepEquals';
|
|
16
18
|
import findSchemaDefinition from '../findSchemaDefinition';
|
|
17
|
-
import getClosestMatchingOption from './getClosestMatchingOption';
|
|
18
19
|
import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema';
|
|
19
20
|
import getSchemaType from '../getSchemaType';
|
|
20
|
-
import
|
|
21
|
+
import isConstant from '../isConstant';
|
|
21
22
|
import isFixedItems from '../isFixedItems';
|
|
23
|
+
import isObject from '../isObject';
|
|
22
24
|
import mergeDefaultsWithFormData from '../mergeDefaultsWithFormData';
|
|
23
25
|
import mergeObjects from '../mergeObjects';
|
|
24
26
|
import mergeSchemas from '../mergeSchemas';
|
|
27
|
+
import optionsList from '../optionsList';
|
|
25
28
|
import {
|
|
26
29
|
Experimental_CustomMergeAllOf,
|
|
27
30
|
Experimental_DefaultFormStateBehavior,
|
|
@@ -31,13 +34,10 @@ import {
|
|
|
31
34
|
StrictRJSFSchema,
|
|
32
35
|
ValidatorType,
|
|
33
36
|
} from '../types';
|
|
37
|
+
import getClosestMatchingOption from './getClosestMatchingOption';
|
|
34
38
|
import isMultiSelect from './isMultiSelect';
|
|
35
39
|
import isSelect from './isSelect';
|
|
36
40
|
import retrieveSchema, { resolveDependencies } from './retrieveSchema';
|
|
37
|
-
import isConstant from '../isConstant';
|
|
38
|
-
import constIsAjvDataReference from '../constIsAjvDataReference';
|
|
39
|
-
import optionsList from '../optionsList';
|
|
40
|
-
import deepEquals from '../deepEquals';
|
|
41
41
|
|
|
42
42
|
const PRIMITIVE_TYPES = ['string', 'number', 'integer', 'boolean', 'null'];
|
|
43
43
|
|
|
@@ -218,7 +218,7 @@ interface ComputeDefaultsProps<T = any, S extends StrictRJSFSchema = RJSFSchema>
|
|
|
218
218
|
*
|
|
219
219
|
* @param validator - an implementation of the `ValidatorType` interface that will be used when necessary
|
|
220
220
|
* @param rawSchema - The schema for which the default state is desired
|
|
221
|
-
* @param
|
|
221
|
+
* @param computeDefaultsProps - Optional props for this function
|
|
222
222
|
* @returns - The resulting `formData` with all the defaults provided
|
|
223
223
|
*/
|
|
224
224
|
export function computeDefaults<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
|
|
@@ -465,7 +465,7 @@ export function ensureFormDataMatchingSchema<
|
|
|
465
465
|
*
|
|
466
466
|
* @param validator - an implementation of the `ValidatorType` interface that will be used when necessary
|
|
467
467
|
* @param rawSchema - The schema for which the default state is desired
|
|
468
|
-
* @param
|
|
468
|
+
* @param computeDefaultsProps - Optional props for this function
|
|
469
469
|
* @param defaults - Optional props for this function
|
|
470
470
|
* @returns - The default value based on the schema type if they are defined for object or array schemas.
|
|
471
471
|
*/
|
|
@@ -594,7 +594,7 @@ export function getObjectDefaults<T = any, S extends StrictRJSFSchema = RJSFSche
|
|
|
594
594
|
*
|
|
595
595
|
* @param validator - an implementation of the `ValidatorType` interface that will be used when necessary
|
|
596
596
|
* @param rawSchema - The schema for which the default state is desired
|
|
597
|
-
* @param
|
|
597
|
+
* @param computeDefaultsProps - Optional props for this function
|
|
598
598
|
* @param defaults - Optional props for this function
|
|
599
599
|
* @returns - The default value based on the schema type if they are defined for object or array schemas.
|
|
600
600
|
*/
|
|
@@ -673,19 +673,13 @@ export function getArrayDefaults<T = any, S extends StrictRJSFSchema = RJSFSchem
|
|
|
673
673
|
}
|
|
674
674
|
}
|
|
675
675
|
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
}
|
|
684
|
-
if (ignoreMinItemsFlagSet && !required) {
|
|
685
|
-
// If no form data exists or defaults are set leave the field empty/non-existent, otherwise
|
|
686
|
-
// return form data/defaults
|
|
687
|
-
return defaults ? defaults : undefined;
|
|
688
|
-
}
|
|
676
|
+
if (neverPopulate) {
|
|
677
|
+
return defaults ?? emptyDefault;
|
|
678
|
+
}
|
|
679
|
+
if (ignoreMinItemsFlagSet && !required) {
|
|
680
|
+
// If no form data exists or defaults are set leave the field empty/non-existent, otherwise
|
|
681
|
+
// return form data/defaults
|
|
682
|
+
return defaults ? defaults : undefined;
|
|
689
683
|
}
|
|
690
684
|
|
|
691
685
|
let arrayDefault: T[] | undefined;
|
|
@@ -726,7 +720,7 @@ export function getArrayDefaults<T = any, S extends StrictRJSFSchema = RJSFSchem
|
|
|
726
720
|
*
|
|
727
721
|
* @param validator - an implementation of the `ValidatorType` interface that will be used when necessary
|
|
728
722
|
* @param rawSchema - The schema for which the default state is desired
|
|
729
|
-
* @param
|
|
723
|
+
* @param computeDefaultsProps - Optional props for this function
|
|
730
724
|
* @param defaults - Optional props for this function
|
|
731
725
|
* @returns - The default value based on the schema type if they are defined for object or array schemas.
|
|
732
726
|
*/
|
|
@@ -2,7 +2,7 @@ import get from 'lodash/get';
|
|
|
2
2
|
import has from 'lodash/has';
|
|
3
3
|
import isEmpty from 'lodash/isEmpty';
|
|
4
4
|
|
|
5
|
-
import
|
|
5
|
+
import { REF_KEY } from '../constants';
|
|
6
6
|
import {
|
|
7
7
|
Experimental_CustomMergeAllOf,
|
|
8
8
|
FormContextType,
|
|
@@ -11,7 +11,7 @@ import {
|
|
|
11
11
|
StrictRJSFSchema,
|
|
12
12
|
ValidatorType,
|
|
13
13
|
} from '../types';
|
|
14
|
-
import
|
|
14
|
+
import retrieveSchema from './retrieveSchema';
|
|
15
15
|
|
|
16
16
|
/** Internal helper function that acts like lodash's `get` but additionally retrieves `$ref`s as needed to get the path
|
|
17
17
|
* for schemas containing potentially nested `$ref`s.
|
package/src/schema/index.ts
CHANGED
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
import findFieldInSchema from './findFieldInSchema';
|
|
2
2
|
import findSelectedOptionInXxxOf from './findSelectedOptionInXxxOf';
|
|
3
|
+
import getClosestMatchingOption from './getClosestMatchingOption';
|
|
3
4
|
import getDefaultFormState from './getDefaultFormState';
|
|
4
5
|
import getDisplayLabel from './getDisplayLabel';
|
|
5
|
-
import getClosestMatchingOption from './getClosestMatchingOption';
|
|
6
6
|
import getFirstMatchingOption from './getFirstMatchingOption';
|
|
7
7
|
import getFromSchema from './getFromSchema';
|
|
8
8
|
import isFilesArray from './isFilesArray';
|
|
9
9
|
import isMultiSelect from './isMultiSelect';
|
|
10
10
|
import isSelect from './isSelect';
|
|
11
|
-
import omitExtraData, { getUsedFormData, getFieldNames } from './omitExtraData';
|
|
12
|
-
import retrieveSchema from './retrieveSchema';
|
|
11
|
+
import omitExtraData, { getUsedFormData, getFieldNames, isValueEmpty } from './omitExtraData';
|
|
12
|
+
import retrieveSchema, { relaxOptionsForScoring } from './retrieveSchema';
|
|
13
13
|
import sanitizeDataForNewSchema from './sanitizeDataForNewSchema';
|
|
14
14
|
import toPathSchema from './toPathSchema';
|
|
15
15
|
|
|
@@ -26,7 +26,9 @@ export {
|
|
|
26
26
|
isFilesArray,
|
|
27
27
|
isMultiSelect,
|
|
28
28
|
isSelect,
|
|
29
|
+
isValueEmpty,
|
|
29
30
|
omitExtraData,
|
|
31
|
+
relaxOptionsForScoring,
|
|
30
32
|
retrieveSchema,
|
|
31
33
|
sanitizeDataForNewSchema,
|
|
32
34
|
toPathSchema,
|
|
@@ -1,11 +1,26 @@
|
|
|
1
|
-
import pick from 'lodash/pick';
|
|
2
|
-
import isEmpty from 'lodash/isEmpty';
|
|
3
1
|
import get from 'lodash/get';
|
|
2
|
+
import isEmpty from 'lodash/isEmpty';
|
|
3
|
+
import isNil from 'lodash/isNil';
|
|
4
|
+
import pick from 'lodash/pick';
|
|
4
5
|
|
|
5
6
|
import { NAME_KEY, RJSF_ADDITIONAL_PROPERTIES_FLAG } from '../constants';
|
|
6
|
-
import
|
|
7
|
-
import
|
|
8
|
-
import
|
|
7
|
+
import findSchemaDefinition from '../findSchemaDefinition';
|
|
8
|
+
import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema';
|
|
9
|
+
import getSchemaType from '../getSchemaType';
|
|
10
|
+
import isObject from '../isObject';
|
|
11
|
+
import {
|
|
12
|
+
Experimental_CustomMergeAllOf,
|
|
13
|
+
FormContextType,
|
|
14
|
+
GenericObjectType,
|
|
15
|
+
PathSchema,
|
|
16
|
+
RJSFSchema,
|
|
17
|
+
StrictRJSFSchema,
|
|
18
|
+
ValidatorType,
|
|
19
|
+
} from '../types';
|
|
20
|
+
import getClosestMatchingOption from './getClosestMatchingOption';
|
|
21
|
+
import isSelect from './isSelect';
|
|
22
|
+
import { relaxOptionsForScoring, resolveAllReferences } from './retrieveSchema';
|
|
23
|
+
import shallowAllOfMerge from './shallowAllOfMerge';
|
|
9
24
|
|
|
10
25
|
/** Returns the `formData` with only the elements specified in the `fields` list
|
|
11
26
|
*
|
|
@@ -68,26 +83,390 @@ export function getFieldNames<T = any>(pathSchema: PathSchema<T>, formData?: T):
|
|
|
68
83
|
return getAllPaths(pathSchema);
|
|
69
84
|
}
|
|
70
85
|
|
|
71
|
-
/**
|
|
72
|
-
*
|
|
73
|
-
*
|
|
86
|
+
/** Returns true when a form value is considered empty: null/undefined/'', an empty array, or a plain
|
|
87
|
+
* object whose every own value is itself empty (recursive). Scalars like `0` and `false` are not empty.
|
|
88
|
+
*
|
|
89
|
+
* @param value - The value to check
|
|
90
|
+
* @returns - True if the value is considered empty, false otherwise
|
|
91
|
+
*/
|
|
92
|
+
export function isValueEmpty(value: unknown): boolean {
|
|
93
|
+
if (isNil(value) || value === '') {
|
|
94
|
+
return true;
|
|
95
|
+
}
|
|
96
|
+
if (Array.isArray(value)) {
|
|
97
|
+
return value.length === 0;
|
|
98
|
+
}
|
|
99
|
+
if (isObject(value)) {
|
|
100
|
+
return Object.values(value as GenericObjectType).every(isValueEmpty);
|
|
101
|
+
}
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** Merges an `allOf` schema into a single flat schema, delegating to `experimental_customMergeAllOf`
|
|
106
|
+
* when provided or falling back to the module-level `shallowAllOfMerge` otherwise.
|
|
107
|
+
*
|
|
108
|
+
* @param schema - A schema containing an `allOf` array to be merged
|
|
109
|
+
* @param [experimental_customMergeAllOf] - Optional custom merge function; see `Form` documentation
|
|
110
|
+
* @returns - The merged schema with `allOf` resolved into a single schema object
|
|
111
|
+
*/
|
|
112
|
+
function doMergeAllOf<S extends StrictRJSFSchema = RJSFSchema>(
|
|
113
|
+
schema: S,
|
|
114
|
+
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>,
|
|
115
|
+
): S {
|
|
116
|
+
return experimental_customMergeAllOf ? experimental_customMergeAllOf(schema) : (shallowAllOfMerge(schema) as S);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** A recursive, schema-driven filter that walks `schema` and `formData` in lockstep, keeping only
|
|
120
|
+
* values that are described by the schema. Handles `$ref`, `allOf`, `anyOf`, `oneOf`, `if/then/else`,
|
|
121
|
+
* `patternProperties`, `additionalProperties`, `propertyNames`, and `dependencies`. Optional object
|
|
122
|
+
* properties whose schema-filtered content is entirely empty (per `isValueEmpty`) are pruned; required
|
|
123
|
+
* properties and scalar values are always kept when schema-defined.
|
|
74
124
|
*
|
|
75
125
|
* @param validator - An implementation of the `ValidatorType` interface that will be used when necessary
|
|
76
|
-
* @param schema - The schema
|
|
77
|
-
* @param [rootSchema] - The root schema, used
|
|
126
|
+
* @param schema - The schema for which to filter the formData
|
|
127
|
+
* @param [rootSchema] - The root schema, used primarily to look up `$ref`s
|
|
78
128
|
* @param [formData] - The data for the `Form`
|
|
79
|
-
* @
|
|
129
|
+
* @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas
|
|
130
|
+
* @returns - The `formData` after omitting extra data, or `undefined` when `formData` is undefined
|
|
80
131
|
*/
|
|
81
132
|
export default function omitExtraData<
|
|
82
133
|
T = any,
|
|
83
134
|
S extends StrictRJSFSchema = RJSFSchema,
|
|
84
135
|
F extends FormContextType = any,
|
|
85
|
-
>(
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
136
|
+
>(
|
|
137
|
+
validator: ValidatorType<T, S, F>,
|
|
138
|
+
schema: S,
|
|
139
|
+
rootSchema: S = {} as S,
|
|
140
|
+
formData?: T,
|
|
141
|
+
experimental_customMergeAllOf?: Experimental_CustomMergeAllOf<S>,
|
|
142
|
+
): T | undefined {
|
|
143
|
+
/** Type predicate that narrows `value` to `GenericObjectType` — true when `value` is a plain,
|
|
144
|
+
* non-array object (i.e. a JSON object). Used to distinguish JSON objects from arrays and primitives.
|
|
145
|
+
*
|
|
146
|
+
* @param value - The value to check
|
|
147
|
+
* @returns - True if `value` is a plain non-array object
|
|
148
|
+
*/
|
|
149
|
+
function isObjectValue(value: unknown): value is GenericObjectType {
|
|
150
|
+
return isObject(value);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
/** Type predicate that narrows a `S | boolean` schema definition to `S` — true when `schemaDef` is
|
|
154
|
+
* a schema object rather than a JSON Schema boolean shorthand (`true` meaning allow-all, `false`
|
|
155
|
+
* meaning deny-all).
|
|
156
|
+
*
|
|
157
|
+
* @param schemaDef - The schema definition to check
|
|
158
|
+
* @returns - True if `schemaDef` is a schema object
|
|
159
|
+
*/
|
|
160
|
+
function isSchemaObj(schemaDef: S | boolean): schemaDef is S {
|
|
161
|
+
return isObject(schemaDef);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
/** Copies schema-defined properties from `source` into `target`, applying `omit` recursively for
|
|
165
|
+
* each value. Handles `properties`, `patternProperties`, `additionalProperties`, and `propertyNames`.
|
|
166
|
+
* Optional object-valued properties are pruned when every key in the filtered result is both
|
|
167
|
+
* optional (per the inner schema's `required`) and empty (per `isValueEmpty`). This preserves
|
|
168
|
+
* optional objects whose required children have empty values, while still dropping objects whose
|
|
169
|
+
* schema-filtered content is entirely optional-and-empty. Required properties and scalar values
|
|
170
|
+
* are always written when defined. Always returns `target` — pruning of the object itself is
|
|
171
|
+
* the caller's responsibility.
|
|
172
|
+
*
|
|
173
|
+
* @param schema - The object schema describing which properties are allowed
|
|
174
|
+
* @param source - The source form data object to read values from
|
|
175
|
+
* @param target - The accumulator object to write filtered values into
|
|
176
|
+
* @returns - `target` after all schema-defined properties have been processed
|
|
177
|
+
*/
|
|
178
|
+
function handleObject(schema: S, source: GenericObjectType, target: GenericObjectType): GenericObjectType {
|
|
179
|
+
const { properties, additionalProperties, patternProperties, propertyNames } = schema;
|
|
180
|
+
const requiredSet = new Set((schema.required ?? []) as string[]);
|
|
181
|
+
|
|
182
|
+
/** Recursively omits extra data from `value` via `omit`, then conditionally writes the result to
|
|
183
|
+
* `target[key]`. Optional object-valued properties are dropped when every key in the filtered
|
|
184
|
+
* result is both optional (within the inner schema's `required` list) and has an empty value per
|
|
185
|
+
* `isValueEmpty`. This per-key approach prevents required-but-empty child properties — kept by
|
|
186
|
+
* inner `setProperty` calls — from inadvertently causing the parent optional object to be dropped,
|
|
187
|
+
* while still pruning optional objects whose schema-filtered content is entirely empty. Vacuously
|
|
188
|
+
* true for `{}`, so empty results are always dropped. Scalar and array values are not pruned here.
|
|
189
|
+
*
|
|
190
|
+
* @param key - The property key to write on `target`
|
|
191
|
+
* @param schemaDef - The schema (or boolean shorthand) that governs the value at `key`
|
|
192
|
+
* @param value - The raw source value to filter
|
|
193
|
+
* @param [required=false] - When true the property is never pruned regardless of its filtered value
|
|
194
|
+
*/
|
|
195
|
+
function setProperty(key: string, schemaDef: S | boolean, value: unknown, required = false) {
|
|
196
|
+
const v = omit(schemaDef, value, target[key]);
|
|
197
|
+
if (!required && isObject(v)) {
|
|
198
|
+
// Resolve $ref so we can inspect the effective required list for the inner schema.
|
|
199
|
+
let sd = isSchemaObj(schemaDef as S | boolean) ? (schemaDef as S) : ({} as S);
|
|
200
|
+
if (sd.$ref !== undefined) {
|
|
201
|
+
sd = findSchemaDefinition(sd.$ref, rootSchema) as S;
|
|
202
|
+
}
|
|
203
|
+
const innerRequired = new Set((sd.required ?? []) as string[]);
|
|
204
|
+
// Drop this optional object when every key in v is both optional in the inner schema
|
|
205
|
+
// and has an empty value. Vacuously true for {} so empty objects are always dropped.
|
|
206
|
+
const shouldDrop = Object.entries(v as GenericObjectType).every(
|
|
207
|
+
([k, val]) => !innerRequired.has(k) && isValueEmpty(val),
|
|
208
|
+
);
|
|
209
|
+
if (shouldDrop) {
|
|
210
|
+
return;
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (v !== undefined) {
|
|
214
|
+
target[key] = v;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (properties !== undefined) {
|
|
219
|
+
for (const [key, schemaDef] of Object.entries(properties)) {
|
|
220
|
+
setProperty(key, schemaDef as S | boolean, source[key], requiredSet.has(key));
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Track keys not handled by properties/patterns so additionalProperties can pick them up.
|
|
225
|
+
let patternPropertiesRest: string[] | undefined;
|
|
226
|
+
if (patternProperties !== undefined) {
|
|
227
|
+
patternPropertiesRest = [];
|
|
228
|
+
const patterns = Object.entries(patternProperties).map(([pattern, schemaDef]): [RegExp, S | boolean] => [
|
|
229
|
+
new RegExp(pattern),
|
|
230
|
+
schemaDef as S | boolean,
|
|
231
|
+
]);
|
|
232
|
+
const knownProperties = new Set(Object.keys(properties ?? {}));
|
|
233
|
+
for (const [key, value] of Object.entries(source)) {
|
|
234
|
+
if (knownProperties.has(key)) {
|
|
235
|
+
continue;
|
|
236
|
+
}
|
|
237
|
+
const matched = patterns.find(([re]) => re.test(key));
|
|
238
|
+
if (matched === undefined) {
|
|
239
|
+
patternPropertiesRest.push(key);
|
|
240
|
+
continue;
|
|
241
|
+
}
|
|
242
|
+
setProperty(key, matched[1], value);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// JSON Schema spec: absent additionalProperties defaults to true (allow all extra keys). Here
|
|
247
|
+
// we treat it as false so omitExtraData never inadvertently passes through undescribed keys.
|
|
248
|
+
if (additionalProperties !== undefined && additionalProperties !== false) {
|
|
249
|
+
const addlSchema = additionalProperties as S | boolean;
|
|
250
|
+
if (patternPropertiesRest !== undefined) {
|
|
251
|
+
for (const key of patternPropertiesRest) {
|
|
252
|
+
setProperty(key, addlSchema, source[key]);
|
|
253
|
+
}
|
|
254
|
+
} else {
|
|
255
|
+
const knownProperties = new Set(Object.keys(properties ?? {}));
|
|
256
|
+
for (const [key, value] of Object.entries(source)) {
|
|
257
|
+
if (knownProperties.has(key)) {
|
|
258
|
+
continue;
|
|
259
|
+
}
|
|
260
|
+
setProperty(key, addlSchema, value);
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
// When propertyNames is present, the schema only constrains key names — all source keys are valid.
|
|
266
|
+
if (propertyNames !== undefined) {
|
|
267
|
+
for (const [key, value] of Object.entries(source)) {
|
|
268
|
+
target[key] = value;
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
return target;
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
/** Filters array elements from `source` into `target` according to `schema.items` and
|
|
276
|
+
* `schema.additionalItems`. For tuple schemas (`items` is an array) each element is filtered by its
|
|
277
|
+
* per-index schema; elements beyond the tuple length are covered by `additionalItems` when present.
|
|
278
|
+
* For list schemas (`items` is a single schema) every element is filtered by that schema.
|
|
279
|
+
*
|
|
280
|
+
* @param schema - The array schema describing `items` and optionally `additionalItems`
|
|
281
|
+
* @param source - The source array to read elements from
|
|
282
|
+
* @param target - The accumulator array to push filtered elements into
|
|
283
|
+
* @returns - `target` after all applicable source elements have been pushed
|
|
284
|
+
*/
|
|
285
|
+
function handleArray(schema: S, source: unknown[], target: unknown[]): unknown[] {
|
|
286
|
+
const { items, additionalItems } = schema;
|
|
287
|
+
if (items !== undefined) {
|
|
288
|
+
if (Array.isArray(items)) {
|
|
289
|
+
for (let i = 0; i < items.length; i++) {
|
|
290
|
+
target.push(omit(items[i] as S | boolean, source[i]));
|
|
291
|
+
}
|
|
292
|
+
} else {
|
|
293
|
+
for (let i = 0; i < source.length; i++) {
|
|
294
|
+
target.push(omit(items as S | boolean, source[i]));
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
// additionalItems covers tuple items beyond the items array length.
|
|
299
|
+
if (additionalItems) {
|
|
300
|
+
for (let i = target.length; i < source.length; i++) {
|
|
301
|
+
target.push(omit(additionalItems as S | boolean, source[i]));
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return target;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
/** Applies the `if/then/else` conditional keywords from `schema` to `target`. When `schema.if` is
|
|
308
|
+
* absent the original `target` is returned unchanged. Otherwise the condition is evaluated — using
|
|
309
|
+
* `validator.isValid` for schema conditions or the boolean value directly — and the matching branch
|
|
310
|
+
* (`then` or `else`) is applied via `omit`. When the selected branch is absent, `target` is returned
|
|
311
|
+
* unchanged.
|
|
312
|
+
*
|
|
313
|
+
* @param schema - The schema potentially containing `if`, `then`, and `else` keywords
|
|
314
|
+
* @param source - The current form data value, passed to `validator.isValid` and the branch `omit`
|
|
315
|
+
* @param target - The already-filtered value to merge the branch result into
|
|
316
|
+
* @returns - The result of applying the matched branch, or `target` when no branch applies
|
|
317
|
+
*/
|
|
318
|
+
function handleConditions(schema: S, source: unknown, target: unknown): unknown {
|
|
319
|
+
const { if: condition, then, else: otherwise } = schema;
|
|
320
|
+
if (condition === undefined) {
|
|
321
|
+
return target;
|
|
322
|
+
}
|
|
323
|
+
// validator.isValid signature: (schema, formData, rootSchema)
|
|
324
|
+
const isThenBranch = isSchemaObj(condition as S | boolean)
|
|
325
|
+
? validator.isValid(condition as S, source as T, rootSchema)
|
|
326
|
+
: condition;
|
|
327
|
+
const branch = isThenBranch ? then : otherwise;
|
|
328
|
+
return branch === undefined ? target : omit(branch as S | boolean, source, target);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
/** Applies the best-matching `oneOf` option to `source`, merging the result into `target`. When
|
|
332
|
+
* `oneOf` is not an array or the schema represents a select widget (enum-driven), `target` is
|
|
333
|
+
* returned unchanged. `additionalProperties: false` is relaxed on each option before scoring so that
|
|
334
|
+
* `getClosestMatchingOption` can validate freely, but the original option schema is used for the
|
|
335
|
+
* actual `omit` call.
|
|
336
|
+
*
|
|
337
|
+
* @param oneOf - The `oneOf` array from the schema, or `undefined`
|
|
338
|
+
* @param schema - The parent schema containing the `oneOf` keyword
|
|
339
|
+
* @param source - The current form data value used to score each option
|
|
340
|
+
* @param target - The already-filtered value to merge the winning option's result into
|
|
341
|
+
* @returns - The result of applying the best-matching option, or `target` when no matching applies
|
|
342
|
+
*/
|
|
343
|
+
function handleOneOf(oneOf: S['oneOf'], schema: S, source: unknown, target: unknown): unknown {
|
|
344
|
+
if (!Array.isArray(oneOf) || isSelect(validator, schema, rootSchema, experimental_customMergeAllOf)) {
|
|
345
|
+
return target;
|
|
346
|
+
}
|
|
347
|
+
// Resolve $refs and relax additionalProperties:false → true in one pass for scoring only.
|
|
348
|
+
// The unrelaxed resolved schema is re-derived for the winning option so that omit() still
|
|
349
|
+
// respects additionalProperties:false during filtering.
|
|
350
|
+
const scoringOptions = relaxOptionsForScoring<S>(oneOf as Array<S | boolean>, true, rootSchema);
|
|
351
|
+
const bestIndex = getClosestMatchingOption<T, S, F>(
|
|
352
|
+
validator,
|
|
353
|
+
rootSchema,
|
|
354
|
+
source as T,
|
|
355
|
+
scoringOptions,
|
|
356
|
+
0,
|
|
357
|
+
getDiscriminatorFieldFromSchema<S>(schema),
|
|
358
|
+
experimental_customMergeAllOf,
|
|
359
|
+
);
|
|
360
|
+
const winning = (oneOf as Array<S | boolean>)[bestIndex];
|
|
361
|
+
// For object options, re-resolve without relaxation so additionalProperties:false is respected.
|
|
362
|
+
// For boolean options, scoringOptions already holds the converted schema (true→{}, false→{not:{}}).
|
|
363
|
+
const resolved: S = isObject(winning)
|
|
364
|
+
? resolveAllReferences<S>(winning as S, rootSchema, [])
|
|
365
|
+
: scoringOptions[bestIndex];
|
|
366
|
+
return omit(resolved, source, target);
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
/** Applies `anyOf` branches from `schema` to `source`, merging results into `target`. When `anyOf`
|
|
370
|
+
* is absent or not an array, `target` is returned unchanged. For undefined or empty sources (so that
|
|
371
|
+
* defaults can flow through all branches) every branch is applied in sequence. For non-empty sources
|
|
372
|
+
* the best-matching branch is selected via `handleOneOf`.
|
|
373
|
+
*
|
|
374
|
+
* @param schema - The schema potentially containing an `anyOf` keyword
|
|
375
|
+
* @param source - The current form data value; empty or undefined triggers all-branch application
|
|
376
|
+
* @param target - The already-filtered value to merge branch results into
|
|
377
|
+
* @returns - The result after applying the relevant `anyOf` branch(es), or `target` when inapplicable
|
|
378
|
+
*/
|
|
379
|
+
function handleAnyOf(schema: S, source: unknown, target: unknown): unknown {
|
|
380
|
+
const { anyOf } = schema;
|
|
381
|
+
if (!Array.isArray(anyOf)) {
|
|
382
|
+
return target;
|
|
383
|
+
}
|
|
384
|
+
// For undefined or empty collections, apply every branch so defaults flow through.
|
|
385
|
+
if (
|
|
386
|
+
source === undefined ||
|
|
387
|
+
(Array.isArray(source) && source.length === 0) ||
|
|
388
|
+
(isObject(source) && Object.keys(source as object).length === 0)
|
|
389
|
+
) {
|
|
390
|
+
for (const branch of anyOf as Array<S | boolean>) {
|
|
391
|
+
target = omit(branch, source, target);
|
|
392
|
+
}
|
|
393
|
+
return target;
|
|
394
|
+
}
|
|
395
|
+
return handleOneOf(anyOf, schema, source, target);
|
|
396
|
+
}
|
|
397
|
+
|
|
398
|
+
/** Applies schema-based `dependencies` from `schema` to `source`, merging each active dependency's
|
|
399
|
+
* schema into `target` via `omit`. Property dependencies (plain string arrays) are skipped — only
|
|
400
|
+
* schema dependencies are processed. A dependency is considered active when its trigger key is
|
|
401
|
+
* present on `source`.
|
|
402
|
+
*
|
|
403
|
+
* @param schema - The schema potentially containing a `dependencies` keyword
|
|
404
|
+
* @param source - The current form data value; must be a plain object for dependencies to apply
|
|
405
|
+
* @param target - The already-filtered value to merge dependency results into
|
|
406
|
+
* @returns - The result after applying all active schema dependencies, or `target` when inapplicable
|
|
407
|
+
*/
|
|
408
|
+
function handleDependencies(schema: S, source: unknown, target: unknown): unknown {
|
|
409
|
+
const { dependencies } = schema;
|
|
410
|
+
if (dependencies === undefined || !isObjectValue(source)) {
|
|
411
|
+
return target;
|
|
412
|
+
}
|
|
413
|
+
for (const [key, deps] of Object.entries(dependencies)) {
|
|
414
|
+
// Skip property dependencies (string arrays); only process schema dependencies.
|
|
415
|
+
if (!(key in source) || Array.isArray(deps)) {
|
|
416
|
+
continue;
|
|
417
|
+
}
|
|
418
|
+
target = omit(deps as S | boolean, source, target);
|
|
419
|
+
}
|
|
420
|
+
return target;
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
/** Core recursive filter. Resolves `$ref`s, merges `allOf`, then delegates to the type-specific
|
|
424
|
+
* handlers (`handleObject`, `handleArray`) and keyword handlers (`handleAnyOf`, `handleOneOf`,
|
|
425
|
+
* `handleConditions`, `handleDependencies`). Returns `undefined` when `source` is undefined or
|
|
426
|
+
* `schemaDef` is `false`; returns `source` unchanged when `schemaDef` is `true` or empty.
|
|
427
|
+
*
|
|
428
|
+
* @param schemaDef - The schema (or boolean shorthand) to filter `source` against
|
|
429
|
+
* @param source - The raw form data value to filter
|
|
430
|
+
* @param [target] - An optional accumulator carrying results from prior oneOf/anyOf processing
|
|
431
|
+
* @returns - The filtered value, or `undefined` when the schema rejects the value
|
|
432
|
+
*/
|
|
433
|
+
function omit(schemaDef: S | boolean, source: unknown, target?: unknown): unknown {
|
|
434
|
+
if (source === undefined || schemaDef === false) {
|
|
435
|
+
return undefined;
|
|
436
|
+
}
|
|
437
|
+
if (schemaDef === true || isEmpty(schemaDef as object)) {
|
|
438
|
+
return source;
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
let schema = schemaDef as S;
|
|
442
|
+
const { $ref: ref, allOf } = schema;
|
|
443
|
+
|
|
444
|
+
if (ref !== undefined) {
|
|
445
|
+
return omit(findSchemaDefinition<S>(ref, rootSchema), source, target);
|
|
446
|
+
}
|
|
447
|
+
if (allOf) {
|
|
448
|
+
schema = doMergeAllOf<S>(schema, experimental_customMergeAllOf);
|
|
449
|
+
}
|
|
450
|
+
|
|
451
|
+
target = handleAnyOf(schema, source, handleOneOf(schema.oneOf, schema, source, target));
|
|
452
|
+
|
|
453
|
+
const type = getSchemaType<S>(schema);
|
|
454
|
+
if (type === 'object') {
|
|
455
|
+
if (!isObjectValue(source)) {
|
|
456
|
+
return undefined;
|
|
457
|
+
}
|
|
458
|
+
target = handleObject(schema, source, isObjectValue(target) ? target : {});
|
|
459
|
+
} else if (type === 'array') {
|
|
460
|
+
if (!Array.isArray(source)) {
|
|
461
|
+
return undefined;
|
|
462
|
+
}
|
|
463
|
+
target = handleArray(schema, source, Array.isArray(target) ? target : []);
|
|
464
|
+
} else if (target === undefined) {
|
|
465
|
+
target = source;
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
return handleDependencies(schema, source, handleConditions(schema, source, target));
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
return omit(schema, formData) as T | undefined;
|
|
93
472
|
}
|