UNPKG

@naturalcycles/js-lib

Version:

Standard library for universal (browser + Node.js) javascript

88 lines (87 loc) 2.31 kB
import { _uniq } from '../../array/index.js'; import { _stringMapEntries } from '../../types.js'; /** * Each row must be an object (current limitation). * * `additionalProperties` is set to `true`, cause it's safer. */ export function generateJsonSchemaFromData(rows) { return objectToJsonSchema(rows); } function objectToJsonSchema(rows) { const typesByKey = {}; rows.forEach(r => { Object.keys(r).forEach(key => { typesByKey[key] ||= new Set(); typesByKey[key].add(getTypeOfValue(r[key])); }); }); const s = { type: 'object', properties: {}, required: [], additionalProperties: true, }; _stringMapEntries(typesByKey).forEach(([key, types]) => { const schema = mergeTypes([...types], rows.map(r => r[key])); if (!schema) return; s.properties[key] = schema; }); // console.log(typesByKey) return s; } function mergeTypes(types, samples) { // skip "undefined" types types = types.filter(t => t !== 'undefined'); if (!types.length) return undefined; if (types.length > 1) { // oneOf const s = { oneOf: types.map(type => mergeTypes([type], samples)), }; return s; } const type = types[0]; if (type === 'null') { return { type: 'null', }; } if (type === 'boolean') { return { type: 'boolean', }; } if (type === 'string') { return { type: 'string', }; } if (type === 'number') { return { type: 'number', }; } if (type === 'object') { return objectToJsonSchema(samples.filter((r) => r && typeof r === 'object')); } if (type === 'array') { // possible feature: detect if it's a tuple // currently assume no-tuple const items = samples.filter(r => Array.isArray(r)).flat(); const itemTypes = _uniq(items.map(i => getTypeOfValue(i))); return { type: 'array', items: mergeTypes(itemTypes, items), }; } } function getTypeOfValue(v) { if (v === null) return 'null'; if (Array.isArray(v)) return 'array'; return typeof v; }