@greguintow/rjsf-utils
Version:
Utility functions for @rjsf/core
1,522 lines (1,475 loc) • 119 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 READONLY_KEY = "readonly";
var REQUIRED_KEY = "required";
var SUBMIT_BTN_OPTIONS_KEY = "submitButtonOptions";
var REF_KEY = "$ref";
var DISCRIMINATOR_PATH = ["discriminator", "propertyName"];
var FORM_CONTEXT_NAME = "formContext";
var LOOKUP_MAP_NAME = "layoutGridLookupMap";
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/findFieldInSchema.ts
import get7 from "lodash/get";
import has3 from "lodash/has";
// src/schema/findSelectedOptionInXxxOf.ts
import get5 from "lodash/get";
import isEqual from "lodash/isEqual";
// 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/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/getDiscriminatorFieldFromSchema.ts
import get from "lodash/get";
import isString from "lodash/isString";
function getDiscriminatorFieldFromSchema(schema) {
let discriminator;
const maybeString = get(schema, DISCRIMINATOR_PATH);
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/getMatchingOption.ts
import get3 from "lodash/get";
import has from "lodash/has";
import isNumber from "lodash/isNumber";
// src/getOptionMatchingSimpleDiscriminator.ts
import get2 from "lodash/get";
function getOptionMatchingSimpleDiscriminator(formData, options, discriminatorField) {
if (formData && discriminatorField) {
const value = get2(formData, discriminatorField);
if (value === void 0) {
return;
}
for (let i = 0; i < options.length; i++) {
const option = options[i];
const discriminator = get2(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 = get3(formData, discriminatorField);
const discriminator = get3(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
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/findSelectedOptionInXxxOf.ts
function findSelectedOptionInXxxOf(validator, rootSchema, schema, fallbackField, xxx, formData = {}, experimental_customMergeAllOf) {
if (Array.isArray(schema[xxx])) {
const discriminator = getDiscriminatorFieldFromSchema(schema);
const selectorField = discriminator || fallbackField;
const xxxOfs = schema[xxx].map(
(xxxOf) => retrieveSchema(validator, xxxOf, rootSchema, formData, experimental_customMergeAllOf)
);
const data = get5(formData, selectorField);
if (data !== void 0) {
return xxxOfs.find((xxx2) => {
return isEqual(
get5(xxx2, [PROPERTIES_KEY, selectorField, DEFAULT_KEY], get5(xxx2, [PROPERTIES_KEY, selectorField, CONST_KEY])),
data
);
});
}
}
return void 0;
}
// src/schema/getFromSchema.ts
import get6 from "lodash/get";
import has2 from "lodash/has";
import isEmpty from "lodash/isEmpty";
function getFromSchemaInternal(validator, rootSchema, schema, path, experimental_customMergeAllOf) {
let fieldSchema = schema;
if (has2(schema, REF_KEY)) {
fieldSchema = retrieveSchema(validator, schema, rootSchema, void 0, experimental_customMergeAllOf);
}
if (isEmpty(path)) {
return fieldSchema;
}
const pathList = Array.isArray(path) ? path : path.split(".");
const [part, ...nestedPath] = pathList;
if (part && has2(fieldSchema, part)) {
fieldSchema = get6(fieldSchema, part);
return getFromSchemaInternal(
validator,
rootSchema,
fieldSchema,
nestedPath,
experimental_customMergeAllOf
);
}
return void 0;
}
function getFromSchema(validator, rootSchema, schema, path, defaultValue, experimental_customMergeAllOf) {
const result = getFromSchemaInternal(validator, rootSchema, schema, path, experimental_customMergeAllOf);
if (result === void 0) {
return defaultValue;
}
return result;
}
// src/schema/findFieldInSchema.ts
var NOT_FOUND_SCHEMA = { title: "!@#$_UNKNOWN_$#@!" };
function findFieldInSchema(validator, rootSchema, schema, path, formData = {}, experimental_customMergeAllOf) {
const pathList = Array.isArray(path) ? [...path] : path.split(".");
let parentField = schema;
const fieldName = pathList.pop();
if (pathList.length) {
pathList.forEach((subPath) => {
parentField = getFromSchema(
validator,
rootSchema,
parentField,
[PROPERTIES_KEY, subPath],
{},
experimental_customMergeAllOf
);
if (has3(parentField, ONE_OF_KEY)) {
parentField = findSelectedOptionInXxxOf(
validator,
rootSchema,
parentField,
fieldName,
ONE_OF_KEY,
get7(formData, subPath),
experimental_customMergeAllOf
);
} else if (has3(parentField, ANY_OF_KEY)) {
parentField = findSelectedOptionInXxxOf(
validator,
rootSchema,
parentField,
fieldName,
ANY_OF_KEY,
get7(formData, subPath),
experimental_customMergeAllOf
);
}
});
}
if (has3(parentField, ONE_OF_KEY)) {
parentField = findSelectedOptionInXxxOf(
validator,
rootSchema,
parentField,
fieldName,
ONE_OF_KEY,
formData,
experimental_customMergeAllOf
);
} else if (has3(parentField, ANY_OF_KEY)) {
parentField = findSelectedOptionInXxxOf(
validator,
rootSchema,
parentField,
fieldName,
ANY_OF_KEY,
formData,
experimental_customMergeAllOf
);
}
let field = getFromSchema(
validator,
rootSchema,
parentField,
[PROPERTIES_KEY, fieldName],
NOT_FOUND_SCHEMA,
experimental_customMergeAllOf
);
if (field === NOT_FOUND_SCHEMA) {
field = void 0;
}
const requiredArray = getFromSchema(
validator,
rootSchema,
parentField,
REQUIRED_KEY,
[],
experimental_customMergeAllOf
);
let isRequired;
if (field && Array.isArray(requiredArray)) {
isRequired = requiredArray.includes(fieldName);
}
return { field, isRequired };
}
// src/schema/getDefaultFormState.ts
import get17 from "lodash/get";
import isEmpty3 from "lodash/isEmpty";
// src/schema/getClosestMatchingOption.ts
import get8 from "lodash/get";
import has4 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";
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 = get8(formData, key);
if (typeof value === "boolean") {
return score;
}
if (has4(value, REF_KEY)) {
const newSchema = retrieveSchema(
validator,
value,
rootSchema,
formValue,
experimental_customMergeAllOf
);
return score + calculateIndexScore(
validator,
rootSchema,
newSchema,
formValue || {},
experimental_customMergeAllOf
);
}
if ((has4(value, ONE_OF_KEY) || has4(value, ANY_OF_KEY)) && formValue) {
const key2 = has4(value, ONE_OF_KEY) ? ONE_OF_KEY : ANY_OF_KEY;
const discriminator = getDiscriminatorFieldFromSchema(value);
return score + getClosestMatchingOption(
validator,
rootSchema,
formValue,
get8(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 get9 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 = get9(formData, key);
const keyExistsInDefaults = isObject(defaults) && key in defaults;
const keyExistsInFormData = key in formData;
acc2[key] = mergeDefaultsWithFormData(
defaults ? get9(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/dataURItoBlob.ts
function dataURItoBlob(dataURILike) {
if (dataURILike.indexOf("data:") === -1) {
throw new Error("File is invalid: URI must be a dataURI");
}
const dataURI = dataURILike.slice(5);
const splitted = dataURI.split(";base64,");
if (splitted.length !== 2) {
throw new Error("File is invalid: dataURI must be base64");
}
const [media, base64] = splitted;
const [mime, ...mediaparams] = media.split(";");
const type = mime || "";
const name = decodeURI(
// parse the parameters into key-value pairs, find a key, and extract a value
// if no key is found, then the name is unknown
mediaparams.map((param) => param.split("=")).find(([key]) => key === "name")?.[1] || "unknown"
);
try {
const binary = atob(base64);
const array = new Array(binary.length);
for (let i = 0; i < binary.length; i++) {
array[i] = binary.charCodeAt(i);
}
const blob = new window.Blob([new Uint8Array(array)], { type });
return { blob, name };
} catch (error) {
throw new Error("File is invalid: " + error.message);
}
}
// src/pad.ts
function pad(num, width) {
let s = String(num);
while (s.length < width) {
s = "0" + s;
}
return s;
}
// src/dateRangeOptions.ts
function dateRangeOptions(start, stop) {
if (start <= 0 && stop <= 0) {
start = (/* @__PURE__ */ new Date()).getFullYear() + start;
stop = (/* @__PURE__ */ new Date()).getFullYear() + stop;
} else if (start < 0 || stop < 0) {
throw new Error(`Both start (${start}) and stop (${stop}) must both be <= 0 or > 0, got one of each`);
}
if (start > stop) {
return dateRangeOptions(stop, start).reverse();
}
const options = [];
for (let i = start; i <= stop; i++) {
options.push({ value: i, label: pad(i, 2) });
}
return options;
}
// src/replaceStringParameters.ts
function replaceStringParameters(inputString, params) {
let output = inputString;
if (Array.isArray(params)) {
const parts = output.split(/(%\d)/);
params.forEach((param, index) => {
const partIndex = parts.findIndex((part) => part === `%${index + 1}`);
if (partIndex >= 0) {
parts[partIndex] = param;
}
});
output = parts.join("");
}
return output;
}
// src/englishStringTranslator.ts
function englishStringTranslator(stringToTranslate, params) {
return replaceStringParameters(stringToTranslate, params);
}
// src/enumOptionsValueForIndex.ts
function enumOptionsValueForIndex(valueIndex, allEnumOptions = [], emptyValue) {
if (Array.isArray(valueIndex)) {
return valueIndex.map((index2) => enumOptionsValueForIndex(index2, allEnumOptions)).filter((val) => val !== emptyValue);
}
const index = valueIndex === "" || valueIndex === null ? -1 : Number(valueIndex);
const option = allEnumOptions[index];
return option ? option.value : emptyValue;
}
// src/enumOptionsDeselectValue.ts
function enumOptionsDeselectValue(valueIndex, selected, allEnumOptions = []) {
const value = enumOptionsValueForIndex(valueIndex, allEnumOptions);
if (Array.isArray(selected)) {
return selected.filter((v) => !deepEquals(v, value));
}
return deepEquals(value, selected) ? void 0 : selected;
}
// src/enumOptionsIsSelected.ts
function enumOptionsIsSelected(value, selected) {
if (Array.isArray(selected)) {
return selected.some((sel) => deepEquals(sel, value));
}
return deepEquals(selected, value);
}
// src/enumOptionsIndexForValue.ts
function enumOptionsIndexForValue(value, allEnumOptions = [], multiple = false) {
const selectedIndexes = allEnumOptions.map((opt, index) => enumOptionsIsSelected(opt.value, value) ? String(index) : void 0).filter((opt) => typeof opt !== "undefined");
if (!multiple) {
return selectedIndexes[0];
}
return selectedIndexes;
}
// src/enumOptionsSelectValue.ts
import isNil2 from "lodash/isNil";
function enumOptionsSelectValue(valueIndex, selected, allEnumOptions = []) {
const value = enumOptionsValueForIndex(valueIndex, allEnumOptions);
if (!isNil2(value)) {
const index = allEnumOptions.findIndex((opt) => value === opt.value);
const all = allEnumOptions.map(({ value: val }) => val);
const updated = selected.slice(0, index).concat(value, selected.slice(index));
return updated.sort((a, b) => Number(all.indexOf(a) > all.indexOf(b)));
}
return selected;
}
// src/ErrorSchemaBuilder.ts
import cloneDeep from "lodash/cloneDeep";
import get10 from "lodash/get";
import set2 from "lodash/set";
import setWith from "lodash/setWith";
var ErrorSchemaBuilder = class {
/** Construct an `ErrorSchemaBuilder` with an optional initial set of errors in an `ErrorSchema`.
*
* @param [initialSchema] - The optional set of initial errors, that will be cloned into the class
*/
constructor(initialSchema) {
/** The error schema being built
*
* @private
*/
this.errorSchema = {};
this.resetAllErrors(initialSchema);
}
/** Returns the `ErrorSchema` that has been updated by the methods of the `ErrorSchemaBuilder`
*/
get ErrorSchema() {
return this.errorSchema;
}
/** Will get an existing `ErrorSchema` at the specified `pathOfError` or create and return one.
*
* @param [pathOfError] - The optional path into the `ErrorSchema` at which to add the error(s)
* @returns - The error block for the given `pathOfError` or the root if not provided
* @private
*/
getOrCreateErrorBlock(pathOfError) {
const hasPath = Array.isArray(pathOfError) && pathOfError.length > 0 || typeof pathOfError === "string";
let errorBlock = hasPath ? get10(this.errorSchema, pathOfError) : this.errorSchema;
if (!errorBlock && pathOfError) {
errorBlock = {};
setWith(this.errorSchema, pathOfError, errorBlock, Object);
}
return errorBlock;
}
/** Resets all errors in the `ErrorSchemaBuilder` back to the `initialSchema` if provided, otherwise an empty set.
*
* @param [initialSchema] - The optional set of initial errors, that will be cloned into the class
* @returns - The `ErrorSchemaBuilder` object for chaining purposes
*/
resetAllErrors(initialSchema) {
this.errorSchema = initialSchema ? cloneDeep(initialSchema) : {};
return this;
}
/** Adds the `errorOrList` to the list of errors in the `ErrorSchema` at either the root level or the location within
* the schema described by the `pathOfError`. For more information about how to specify the path see the
* [eslint lodash plugin docs](https://github.com/wix/eslint-plugin-lodash/blob/master/docs/rules/path-style.md).
*
* @param errorOrList - The error or list of errors to add into the `ErrorSchema`
* @param [pathOfError] - The optional path into the `ErrorSchema` at which to add the error(s)
* @returns - The `ErrorSchemaBuilder` object for chaining purposes
*/
addErrors(errorOrList, pathOfError) {
const errorBlock = this.getOrCreateErrorBlock(pathOfError);
let errorsList = get10(errorBlock, ERRORS_KEY);
if (!Array.isArray(errorsList)) {
errorsList = [];
errorBlock[ERRORS_KEY] = errorsList;
}
if (Array.isArray(errorOrList)) {
set2(errorBlock, ERRORS_KEY, [.../* @__PURE__ */ new Set([...errorsList, ...errorOrList])]);
} else {
set2(errorBlock, ERRORS_KEY, [.../* @__PURE__ */ new Set([...errorsList, errorOrList])]);
}
return this;
}
/** Sets/replaces the `errorOrList` as the error(s) in the `ErrorSchema` at either the root level or the location
* within the schema described by the `pathOfError`. For more information about how to specify the path see the
* [eslint lodash plugin docs](https://github.com/wix/eslint-plugin-lodash/blob/master/docs/rules/path-style.md).
*
* @param errorOrList - The error or list of errors to set into the `ErrorSchema`
* @param [pathOfError] - The optional path into the `ErrorSchema` at which to set the error(s)
* @returns - The `ErrorSchemaBuilder` object for chaining purposes
*/
setErrors(errorOrList, pathOfError) {
const errorBlock = this.getOrCreateErrorBlock(pathOfError);
const listToAdd = Array.isArray(errorOrList) ? [.../* @__PURE__ */ new Set([...errorOrList])] : [errorOrList];
set2(errorBlock, ERRORS_KEY, listToAdd);
return this;
}
/** Clears the error(s) in the `ErrorSchema` at either the root level or the location within the schema described by
* the `pathOfError`. For more information about how to specify the path see the
* [eslint lodash plugin docs](https://github.com/wix/eslint-plugin-lodash/blob/master/docs/rules/path-style.md).
*
* @param [pathOfError] - The optional path into the `ErrorSchema` at which to clear the error(s)
* @returns - The `ErrorSchemaBuilder` object for chaining purposes
*/
clearErrors(pathOfError) {
const errorBlock = this.getOrCreateErrorBlock(pathOfError);
set2(errorBlock, ERRORS_KEY, []);
return this;
}
};
// src/getDateElementProps.ts
function getDateElementProps(date, time, yearRange = [1900, (/* @__PURE__ */ new Date()).getFullYear() + 2], format = "YMD") {
const { day, month, year, hour, minute, second } = date;
const dayObj = { type: "day", range: [1, 31], value: day };
const monthObj = { type: "month", range: [1, 12], value: month };
const yearObj = { type: "year", range: yearRange, value: year };
const dateElementProp = [];
switch (format) {
case "MDY":
dateElementProp.push(monthObj, dayObj, yearObj);
break;
case "DMY":
dateElementProp.push(dayObj, monthObj, yearObj);
break;
case "YMD":
default:
dateElementProp.push(yearObj, monthObj, dayObj);
}
if (time) {
dateElementProp.push(
{ type: "hour", range: [0, 23], value: hour },
{ type: "minute", range: [0, 59], value: minute },
{ type: "second", range: [0, 59], value: second }
);
}
return dateElementProp;
}
// src/rangeSpec.ts
function rangeSpec(schema) {
const spec = {};
if (schema.multipleOf) {
spec.step = schema.multipleOf;
}
if (schema.minimum || schema.minimum === 0) {
spec.min = schema.minimum;
}
if (schema.maximum || schema.maximum === 0) {
spec.max = schema.maximum;
}
return spec;
}
// src/getInputProps.ts
function getInputProps(schema, defaultType, options = {}, autoDefaultStepAny = true) {
const inputProps = {
type: defaultType || "text",
...rangeSpec(schema)
};
if (options.inputType) {
inputProps.type = options.inputType;
} else if (!defaultType) {
if (schema.type === "number") {
inputProps.type = "number";
if (autoDefaultStepAny && inputProps.step === void 0) {
inputProps.step = "any";
}
} else if (schema.type === "integer") {
inputProps.type = "number";
if (inputProps.step === void 0) {
inputProps.step = 1;
}
}
}
if (options.autocomplete) {
inputProps.autoComplete = options.autocomplete;
}
if (options.accept) {
inputProps.accept = options.accept;
}
return inputProps;
}
// src/getSubmitButtonOptions.ts
var DEFAULT_OPTIONS = {
props: {
disabled: false
},
submitText: "Submit",
norender: false
};
function getSubmitButtonOptions(uiSchema = {}) {
const uiOptions = getUiOptions(uiSchema);
if (uiOptions && uiOptions[SUBMIT_BTN_OPTIONS_KEY]) {
const options = uiO