From 2ad0c03615f9381f3d0e3036f9e5dac44bdf1687 Mon Sep 17 00:00:00 2001 From: Thomas Humper Date: Mon, 11 May 2026 20:15:36 +0200 Subject: [PATCH] Update index.ts --- index.ts | 208 ++++++++++++++++++++++--------------------------------- 1 file changed, 81 insertions(+), 127 deletions(-) diff --git a/index.ts b/index.ts index 95e4d38..186241b 100644 --- a/index.ts +++ b/index.ts @@ -2,7 +2,12 @@ import path from "path"; import { parseArgs } from "util"; import { z } from "zod"; import fs from "node:fs"; +import { fileURLToPath } from "url"; +import { dirname } from "path"; +import { zodToJsonSchema } from "zod-to-json-schema"; +const __filename = fileURLToPath(import.meta.url); +const __dirname = dirname(__filename); const topicsDir = path.join(__dirname, "./metadata/topics"); const languagesDir = path.join(__dirname, "./metadata/languages"); @@ -18,143 +23,92 @@ if (allValidTags.length === 0) { const EntityTagEnum = z.enum([ allValidTags[0], - ...allValidTags.slice(1) + ...allValidTags.slice(1), ] as [string, ...string[]]); /** - * The Pricing of a Resource, which can either be Free/Freemium or Paid (Subscription/One Time) + * Pricing schema */ -const PricingSchema = z - .union([ - z - .object({ - model: z - .enum(["Free", "Freemium"]) - .describe( - "The Free(mium) Pricing Model of this resource. 'Free' should be used for resources where 100% (or close) of the content is free. 'Freemium' describes a pricing model where the core content is available for free, but features paid extensions. If the resource has a freemium model but the free portion is very limited, consider using 'Paid' instead and providing an estimated price for the full version. ", - ), - }) - .strict(), - - z.object({ - model: z - .enum(["Subscription", "One Time"]) - .describe( - "The Paid Pricing Model of this resource. 'Subscription' means the resource is paid on a recurring basis (e.g. monthly or yearly), while 'One Time' means the resource is paid with a single upfront payment. If the price varies or is not fixed, provide a close approximation. Note that the subscription renewal cycle is not specified, so if the price has different renewal cycles, provide the most common or default one (usually monthly).", - ), - amount: z - .number() - .gt(0) - .describe("The price of this resource, in US Dollars."), - }), - ]) - .describe("Details about the cost of the resource."); - -export const LanguageDomainSchema = z - .enum([ - "Web Development", - "Data Science", - "Mobile Development", - "Game Development", - "Systems Programming", - "Scripting", - "General Purpose", - "DevOps" - ]) - .describe("A domain that a programming language may be used in."); - -export const ProgrammingParadigmSchema = z - .enum([ - "Object-Oriented Programming", - "Functional Programming", - "Procedural Programming", - "Logic Programming", - ]) - .describe("A programming paradigm."); - -export const ResourceCategorySchema = z - .discriminatedUnion("type", [ - z.object({ - type: z.literal("Language"), - paradigms: z - .array(ProgrammingParadigmSchema) - .describe( - "The programming paradigms that this language focuses on, e.g. 'Object-Oriented Programming', 'Functional Programming', 'Procedural Programming', etc.", - ), - }), - z.object({ - type: z.literal("Platform"), - }).describe( - "A platform used to learn programming, which may teach a variety of languages and concepts.", +const PricingSchema = z.union([ + z.object({ + model: z.enum(["Free", "Freemium"]).describe( + "Free(mium) pricing model." ), - z.object({ - type: z.literal("Tool"), - }) - ]) - .describe("The category of the resource"); + }).strict(), -const ResourceTypeSchema = z.enum(["Video", "Article", "Interactive Tutorial", "Book", "Course"]).describe( - "The type of the resource", -); + z.object({ + model: z.enum(["Subscription", "One Time"]).describe( + "Paid pricing model." + ), + amount: z.number().gt(0).describe("Price in USD"), + }).strict(), +]); + +export const LanguageDomainSchema = z.enum([ + "Web Development", + "Data Science", + "Mobile Development", + "Game Development", + "Systems Programming", + "Scripting", + "General Purpose", + "DevOps", +]); + +export const ProgrammingParadigmSchema = z.enum([ + "Object-Oriented Programming", + "Functional Programming", + "Procedural Programming", + "Logic Programming", +]); + +export const ResourceCategorySchema = z.discriminatedUnion("type", [ + z.object({ + type: z.literal("Language"), + paradigms: z.array(ProgrammingParadigmSchema), + }), + z.object({ + type: z.literal("Platform"), + }), + z.object({ + type: z.literal("Tool"), + }), +]); + +const ResourceTypeSchema = z.enum([ + "Video", + "Article", + "Interactive Tutorial", + "Book", + "Course", +]); export const ResourceSchema = z.object({ - name: z.string().describe("The official name of the resource"), - description: z - .string() - .max(256) - .optional() - .describe("A brief description of the resource"), - url: z.url().describe("URL to the resource"), - type: z.array(ResourceTypeSchema).min(1, "Must specify at least one resource type").describe("The type(s) of the resource, e.g. 'Video', 'Book', 'Course', etc."), - teaches: z.array(EntityTagEnum).min(1, "Must teach at least one topic").describe("The topics that this resource teaches."), + name: z.string(), + description: z.string().max(256).optional(), + url: z.string().url(), + type: z.array(ResourceTypeSchema).min(1), + teaches: z.array(EntityTagEnum).min(1), pricing: PricingSchema, - pros: z - .array(z.string()) - .optional() - .describe( - "Array of pros for using the resource, e.g. 'explains difficult concepts with good analogies'", - ), - cons: z - .array(z.string()) - .optional() - .describe( - "Array of cons for using the resource, e.g. 'only teaches the basics rather than more advanced concepts'", - ), + pros: z.array(z.string()).optional(), + cons: z.array(z.string()).optional(), }).strict(); - export const MetaSchema = z.object({ - name: z - .string() - .describe( - "The name of the language, tool, etc being described by this metadata.", - ), - description: z - .string() - .describe( - "A brief description of the language, tool, etc being described by this metadata.", - ), - emoji: z - .string() - .optional() - .describe( - "A Unicode emoji glyph to represent the entity, if applicable. If there is no suitable (Unicode) emoji, omit this field. Consumers may choose to ignore this field, or replace it with a custom image.", - ), - domains: z - .array(LanguageDomainSchema) - .describe( - "The domain(s) that the entity is commonly used in, or best suited for.", - ), + name: z.string(), + description: z.string(), + emoji: z.string().optional(), + domains: z.array(LanguageDomainSchema), category: ResourceCategorySchema, }).strict(); export const CompiledMetaSchema = MetaSchema.extend({ - id: z.string().describe("The unique identifier of the entity"), + id: z.string(), }).strict(); export const DatabaseSchema = z.object({ - metadata: z.array(CompiledMetaSchema).describe("List of all entities in the system"), - resources: z.array(ResourceSchema).describe("List of all learning resources"), + metadata: z.array(CompiledMetaSchema), + resources: z.array(ResourceSchema), }).strict(); export type Meta = z.infer; @@ -164,18 +118,17 @@ export type Database = z.infer; function main() { const header = "// Generated by index.ts - DO NOT EDIT THIS FILE DIRECTLY"; - const { values, positionals: _ } = parseArgs({ - args: Bun.argv, + const { values } = parseArgs({ + args: process.argv, options: { - schema: { - type: "string", - }, + schema: { type: "string" }, }, strict: true, allowPositionals: true, }); - let schema: z.ZodObject; + let schema: z.ZodTypeAny; + switch (values.schema?.toLowerCase()) { case "metadata": schema = MetaSchema; @@ -187,7 +140,9 @@ function main() { schema = DatabaseSchema; break; case undefined: - console.error("No schema specified. Use --schema to specify which schema to generate (e.g. --schema resource)"); + console.error( + "No schema specified. Use --schema (metadata | resource | database)" + ); process.exit(1); default: console.error(`Unknown schema: ${values.schema}`); @@ -195,10 +150,9 @@ function main() { } console.log(header); - console.log(JSON.stringify(z.toJSONSchema(schema, {}), null, 2)); + console.log(JSON.stringify(zodToJsonSchema(schema), null, 2)); } if (import.meta.main) { main(); } -