From 896554ae82028336f069d71115942e6d2ba3f0b1 Mon Sep 17 00:00:00 2001 From: "." Date: Mon, 9 Mar 2026 00:31:07 +0800 Subject: [PATCH] feat: support nested objects, typed arrays, and enums in JSON Schema conversion The jsonSchemaToTypebox function previously flattened nested objects into Record and arrays into Array, losing all type information. Enum values were also ignored. Extract a recursive jsonSchemaPropToTypebox helper that: - Recursively converts nested object schemas with their properties - Preserves array item schemas (e.g. Array instead of Array) - Converts enum values to TypeBox Union/Literal types --- index.ts | 55 ++++++++++++++++++++++++++++++++++--------------------- 1 file changed, 34 insertions(+), 21 deletions(-) diff --git a/index.ts b/index.ts index bba3593..2fd7b32 100644 --- a/index.ts +++ b/index.ts @@ -28,6 +28,38 @@ function getPort(pluginConfig?: Record): number { return DEFAULT_PORT; } +/** Convert a single JSON Schema property to its TypeBox equivalent. */ +function jsonSchemaPropToTypebox(prop: Record): unknown { + const desc = typeof prop.description === "string" ? prop.description : undefined; + + if (Array.isArray(prop.enum) && prop.enum.length > 0) { + const literals = prop.enum.map((v: unknown) => Type.Literal(v as string | number | boolean)); + return literals.length === 1 ? literals[0] : Type.Union(literals, { description: desc }); + } + + switch (prop.type) { + case "number": + case "integer": + return Type.Number({ description: desc }); + case "boolean": + return Type.Boolean({ description: desc }); + case "array": { + const items = prop.items as Record | undefined; + const itemSchema = items ? jsonSchemaPropToTypebox(items) : Type.Unknown(); + // @ts-expect-error -- dynamic schema + return Type.Array(itemSchema, { description: desc }); + } + case "object": { + if (prop.properties) { + return jsonSchemaToTypebox(prop as Record); + } + return Type.Record(Type.String(), Type.Unknown(), { description: desc }); + } + default: + return Type.String({ description: desc }); + } +} + /** Convert a JSON Schema properties object into a TypeBox Type.Object schema. */ function jsonSchemaToTypebox( inputSchema?: Record, @@ -41,27 +73,8 @@ function jsonSchemaToTypebox( const tbProps: Record = {}; for (const [key, prop] of Object.entries(properties)) { - const desc = typeof prop.description === "string" ? prop.description : undefined; - let inner; - switch (prop.type) { - case "number": - case "integer": - inner = Type.Number({ description: desc }); - break; - case "boolean": - inner = Type.Boolean({ description: desc }); - break; - case "array": - inner = Type.Array(Type.Unknown(), { description: desc }); - break; - case "object": - inner = Type.Record(Type.String(), Type.Unknown(), { description: desc }); - break; - default: - inner = Type.String({ description: desc }); - break; - } - tbProps[key] = required.has(key) ? inner : Type.Optional(inner); + const inner = jsonSchemaPropToTypebox(prop); + tbProps[key] = required.has(key) ? inner : Type.Optional(inner as ReturnType); } // @ts-expect-error -- heterogeneous property map built dynamically