schema.ts 2.6 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576
  1. #!/usr/bin/env bun
  2. import { Config } from "@/config/config"
  3. import { Schema } from "effect"
  4. import { TuiInfo } from "../src/cli/cmd/tui/config/tui-schema"
  5. type JsonSchema = Record<string, unknown>
  6. const MODEL_REF = "https://models.dev/model-schema.json#/$defs/Model"
  7. function generateEffect(schema: Schema.Top) {
  8. const document = Schema.toJsonSchemaDocument(schema)
  9. const normalized = normalize({
  10. $schema: "https://json-schema.org/draft/2020-12/schema",
  11. ...document.schema,
  12. $defs: document.definitions,
  13. })
  14. if (!isRecord(normalized)) throw new Error("schema generator produced a non-object schema")
  15. const restored = restoreModelRefs(normalized)
  16. if (!isRecord(restored)) throw new Error("schema generator produced a non-object schema")
  17. restored.allowComments = true
  18. restored.allowTrailingCommas = true
  19. return restored
  20. }
  21. function normalize(value: unknown): unknown {
  22. if (Array.isArray(value)) return value.map(normalize)
  23. if (!isRecord(value)) return value
  24. const schema = Object.fromEntries(Object.entries(value).map(([key, item]) => [key, normalize(item)]))
  25. if (Array.isArray(schema.anyOf)) {
  26. const anyOf = schema.anyOf.filter((item) => !isRecord(item) || item.type !== "null")
  27. if (anyOf.length !== schema.anyOf.length) {
  28. const { anyOf: _, ...rest } = schema
  29. if (anyOf.length === 1 && isRecord(anyOf[0])) return normalize({ ...anyOf[0], ...rest })
  30. return { ...rest, anyOf }
  31. }
  32. }
  33. if (Array.isArray(schema.allOf) && schema.allOf.length === 1 && isRecord(schema.allOf[0])) {
  34. const { allOf: _, ...rest } = schema
  35. return normalize({ ...schema.allOf[0], ...rest })
  36. }
  37. if (schema.type === "integer" && schema.maximum === undefined) {
  38. return { ...schema, maximum: Number.MAX_SAFE_INTEGER }
  39. }
  40. return schema
  41. }
  42. function restoreModelRefs(value: unknown, key?: string): unknown {
  43. if (Array.isArray(value)) return value.map((item) => restoreModelRefs(item))
  44. if (!isRecord(value)) return value
  45. const schema = Object.fromEntries(Object.entries(value).map(([name, item]) => [name, restoreModelRefs(item, name)]))
  46. if ((key === "model" || key === "small_model") && schema.type === "string") {
  47. return { ...schema, $ref: MODEL_REF }
  48. }
  49. return schema
  50. }
  51. function isRecord(value: unknown): value is JsonSchema {
  52. return typeof value === "object" && value !== null && !Array.isArray(value)
  53. }
  54. const configFile = process.argv[2]
  55. const tuiFile = process.argv[3]
  56. console.log(configFile)
  57. await Bun.write(configFile, JSON.stringify(generateEffect(Config.Info), null, 2))
  58. if (tuiFile) {
  59. console.log(tuiFile)
  60. await Bun.write(tuiFile, JSON.stringify(generateEffect(TuiInfo), null, 2))
  61. }