@rjsf/utils
Version:
Utility functions for @rjsf/core
1,467 lines (1,435 loc) • 111 kB
JavaScript
// src/isObject.ts
function isObject(thing) {
if (typeof thing !== "object" || thing === null) {
return false;
}
if (typeof thing.lastModified === "number" && typeof File !== "undefined" && thing instanceof File) {
return false;
}
if (typeof thing.getMonth === "function" && typeof Date !== "undefined" && thing instanceof Date) {
return false;
}
return !Array.isArray(thing);
}
// src/allowAdditionalItems.ts
function allowAdditionalItems(schema) {
if (schema.additionalItems === true) {
console.warn("additionalItems=true is currently not supported");
}
return isObject(schema.additionalItems);
}
// src/asNumber.ts
function asNumber(value) {
if (value === "") {
return void 0;
}
if (value === null) {
return null;
}
if (/\.$/.test(value)) {
return value;
}
if (/\.0$/.test(value)) {
return value;
}
if (/\.\d*0$/.test(value)) {
return value;
}
const n = Number(value);
const valid = typeof n === "number" && !Number.isNaN(n);
return valid ? n : value;
}
// src/constants.ts
var ADDITIONAL_PROPERTY_FLAG = "__additional_property";
var ADDITIONAL_PROPERTIES_KEY = "additionalProperties";
var ALL_OF_KEY = "allOf";
var ANY_OF_KEY = "anyOf";
var CONST_KEY = "const";
var DEFAULT_KEY = "default";
var DEFINITIONS_KEY = "definitions";
var DEPENDENCIES_KEY = "dependencies";
var ENUM_KEY = "enum";
var ERRORS_KEY = "__errors";
var ID_KEY = "$id";
var IF_KEY = "if";
var ITEMS_KEY = "items";
var JUNK_OPTION_ID = "_$junk_option_schema_id$_";
var NAME_KEY = "$name";
var ONE_OF_KEY = "oneOf";
var PROPERTIES_KEY = "properties";
var REQUIRED_KEY = "required";
var SUBMIT_BTN_OPTIONS_KEY = "submitButtonOptions";
var REF_KEY = "$ref";
var RJSF_ADDITONAL_PROPERTIES_FLAG = "__rjsf_additionalProperties";
var RJSF_ADDITIONAL_PROPERTIES_FLAG = "__rjsf_additionalProperties";
var ROOT_SCHEMA_PREFIX = "__rjsf_rootSchema";
var UI_FIELD_KEY = "ui:field";
var UI_WIDGET_KEY = "ui:widget";
var UI_OPTIONS_KEY = "ui:options";
var UI_GLOBAL_OPTIONS_KEY = "ui:globalOptions";
// src/getUiOptions.ts
function getUiOptions(uiSchema = {}, globalOptions = {}) {
return Object.keys(uiSchema).filter((key) => key.indexOf("ui:") === 0).reduce(
(options, key) => {
const value = uiSchema[key];
if (key === UI_WIDGET_KEY && isObject(value)) {
console.error("Setting options via ui:widget object is no longer supported, use ui:options instead");
return options;
}
if (key === UI_OPTIONS_KEY && isObject(value)) {
return { ...options, ...value };
}
return { ...options, [key.substring(3)]: value };
},
{ ...globalOptions }
);
}
// src/canExpand.ts
function canExpand(schema, uiSchema = {}, formData) {
if (!schema.additionalProperties) {
return false;
}
const { expandable = true } = getUiOptions(uiSchema);
if (expandable === false) {
return expandable;
}
if (schema.maxProperties !== void 0 && formData) {
return Object.keys(formData).length < schema.maxProperties;
}
return true;
}
// src/createErrorHandler.ts
import isPlainObject from "lodash/isPlainObject";
function createErrorHandler(formData) {
const handler = {
// We store the list of errors for this node in a property named __errors
// to avoid name collision with a possible sub schema field named
// 'errors' (see `utils.toErrorSchema`).
[ERRORS_KEY]: [],
addError(message) {
this[ERRORS_KEY].push(message);
}
};
if (Array.isArray(formData)) {
return formData.reduce((acc, value, key) => {
return { ...acc, [key]: createErrorHandler(value) };
}, handler);
}
if (isPlainObject(formData)) {
const formObject = formData;
return Object.keys(formObject).reduce((acc, key) => {
return { ...acc, [key]: createErrorHandler(formObject[key]) };
}, handler);
}
return handler;
}
// src/deepEquals.ts
import isEqualWith from "lodash/isEqualWith";
function deepEquals(a, b) {
return isEqualWith(a, b, (obj, other) => {
if (typeof obj === "function" && typeof other === "function") {
return true;
}
return void 0;
});
}
// src/schema/getDefaultFormState.ts
import get7 from "lodash/get";
import isEmpty from "lodash/isEmpty";
// src/findSchemaDefinition.ts
import jsonpointer from "jsonpointer";
import omit from "lodash/omit";
function splitKeyElementFromObject(key, object) {
const value = object[key];
const remaining = omit(object, [key]);
return [remaining, value];
}
function findSchemaDefinitionRecursive($ref, rootSchema = {}, recurseList = []) {
const ref = $ref || "";
let decodedRef;
if (ref.startsWith("#")) {
decodedRef = decodeURIComponent(ref.substring(1));
} else {
throw new Error(`Could not find a definition for ${$ref}.`);
}
const current = jsonpointer.get(rootSchema, decodedRef);
if (current === void 0) {
throw new Error(`Could not find a definition for ${$ref}.`);
}
const nextRef = current[REF_KEY];
if (nextRef) {
if (recurseList.includes(nextRef)) {
if (recurseList.length === 1) {
throw new Error(`Definition for ${$ref} is a circular reference`);
}
const [firstRef, ...restRefs] = recurseList;
const circularPath = [...restRefs, ref, firstRef].join(" -> ");
throw new Error(`Definition for ${firstRef} contains a circular reference through ${circularPath}`);
}
const [remaining, theRef] = splitKeyElementFromObject(REF_KEY, current);
const subSchema = findSchemaDefinitionRecursive(theRef, rootSchema, [...recurseList, ref]);
if (Object.keys(remaining).length > 0) {
return { ...remaining, ...subSchema };
}
return subSchema;
}
return current;
}
function findSchemaDefinition($ref, rootSchema = {}) {
const recurseList = [];
return findSchemaDefinitionRecursive($ref, rootSchema, recurseList);
}
// src/schema/getClosestMatchingOption.ts
import get5 from "lodash/get";
import has2 from "lodash/has";
import isNumber2 from "lodash/isNumber";
import isObject2 from "lodash/isObject";
import isString2 from "lodash/isString";
import reduce from "lodash/reduce";
import times2 from "lodash/times";
// src/schema/getMatchingOption.ts
import get2 from "lodash/get";
import has from "lodash/has";
import isNumber from "lodash/isNumber";
// src/getOptionMatchingSimpleDiscriminator.ts
import get from "lodash/get";
function getOptionMatchingSimpleDiscriminator(formData, options, discriminatorField) {
if (formData && discriminatorField) {
const value = get(formData, discriminatorField);
if (value === void 0) {
return;
}
for (let i = 0; i < options.length; i++) {
const option = options[i];
const discriminator = get(option, [PROPERTIES_KEY, discriminatorField], {});
if (discriminator.type === "object" || discriminator.type === "array") {
continue;
}
if (discriminator.const === value) {
return i;
}
if (discriminator.enum?.includes(value)) {
return i;
}
}
}
return;
}
// src/schema/getMatchingOption.ts
function getMatchingOption(validator, formData, options, rootSchema, discriminatorField) {
if (formData === void 0) {
return 0;
}
const simpleDiscriminatorMatch = getOptionMatchingSimpleDiscriminator(formData, options, discriminatorField);
if (isNumber(simpleDiscriminatorMatch)) {
return simpleDiscriminatorMatch;
}
for (let i = 0; i < options.length; i++) {
const option = options[i];
if (discriminatorField && has(option, [PROPERTIES_KEY, discriminatorField])) {
const value = get2(formData, discriminatorField);
const discriminator = get2(option, [PROPERTIES_KEY, discriminatorField], {});
if (validator.isValid(discriminator, value, rootSchema)) {
return i;
}
} else if (option[PROPERTIES_KEY]) {
const requiresAnyOf = {
anyOf: Object.keys(option[PROPERTIES_KEY]).map((key) => ({
required: [key]
}))
};
let augmentedSchema;
if (option.anyOf) {
const { ...shallowClone } = option;
if (!shallowClone.allOf) {
shallowClone.allOf = [];
} else {
shallowClone.allOf = shallowClone.allOf.slice();
}
shallowClone.allOf.push(requiresAnyOf);
augmentedSchema = shallowClone;
} else {
augmentedSchema = Object.assign({}, option, requiresAnyOf);
}
delete augmentedSchema.required;
if (validator.isValid(augmentedSchema, formData, rootSchema)) {
return i;
}
} else if (validator.isValid(option, formData, rootSchema)) {
return i;
}
}
return 0;
}
// src/schema/getFirstMatchingOption.ts
function getFirstMatchingOption(validator, formData, options, rootSchema, discriminatorField) {
return getMatchingOption(validator, formData, options, rootSchema, discriminatorField);
}
// src/schema/retrieveSchema.ts
import get4 from "lodash/get";
import set from "lodash/set";
import times from "lodash/times";
import transform from "lodash/transform";
import merge from "lodash/merge";
import flattenDeep from "lodash/flattenDeep";
import uniq from "lodash/uniq";
import mergeAllOf from "json-schema-merge-allof";
// src/getDiscriminatorFieldFromSchema.ts
import get3 from "lodash/get";
import isString from "lodash/isString";
function getDiscriminatorFieldFromSchema(schema) {
let discriminator;
const maybeString = get3(schema, "discriminator.propertyName", void 0);
if (isString(maybeString)) {
discriminator = maybeString;
} else if (maybeString !== void 0) {
console.warn(`Expecting discriminator to be a string, got "${typeof maybeString}" instead`);
}
return discriminator;
}
// src/guessType.ts
function guessType(value) {
if (Array.isArray(value)) {
return "array";
}
if (typeof value === "string") {
return "string";
}
if (value == null) {
return "null";
}
if (typeof value === "boolean") {
return "boolean";
}
if (!isNaN(value)) {
return "number";
}
if (typeof value === "object") {
return "object";
}
return "string";
}
// src/mergeSchemas.ts
import union from "lodash/union";
// src/getSchemaType.ts
function getSchemaType(schema) {
let { type } = schema;
if (!type && schema.const) {
return guessType(schema.const);
}
if (!type && schema.enum) {
return "string";
}
if (!type && (schema.properties || schema.additionalProperties)) {
return "object";
}
if (Array.isArray(type)) {
if (type.length === 2 && type.includes("null")) {
type = type.find((type2) => type2 !== "null");
} else {
type = type[0];
}
}
return type;
}
// src/mergeSchemas.ts
function mergeSchemas(obj1, obj2) {
const acc = Object.assign({}, obj1);
return Object.keys(obj2).reduce((acc2, key) => {
const left = obj1 ? obj1[key] : {}, right = obj2[key];
if (obj1 && key in obj1 && isObject(right)) {
acc2[key] = mergeSchemas(left, right);
} else if (obj1 && obj2 && (getSchemaType(obj1) === "object" || getSchemaType(obj2) === "object") && key === REQUIRED_KEY && Array.isArray(left) && Array.isArray(right)) {
acc2[key] = union(left, right);
} else {
acc2[key] = right;
}
return acc2;
}, acc);
}
// src/schema/retrieveSchema.ts
function retrieveSchema(validator, schema, rootSchema = {}, rawFormData, experimental_customMergeAllOf) {
return retrieveSchemaInternal(
validator,
schema,
rootSchema,
rawFormData,
void 0,
void 0,
experimental_customMergeAllOf
)[0];
}
function resolveCondition(validator, schema, rootSchema, expandAllBranches, recurseList, formData, experimental_customMergeAllOf) {
const { if: expression, then, else: otherwise, ...resolvedSchemaLessConditional } = schema;
const conditionValue = validator.isValid(expression, formData || {}, rootSchema);
let resolvedSchemas = [resolvedSchemaLessConditional];
let schemas = [];
if (expandAllBranches) {
if (then && typeof then !== "boolean") {
schemas = schemas.concat(
retrieveSchemaInternal(
validator,
then,
rootSchema,
formData,
expandAllBranches,
recurseList,
experimental_customMergeAllOf
)
);
}
if (otherwise && typeof otherwise !== "boolean") {
schemas = schemas.concat(
retrieveSchemaInternal(
validator,
otherwise,
rootSchema,
formData,
expandAllBranches,
recurseList,
experimental_customMergeAllOf
)
);
}
} else {
const conditionalSchema = conditionValue ? then : otherwise;
if (conditionalSchema && typeof conditionalSchema !== "boolean") {
schemas = schemas.concat(
retrieveSchemaInternal(
validator,
conditionalSchema,
rootSchema,
formData,
expandAllBranches,
recurseList,
experimental_customMergeAllOf
)
);
}
}
if (schemas.length) {
resolvedSchemas = schemas.map((s) => mergeSchemas(resolvedSchemaLessConditional, s));
}
return resolvedSchemas.flatMap(
(s) => retrieveSchemaInternal(
validator,
s,
rootSchema,
formData,
expandAllBranches,
recurseList,
experimental_customMergeAllOf
)
);
}
function getAllPermutationsOfXxxOf(listOfLists) {
const allPermutations = listOfLists.reduce(
(permutations, list) => {
if (list.length > 1) {
return list.flatMap((element) => times(permutations.length, (i) => [...permutations[i]].concat(element)));
}
permutations.forEach((permutation) => permutation.push(list[0]));
return permutations;
},
[[]]
// Start with an empty list
);
return allPermutations;
}
function resolveSchema(validator, schema, rootSchema, expandAllBranches, recurseList, formData, experimental_customMergeAllOf) {
const updatedSchemas = resolveReference(
validator,
schema,
rootSchema,
expandAllBranches,
recurseList,
formData
);
if (updatedSchemas.length > 1 || updatedSchemas[0] !== schema) {
return updatedSchemas;
}
if (DEPENDENCIES_KEY in schema) {
const resolvedSchemas = resolveDependencies(
validator,
schema,
rootSchema,
expandAllBranches,
recurseList,
formData
);
return resolvedSchemas.flatMap((s) => {
return retrieveSchemaInternal(
validator,
s,
rootSchema,
formData,
expandAllBranches,
recurseList,
experimental_customMergeAllOf
);
});
}
if (ALL_OF_KEY in schema && Array.isArray(schema.allOf)) {
const allOfSchemaElements = schema.allOf.map(
(allOfSubschema) => retrieveSchemaInternal(
validator,
allOfSubschema,
rootSchema,
formData,
expandAllBranches,
recurseList,
experimental_customMergeAllOf
)
);
const allPermutations = getAllPermutationsOfXxxOf(allOfSchemaElements);
return allPermutations.map((permutation) => ({
...schema,
allOf: permutation
}));
}
return [schema];
}
function resolveReference(validator, schema, rootSchema, expandAllBranches, recurseList, formData, experimental_customMergeAllOf) {
const updatedSchema = resolveAllReferences(schema, rootSchema, recurseList);
if (updatedSchema !== schema) {
return retrieveSchemaInternal(
validator,
updatedSchema,
rootSchema,
formData,
expandAllBranches,
recurseList,
experimental_customMergeAllOf
);
}
return [schema];
}
function resolveAllReferences(schema, rootSchema, recurseList) {
if (!isObject(schema)) {
return schema;
}
let resolvedSchema = schema;
if (REF_KEY in resolvedSchema) {
const { $ref, ...localSchema } = resolvedSchema;
if (recurseList.includes($ref)) {
return resolvedSchema;
}
recurseList.push($ref);
const refSchema = findSchemaDefinition($ref, rootSchema);
resolvedSchema = { ...refSchema, ...localSchema };
}
if (PROPERTIES_KEY in resolvedSchema) {
const childrenLists = [];
const updatedProps = transform(
resolvedSchema[PROPERTIES_KEY],
(result, value, key) => {
const childList = [...recurseList];
result[key] = resolveAllReferences(value, rootSchema, childList);
childrenLists.push(childList);
},
{}
);
merge(recurseList, uniq(flattenDeep(childrenLists)));
resolvedSchema = { ...resolvedSchema, [PROPERTIES_KEY]: updatedProps };
}
if (ITEMS_KEY in resolvedSchema && !Array.isArray(resolvedSchema.items) && typeof resolvedSchema.items !== "boolean") {
resolvedSchema = {
...resolvedSchema,
items: resolveAllReferences(resolvedSchema.items, rootSchema, recurseList)
};
}
return deepEquals(schema, resolvedSchema) ? schema : resolvedSchema;
}
function stubExistingAdditionalProperties(validator, theSchema, rootSchema, aFormData, experimental_customMergeAllOf) {
const schema = {
...theSchema,
properties: { ...theSchema.properties }
};
const formData = aFormData && isObject(aFormData) ? aFormData : {};
Object.keys(formData).forEach((key) => {
if (key in schema.properties) {
return;
}
let additionalProperties = {};
if (typeof schema.additionalProperties !== "boolean") {
if (REF_KEY in schema.additionalProperties) {
additionalProperties = retrieveSchema(
validator,
{ $ref: get4(schema.additionalProperties, [REF_KEY]) },
rootSchema,
formData,
experimental_customMergeAllOf
);
} else if ("type" in schema.additionalProperties) {
additionalProperties = { ...schema.additionalProperties };
} else if (ANY_OF_KEY in schema.additionalProperties || ONE_OF_KEY in schema.additionalProperties) {
additionalProperties = {
type: "object",
...schema.additionalProperties
};
} else {
additionalProperties = { type: guessType(get4(formData, [key])) };
}
} else {
additionalProperties = { type: guessType(get4(formData, [key])) };
}
schema.properties[key] = additionalProperties;
set(schema.properties, [key, ADDITIONAL_PROPERTY_FLAG], true);
});
return schema;
}
function retrieveSchemaInternal(validator, schema, rootSchema, rawFormData, expandAllBranches = false, recurseList = [], experimental_customMergeAllOf) {
if (!isObject(schema)) {
return [{}];
}
const resolvedSchemas = resolveSchema(
validator,
schema,
rootSchema,
expandAllBranches,
recurseList,
rawFormData,
experimental_customMergeAllOf
);
return resolvedSchemas.flatMap((s) => {
let resolvedSchema = s;
if (IF_KEY in resolvedSchema) {
return resolveCondition(
validator,
resolvedSchema,
rootSchema,
expandAllBranches,
recurseList,
rawFormData,
experimental_customMergeAllOf
);
}
if (ALL_OF_KEY in resolvedSchema) {
if (expandAllBranches) {
const { allOf, ...restOfSchema } = resolvedSchema;
return [...allOf, restOfSchema];
}
try {
const withContainsSchemas = [];
const withoutContainsSchemas = [];
resolvedSchema.allOf?.forEach((s2) => {
if (typeof s2 === "object" && s2.contains) {
withContainsSchemas.push(s2);
} else {
withoutContainsSchemas.push(s2);
}
});
if (withContainsSchemas.length) {
resolvedSchema = { ...resolvedSchema, allOf: withoutContainsSchemas };
}
resolvedSchema = experimental_customMergeAllOf ? experimental_customMergeAllOf(resolvedSchema) : mergeAllOf(resolvedSchema, {
deep: false
});
if (withContainsSchemas.length) {
resolvedSchema.allOf = withContainsSchemas;
}
} catch (e) {
console.warn("could not merge subschemas in allOf:\n", e);
const { allOf, ...resolvedSchemaWithoutAllOf } = resolvedSchema;
return resolvedSchemaWithoutAllOf;
}
}
const hasAdditionalProperties = ADDITIONAL_PROPERTIES_KEY in resolvedSchema && resolvedSchema.additionalProperties !== false;
if (hasAdditionalProperties) {
return stubExistingAdditionalProperties(
validator,
resolvedSchema,
rootSchema,
rawFormData,
experimental_customMergeAllOf
);
}
return resolvedSchema;
});
}
function resolveAnyOrOneOfSchemas(validator, schema, rootSchema, expandAllBranches, rawFormData) {
let anyOrOneOf;
const { oneOf, anyOf, ...remaining } = schema;
if (Array.isArray(oneOf)) {
anyOrOneOf = oneOf;
} else if (Array.isArray(anyOf)) {
anyOrOneOf = anyOf;
}
if (anyOrOneOf) {
const formData = rawFormData === void 0 && expandAllBranches ? {} : rawFormData;
const discriminator = getDiscriminatorFieldFromSchema(schema);
anyOrOneOf = anyOrOneOf.map((s) => {
return resolveAllReferences(s, rootSchema, []);
});
const option = getFirstMatchingOption(validator, formData, anyOrOneOf, rootSchema, discriminator);
if (expandAllBranches) {
return anyOrOneOf.map((item) => mergeSchemas(remaining, item));
}
schema = mergeSchemas(remaining, anyOrOneOf[option]);
}
return [schema];
}
function resolveDependencies(validator, schema, rootSchema, expandAllBranches, recurseList, formData, experimental_customMergeAllOf) {
const { dependencies, ...remainingSchema } = schema;
const resolvedSchemas = resolveAnyOrOneOfSchemas(
validator,
remainingSchema,
rootSchema,
expandAllBranches,
formData
);
return resolvedSchemas.flatMap(
(resolvedSchema) => processDependencies(
validator,
dependencies,
resolvedSchema,
rootSchema,
expandAllBranches,
recurseList,
formData,
experimental_customMergeAllOf
)
);
}
function processDependencies(validator, dependencies, resolvedSchema, rootSchema, expandAllBranches, recurseList, formData, experimental_customMergeAllOf) {
let schemas = [resolvedSchema];
for (const dependencyKey in dependencies) {
if (!expandAllBranches && get4(formData, [dependencyKey]) === void 0) {
continue;
}
if (resolvedSchema.properties && !(dependencyKey in resolvedSchema.properties)) {
continue;
}
const [remainingDependencies, dependencyValue] = splitKeyElementFromObject(
dependencyKey,
dependencies
);
if (Array.isArray(dependencyValue)) {
schemas[0] = withDependentProperties(resolvedSchema, dependencyValue);
} else if (isObject(dependencyValue)) {
schemas = withDependentSchema(
validator,
resolvedSchema,
rootSchema,
dependencyKey,
dependencyValue,
expandAllBranches,
recurseList,
formData,
experimental_customMergeAllOf
);
}
return schemas.flatMap(
(schema) => processDependencies(
validator,
remainingDependencies,
schema,
rootSchema,
expandAllBranches,
recurseList,
formData,
experimental_customMergeAllOf
)
);
}
return schemas;
}
function withDependentProperties(schema, additionallyRequired) {
if (!additionallyRequired) {
return schema;
}
const required = Array.isArray(schema.required) ? Array.from(/* @__PURE__ */ new Set([...schema.required, ...additionallyRequired])) : additionallyRequired;
return { ...schema, required };
}
function withDependentSchema(validator, schema, rootSchema, dependencyKey, dependencyValue, expandAllBranches, recurseList, formData, experimental_customMergeAllOf) {
const dependentSchemas = retrieveSchemaInternal(
validator,
dependencyValue,
rootSchema,
formData,
expandAllBranches,
recurseList,
experimental_customMergeAllOf
);
return dependentSchemas.flatMap((dependent) => {
const { oneOf, ...dependentSchema } = dependent;
schema = mergeSchemas(schema, dependentSchema);
if (oneOf === void 0) {
return schema;
}
const resolvedOneOfs = oneOf.map((subschema) => {
if (typeof subschema === "boolean" || !(REF_KEY in subschema)) {
return [subschema];
}
return resolveReference(validator, subschema, rootSchema, expandAllBranches, recurseList, formData);
});
const allPermutations = getAllPermutationsOfXxxOf(resolvedOneOfs);
return allPermutations.flatMap(
(resolvedOneOf) => withExactlyOneSubschema(
validator,
schema,
rootSchema,
dependencyKey,
resolvedOneOf,
expandAllBranches,
recurseList,
formData,
experimental_customMergeAllOf
)
);
});
}
function withExactlyOneSubschema(validator, schema, rootSchema, dependencyKey, oneOf, expandAllBranches, recurseList, formData, experimental_customMergeAllOf) {
const validSubschemas = oneOf.filter((subschema) => {
if (typeof subschema === "boolean" || !subschema || !subschema.properties) {
return false;
}
const { [dependencyKey]: conditionPropertySchema } = subschema.properties;
if (conditionPropertySchema) {
const conditionSchema = {
type: "object",
properties: {
[dependencyKey]: conditionPropertySchema
}
};
return validator.isValid(conditionSchema, formData, rootSchema) || expandAllBranches;
}
return false;
});
if (!expandAllBranches && validSubschemas.length !== 1) {
console.warn("ignoring oneOf in dependencies because there isn't exactly one subschema that is valid");
return [schema];
}
return validSubschemas.flatMap((s) => {
const subschema = s;
const [dependentSubschema] = splitKeyElementFromObject(dependencyKey, subschema.properties);
const dependentSchema = { ...subschema, properties: dependentSubschema };
const schemas = retrieveSchemaInternal(
validator,
dependentSchema,
rootSchema,
formData,
expandAllBranches,
recurseList,
experimental_customMergeAllOf
);
return schemas.map((s2) => mergeSchemas(schema, s2));
});
}
// src/schema/getClosestMatchingOption.ts
var JUNK_OPTION = {
type: "object",
$id: JUNK_OPTION_ID,
properties: {
__not_really_there__: {
type: "number"
}
}
};
function calculateIndexScore(validator, rootSchema, schema, formData, experimental_customMergeAllOf) {
let totalScore = 0;
if (schema) {
if (isObject2(schema.properties)) {
totalScore += reduce(
schema.properties,
(score, value, key) => {
const formValue = get5(formData, key);
if (typeof value === "boolean") {
return score;
}
if (has2(value, REF_KEY)) {
const newSchema = retrieveSchema(
validator,
value,
rootSchema,
formValue,
experimental_customMergeAllOf
);
return score + calculateIndexScore(
validator,
rootSchema,
newSchema,
formValue || {},
experimental_customMergeAllOf
);
}
if ((has2(value, ONE_OF_KEY) || has2(value, ANY_OF_KEY)) && formValue) {
const key2 = has2(value, ONE_OF_KEY) ? ONE_OF_KEY : ANY_OF_KEY;
const discriminator = getDiscriminatorFieldFromSchema(value);
return score + getClosestMatchingOption(
validator,
rootSchema,
formValue,
get5(value, key2),
-1,
discriminator,
experimental_customMergeAllOf
);
}
if (value.type === "object") {
if (isObject2(formValue)) {
score += 1;
}
return score + calculateIndexScore(validator, rootSchema, value, formValue, experimental_customMergeAllOf);
}
if (value.type === guessType(formValue)) {
let newScore = score + 1;
if (value.default) {
newScore += formValue === value.default ? 1 : -1;
} else if (value.const) {
newScore += formValue === value.const ? 1 : -1;
}
return newScore;
}
return score;
},
0
);
} else if (isString2(schema.type) && schema.type === guessType(formData)) {
totalScore += 1;
}
}
return totalScore;
}
function getClosestMatchingOption(validator, rootSchema, formData, options, selectedOption = -1, discriminatorField, experimental_customMergeAllOf) {
const resolvedOptions = options.map((option) => {
return resolveAllReferences(option, rootSchema, []);
});
const simpleDiscriminatorMatch = getOptionMatchingSimpleDiscriminator(formData, options, discriminatorField);
if (isNumber2(simpleDiscriminatorMatch)) {
return simpleDiscriminatorMatch;
}
const allValidIndexes = resolvedOptions.reduce((validList, option, index) => {
const testOptions = [JUNK_OPTION, option];
const match = getFirstMatchingOption(validator, formData, testOptions, rootSchema, discriminatorField);
if (match === 1) {
validList.push(index);
}
return validList;
}, []);
if (allValidIndexes.length === 1) {
return allValidIndexes[0];
}
if (!allValidIndexes.length) {
times2(resolvedOptions.length, (i) => allValidIndexes.push(i));
}
const scoreCount = /* @__PURE__ */ new Set();
const { bestIndex } = allValidIndexes.reduce(
(scoreData, index) => {
const { bestScore } = scoreData;
const option = resolvedOptions[index];
const score = calculateIndexScore(validator, rootSchema, option, formData, experimental_customMergeAllOf);
scoreCount.add(score);
if (score > bestScore) {
return { bestIndex: index, bestScore: score };
}
return scoreData;
},
{ bestIndex: selectedOption, bestScore: 0 }
);
if (scoreCount.size === 1 && selectedOption >= 0) {
return selectedOption;
}
return bestIndex;
}
// src/isFixedItems.ts
function isFixedItems(schema) {
return Array.isArray(schema.items) && schema.items.length > 0 && schema.items.every((item) => isObject(item));
}
// src/mergeDefaultsWithFormData.ts
import get6 from "lodash/get";
import isNil from "lodash/isNil";
function mergeDefaultsWithFormData(defaults, formData, mergeExtraArrayDefaults = false, defaultSupercedesUndefined = false, overrideFormDataWithDefaults = false) {
if (Array.isArray(formData)) {
const defaultsArray = Array.isArray(defaults) ? defaults : [];
const overrideArray = overrideFormDataWithDefaults ? defaultsArray : formData;
const overrideOppositeArray = overrideFormDataWithDefaults ? formData : defaultsArray;
const mapped = overrideArray.map((value, idx) => {
if (overrideOppositeArray[idx] !== void 0) {
return mergeDefaultsWithFormData(
defaultsArray[idx],
formData[idx],
mergeExtraArrayDefaults,
defaultSupercedesUndefined,
overrideFormDataWithDefaults
);
}
return value;
});
if ((mergeExtraArrayDefaults || overrideFormDataWithDefaults) && mapped.length < overrideOppositeArray.length) {
mapped.push(...overrideOppositeArray.slice(mapped.length));
}
return mapped;
}
if (isObject(formData)) {
const acc = Object.assign({}, defaults);
return Object.keys(formData).reduce((acc2, key) => {
const keyValue = get6(formData, key);
const keyExistsInDefaults = isObject(defaults) && key in defaults;
const keyExistsInFormData = key in formData;
acc2[key] = mergeDefaultsWithFormData(
defaults ? get6(defaults, key) : {},
keyValue,
mergeExtraArrayDefaults,
defaultSupercedesUndefined,
// overrideFormDataWithDefaults can be true only when the key value exists in defaults
// Or if the key value doesn't exist in formData
overrideFormDataWithDefaults && (keyExistsInDefaults || !keyExistsInFormData)
);
return acc2;
}, acc);
}
if (defaultSupercedesUndefined && (!isNil(defaults) && isNil(formData) || typeof formData === "number" && isNaN(formData)) || overrideFormDataWithDefaults && !isNil(formData)) {
return defaults;
}
return formData;
}
// src/mergeObjects.ts
function mergeObjects(obj1, obj2, concatArrays = false) {
return Object.keys(obj2).reduce((acc, key) => {
const left = obj1 ? obj1[key] : {}, right = obj2[key];
if (obj1 && key in obj1 && isObject(right)) {
acc[key] = mergeObjects(left, right, concatArrays);
} else if (concatArrays && Array.isArray(left) && Array.isArray(right)) {
let toMerge = right;
if (concatArrays === "preventDuplicates") {
toMerge = right.reduce((result, value) => {
if (!left.includes(value)) {
result.push(value);
}
return result;
}, []);
}
acc[key] = left.concat(toMerge);
} else {
acc[key] = right;
}
return acc;
}, Object.assign({}, obj1));
}
// src/isConstant.ts
function isConstant(schema) {
return Array.isArray(schema.enum) && schema.enum.length === 1 || CONST_KEY in schema;
}
// src/schema/isSelect.ts
function isSelect(validator, theSchema, rootSchema = {}, experimental_customMergeAllOf) {
const schema = retrieveSchema(validator, theSchema, rootSchema, void 0, experimental_customMergeAllOf);
const altSchemas = schema.oneOf || schema.anyOf;
if (Array.isArray(schema.enum)) {
return true;
}
if (Array.isArray(altSchemas)) {
return altSchemas.every((altSchemas2) => typeof altSchemas2 !== "boolean" && isConstant(altSchemas2));
}
return false;
}
// src/schema/isMultiSelect.ts
function isMultiSelect(validator, schema, rootSchema, experimental_customMergeAllOf) {
if (!schema.uniqueItems || !schema.items || typeof schema.items === "boolean") {
return false;
}
return isSelect(validator, schema.items, rootSchema, experimental_customMergeAllOf);
}
// src/constIsAjvDataReference.ts
import isString3 from "lodash/isString";
function constIsAjvDataReference(schema) {
const schemaConst = schema[CONST_KEY];
const schemaType = getSchemaType(schema);
return isObject(schemaConst) && isString3(schemaConst?.$data) && schemaType !== "object" && schemaType !== "array";
}
// src/toConstant.ts
function toConstant(schema) {
if (ENUM_KEY in schema && Array.isArray(schema.enum) && schema.enum.length === 1) {
return schema.enum[0];
}
if (CONST_KEY in schema) {
return schema.const;
}
throw new Error("schema cannot be inferred as a constant");
}
// src/optionsList.ts
function optionsList(schema, uiSchema) {
const schemaWithEnumNames = schema;
if (schema.enum) {
let enumNames;
if (uiSchema) {
const { enumNames: uiEnumNames } = getUiOptions(uiSchema);
enumNames = uiEnumNames;
}
if (!enumNames && schemaWithEnumNames.enumNames) {
if (true) {
console.warn(
'The "enumNames" property in the schema is deprecated and will be removed in a future major release. Use the "ui:enumNames" property in the uiSchema instead.'
);
}
enumNames = schemaWithEnumNames.enumNames;
}
return schema.enum.map((value, i) => {
const label = enumNames?.[i] || String(value);
return { label, value };
});
}
let altSchemas = void 0;
let altUiSchemas = void 0;
if (schema.anyOf) {
altSchemas = schema.anyOf;
altUiSchemas = uiSchema?.anyOf;
} else if (schema.oneOf) {
altSchemas = schema.oneOf;
altUiSchemas = uiSchema?.oneOf;
}
return altSchemas && altSchemas.map((aSchemaDef, index) => {
const { title } = getUiOptions(altUiSchemas?.[index]);
const aSchema = aSchemaDef;
const value = toConstant(aSchema);
const label = title || aSchema.title || String(value);
return {
schema: aSchema,
label,
value
};
});
}
// src/schema/getDefaultFormState.ts
var PRIMITIVE_TYPES = ["string", "number", "integer", "boolean", "null"];
function getInnerSchemaForArrayItem(schema, additionalItems = 0 /* Ignore */, idx = -1) {
if (idx >= 0) {
if (Array.isArray(schema.items) && idx < schema.items.length) {
const item = schema.items[idx];
if (typeof item !== "boolean") {
return item;
}
}
} else if (schema.items && !Array.isArray(schema.items) && typeof schema.items !== "boolean") {
return schema.items;
}
if (additionalItems !== 0 /* Ignore */ && isObject(schema.additionalItems)) {
return schema.additionalItems;
}
return {};
}
function maybeAddDefaultToObject(obj, key, computedDefault, includeUndefinedValues, isParentRequired, requiredFields = [], experimental_defaultFormStateBehavior = {}, isConst = false) {
const { emptyObjectFields = "populateAllDefaults" } = experimental_defaultFormStateBehavior;
if (includeUndefinedValues || isConst) {
obj[key] = computedDefault;
} else if (emptyObjectFields !== "skipDefaults") {
const isSelfOrParentRequired = isParentRequired === void 0 ? requiredFields.includes(key) : isParentRequired;
if (isObject(computedDefault)) {
if (emptyObjectFields === "skipEmptyDefaults") {
if (!isEmpty(computedDefault)) {
obj[key] = computedDefault;
}
} else if ((!isEmpty(computedDefault) || requiredFields.includes(key)) && (isSelfOrParentRequired || emptyObjectFields !== "populateRequiredDefaults")) {
obj[key] = computedDefault;
}
} else if (
// Store computedDefault if it's a defined primitive (e.g., true) and satisfies certain conditions
// Condition 1: computedDefault is not undefined
// Condition 2: If emptyObjectFields is 'populateAllDefaults' or 'skipEmptyDefaults)
// Or if isSelfOrParentRequired is 'true' and the key is a required field
computedDefault !== void 0 && (emptyObjectFields === "populateAllDefaults" || emptyObjectFields === "skipEmptyDefaults" || isSelfOrParentRequired && requiredFields.includes(key))
) {
obj[key] = computedDefault;
}
}
}
function computeDefaults(validator, rawSchema, computeDefaultsProps = {}) {
const {
parentDefaults,
rawFormData,
rootSchema = {},
includeUndefinedValues = false,
_recurseList = [],
experimental_defaultFormStateBehavior = void 0,
experimental_customMergeAllOf = void 0,
required,
shouldMergeDefaultsIntoFormData = false
} = computeDefaultsProps;
let formData = isObject(rawFormData) ? rawFormData : {};
const schema = isObject(rawSchema) ? rawSchema : {};
let defaults = parentDefaults;
let schemaToCompute = null;
let experimental_dfsb_to_compute = experimental_defaultFormStateBehavior;
let updatedRecurseList = _recurseList;
if (schema[CONST_KEY] !== void 0 && experimental_defaultFormStateBehavior?.constAsDefaults !== "never" && !constIsAjvDataReference(schema)) {
defaults = schema[CONST_KEY];
} else if (isObject(defaults) && isObject(schema.default)) {
defaults = mergeObjects(defaults, schema.default);
} else if (DEFAULT_KEY in schema && !schema[ANY_OF_KEY] && !schema[ONE_OF_KEY]) {
defaults = schema.default;
} else if (REF_KEY in schema) {
const refName = schema[REF_KEY];
if (!_recurseList.includes(refName)) {
updatedRecurseList = _recurseList.concat(refName);
schemaToCompute = findSchemaDefinition(refName, rootSchema);
}
if (schemaToCompute && !defaults) {
defaults = schema.default;
}
if (shouldMergeDefaultsIntoFormData && schemaToCompute && !isObject(rawFormData)) {
formData = rawFormData;
}
} else if (DEPENDENCIES_KEY in schema) {
const defaultFormData = {
...getDefaultBasedOnSchemaType(validator, schema, computeDefaultsProps, defaults),
...formData
};
const resolvedSchema = resolveDependencies(
validator,
schema,
rootSchema,
false,
[],
defaultFormData,
experimental_customMergeAllOf
);
schemaToCompute = resolvedSchema[0];
} else if (isFixedItems(schema)) {
defaults = schema.items.map(
(itemSchema, idx) => computeDefaults(validator, itemSchema, {
rootSchema,
includeUndefinedValues,
_recurseList,
experimental_defaultFormStateBehavior,
experimental_customMergeAllOf,
parentDefaults: Array.isArray(parentDefaults) ? parentDefaults[idx] : void 0,
rawFormData: formData,
required,
shouldMergeDefaultsIntoFormData
})
);
} else if (ONE_OF_KEY in schema) {
const { oneOf, ...remaining } = schema;
if (oneOf.length === 0) {
return void 0;
}
const discriminator = getDiscriminatorFieldFromSchema(schema);
const { type = "null" } = remaining;
if (!Array.isArray(type) && PRIMITIVE_TYPES.includes(type) && experimental_dfsb_to_compute?.constAsDefaults === "skipOneOf") {
experimental_dfsb_to_compute = {
...experimental_dfsb_to_compute,
constAsDefaults: "never"
};
}
schemaToCompute = oneOf[getClosestMatchingOption(
validator,
rootSchema,
rawFormData ?? schema.default,
oneOf,
0,
discriminator,
experimental_customMergeAllOf
)];
schemaToCompute = mergeSchemas(remaining, schemaToCompute);
} else if (ANY_OF_KEY in schema) {
const { anyOf, ...remaining } = schema;
if (anyOf.length === 0) {
return void 0;
}
const discriminator = getDiscriminatorFieldFromSchema(schema);
schemaToCompute = anyOf[getClosestMatchingOption(
validator,
rootSchema,
rawFormData ?? schema.default,
anyOf,
0,
discriminator,
experimental_customMergeAllOf
)];
schemaToCompute = mergeSchemas(remaining, schemaToCompute);
}
if (schemaToCompute) {
return computeDefaults(validator, schemaToCompute, {
rootSchema,
includeUndefinedValues,
_recurseList: updatedRecurseList,
experimental_defaultFormStateBehavior: experimental_dfsb_to_compute,
experimental_customMergeAllOf,
parentDefaults: defaults,
rawFormData: rawFormData ?? formData,
required,
shouldMergeDefaultsIntoFormData
});
}
if (defaults === void 0) {
defaults = schema.default;
}
const defaultBasedOnSchemaType = getDefaultBasedOnSchemaType(validator, schema, computeDefaultsProps, defaults);
let defaultsWithFormData = defaultBasedOnSchemaType ?? defaults;
if (shouldMergeDefaultsIntoFormData) {
const { arrayMinItems = {} } = experimental_defaultFormStateBehavior || {};
const { mergeExtraDefaults } = arrayMinItems;
const matchingFormData = ensureFormDataMatchingSchema(
validator,
schema,
rootSchema,
rawFormData,
experimental_defaultFormStateBehavior,
experimental_customMergeAllOf
);
if (!isObject(rawFormData) || ALL_OF_KEY in schema) {
defaultsWithFormData = mergeDefaultsWithFormData(
defaultsWithFormData,
matchingFormData,
mergeExtraDefaults,
true
);
}
}
return defaultsWithFormData;
}
function ensureFormDataMatchingSchema(validator, schema, rootSchema, formData, experimental_defaultFormStateBehavior, experimental_customMergeAllOf) {
const isSelectField = !isConstant(schema) && isSelect(validator, schema, rootSchema, experimental_customMergeAllOf);
let validFormData = formData;
if (isSelectField) {
const getOptionsList = optionsList(schema);
const isValid = getOptionsList?.some((option) => deepEquals(option.value, formData));
validFormData = isValid ? formData : void 0;
}
const constTakesPrecedence = schema[CONST_KEY] && experimental_defaultFormStateBehavior?.constAsDefaults === "always";
if (constTakesPrecedence) {
validFormData = schema.const;
}
return validFormData;
}
function getObjectDefaults(validator, rawSchema, {
rawFormData,
rootSchema = {},
includeUndefinedValues = false,
_recurseList = [],
experimental_defaultFormStateBehavior = void 0,
experimental_customMergeAllOf = void 0,
required,
shouldMergeDefaultsIntoFormData
} = {}, defaults) {
{
const formData = isObject(rawFormData) ? rawFormData : {};
const schema = rawSchema;
const retrievedSchema = experimental_defaultFormStateBehavior?.allOf === "populateDefaults" && ALL_OF_KEY in schema ? retrieveSchema(validator, schema, rootSchema, formData, experimental_customMergeAllOf) : schema;
const parentConst = retrievedSchema[CONST_KEY];
const objectDefaults = Object.keys(retrievedSchema.properties || {}).reduce(
(acc, key) => {
const propertySchema = get7(retrievedSchema, [PROPERTIES_KEY, key]);
const hasParentConst = isObject(parentConst) && parentConst[key] !== void 0;
const hasConst = (isObject(propertySchema) && CONST_KEY in propertySchema || hasParentConst) && experimental_defaultFormStateBehavior?.constAsDefaults !== "never" && !constIsAjvDataReference(propertySchema);
const computedDefault = computeDefaults(validator, propertySchema, {
rootSchema,
_recurseList,
experimental_defaultFormStateBehavior,
experimental_customMergeAllOf,
includeUndefinedValues: includeUndefinedValues === true,
parentDefaults: get7(defaults, [key]),
rawFormData: get7(formData, [key]),
required: retrievedSchema.required?.includes(key),
shouldMergeDefaultsIntoFormData
});
maybeAddDefaultToObject(
acc,
key,
computedDefault,
includeUndefinedValues,
required,
retrievedSchema.required,
experimental_defaultFormStateBehavior,
hasConst
);
return acc;
},
{}
);
if (retrievedSchema.additionalProperties) {
const additionalPropertiesSchema = isObject(retrievedSchema.additionalProperties) ? retrievedSchema.additionalProperties : {};
const keys2 = /* @__PURE__ */ new Set();
if (isObject(defaults)) {
Object.keys(defaults).filter((key) => !retrievedSchema.properties || !retrievedSchema.properties[key]).forEach((key) => keys2.add(key));
}
const formDataRequired = [];
Object.keys(formData).filter((key) => !retrievedSchema.properties || !retrievedSchema.properties[key]).forEach((key) => {
keys2.add(key);
formDataRequired.push(key);
});
keys2.forEach((key) => {
const computedDefault = computeDefaults(validator, additionalPropertiesSchema, {
rootSchema,
_recurseList,
experimental_defaultFormStateBehavior,
experimental_customMergeAllOf,
includeUndefinedValues: includeUndefinedValues === true,
parentDefaults: get7(defaults, [key]),
rawFormData: get7(formData, [key]),
required: retrievedSchema.required?.includes(key),
shouldMergeDefaultsIntoFormData
});
maybeAddDefaultToObject(
objectDefaults,
key,
computedDefault,
includeUndefinedValues,
required,
formDataRequired
);
});
}
return objectDefaults;
}
}
function getArrayDefaults(validator, rawSchema, {
rawFormData,
rootSchema = {},
_recurseList = [],
experimental_defaultFormStateBehavior = void 0,
experimental_customMergeAllOf = void 0,
required,
shouldMergeDefaultsIntoFormData
} = {}, defaults) {
const schema = rawSchema;
const arrayMinItemsStateBehavior = experimental_defaultFormStateBehavior?.arrayMinItems ?? {};
const { populate: arrayMinItemsPopulate, mergeExtraDefaults: arrayMergeExtraDefaults } = arrayMinItemsStateBehavior;
const neverPopulate = arrayMinItemsPopulate === "never";
const ignoreMinItemsFlagSet = arrayMinItemsPopulate === "requiredOnly";
const isPopulateAll = arrayMinItemsPopulate === "all" || !neverPopulate && !ignoreMinItemsFlagSet;
const computeSkipPopulate = arrayMinItemsStateBehavior?.computeSkipPopulate ?? (() => false);
const isSkipEmptyDefaults = experimental_defaultFormStateBehavior?.emptyObjectFields === "skipEmptyDefaults";
const emptyDefault = isSkipEmptyDefaults ? void 0 : [];
if (Array.isArray(defaults)) {
defaults = defaults.map((item, idx) => {
const schemaItem = getInnerSchemaForArrayItem(schema, 2 /* Fallback */, idx);
return computeDefaults(validator, schemaItem, {
rootSchema,
_recurseList,
experimental_defaultFormStateBehavior,
experimental_customMergeAllOf,
parentDefaults: item,
required,
shouldMergeDefaultsIntoFormData
});
});
}
if (Array.isArray(rawFormData)) {
const schemaItem = getInnerSchemaForArrayItem(schema);
if (neverPopulate) {
defaults = rawFormData;
} else {
const itemDefaults = rawFormData.map((item, idx) => {
return computeDefaults(validator, schemaItem, {
rootSchema,
_recurseList,
experimental_defaultFormStateBehavior,
experimental_customMergeAllOf,
rawFormData: item,
parentDefaults: get7(defaults, [idx]),
required,
shouldMergeDefaultsIntoFormData
});
});
const mergeExtraDefaults = (ignoreMinItemsFlagSet && required || isPopulateAll) && arrayMergeExtraDefaults;
defaults = mergeDefaultsWithFormData(defaults, itemDefaults, mergeExtraDefaults);
}
}
const hasConst = isObject(schema) && CONST_KEY in schema && experimental_defaultFormStateBehavior?.constAsDefaults !== "never";
if (hasConst === false) {
if (neverPopulate) {
return defaults ?? emptyDefault;
}
if (ignoreMinItemsFlagSet && !required) {
return defaults ? defaults : void