UNPKG

@appsemble/lang-sdk

Version:

Language SDK for Appsemble

185 lines 7 kB
import lcm from 'lcm'; import { mapValues } from './mapValues.js'; /** * Generate data based on a JSON schema. * * The generated data doesn’t necessarily conform to the JSON schema. This is useful to prefill * forms that are based on a JSON schema, but where user input is still needed to verify the data. * * @param schema The JSON schema to generate data from. * @returns A JSON value estimated from the schema. */ export function generateDataFromSchema(schema) { if (!schema) { return null; } // Let’s assume the default conforms to the schema, although this might not be true. if ('default' in schema) { return schema.default ?? null; } // If no predefined value exists, generate something based on its type. switch (schema.type) { case 'array': return Array.from({ length: schema.minItems ?? 0 }, (empty, index) => generateDataFromSchema(Array.isArray(schema.items) ? schema.items[index] || (typeof schema.additionalItems === 'object' && schema.additionalItems) : schema.items)); case 'boolean': return false; case 'integer': case 'number': { const { maximum = 0, minimum = 0, multipleOf = schema.type === 'integer' ? 1 : undefined, } = schema; if (minimum > 0) { return multipleOf ? minimum + multipleOf - (minimum % multipleOf) : minimum; } if (maximum < 0) { return multipleOf ? maximum - multipleOf + (-maximum % multipleOf) : maximum; } return 0; } case 'null': return null; case 'object': return mapValues(schema.properties || {}, generateDataFromSchema); case 'string': return ''; default: break; } return null; } /** * Combine a list of schemas into one schema matching all of them. * * The main purpose of this function is to combine a schema using `allOf` into one schema that can * be rendered. Do not use this for actual validation. Use the original `allOf` schema instead. * * @param schemas The schemas to combine. * @returns The combined schema. */ export function combineSchemas(...schemas) { const result = {}; for (const schema of schemas) { if ('type' in schema) { result.type || (result.type = schema.type); } if ('format' in schema) { result.format || (result.format = schema.format); } if ('minimum' in schema) { result.minimum = // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error // @ts-ignore 2345 argument of type is not assignable to parameter of type // (strictNullChecks) 'minimum' in result ? Math.max(result.minimum, schema.minimum) : schema.minimum; } if ('minLength' in schema) { result.minLength = // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error // @ts-ignore 2345 argument of type is not assignable to parameter of type // (strictNullChecks) 'minLength' in result ? Math.max(result.minLength, schema.minLength) : schema.minLength; } if ('maximum' in schema) { result.maximum = // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error // @ts-ignore 2345 argument of type is not assignable to parameter of type // (strictNullChecks) 'maximum' in result ? Math.min(result.maximum, schema.maximum) : schema.maximum; } if ('maxLength' in schema) { result.maxLength = // eslint-disable-next-line @typescript-eslint/prefer-ts-expect-error // @ts-ignore 2345 argument of type is not assignable to parameter of type // (strictNullChecks) 'maxLength' in result ? Math.min(result.maxLength, schema.maxLength) : schema.maxLength; } if (schema.multipleOf) { result.multipleOf = result.multipleOf ? lcm(result.multipleOf, schema.multipleOf) : schema.multipleOf; } if ('default' in schema) { result.default ?? (result.default = schema.default); } if (schema.description) { result.description || (result.description = schema.description); } if (schema.pattern) { result.pattern || (result.pattern = schema.pattern); } if (schema.title) { result.title || (result.title = schema.title); } if ('uniqueItems' in schema) { result.uniqueItems || (result.uniqueItems = schema.uniqueItems); } if (Array.isArray(schema.required)) { result.required || (result.required = []); if (Array.isArray(result.required)) { result.required.push(...schema.required); } } else if (schema.required) { result.required = true; } if (schema.properties) { result.properties || (result.properties = {}); for (const [key, property] of Object.entries(schema.properties)) { result.properties[key] = result.properties[key] ? combineSchemas(result.properties[key], property) : property; } } } return result; } /** * Recursively iterate over a JSON schema and call the callback with every sub schema found. * * @param schema The JSON schema to iterate. * @param onSchema The callback to call with the found JSON schema. */ export function iterJSONSchema(schema, onSchema) { if (!schema) { return; } onSchema(schema); if (schema.properties) { for (const property of Object.values(schema.properties)) { iterJSONSchema(property, onSchema); } } if (typeof schema.additionalProperties === 'object') { iterJSONSchema(schema.additionalProperties, onSchema); } if (schema.items) { if (Array.isArray(schema.items)) { for (const item of schema.items) { iterJSONSchema(item, onSchema); } } else { iterJSONSchema(schema.items, onSchema); } } if (typeof schema.additionalItems === 'object') { iterJSONSchema(schema.additionalItems, onSchema); } if (schema.anyOf) { for (const anyOf of schema.anyOf) { iterJSONSchema(anyOf, onSchema); } } if (schema.oneOf) { for (const oneOf of schema.oneOf) { iterJSONSchema(oneOf, onSchema); } } if (schema.allOf) { for (const allOf of schema.allOf) { iterJSONSchema(allOf, onSchema); } } } //# sourceMappingURL=jsonschema.js.map