diff --git a/backend/src/api/docs.ts b/backend/src/api/docs.ts index dcb75c49..5b9de52b 100644 --- a/backend/src/api/docs.ts +++ b/backend/src/api/docs.ts @@ -1,34 +1,99 @@ import express from "express"; +import z from "zod"; import { guildPlugins } from "../plugins/availablePlugins"; import { indentLines } from "../utils"; import { notFound } from "./responses"; -function formatConfigSchema(schema) { - if (schema._tag === "InterfaceType" || schema._tag === "PartialType") { +function isZodObject(schema: z.ZodTypeAny): schema is z.ZodObject { + return schema._def.typeName === "ZodObject"; +} + +function isZodRecord(schema: z.ZodTypeAny): schema is z.ZodRecord { + return schema._def.typeName === "ZodRecord"; +} + +function isZodEffects(schema: z.ZodTypeAny): schema is z.ZodEffects { + return schema._def.typeName === "ZodEffects"; +} + +function isZodOptional(schema: z.ZodTypeAny): schema is z.ZodOptional { + return schema._def.typeName === "ZodOptional"; +} + +function isZodArray(schema: z.ZodTypeAny): schema is z.ZodArray { + return schema._def.typeName === "ZodArray"; +} + +function isZodUnion(schema: z.ZodTypeAny): schema is z.ZodUnion { + return schema._def.typeName === "ZodUnion"; +} + +function isZodNullable(schema: z.ZodTypeAny): schema is z.ZodNullable { + return schema._def.typeName === "ZodNullable"; +} + +function isZodDefault(schema: z.ZodTypeAny): schema is z.ZodDefault { + return schema._def.typeName === "ZodDefault"; +} + +function isZodLiteral(schema: z.ZodTypeAny): schema is z.ZodLiteral { + return schema._def.typeName === "ZodLiteral"; +} + +function isZodIntersection(schema: z.ZodTypeAny): schema is z.ZodIntersection { + return schema._def.typeName === "ZodIntersection"; +} + +function formatZodConfigSchema(schema: z.ZodTypeAny) { + if (isZodObject(schema)) { return ( `{\n` + - Object.entries(schema.props) - .map(([k, value]) => indentLines(`${k}: ${formatConfigSchema(value)}`, 2)) + Object.entries(schema._def.shape()) + .map(([k, value]) => indentLines(`${k}: ${formatZodConfigSchema(value as z.ZodTypeAny)}`, 2)) .join("\n") + "\n}" ); - } else if (schema._tag === "DictionaryType") { - return "{\n" + indentLines(`[string]: ${formatConfigSchema(schema.codomain)}`, 2) + "\n}"; - } else if (schema._tag === "ArrayType") { - return `Array<${formatConfigSchema(schema.type)}>`; - } else if (schema._tag === "UnionType") { - if (schema.name.startsWith("Nullable<")) { - return `Nullable<${formatConfigSchema(schema.types[0])}>`; - } else if (schema.name.startsWith("Optional<")) { - return `Optional<${formatConfigSchema(schema.types[0])}>`; - } else { - return schema.types.map((t) => formatConfigSchema(t)).join(" | "); - } - } else if (schema._tag === "IntersectionType") { - return schema.types.map((t) => formatConfigSchema(t)).join(" & "); - } else { - return schema.name; } + if (isZodRecord(schema)) { + return "{\n" + indentLines(`[string]: ${formatZodConfigSchema(schema._def.valueType)}`, 2) + "\n}"; + } + if (isZodEffects(schema)) { + return formatZodConfigSchema(schema._def.schema); + } + if (isZodOptional(schema)) { + return `Optional<${formatZodConfigSchema(schema._def.innerType)}>`; + } + if (isZodArray(schema)) { + return `Array<${formatZodConfigSchema(schema._def.type)}>`; + } + if (isZodUnion(schema)) { + return schema._def.options.map((t) => formatZodConfigSchema(t)).join(" | "); + } + if (isZodNullable(schema)) { + return `Nullable<${formatZodConfigSchema(schema._def.innerType)}>`; + } + if (isZodDefault(schema)) { + return formatZodConfigSchema(schema._def.innerType); + } + if (isZodLiteral(schema)) { + return schema._def.value; + } + if (isZodIntersection(schema)) { + return [formatZodConfigSchema(schema._def.left), formatZodConfigSchema(schema._def.right)].join(" & "); + } + if (schema._def.typeName === "ZodString") { + return "string"; + } + if (schema._def.typeName === "ZodNumber") { + return "number"; + } + if (schema._def.typeName === "ZodBoolean") { + return "boolean"; + } + if (schema._def.typeName === "ZodNever") { + return "never"; + } + return "unknown"; } export function initDocs(app: express.Express) { @@ -67,7 +132,7 @@ export function initDocs(app: express.Express) { })); const defaultOptions = plugin.defaultOptions || {}; - const configSchema = plugin.info?.configSchema && formatConfigSchema(plugin.info.configSchema); + const configSchema = plugin.info?.configSchema && formatZodConfigSchema(plugin.info.configSchema); res.json({ name,