@rjsf/utils 6.5.2 → 6.6.0

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 (146) hide show
  1. package/dist/index.cjs +628 -441
  2. package/dist/index.cjs.map +4 -4
  3. package/dist/utils.esm.js +624 -437
  4. package/dist/utils.esm.js.map +4 -4
  5. package/dist/utils.umd.js +632 -445
  6. package/lib/ErrorSchemaBuilder.js.map +1 -1
  7. package/lib/canExpand.js.map +1 -1
  8. package/lib/constIsAjvDataReference.js +1 -1
  9. package/lib/constIsAjvDataReference.js.map +1 -1
  10. package/lib/createSchemaUtils.js +3 -3
  11. package/lib/createSchemaUtils.js.map +1 -1
  12. package/lib/enumOptionSelectedValue.js.map +1 -1
  13. package/lib/enumOptionValueDecoder.js.map +1 -1
  14. package/lib/enumOptionsDeselectValue.js +1 -1
  15. package/lib/enumOptionsDeselectValue.js.map +1 -1
  16. package/lib/enumOptionsIndexForValue.js.map +1 -1
  17. package/lib/enumOptionsIsSelected.js.map +1 -1
  18. package/lib/enumOptionsSelectValue.js +1 -1
  19. package/lib/enumOptionsSelectValue.js.map +1 -1
  20. package/lib/enumOptionsValueForIndex.js +2 -2
  21. package/lib/enumOptionsValueForIndex.js.map +1 -1
  22. package/lib/enums.d.ts +2 -0
  23. package/lib/enums.js +2 -0
  24. package/lib/enums.js.map +1 -1
  25. package/lib/findSchemaDefinition.js +4 -4
  26. package/lib/findSchemaDefinition.js.map +1 -1
  27. package/lib/getChangedFields.js +3 -3
  28. package/lib/getChangedFields.js.map +1 -1
  29. package/lib/getDiscriminatorFieldFromSchema.js.map +1 -1
  30. package/lib/getOptionMatchingSimpleDiscriminator.js.map +1 -1
  31. package/lib/getTestIds.d.ts +1 -1
  32. package/lib/getTestIds.js +4 -3
  33. package/lib/getTestIds.js.map +1 -1
  34. package/lib/getWidget.js +1 -1
  35. package/lib/getWidget.js.map +1 -1
  36. package/lib/idGenerators.js.map +1 -1
  37. package/lib/index.d.ts +6 -6
  38. package/lib/index.js +6 -6
  39. package/lib/index.js.map +1 -1
  40. package/lib/isFormDataAvailable.js +1 -1
  41. package/lib/isFormDataAvailable.js.map +1 -1
  42. package/lib/isRootSchema.js +1 -1
  43. package/lib/isRootSchema.js.map +1 -1
  44. package/lib/jsonSchemaAugmentation.d.ts +19 -0
  45. package/lib/jsonSchemaAugmentation.js +2 -0
  46. package/lib/jsonSchemaAugmentation.js.map +1 -0
  47. package/lib/mergeDefaultsWithFormData.js +2 -2
  48. package/lib/mergeDefaultsWithFormData.js.map +1 -1
  49. package/lib/parser/ParserValidator.js +1 -1
  50. package/lib/parser/ParserValidator.js.map +1 -1
  51. package/lib/parser/index.d.ts +1 -1
  52. package/lib/parser/index.js.map +1 -1
  53. package/lib/parser/schemaParser.js +2 -2
  54. package/lib/parser/schemaParser.js.map +1 -1
  55. package/lib/removeOptionalEmptyObjects.d.ts +2 -0
  56. package/lib/removeOptionalEmptyObjects.js +8 -29
  57. package/lib/removeOptionalEmptyObjects.js.map +1 -1
  58. package/lib/schema/findFieldInSchema.d.ts +2 -2
  59. package/lib/schema/findFieldInSchema.js +7 -6
  60. package/lib/schema/findFieldInSchema.js.map +1 -1
  61. package/lib/schema/findSelectedOptionInXxxOf.js +1 -1
  62. package/lib/schema/findSelectedOptionInXxxOf.js.map +1 -1
  63. package/lib/schema/getClosestMatchingOption.js +3 -3
  64. package/lib/schema/getClosestMatchingOption.js.map +1 -1
  65. package/lib/schema/getDefaultFormState.d.ts +4 -4
  66. package/lib/schema/getDefaultFormState.js +17 -22
  67. package/lib/schema/getDefaultFormState.js.map +1 -1
  68. package/lib/schema/getFromSchema.d.ts +3 -3
  69. package/lib/schema/getFromSchema.js +3 -3
  70. package/lib/schema/getFromSchema.js.map +1 -1
  71. package/lib/schema/index.d.ts +4 -4
  72. package/lib/schema/index.js +4 -4
  73. package/lib/schema/index.js.map +1 -1
  74. package/lib/schema/isMultiSelect.js.map +1 -1
  75. package/lib/schema/omitExtraData.d.ts +18 -8
  76. package/lib/schema/omitExtraData.js +352 -16
  77. package/lib/schema/omitExtraData.js.map +1 -1
  78. package/lib/schema/retrieveSchema.d.ts +15 -0
  79. package/lib/schema/retrieveSchema.js +36 -13
  80. package/lib/schema/retrieveSchema.js.map +1 -1
  81. package/lib/schema/sanitizeDataForNewSchema.js.map +1 -1
  82. package/lib/schema/shallowAllOfMerge.d.ts +11 -0
  83. package/lib/schema/shallowAllOfMerge.js +18 -0
  84. package/lib/schema/shallowAllOfMerge.js.map +1 -0
  85. package/lib/schema/toPathSchema.d.ts +1 -0
  86. package/lib/schema/toPathSchema.js +2 -1
  87. package/lib/schema/toPathSchema.js.map +1 -1
  88. package/lib/shouldRenderOptionalField.js +1 -1
  89. package/lib/shouldRenderOptionalField.js.map +1 -1
  90. package/lib/toErrorSchema.js.map +1 -1
  91. package/lib/tsconfig.tsbuildinfo +1 -1
  92. package/lib/types.d.ts +17 -5
  93. package/lib/types.js +1 -1
  94. package/lib/types.js.map +1 -1
  95. package/lib/useFileWidgetProps.d.ts +1 -1
  96. package/lib/useFileWidgetProps.js +9 -10
  97. package/lib/useFileWidgetProps.js.map +1 -1
  98. package/lib/withIdRefPrefix.js +1 -1
  99. package/lib/withIdRefPrefix.js.map +1 -1
  100. package/package.json +11 -11
  101. package/src/ErrorSchemaBuilder.ts +1 -1
  102. package/src/canExpand.ts +1 -1
  103. package/src/constIsAjvDataReference.ts +3 -2
  104. package/src/createSchemaUtils.ts +22 -20
  105. package/src/enumOptionSelectedValue.ts +1 -1
  106. package/src/enumOptionValueDecoder.ts +1 -1
  107. package/src/enumOptionsDeselectValue.ts +2 -2
  108. package/src/enumOptionsIndexForValue.ts +1 -1
  109. package/src/enumOptionsIsSelected.ts +1 -1
  110. package/src/enumOptionsSelectValue.ts +3 -2
  111. package/src/enumOptionsValueForIndex.ts +2 -2
  112. package/src/enums.ts +2 -0
  113. package/src/findSchemaDefinition.ts +4 -4
  114. package/src/getChangedFields.ts +4 -3
  115. package/src/getDiscriminatorFieldFromSchema.ts +1 -1
  116. package/src/getOptionMatchingSimpleDiscriminator.ts +1 -0
  117. package/src/getTestIds.ts +4 -3
  118. package/src/getWidget.tsx +2 -2
  119. package/src/idGenerators.ts +1 -1
  120. package/src/index.ts +6 -6
  121. package/src/isFormDataAvailable.ts +1 -1
  122. package/src/isRootSchema.ts +1 -1
  123. package/src/jsonSchemaAugmentation.ts +21 -0
  124. package/src/mergeDefaultsWithFormData.ts +4 -3
  125. package/src/parser/ParserValidator.ts +1 -1
  126. package/src/parser/index.ts +1 -1
  127. package/src/parser/schemaParser.ts +3 -3
  128. package/src/removeOptionalEmptyObjects.ts +8 -30
  129. package/src/schema/findFieldInSchema.ts +10 -8
  130. package/src/schema/findSelectedOptionInXxxOf.ts +1 -1
  131. package/src/schema/getClosestMatchingOption.ts +4 -4
  132. package/src/schema/getDefaultFormState.ts +18 -24
  133. package/src/schema/getFromSchema.ts +15 -8
  134. package/src/schema/index.ts +5 -3
  135. package/src/schema/isMultiSelect.ts +0 -1
  136. package/src/schema/omitExtraData.ts +398 -19
  137. package/src/schema/retrieveSchema.ts +41 -15
  138. package/src/schema/sanitizeDataForNewSchema.ts +1 -1
  139. package/src/schema/shallowAllOfMerge.ts +19 -0
  140. package/src/schema/toPathSchema.ts +2 -1
  141. package/src/shouldRenderOptionalField.ts +2 -2
  142. package/src/toErrorSchema.ts +1 -1
  143. package/src/tsconfig.json +0 -1
  144. package/src/types.ts +18 -5
  145. package/src/useFileWidgetProps.ts +9 -10
  146. package/src/withIdRefPrefix.ts +2 -1
@@ -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 { GenericObjectType, PathSchema, FormContextType, RJSFSchema, StrictRJSFSchema, ValidatorType } from '../types';
7
- import retrieveSchema from './retrieveSchema';
8
- import toPathSchema from './toPathSchema';
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
- /** Takes a `schema` and `formData` and returns a copy of the formData with any fields not defined in the schema removed.
72
- * This is useful for ensuring that only data that is relevant to the schema is preserved. Objects with
73
- * `additionalProperties` keyword set to `true` will not have their extra fields removed.
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 to use for filtering the formData
77
- * @param [rootSchema] - The root schema, used to primarily to look up `$ref`s
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
- * @returns The `formData` after omitting extra data
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
- >(validator: ValidatorType<T, S, F>, schema: S, rootSchema: S = {} as S, formData?: T): T | undefined {
86
- const retrievedSchema = retrieveSchema(validator, schema, rootSchema, formData);
87
- const pathSchema = toPathSchema(validator, retrievedSchema, '', rootSchema, formData);
88
- const fieldNames = getFieldNames(pathSchema, formData);
89
- const lodashFieldNames = fieldNames.map((fieldPaths: string[]) =>
90
- Array.isArray(fieldPaths) ? fieldPaths.join('.') : fieldPaths,
91
- );
92
- return getUsedFormData(formData, lodashFieldNames);
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
  }
@@ -1,13 +1,11 @@
1
+ import flattenDeep from 'lodash/flattenDeep';
1
2
  import get from 'lodash/get';
3
+ import isEmpty from 'lodash/isEmpty';
4
+ import merge from 'lodash/merge';
2
5
  import set from 'lodash/set';
3
6
  import times from 'lodash/times';
4
7
  import transform from 'lodash/transform';
5
- import merge from 'lodash/merge';
6
- import flattenDeep from 'lodash/flattenDeep';
7
8
  import uniq from 'lodash/uniq';
8
- import isEmpty from 'lodash/isEmpty';
9
- import { createComparator, createMerger, createShallowAllOfMerge } from '@x0k/json-schema-merge';
10
- import { createDeduplicator, createIntersector } from '@x0k/json-schema-merge/lib/array';
11
9
 
12
10
  import {
13
11
  ADDITIONAL_PROPERTIES_KEY,
@@ -24,6 +22,7 @@ import {
24
22
  REF_KEY,
25
23
  RJSF_REF_KEY,
26
24
  } from '../constants';
25
+ import deepEquals from '../deepEquals';
27
26
  import findSchemaDefinition, { splitKeyElementFromObject } from '../findSchemaDefinition';
28
27
  import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema';
29
28
  import guessType from '../guessType';
@@ -38,7 +37,7 @@ import {
38
37
  ValidatorType,
39
38
  } from '../types';
40
39
  import getFirstMatchingOption from './getFirstMatchingOption';
41
- import deepEquals from '../deepEquals';
40
+ import shallowAllOfMerge from './shallowAllOfMerge';
42
41
 
43
42
  /** Retrieves an expanded schema that has had all of its conditions, additional properties, references and dependencies
44
43
  * resolved and merged into the `schema` given a `validator`, `rootSchema` and `rawFormData` that is used to do the
@@ -514,15 +513,6 @@ export function stubExistingAdditionalProperties<
514
513
  return schema;
515
514
  }
516
515
 
517
- // Set up @x0k/json-schema-merge utilities
518
- const { compareSchemaDefinitions, compareSchemaValues } = createComparator();
519
- const { mergeArrayOfSchemaDefinitions } = createMerger({
520
- intersectJson: createIntersector(compareSchemaValues),
521
- deduplicateJsonSchemaDef: createDeduplicator(compareSchemaDefinitions),
522
- });
523
-
524
- const shallowAllOfMerge = createShallowAllOfMerge(mergeArrayOfSchemaDefinitions);
525
-
526
516
  /**
527
517
  * Internal helper that merges allOf schemas using @x0k/json-schema-merge's shallow allOf merge
528
518
  * @param schema - The schema containing an `allOf` keyword
@@ -694,6 +684,14 @@ export function resolveAnyOrOneOfSchemas<
694
684
  // Call this to trigger the set of isValid() calls that the schema parser will need
695
685
  const option = getFirstMatchingOption<T, S, F>(validator, formData, anyOrOneOf, rootSchema, discriminator);
696
686
  if (expandAllBranches) {
687
+ // Also trigger isValid() for the relaxed variants so that precompiled validators capture their hashes.
688
+ // omitExtraData's handleOneOf relaxes additionalProperties:false → true before scoring; those mutated
689
+ // schemas must be present in a precompiled validator's compiled set or isValid() will throw at runtime.
690
+ // Using getFirstMatchingOption (rather than calling isValid directly) ensures that the augmented forms
691
+ // of each option (as constructed internally by getFirstMatchingOption for options with properties) are
692
+ // also captured. The return value is discarded — the call is purely for ParserValidator's side effect.
693
+ const relaxed = relaxOptionsForScoring<S>(anyOrOneOf, false, rootSchema);
694
+ getFirstMatchingOption<T, S, F>(validator, formData, relaxed, rootSchema, discriminator);
697
695
  return anyOrOneOf.map((item) => mergeSchemas(remaining, item) as S);
698
696
  }
699
697
  schema = mergeSchemas(remaining, anyOrOneOf[option]) as S;
@@ -701,6 +699,34 @@ export function resolveAnyOrOneOfSchemas<
701
699
  return [schema];
702
700
  }
703
701
 
702
+ /** Normalises a list of `oneOf`/`anyOf` options for use in option-scoring only (not for filtering).
703
+ * Boolean schemas are converted to their object equivalents (`true` → `{}`, `false` → `{not:{}}`).
704
+ * When `resolveRefs` is `true`, each object option is first passed through `resolveAllReferences`
705
+ * so that `$ref`-based options expose their `additionalProperties` constraint before relaxation.
706
+ * Any option whose `additionalProperties` is `false` is widened to `true` so that
707
+ * `getClosestMatchingOption` / `validator.isValid()` does not produce false negatives when the
708
+ * form data contains keys not listed in `properties`.
709
+ *
710
+ * @param options - The raw `oneOf`/`anyOf` array, which may contain boolean schemas
711
+ * @param [resolveRefs=false] - When `true`, resolve `$ref`s in each option before relaxing; pass
712
+ * `rootSchema` as well. Set `false` (default) when refs are already resolved at the call site.
713
+ * @param [rootSchema] - Required when `resolveRefs` is `true`; the root schema used to look up `$ref`s
714
+ * @returns - A new array of plain schema objects with `additionalProperties` relaxed where needed
715
+ */
716
+ export function relaxOptionsForScoring<S extends StrictRJSFSchema = RJSFSchema>(
717
+ options: Array<S | boolean>,
718
+ resolveRefs = false,
719
+ rootSchema?: S,
720
+ ): S[] {
721
+ return options.map((d) => {
722
+ if (!isObject(d)) {
723
+ return (d ? {} : { not: {} }) as S;
724
+ }
725
+ const schema = resolveRefs && rootSchema ? resolveAllReferences<S>(d as S, rootSchema, []) : (d as S);
726
+ return schema.additionalProperties === false ? { ...schema, additionalProperties: true } : schema;
727
+ });
728
+ }
729
+
704
730
  /** Resolves dependencies within a schema and its 'anyOf/oneOf' children. Passes the `expandAllBranches` flag down to
705
731
  * the `resolveAnyOrOneOfSchema()` and `processDependencies()` helper calls.
706
732
  *
@@ -1,6 +1,7 @@
1
1
  import get from 'lodash/get';
2
2
  import has from 'lodash/has';
3
3
 
4
+ import { PROPERTIES_KEY, REF_KEY } from '../constants';
4
5
  import {
5
6
  Experimental_CustomMergeAllOf,
6
7
  FormContextType,
@@ -9,7 +10,6 @@ import {
9
10
  StrictRJSFSchema,
10
11
  ValidatorType,
11
12
  } from '../types';
12
- import { PROPERTIES_KEY, REF_KEY } from '../constants';
13
13
  import retrieveSchema from './retrieveSchema';
14
14
 
15
15
  const NO_VALUE = Symbol('no Value');
@@ -0,0 +1,19 @@
1
+ import { createComparator, createMerger, createShallowAllOfMerge } from '@x0k/json-schema-merge';
2
+ import { createDeduplicator, createIntersector } from '@x0k/json-schema-merge/lib/array';
3
+
4
+ const { compareSchemaDefinitions, compareSchemaValues } = createComparator();
5
+ const { mergeArrayOfSchemaDefinitions } = createMerger({
6
+ intersectJson: createIntersector(compareSchemaValues),
7
+ deduplicateJsonSchemaDef: createDeduplicator(compareSchemaDefinitions),
8
+ });
9
+
10
+ /** Shared `@x0k/json-schema-merge` shallow-allOf merge function used by `retrieveSchema` and
11
+ * `omitExtraData`. Constructed once from a single comparator/merger/intersector/deduplicator
12
+ * pipeline so both consumers share the same configuration without duplicating setup code.
13
+ *
14
+ * Usage: pass a schema that contains an `allOf` keyword and receive the merged result.
15
+ *
16
+ * @example
17
+ * const merged = shallowAllOfMerge(schema); // schema.allOf is merged into the parent schema
18
+ */
19
+ export default createShallowAllOfMerge(mergeArrayOfSchemaDefinitions);
@@ -15,6 +15,7 @@ import {
15
15
  REF_KEY,
16
16
  RJSF_ADDITIONAL_PROPERTIES_FLAG,
17
17
  } from '../constants';
18
+ import deepEquals from '../deepEquals';
18
19
  import getDiscriminatorFieldFromSchema from '../getDiscriminatorFieldFromSchema';
19
20
  import {
20
21
  Experimental_CustomMergeAllOf,
@@ -27,7 +28,6 @@ import {
27
28
  } from '../types';
28
29
  import getClosestMatchingOption from './getClosestMatchingOption';
29
30
  import retrieveSchema from './retrieveSchema';
30
- import deepEquals from '../deepEquals';
31
31
 
32
32
  /** An internal helper that generates an `PathSchema` object for the `schema`, recursively with protection against
33
33
  * infinite recursion
@@ -188,6 +188,7 @@ function toPathSchemaInternal<T = any, S extends StrictRJSFSchema = RJSFSchema,
188
188
  * @param [formData] - The current formData, if any, to assist retrieving a schema
189
189
  * @param [experimental_customMergeAllOf] - Optional function that allows for custom merging of `allOf` schemas
190
190
  * @returns - The `PathSchema` object for the `schema`
191
+ * @deprecated - To be removed as an exported `@rjsf/utils` function in a future release
191
192
  */
192
193
  export default function toPathSchema<T = any, S extends StrictRJSFSchema = RJSFSchema, F extends FormContextType = any>(
193
194
  validator: ValidatorType<T, S, F>,
@@ -1,11 +1,11 @@
1
1
  import isObject from 'lodash/isObject';
2
2
  import uniq from 'lodash/uniq';
3
3
 
4
- import { FormContextType, Registry, RJSFSchema, StrictRJSFSchema, UiSchema } from './types';
4
+ import { ANY_OF_KEY, ONE_OF_KEY } from './constants';
5
5
  import getSchemaType from './getSchemaType';
6
6
  import getUiOptions from './getUiOptions';
7
7
  import isRootSchema from './isRootSchema';
8
- import { ANY_OF_KEY, ONE_OF_KEY } from './constants';
8
+ import { FormContextType, Registry, RJSFSchema, StrictRJSFSchema, UiSchema } from './types';
9
9
 
10
10
  /** Returns the unique list of schema types for all of the options in a anyOf/oneOf
11
11
  *
@@ -1,7 +1,7 @@
1
1
  import toPath from 'lodash/toPath';
2
2
 
3
- import { ErrorSchema, RJSFValidationError } from './types';
4
3
  import ErrorSchemaBuilder from './ErrorSchemaBuilder';
4
+ import { ErrorSchema, RJSFValidationError } from './types';
5
5
 
6
6
  /** Transforms a rjsf validation errors list:
7
7
  * [
package/src/tsconfig.json CHANGED
@@ -4,7 +4,6 @@
4
4
  "compilerOptions": {
5
5
  "rootDir": "./",
6
6
  "outDir": "../lib",
7
- "baseUrl": "../",
8
7
  "jsx": "react-jsx"
9
8
  }
10
9
  }