Spaces:
Sleeping
Sleeping
File size: 3,058 Bytes
b6ecafa | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 | type JsonSchema = {
type?: string | string[]
title?: string
description?: string
tags?: string[]
'x-tags'?: string[]
properties?: Record<string, JsonSchema>
items?: JsonSchema | JsonSchema[]
additionalProperties?: JsonSchema | boolean
enum?: unknown[]
const?: unknown
default?: unknown
anyOf?: JsonSchema[]
oneOf?: JsonSchema[]
allOf?: JsonSchema[]
nullable?: boolean
minimum?: number
maximum?: number
pattern?: string
}
export type { JsonSchema }
/** Resolve the primary type from a schema node */
export function schemaType(schema: JsonSchema | undefined): string | undefined {
if (!schema) return undefined
if (Array.isArray(schema.type)) {
const filtered = schema.type.filter(t => t !== 'null')
return filtered[0] ?? schema.type[0]
}
if (schema.type) return schema.type
if (schema.properties || schema.additionalProperties) return 'object'
return undefined
}
/** Normalize union schemas (anyOf/oneOf) into a simpler form */
export function normalizeSchema(schema: JsonSchema): JsonSchema {
if (!schema.anyOf && !schema.oneOf) return schema
const union = schema.anyOf ?? schema.oneOf ?? []
const literals: unknown[] = []
const remaining: JsonSchema[] = []
let nullable = false
for (const entry of union) {
if (!entry || typeof entry !== 'object') continue
if (Array.isArray(entry.enum)) {
for (const v of entry.enum) {
if (v == null) { nullable = true; continue }
if (!literals.some(ex => Object.is(ex, v))) literals.push(v)
}
continue
}
if ('const' in entry) {
if (entry.const == null) { nullable = true; continue }
literals.push(entry.const)
continue
}
if (schemaType(entry) === 'null') { nullable = true; continue }
remaining.push(entry)
}
if (literals.length > 0 && remaining.length === 0) {
return { ...schema, enum: literals, nullable, anyOf: undefined, oneOf: undefined }
}
if (remaining.length === 1) {
return { ...remaining[0], nullable, anyOf: undefined, oneOf: undefined, title: schema.title, description: schema.description }
}
return schema
}
/** Infer a field type string from a raw config value (schema-less fallback) */
export function inferFieldType(value: unknown): string {
if (value == null) return 'string'
if (typeof value === 'boolean') return 'boolean'
if (typeof value === 'number') return 'number'
if (typeof value === 'string') return 'string'
if (Array.isArray(value)) return 'array'
if (typeof value === 'object') return 'object'
return 'string'
}
/** Collect all tags from a schema tree */
export function extractSchemaTags(schema: JsonSchema): string[] {
const tags = new Set<string>()
function walk(s: JsonSchema) {
for (const t of (s['x-tags'] ?? s.tags ?? [])) {
if (typeof t === 'string') tags.add(t.toLowerCase())
}
if (s.properties) {
for (const child of Object.values(s.properties)) walk(child)
}
if (s.items && !Array.isArray(s.items)) walk(s.items)
}
walk(schema)
return [...tags]
}
|