From 7be3d8b8abd357e50efc339131e5965f13710977 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Fri, 9 May 2025 13:09:11 -0400 Subject: [PATCH 01/11] basic implementation of zod schemas from our schemas --- package/src/common/create-schema-types.ts | 7 +- src/core/schema/build-schema-file.ts | 2 + src/core/schema/types-from-schema.ts | 8 +- src/core/schema/zod-types-from-schema.ts | 448 ++++ src/import_map.json | 2 + src/resources/editor/tools/vs-code.mjs | 6 +- src/resources/editor/tools/yaml/web-worker.js | 6 +- .../yaml/yaml-intelligence-resources.json | 6 +- src/resources/types/schema-types.ts | 4 +- .../types/zod/handwritten-schema-types.ts | 118 + src/resources/types/zod/schema-types.ts | 2004 +++++++++++++++++ 11 files changed, 2595 insertions(+), 16 deletions(-) create mode 100644 src/core/schema/zod-types-from-schema.ts create mode 100644 src/resources/types/zod/handwritten-schema-types.ts create mode 100644 src/resources/types/zod/schema-types.ts diff --git a/package/src/common/create-schema-types.ts b/package/src/common/create-schema-types.ts index 9ec3e1aa0cd..48d7907885a 100644 --- a/package/src/common/create-schema-types.ts +++ b/package/src/common/create-schema-types.ts @@ -14,6 +14,11 @@ import { generateJsonSchemasFromSchemas, } from "../../../src/core/schema/json-schema-from-schema.ts"; +import { + generateZodTypesFromSchemas, +} from "../../../src/core/schema/zod-types-from-schema.ts"; + await generateTypesFromSchemas(Deno.args[0]); await generateSchemaTypes(Deno.args[0]); -await generateJsonSchemasFromSchemas(Deno.args[0]); \ No newline at end of file +await generateJsonSchemasFromSchemas(Deno.args[0]); +await generateZodTypesFromSchemas(Deno.args[0]); \ No newline at end of file diff --git a/src/core/schema/build-schema-file.ts b/src/core/schema/build-schema-file.ts index 5505bcbeb96..852fcc24857 100644 --- a/src/core/schema/build-schema-file.ts +++ b/src/core/schema/build-schema-file.ts @@ -37,6 +37,7 @@ import { kLangCommentChars } from "../lib/partition-cell-options.ts"; import { generateTypesFromSchemas } from "./types-from-schema.ts"; import { generateJsonSchemasFromSchemas } from "./json-schema-from-schema.ts"; import { InternalError } from "../lib/error.ts"; +import { generateZodTypesFromSchemas } from "./zod-types-from-schema.ts"; //////////////////////////////////////////////////////////////////////////////// @@ -90,6 +91,7 @@ export async function buildIntelligenceResources() { await Promise.all([ generateTypesFromSchemas(path), generateJsonSchemasFromSchemas(path), + generateZodTypesFromSchemas(path), ]); } diff --git a/src/core/schema/types-from-schema.ts b/src/core/schema/types-from-schema.ts index da7c69c5e54..15a096447b3 100644 --- a/src/core/schema/types-from-schema.ts +++ b/src/core/schema/types-from-schema.ts @@ -26,16 +26,16 @@ export function typeNameFromSchemaName(schemaName: string) { return capitalize(toCapitalizationCase(schemaName.replaceAll("/", "-"))); } -function fmtSource(source: string) { +export function fmtSource(source: string) { return new Deno.Command(Deno.execPath(), { args: ["fmt", source], }).output(); } export const generatedSrcMessage = - `// This file is automatically generated by \`quarto build-js\`! Do not edit.", + `// This file is automatically generated by \`quarto dev-call build-artifacts\`! Do not edit.", // -// If you find yourself trying to rebuild types and \`quarto build-js\` won't run because +// If you find yourself trying to rebuild types and \`quarto dev-call build-artifacts\` won't run because // of bad type definitions, run the following: // $ cd $QUARTO_ROOT // $ ./package/dist/bin/tools/deno run --importmap=./src/import_map.json --allow-all ./package/src/common/create-schema-types.ts ./src/resources @@ -104,7 +104,7 @@ export async function generateTypesFromSchemas(resourcePath: string) { await fmtSource(join(resourcePath, "/types/schema-types.ts")); } -function yamlToTypeScriptKey(key: string) { +export function yamlToTypeScriptKey(key: string) { // if the key isn't a valid typescript identifier, quote it if (!/^[a-zA-Z_$][0-9a-zA-Z_$]*$/.test(key)) { return JSON.stringify(key); diff --git a/src/core/schema/zod-types-from-schema.ts b/src/core/schema/zod-types-from-schema.ts new file mode 100644 index 00000000000..df1aa4da26b --- /dev/null +++ b/src/core/schema/zod-types-from-schema.ts @@ -0,0 +1,448 @@ +/* + * zod-types-from-schema.ts + * + * Tools to convert our schemas to Zod schemas. + * + * Copyright (C) 2025 Posit Software, PBC + */ + +/* + +This file intentionally does not import from a number of quarto libraries +in order to minimize its dependencies. + +We do this because you might need to run this file to regenerate quarto types, +and it's possible that you're doing it from a position where `quarto` doesn't +itself run. + +*/ + +import { ensureDirSync } from "../../deno_ral/fs.ts"; +import { join } from "../../deno_ral/path.ts"; +import { + fmtSource, + generatedSrcMessage, + schemaDefinitions, + typeNameFromSchemaName, + yamlToTypeScriptKey, +} from "./types-from-schema.ts"; + +class ZodDeclarationFileBuilder { + constructor() { + this.declarations = []; + this.inferredTypes = []; + } + + declarations: string[] = []; + inferredTypes: string[] = []; + + // we keep track of the schemas we've seen so we can + // process resolve-ref schemas + schemas: Record = {}; + + // deno-lint-ignore no-explicit-any + addSchema(obj: { name: string; schema: any }, inComments = false) { + this.schemas[obj.name] = obj.schema; + const schemaName = typeNameFromSchemaName(obj.name); + const schema = obj.schema; + const zodType = this.convertToZodJsSource(schema); + const startComment = inComments ? "/* " : ""; + const endComment = inComments ? " */" : ""; + this.declarations.push( + `${startComment}export const Zod${schemaName} = ${zodType};${endComment}\n`, + ); + this.inferredTypes.push( + `${startComment}export type ${schemaName} = z.infer;${endComment}\n`, + ); + } + + fileSource(): string { + const zodDeclarations = this.declarations.join("\n"); + const zodInferredTypes = this.inferredTypes.join("\n"); + return `import { z } from "zod"; + +import { ZodSidebarContents, ZodNavigationItem, ZodNavigationItemObject } from "./handwritten-schema-types.ts"; +export { ZodSidebarContents, ZodNavigationItem, ZodNavigationItemObject } from "./handwritten-schema-types.ts"; + +${generatedSrcMessage} + +${zodDeclarations} +${zodInferredTypes}`; + } + + // deno-lint-ignore no-explicit-any + convertToZodJsSource(schema: any): string { + // we're doing a syntax-directed translation of the schema objects + // as they exist in our JSON format. Follow schema-schema-types.ts + // for the schema types themselves. + // + // To reiterate, we don't import that file directly here to avoid + // bootstrapping problems in the case the build process errors out. + + const result = this.convertSchemaNull(schema) || + this.convertSchemaEnum(schema) || + this.convertExplicitSchema(schema) || + this.convertStringSchema(schema) || + this.convertNumberSchema(schema) || + this.convertBooleanSchema(schema) || + this.convertResolveRefSchema(schema) || + this.convertRefSchema(schema) || + this.convertMaybeArrayOfSchema(schema) || + this.convertArrayOfSchema(schema) || + this.convertAllOfSchema(schema) || + this.convertAnyOfSchema(schema) || + this.convertRecordSchema(schema) || + this.convertObjectSchema(schema); + if (result) { + return result; + } + + throw new Error( + `Unsupported schema: ${JSON.stringify(schema, null, 2)}`, + ); + } + + // deno-lint-ignore no-explicit-any + convertSchemaEnum(schema: any): string | undefined { + // deno-lint-ignore no-explicit-any + const zodEnumDeclaration = (values: any[]) => { + // deno-lint-ignore no-explicit-any + const zodEnumValues = values.map((value: any) => { + return JSON.stringify(value); + }); + + // first filter out strings + const zodEnumStrings = zodEnumValues.filter((value) => { + return typeof value === "string"; + }).map((value) => { + return JSON.stringify(value); + }); + const zodLiterals = zodEnumValues.filter((x) => { + return typeof x !== "string"; + }); + const zodTypes: string[] = []; + + if (zodEnumStrings.length) { + zodTypes.push( + `z.enum([${zodEnumStrings.join(", ")}] as const)`, + ); + } + if (zodLiterals.length) { + zodTypes.push( + ...zodLiterals.map((x) => { + return `z.literal(${JSON.stringify(x)})`; + }).join(", "), + ); + } + if (zodTypes.length === 0) { + throw new Error( + `Unimplemented: ${JSON.stringify(schema, null, 2)}`, + ); + } else if (zodTypes.length === 1) { + return zodTypes[0]; + } else { + return `z.union([${zodTypes.join(", ")}])`; + } + }; + if (schema.enum) { + if (Array.isArray(schema.enum)) { + return zodEnumDeclaration(schema.enum); + } + return this.convertSchemaEnum(schema.enum); + } + if (schema.values) { + return zodEnumDeclaration(schema.values); + } + return undefined; + } + + // deno-lint-ignore no-explicit-any + convertSchemaNull(schema: any): string | undefined { + if (schema === "null" || schema === null || schema.null) { + return `z.null()`; + } + return undefined; + } + + // deno-lint-ignore no-explicit-any + convertExplicitSchema(schema: any): string | undefined { + if (schema.schema) { + return this.convertToZodJsSource(schema.schema); + } + return undefined; + } + + // deno-lint-ignore no-explicit-any + convertStringSchema(schema: any): string | undefined { + // SchemaExplicitPatternString + // SchemaString + if (schema === "string") { + return `z.string()`; + } + if (schema === "path") { + return `z.string()`; + } + // deno-lint-ignore no-explicit-any + const zodDeclStringWithMaybePattern = (innerSchema: any) => { + if (innerSchema.pattern) { + return `z.string().regex(new RegExp(${ + JSON.stringify( + innerSchema.pattern, + ) + }))`; + } else { + return `z.string()`; + } + }; + if (schema.path) { + return zodDeclStringWithMaybePattern(schema.path); + } + if (schema.string) { + return zodDeclStringWithMaybePattern(schema.string); + } + if (schema.pattern) { + return zodDeclStringWithMaybePattern(schema); + } + return undefined; + } + + // deno-lint-ignore no-explicit-any + convertNumberSchema(schema: any): string | undefined { + if (schema === "number") { + return `z.number()`; + } + if (schema.number) { + return `z.number()`; + } + return undefined; + } + + // deno-lint-ignore no-explicit-any + convertBooleanSchema(schema: any): string | undefined { + if (schema === "boolean") { + return `z.boolean()`; + } + if (schema.boolean) { + return `z.boolean()`; + } + return undefined; + } + + // deno-lint-ignore no-explicit-any + convertResolveRefSchema(schema: any): string | undefined { + if (schema.resolveRef) { + throw new Error("Unimplemented: convertResolveRefSchema"); + } + return undefined; + } + + // TODO: if this is a recursive schema, we need to know about that + // and change the top-level schema to have an explicit type annotation + // cf https://zod.dev/?id=recursive-types + // deno-lint-ignore no-explicit-any + convertRefSchema(schema: any): string | undefined { + if (schema.ref) { + const refName = typeNameFromSchemaName(schema.ref); + return `z.lazy(() => Zod${refName})`; + } + return undefined; + } + + // deno-lint-ignore no-explicit-any + convertMaybeArrayOfSchema(schema: any): string | undefined { + if (schema.maybeArrayOf) { + const innerSchema = schema.maybeArrayOf; + const innerZodType = this.convertToZodJsSource(innerSchema); + return `z.union([${innerZodType}, z.array(${innerZodType})])`; + } + return undefined; + } + + // deno-lint-ignore no-explicit-any + convertArrayOfSchema(schema: any): string | undefined { + if (schema.arrayOf) { + const innerSchema = schema.arrayOf; + const innerZodType = this.convertToZodJsSource(innerSchema); + return `z.array(${innerZodType})`; + } + return undefined; + } + + // deno-lint-ignore no-explicit-any + convertAllOfSchema(schema: any): string | undefined { + if (!schema.allOf) { + return undefined; + } + const innerSchemas = schema.allOf; + // deno-lint-ignore no-explicit-any + const innerZodTypes = innerSchemas.map((s: any) => { + return this.convertToZodJsSource(s); + }); + let first = innerZodTypes.shift(); + // z.intersection doesn't take more than 2 arguments + // so we need to chain sese together + while (innerZodTypes.length > 0) { + const innerZodType = innerZodTypes.shift(); + first = `${first}.and(${innerZodType})`; + } + return first; + } + + // deno-lint-ignore no-explicit-any + convertAnyOfSchema(schema: any): string | undefined { + if (!schema.anyOf) { + return undefined; + } + const innerSchemas = schema.anyOf; + // deno-lint-ignore no-explicit-any + const innerZodTypes = innerSchemas.map((s: any) => { + return this.convertToZodJsSource(s); + }); + return `z.union([${innerZodTypes.join(", ")}])`; + } + + // deno-lint-ignore no-explicit-any + convertRecordSchema(schema: any): string | undefined { + if (!schema.record) { + return undefined; + } + const properties = schema.record.properties || schema.record; + return `z.object({ ${ + Object.entries(properties).map(([key, value]) => { + return `${yamlToTypeScriptKey(key)}: ${ + this.convertToZodJsSource(value) + }`; + }) + }}).strict()`; + } + + // deno-lint-ignore no-explicit-any + convertObjectSchema(schema: any): string | undefined { + // deno-lint-ignore no-explicit-any + const getProperties = (s: any) => { + if (s.properties) { + return s.properties; + } + if (s.schema) { + return getProperties(s.schema); + } + if (s.object) { + return getProperties(s.object); + } + return {}; + }; + // deno-lint-ignore no-explicit-any + const resolveSuper = (s: any) => { + if (!s.super) { + return s; + } + // deno-lint-ignore no-explicit-any + let superSchemas: any[]; + if (Array.isArray(s.super)) { + superSchemas = s.super.map(resolveSuper); + } else if (s.super.resolveRef) { + superSchemas = [ + this.schemas[typeNameFromSchemaName(s.super.resolveRef)], + ]; + } else { + throw new Error( + `Unimplemented: ${JSON.stringify(s.super, null, 2)}`, + ); + } + // deno-lint-ignore no-explicit-any + const properties: Record = {}; + for (const superSchema of superSchemas) { + const superProperties = getProperties(superSchema); + for (const key in superProperties) { + properties[key] = superProperties[key]; + } + } + + return { + ...s, + properties: { + ...properties, + ...getProperties(s), + }, + }; + }; + if (schema === "object") { + return `z.object({}).passthrough()`; + } + if (!schema.object) { + return undefined; + } + let obj = schema.object; + if (obj.namingConvention) { + console.warn( + "namingConvention enforcement is not supported. Ignoring.", + ); + console.warn(JSON.stringify(obj, null, 2)); + } + const closed = obj.closed || false; + const required = obj.required; + obj = resolveSuper(obj); + let baseObj = `z.object({${ + Object.entries(obj.properties || {}).map(([key, value]) => { + return `${yamlToTypeScriptKey(key)}: ${ + this.convertToZodJsSource(value) + }`; + }).join(", ") + }})`; + if (closed) { + baseObj = `${baseObj}.strict()`; + } else { + baseObj = `${baseObj}.passthrough()`; + } + if (Array.isArray(required)) { + baseObj = `${baseObj}.partial({${ + required.map((key: string) => { + return `${key}: true`; + }).join(", ") + }})`; + } else if (required === undefined) { + baseObj = `${baseObj}.partial()`; + } + if (obj.additionalProperties) { + baseObj = `z.record(${ + this.convertToZodJsSource( + obj.additionalProperties, + ) + }).and(${baseObj})`; + } + + return baseObj; + } +} + +export async function generateZodTypesFromSchemas(resourcePath: string) { + const builder = new ZodDeclarationFileBuilder(); + const schemas = schemaDefinitions(resourcePath); + + const schemasToSkip = [ + "SidebarContents", + "NavigationItem", + "NavigationItemObject", + ]; + + for (const schema of schemas) { + builder.addSchema(schema, schemasToSkip.includes(schema.name)); + } + const zodTypes = builder.fileSource(); + const schemaSchemaSourcePath = join( + resourcePath, + "types", + "zod", + "schema-types.ts", + ); + ensureDirSync(join( + resourcePath, + "types", + "zod", + )); + Deno.writeTextFileSync( + schemaSchemaSourcePath, + zodTypes, + ); + await fmtSource(schemaSchemaSourcePath); +} diff --git a/src/import_map.json b/src/import_map.json index c1c370a7409..23c5fb371b2 100644 --- a/src/import_map.json +++ b/src/import_map.json @@ -26,6 +26,8 @@ "log": "jsr:/@std/log@0.224.0", "log/": "jsr:/@std/log@0.224.0/", + "zod": "npm:/zod@3.24.4", + "yaml": "https://cdn.skypack.dev/js-yaml", "events/": "https://deno.land/x/events@v1.0.0/", "cache/": "https://deno.land/x/cache@0.2.12/", diff --git a/src/resources/editor/tools/vs-code.mjs b/src/resources/editor/tools/vs-code.mjs index a5156b04bdf..12091aafe42 100644 --- a/src/resources/editor/tools/vs-code.mjs +++ b/src/resources/editor/tools/vs-code.mjs @@ -24345,12 +24345,12 @@ var require_yaml_intelligence_resources = __commonJS({ mermaid: "%%" }, "handlers/mermaid/schema.yml": { - _internalId: 190556, + _internalId: 195600, type: "object", description: "be an object", properties: { "mermaid-format": { - _internalId: 190548, + _internalId: 195592, type: "enum", enum: [ "png", @@ -24366,7 +24366,7 @@ var require_yaml_intelligence_resources = __commonJS({ exhaustiveCompletions: true }, theme: { - _internalId: 190555, + _internalId: 195599, type: "anyOf", anyOf: [ { diff --git a/src/resources/editor/tools/yaml/web-worker.js b/src/resources/editor/tools/yaml/web-worker.js index c88c1730ba0..cc7bcf9ea9d 100644 --- a/src/resources/editor/tools/yaml/web-worker.js +++ b/src/resources/editor/tools/yaml/web-worker.js @@ -24346,12 +24346,12 @@ try { mermaid: "%%" }, "handlers/mermaid/schema.yml": { - _internalId: 190556, + _internalId: 195600, type: "object", description: "be an object", properties: { "mermaid-format": { - _internalId: 190548, + _internalId: 195592, type: "enum", enum: [ "png", @@ -24367,7 +24367,7 @@ try { exhaustiveCompletions: true }, theme: { - _internalId: 190555, + _internalId: 195599, type: "anyOf", anyOf: [ { diff --git a/src/resources/editor/tools/yaml/yaml-intelligence-resources.json b/src/resources/editor/tools/yaml/yaml-intelligence-resources.json index d2caed321e1..cc307b696cf 100644 --- a/src/resources/editor/tools/yaml/yaml-intelligence-resources.json +++ b/src/resources/editor/tools/yaml/yaml-intelligence-resources.json @@ -17317,12 +17317,12 @@ "mermaid": "%%" }, "handlers/mermaid/schema.yml": { - "_internalId": 190556, + "_internalId": 195600, "type": "object", "description": "be an object", "properties": { "mermaid-format": { - "_internalId": 190548, + "_internalId": 195592, "type": "enum", "enum": [ "png", @@ -17338,7 +17338,7 @@ "exhaustiveCompletions": true }, "theme": { - "_internalId": 190555, + "_internalId": 195599, "type": "anyOf", "anyOf": [ { diff --git a/src/resources/types/schema-types.ts b/src/resources/types/schema-types.ts index 583f7fa7e92..29ed0532749 100644 --- a/src/resources/types/schema-types.ts +++ b/src/resources/types/schema-types.ts @@ -1,6 +1,6 @@ -// This file is automatically generated by `quarto build-js`! Do not edit.", +// This file is automatically generated by `quarto dev-call build-artifacts`! Do not edit.", // -// If you find yourself trying to rebuild types and `quarto build-js` won't run because +// If you find yourself trying to rebuild types and `quarto dev-call build-artifacts` won't run because // of bad type definitions, run the following: // $ cd $QUARTO_ROOT // $ ./package/dist/bin/tools/deno run --importmap=./src/import_map.json --allow-all ./package/src/common/create-schema-types.ts ./src/resources diff --git a/src/resources/types/zod/handwritten-schema-types.ts b/src/resources/types/zod/handwritten-schema-types.ts new file mode 100644 index 00000000000..1ea74e0032a --- /dev/null +++ b/src/resources/types/zod/handwritten-schema-types.ts @@ -0,0 +1,118 @@ +/* + * handwritten-schema-types.ts + * + * Some Zod schemas that are easier to write by hand than to generate: + * + * - SidebarContents + * - NavigationItem + * + * If you need to change these, the easiest way is to generate the + * schema-types.ts file, and then start from the commented-out, + * slightly incorrect version of the zod schemas to edit. + * + * We're using the strategy illustrated in https://zod.dev/?id=recursive-types + * to solve these recursive types. + * + * Copyright (C) 2025 Posit Software, PBC + */ + +import { z } from "zod"; + +import { ZodContentsAuto } from "./schema-types.ts"; + +import { type ContentsAuto } from "./schema-types.ts"; + +const Base_ZodSidebarContentsObject = z.object({ + section: z.union([z.string(), z.null()]), + /* hide the recursive declaration + + contents: z.lazy(() => ZodSidebarContents), + */ +}).strict().partial(); + +export type SidebarContentsObject = + & z.infer + & { + contents: SidebarContents; + }; + +const ZodSidebarContentsObject: z.ZodType = + Base_ZodSidebarContentsObject.extend({ + contents: z.lazy(() => ZodSidebarContents), + }); + +const Base_ZodSidebarContents = z.union([ + z.string(), + z.lazy(() => ZodContentsAuto), + /* hide the recursive declaration + + z.array(z.union([ + z.lazy(() => ZodNavigationItem), + z.string(), + z.lazy(() => ZodSidebarContentsObject), + z.lazy(() => ZodContentsAuto), + ])), + */ +]); + +export type SidebarContents = + | z.infer + | (NavigationItem | string | SidebarContentsObject | ContentsAuto)[]; + +export const ZodSidebarContents = z.union([ + Base_ZodSidebarContents, + z.array(z.union([ + z.lazy(() => ZodNavigationItem), + z.string(), + z.lazy(() => ZodSidebarContentsObject), + z.lazy(() => ZodContentsAuto), + ])), +]); + +/* export type NavigationItem = z.infer; */ + +/* export type NavigationItemObject = z.infer; */ + +/* export type SidebarContents = z.infer; */ + +export const Base_ZodNavigationItem = z.string(); +/* + hide the recursive declaration +z.union([ + z.string(), + z.lazy(() => ZodNavigationItemObject), +]); +*/ +export type NavigationItem = + | z.infer + | NavigationItemObject; + +export const ZodNavigationItem = z.union([ + Base_ZodNavigationItem, + z.lazy(() => ZodNavigationItemObject), +]); + +export const Base_ZodNavigationItemObject = z.object({ + "aria-label": z.string(), + file: z.string(), + href: z.string(), + icon: z.string(), + id: z.string(), + /* hide the recursive declaration + + menu: z.array(z.lazy(() => ZodNavigationItem)), + */ + text: z.string(), + url: z.string(), + rel: z.string(), + target: z.string(), +}).strict().partial(); + +type NavigationItemObject = z.infer & { + menu: NavigationItem[]; +}; + +export const ZodNavigationItemObject: z.ZodType = + Base_ZodNavigationItemObject.extend({ + menu: z.array(z.lazy(() => ZodNavigationItem)), + }); diff --git a/src/resources/types/zod/schema-types.ts b/src/resources/types/zod/schema-types.ts new file mode 100644 index 00000000000..59ab93ecdcc --- /dev/null +++ b/src/resources/types/zod/schema-types.ts @@ -0,0 +1,2004 @@ +import { z } from "zod"; + +import { + ZodNavigationItem, + ZodNavigationItemObject, + ZodSidebarContents, +} from "./handwritten-schema-types.ts"; +export { + ZodNavigationItem, + ZodNavigationItemObject, + ZodSidebarContents, +} from "./handwritten-schema-types.ts"; + +// This file is automatically generated by `quarto dev-call build-artifacts`! Do not edit.", +// +// If you find yourself trying to rebuild types and `quarto dev-call build-artifacts` won't run because +// of bad type definitions, run the following: +// $ cd $QUARTO_ROOT +// $ ./package/dist/bin/tools/deno run --importmap=./src/import_map.json --allow-all ./package/src/common/create-schema-types.ts ./src/resources + +export type MaybeArrayOf = T | T[]; +export type JsonObject = { [key: string]: unknown }; +// export type SchemaObject = { [key: string]: string }; + +export const ZodDate = z.union([ + z.string(), + z.object({ value: z.string(), format: z.string() }).passthrough().partial({ + value: true, + }), +]); + +export const ZodMathMethods = z.enum( + [ + '"plain"', + '"webtex"', + '"gladtex"', + '"mathml"', + '"mathjax"', + '"katex"', + ] as const, +); + +export const ZodPandocFormatRequestHeaders = z.array(z.array(z.string())); + +export const ZodPandocFormatOutputFile = z.union([ + z.string(), + z.enum(["null"] as const), +]); + +export const ZodPandocFormatFilters = z.array( + z.union([ + z.string(), + z.object({ type: z.string(), path: z.string() }).passthrough().partial({ + path: true, + }), + z.object({ + type: z.string(), + path: z.string(), + at: z.enum( + [ + '"pre-ast"', + '"post-ast"', + '"pre-quarto"', + '"post-quarto"', + '"pre-render"', + '"post-render"', + ] as const, + ), + }).passthrough().partial({ path: true, at: true }), + z.object({ type: z.enum(['"citeproc"'] as const) }).strict(), + ]), +); + +export const ZodPandocShortcodes = z.array(z.string()); + +export const ZodPageColumn = z.enum( + [ + '"body"', + '"body-outset"', + '"body-outset-left"', + '"body-outset-right"', + '"page"', + '"page-left"', + '"page-right"', + '"page-inset"', + '"page-inset-left"', + '"page-inset-right"', + '"screen"', + '"screen-left"', + '"screen-right"', + '"screen-inset"', + '"screen-inset-shaded"', + '"screen-inset-left"', + '"screen-inset-right"', + '"margin"', + ] as const, +); + +export const ZodContentsAuto = z.object({ + auto: z.union([z.boolean(), z.union([z.string(), z.array(z.string())])]), +}).passthrough().partial(); + +/* export const ZodNavigationItem = z.union([z.string(), z.lazy(() => ZodNavigationItemObject)]); */ + +/* export const ZodNavigationItemObject = z.object({"aria-label": z.string(), file: z.string(), href: z.string(), icon: z.string(), id: z.string(), menu: z.array(z.lazy(() => ZodNavigationItem)), text: z.string(), url: z.string(), rel: z.string(), target: z.string()}).strict().partial(); */ + +export const ZodGiscusThemes = z.enum( + [ + '"light"', + '"light_high_contrast"', + '"light_protanopia"', + '"light_tritanopia"', + '"dark"', + '"dark_high_contrast"', + '"dark_protanopia"', + '"dark_tritanopia"', + '"dark_dimmed"', + '"transparent_dark"', + '"cobalt"', + '"purple_dark"', + '"noborder_light"', + '"noborder_dark"', + '"noborder_gray"', + '"preferred_color_scheme"', + ] as const, +); + +export const ZodGiscusConfiguration = z.object({ + repo: z.string(), + "repo-id": z.string(), + category: z.string(), + "category-id": z.string(), + mapping: z.union([ + z.enum(['"pathname"', '"url"', '"title"', '"og:title"'] as const), + z.string(), + ]), + "reactions-enabled": z.boolean(), + loading: z.enum(['"lazy"'] as const), + "input-position": z.enum(['"top"', '"bottom"'] as const), + theme: z.union([ + z.string(), + z.lazy(() => ZodGiscusThemes), + z.object({ + light: z.union([z.string(), z.lazy(() => ZodGiscusThemes)]), + dark: z.union([z.string(), z.lazy(() => ZodGiscusThemes)]), + }).strict().partial(), + ]), + language: z.string(), +}).strict().partial({ repo: true }); + +export const ZodDocumentCommentsConfiguration = z.union([ + z.enum(["false"] as const), + z.object({ + utterances: z.object({ + repo: z.string(), + label: z.string(), + theme: z.string(), + "issue-term": z.string(), + }).strict().partial({ repo: true }), + giscus: z.lazy(() => ZodGiscusConfiguration), + hypothesis: z.union([ + z.boolean(), + z.object({ + "client-url": z.string(), + openSidebar: z.boolean(), + showHighlights: z.union([ + z.boolean(), + z.enum(['"always"', '"whenSidebarOpen"', '"never"'] as const), + ]), + theme: z.enum(['"classic"', '"clean"'] as const), + enableExperimentalNewNoteButton: z.boolean(), + usernameUrl: z.string(), + services: z.array( + z.object({ + apiUrl: z.string(), + authority: z.string(), + grantToken: z.string(), + allowLeavingGroups: z.boolean(), + enableShareLinks: z.boolean(), + groups: z.union([ + z.enum(['"$rpc:requestGroups"'] as const), + z.array(z.string()), + ]), + icon: z.string(), + }).passthrough().partial({ + apiUrl: true, + authority: true, + grantToken: true, + }), + ), + branding: z.object({ + accentColor: z.string(), + appBackgroundColor: z.string(), + ctaBackgroundColor: z.string(), + selectionFontFamily: z.string(), + annotationFontFamily: z.string(), + }).passthrough().partial(), + externalContainerSelector: z.string(), + focus: z.object({ + user: z.object({ + username: z.string(), + userid: z.string(), + displayName: z.string(), + }).passthrough().partial(), + }).passthrough().partial({ user: true }), + requestConfigFromFrame: z.object({ + origin: z.string(), + ancestorLevel: z.number(), + }).passthrough().partial(), + assetRoot: z.string(), + sidebarAppUrl: z.string(), + }).strict().partial(), + ]), + }).strict().partial(), +]); + +export const ZodSocialMetadata = z.object({ + title: z.string(), + description: z.string(), + image: z.string(), + "image-alt": z.string(), + "image-width": z.number(), + "image-height": z.number(), +}).strict().partial(); + +export const ZodPageFooterRegion = z.union([ + z.string(), + z.array(z.lazy(() => ZodNavigationItem)), +]); + +/* export const ZodSidebarContents = z.union([z.string(), z.lazy(() => ZodContentsAuto), z.array(z.union([z.lazy(() => ZodNavigationItem), z.string(), z.object({section: z.union([z.string(), z.null()]), contents: z.lazy(() => ZodSidebarContents)}).strict().partial(), z.lazy(() => ZodContentsAuto)]))]); */ + +export const ZodProjectPreview = z.object({ + port: z.number(), + host: z.string(), + serve: z.lazy(() => ZodProjectServe), + browser: z.boolean(), + "watch-inputs": z.boolean(), + navigate: z.boolean(), + timeout: z.number(), +}).strict().partial(); + +export const ZodProjectServe = z.object({ + cmd: z.string(), + args: z.string(), + env: z.object({}).passthrough().partial(), + ready: z.string(), +}).strict().partial({ cmd: true, ready: true }); + +export const ZodPublish = z.object({ + netlify: z.array(z.lazy(() => ZodPublishRecord)), +}).strict().partial(); + +export const ZodPublishRecord = z.object({ id: z.string(), url: z.string() }) + .strict().partial(); + +export const ZodTwitterCardConfig = z.object({ + title: z.string(), + description: z.string(), + image: z.string(), + "image-alt": z.string(), + "image-width": z.number(), + "image-height": z.number(), + "card-style": z.enum(['"summary"', '"summary_large_image"'] as const), + creator: z.string(), + site: z.string(), +}).strict().partial(); + +export const ZodOpenGraphConfig = z.object({ + title: z.string(), + description: z.string(), + image: z.string(), + "image-alt": z.string(), + "image-width": z.number(), + "image-height": z.number(), + locale: z.string(), + "site-name": z.string(), +}).strict().partial(); + +export const ZodPageFooter = z.object({ + left: z.lazy(() => ZodPageFooterRegion), + right: z.lazy(() => ZodPageFooterRegion), + center: z.lazy(() => ZodPageFooterRegion), + border: z.union([z.boolean(), z.string()]), + background: z.string(), + foreground: z.string(), +}).strict().partial(); + +export const ZodBaseWebsite = z.object({ + title: z.string(), + description: z.string(), + favicon: z.string(), + "site-url": z.string(), + "site-path": z.string(), + "repo-url": z.string(), + "repo-link-target": z.string(), + "repo-link-rel": z.string(), + "repo-subdir": z.string(), + "repo-branch": z.string(), + "issue-url": z.string(), + "repo-actions": z.union([ + z.enum(['"none"', '"edit"', '"source"', '"issue"'] as const), + z.array(z.enum(['"none"', '"edit"', '"source"', '"issue"'] as const)), + ]), + "reader-mode": z.boolean(), + "google-analytics": z.union([ + z.string(), + z.object({ + "tracking-id": z.string(), + storage: z.enum(['"cookies"', '"none"'] as const), + "anonymize-ip": z.boolean(), + version: z.enum(["3", "4"] as const), + }).passthrough().partial(), + ]), + announcement: z.union([ + z.string(), + z.object({ + content: z.string(), + dismissable: z.boolean(), + icon: z.string(), + position: z.enum(['"above-navbar"', '"below-navbar"'] as const), + type: z.enum( + [ + '"primary"', + '"secondary"', + '"success"', + '"danger"', + '"warning"', + '"info"', + '"light"', + '"dark"', + ] as const, + ), + }).passthrough().partial(), + ]), + "cookie-consent": z.union([ + z.enum(['"express"', '"implied"'] as const), + z.boolean(), + z.object({ + type: z.enum(['"implied"', '"express"'] as const), + style: z.enum( + ['"simple"', '"headline"', '"interstitial"', '"standalone"'] as const, + ), + palette: z.enum(['"light"', '"dark"'] as const), + "policy-url": z.string(), + language: z.string(), + "prefs-text": z.string(), + }).passthrough().partial(), + ]), + search: z.union([ + z.boolean(), + z.object({ + location: z.enum(['"navbar"', '"sidebar"'] as const), + type: z.enum(['"overlay"', '"textbox"'] as const), + limit: z.number(), + "collapse-after": z.number(), + "copy-button": z.boolean(), + "merge-navbar-crumbs": z.boolean(), + "keyboard-shortcut": z.union([z.string(), z.array(z.string())]), + "show-item-context": z.union([ + z.enum(['"tree"', '"parent"', '"root"'] as const), + z.boolean(), + ]), + algolia: z.object({ + "index-name": z.string(), + "application-id": z.string(), + "search-only-api-key": z.string(), + "analytics-events": z.boolean(), + "show-logo": z.boolean(), + "index-fields": z.object({ + href: z.string(), + title: z.string(), + text: z.string(), + section: z.string(), + }).strict().partial(), + params: z.object({}).passthrough().partial(), + }).strict().partial(), + }).strict().partial(), + ]), + navbar: z.union([ + z.boolean(), + z.object({ + title: z.union([z.string(), z.boolean()]), + logo: z.string(), + "logo-alt": z.string(), + "logo-href": z.string(), + background: z.union([ + z.enum( + [ + '"primary"', + '"secondary"', + '"success"', + '"danger"', + '"warning"', + '"info"', + '"light"', + '"dark"', + ] as const, + ), + z.string(), + ]), + foreground: z.union([ + z.enum( + [ + '"primary"', + '"secondary"', + '"success"', + '"danger"', + '"warning"', + '"info"', + '"light"', + '"dark"', + ] as const, + ), + z.string(), + ]), + search: z.boolean(), + pinned: z.boolean(), + collapse: z.boolean(), + "collapse-below": z.enum( + ['"sm"', '"md"', '"lg"', '"xl"', '"xxl"'] as const, + ), + left: z.array(z.lazy(() => ZodNavigationItem)), + right: z.array(z.lazy(() => ZodNavigationItem)), + "toggle-position": z.enum(['"left"', '"right"'] as const), + "tools-collapse": z.boolean(), + }).passthrough().partial(), + ]), + sidebar: z.union([ + z.boolean(), + z.union([ + z.object({ + id: z.string(), + title: z.union([z.string(), z.boolean()]), + logo: z.string(), + "logo-alt": z.string(), + "logo-href": z.string(), + search: z.boolean(), + tools: z.array(z.lazy(() => ZodNavigationItemObject)), + contents: z.lazy(() => ZodSidebarContents), + style: z.enum(['"docked"', '"floating"'] as const), + background: z.union([ + z.enum( + [ + '"primary"', + '"secondary"', + '"success"', + '"danger"', + '"warning"', + '"info"', + '"light"', + '"dark"', + ] as const, + ), + z.string(), + ]), + foreground: z.union([ + z.enum( + [ + '"primary"', + '"secondary"', + '"success"', + '"danger"', + '"warning"', + '"info"', + '"light"', + '"dark"', + ] as const, + ), + z.string(), + ]), + border: z.boolean(), + alignment: z.enum(['"left"', '"right"', '"center"'] as const), + "collapse-level": z.number(), + pinned: z.boolean(), + header: z.union([z.string(), z.array(z.string())]), + footer: z.union([z.string(), z.array(z.string())]), + }).passthrough().partial(), + z.array( + z.object({ + id: z.string(), + title: z.union([z.string(), z.boolean()]), + logo: z.string(), + "logo-alt": z.string(), + "logo-href": z.string(), + search: z.boolean(), + tools: z.array(z.lazy(() => ZodNavigationItemObject)), + contents: z.lazy(() => ZodSidebarContents), + style: z.enum(['"docked"', '"floating"'] as const), + background: z.union([ + z.enum( + [ + '"primary"', + '"secondary"', + '"success"', + '"danger"', + '"warning"', + '"info"', + '"light"', + '"dark"', + ] as const, + ), + z.string(), + ]), + foreground: z.union([ + z.enum( + [ + '"primary"', + '"secondary"', + '"success"', + '"danger"', + '"warning"', + '"info"', + '"light"', + '"dark"', + ] as const, + ), + z.string(), + ]), + border: z.boolean(), + alignment: z.enum(['"left"', '"right"', '"center"'] as const), + "collapse-level": z.number(), + pinned: z.boolean(), + header: z.union([z.string(), z.array(z.string())]), + footer: z.union([z.string(), z.array(z.string())]), + }).passthrough().partial(), + ), + ]), + ]), + "body-header": z.string(), + "body-footer": z.string(), + "margin-header": z.union([z.string(), z.array(z.string())]), + "margin-footer": z.union([z.string(), z.array(z.string())]), + "page-navigation": z.boolean(), + "back-to-top-navigation": z.boolean(), + "bread-crumbs": z.boolean(), + "page-footer": z.union([z.string(), z.lazy(() => ZodPageFooter)]), + image: z.string(), + "image-alt": z.string(), + comments: z.lazy(() => ZodDocumentCommentsConfiguration), + "open-graph": z.union([z.boolean(), z.lazy(() => ZodOpenGraphConfig)]), + "twitter-card": z.union([z.boolean(), z.lazy(() => ZodTwitterCardConfig)]), + "other-links": z.lazy(() => ZodOtherLinks), + "code-links": z.union([z.boolean(), z.lazy(() => ZodCodeLinksSchema)]), + drafts: z.union([z.string(), z.array(z.string())]), + "draft-mode": z.enum(['"visible"', '"unlinked"', '"gone"'] as const), +}).strict().partial(); + +export const ZodBookSchema = z.object({ + title: z.string(), + description: z.string(), + favicon: z.string(), + "site-url": z.string(), + "site-path": z.string(), + "repo-url": z.string(), + "repo-link-target": z.string(), + "repo-link-rel": z.string(), + "repo-subdir": z.string(), + "repo-branch": z.string(), + "issue-url": z.string(), + "repo-actions": z.union([ + z.enum(['"none"', '"edit"', '"source"', '"issue"'] as const), + z.array(z.enum(['"none"', '"edit"', '"source"', '"issue"'] as const)), + ]), + "reader-mode": z.boolean(), + "google-analytics": z.union([ + z.string(), + z.object({ + "tracking-id": z.string(), + storage: z.enum(['"cookies"', '"none"'] as const), + "anonymize-ip": z.boolean(), + version: z.enum(["3", "4"] as const), + }).passthrough().partial(), + ]), + announcement: z.union([ + z.string(), + z.object({ + content: z.string(), + dismissable: z.boolean(), + icon: z.string(), + position: z.enum(['"above-navbar"', '"below-navbar"'] as const), + type: z.enum( + [ + '"primary"', + '"secondary"', + '"success"', + '"danger"', + '"warning"', + '"info"', + '"light"', + '"dark"', + ] as const, + ), + }).passthrough().partial(), + ]), + "cookie-consent": z.union([ + z.enum(['"express"', '"implied"'] as const), + z.boolean(), + z.object({ + type: z.enum(['"implied"', '"express"'] as const), + style: z.enum( + ['"simple"', '"headline"', '"interstitial"', '"standalone"'] as const, + ), + palette: z.enum(['"light"', '"dark"'] as const), + "policy-url": z.string(), + language: z.string(), + "prefs-text": z.string(), + }).passthrough().partial(), + ]), + search: z.union([ + z.boolean(), + z.object({ + location: z.enum(['"navbar"', '"sidebar"'] as const), + type: z.enum(['"overlay"', '"textbox"'] as const), + limit: z.number(), + "collapse-after": z.number(), + "copy-button": z.boolean(), + "merge-navbar-crumbs": z.boolean(), + "keyboard-shortcut": z.union([z.string(), z.array(z.string())]), + "show-item-context": z.union([ + z.enum(['"tree"', '"parent"', '"root"'] as const), + z.boolean(), + ]), + algolia: z.object({ + "index-name": z.string(), + "application-id": z.string(), + "search-only-api-key": z.string(), + "analytics-events": z.boolean(), + "show-logo": z.boolean(), + "index-fields": z.object({ + href: z.string(), + title: z.string(), + text: z.string(), + section: z.string(), + }).strict().partial(), + params: z.object({}).passthrough().partial(), + }).strict().partial(), + }).strict().partial(), + ]), + navbar: z.union([ + z.boolean(), + z.object({ + title: z.union([z.string(), z.boolean()]), + logo: z.string(), + "logo-alt": z.string(), + "logo-href": z.string(), + background: z.union([ + z.enum( + [ + '"primary"', + '"secondary"', + '"success"', + '"danger"', + '"warning"', + '"info"', + '"light"', + '"dark"', + ] as const, + ), + z.string(), + ]), + foreground: z.union([ + z.enum( + [ + '"primary"', + '"secondary"', + '"success"', + '"danger"', + '"warning"', + '"info"', + '"light"', + '"dark"', + ] as const, + ), + z.string(), + ]), + search: z.boolean(), + pinned: z.boolean(), + collapse: z.boolean(), + "collapse-below": z.enum( + ['"sm"', '"md"', '"lg"', '"xl"', '"xxl"'] as const, + ), + left: z.array(z.lazy(() => ZodNavigationItem)), + right: z.array(z.lazy(() => ZodNavigationItem)), + "toggle-position": z.enum(['"left"', '"right"'] as const), + "tools-collapse": z.boolean(), + }).passthrough().partial(), + ]), + sidebar: z.union([ + z.boolean(), + z.union([ + z.object({ + id: z.string(), + title: z.union([z.string(), z.boolean()]), + logo: z.string(), + "logo-alt": z.string(), + "logo-href": z.string(), + search: z.boolean(), + tools: z.array(z.lazy(() => ZodNavigationItemObject)), + contents: z.lazy(() => ZodSidebarContents), + style: z.enum(['"docked"', '"floating"'] as const), + background: z.union([ + z.enum( + [ + '"primary"', + '"secondary"', + '"success"', + '"danger"', + '"warning"', + '"info"', + '"light"', + '"dark"', + ] as const, + ), + z.string(), + ]), + foreground: z.union([ + z.enum( + [ + '"primary"', + '"secondary"', + '"success"', + '"danger"', + '"warning"', + '"info"', + '"light"', + '"dark"', + ] as const, + ), + z.string(), + ]), + border: z.boolean(), + alignment: z.enum(['"left"', '"right"', '"center"'] as const), + "collapse-level": z.number(), + pinned: z.boolean(), + header: z.union([z.string(), z.array(z.string())]), + footer: z.union([z.string(), z.array(z.string())]), + }).passthrough().partial(), + z.array( + z.object({ + id: z.string(), + title: z.union([z.string(), z.boolean()]), + logo: z.string(), + "logo-alt": z.string(), + "logo-href": z.string(), + search: z.boolean(), + tools: z.array(z.lazy(() => ZodNavigationItemObject)), + contents: z.lazy(() => ZodSidebarContents), + style: z.enum(['"docked"', '"floating"'] as const), + background: z.union([ + z.enum( + [ + '"primary"', + '"secondary"', + '"success"', + '"danger"', + '"warning"', + '"info"', + '"light"', + '"dark"', + ] as const, + ), + z.string(), + ]), + foreground: z.union([ + z.enum( + [ + '"primary"', + '"secondary"', + '"success"', + '"danger"', + '"warning"', + '"info"', + '"light"', + '"dark"', + ] as const, + ), + z.string(), + ]), + border: z.boolean(), + alignment: z.enum(['"left"', '"right"', '"center"'] as const), + "collapse-level": z.number(), + pinned: z.boolean(), + header: z.union([z.string(), z.array(z.string())]), + footer: z.union([z.string(), z.array(z.string())]), + }).passthrough().partial(), + ), + ]), + ]), + "body-header": z.string(), + "body-footer": z.string(), + "margin-header": z.union([z.string(), z.array(z.string())]), + "margin-footer": z.union([z.string(), z.array(z.string())]), + "page-navigation": z.boolean(), + "back-to-top-navigation": z.boolean(), + "bread-crumbs": z.boolean(), + "page-footer": z.union([z.string(), z.lazy(() => ZodPageFooter)]), + image: z.string(), + "image-alt": z.string(), + comments: z.lazy(() => ZodDocumentCommentsConfiguration), + "open-graph": z.union([z.boolean(), z.lazy(() => ZodOpenGraphConfig)]), + "twitter-card": z.union([z.boolean(), z.lazy(() => ZodTwitterCardConfig)]), + "other-links": z.lazy(() => ZodOtherLinks), + "code-links": z.union([z.boolean(), z.lazy(() => ZodCodeLinksSchema)]), + drafts: z.union([z.string(), z.array(z.string())]), + "draft-mode": z.enum(['"visible"', '"unlinked"', '"gone"'] as const), + subtitle: z.string(), + author: z.union([ + z.union([z.string(), z.object({}).passthrough()]), + z.array(z.union([z.string(), z.object({}).passthrough()])), + ]), + date: z.string(), + "date-format": z.string(), + abstract: z.string(), + chapters: z.lazy(() => ZodChapterList), + appendices: z.lazy(() => ZodChapterList), + references: z.string(), + "output-file": z.string(), + "cover-image": z.string(), + "cover-image-alt": z.string(), + sharing: z.union([ + z.enum(['"twitter"', '"facebook"', '"linkedin"'] as const), + z.array(z.enum(['"twitter"', '"facebook"', '"linkedin"'] as const)), + ]), + downloads: z.union([ + z.enum(['"pdf"', '"epub"', '"docx"'] as const), + z.array(z.enum(['"pdf"', '"epub"', '"docx"'] as const)), + ]), + tools: z.array(z.lazy(() => ZodNavigationItem)), + doi: z.string(), +}).strict().partial(); + +export const ZodChapterItem = z.union([ + z.lazy(() => ZodNavigationItem), + z.object({ + part: z.string(), + chapters: z.array(z.lazy(() => ZodNavigationItem)), + }).passthrough().partial({ part: true }), +]); + +export const ZodChapterList = z.array(z.lazy(() => ZodChapterItem)); + +export const ZodOtherLinks = z.array( + z.object({ + text: z.string(), + href: z.string(), + icon: z.string(), + rel: z.string(), + target: z.string(), + }).passthrough().partial({ text: true, href: true }), +); + +export const ZodCrossrefLabelsSchema = z.string(); + +export const ZodEpubContributor = z.union([ + z.string(), + z.union([ + z.object({ role: z.string(), "file-as": z.string(), text: z.string() }) + .strict().partial(), + z.array( + z.object({ role: z.string(), "file-as": z.string(), text: z.string() }) + .strict().partial(), + ), + ]), +]); + +export const ZodFormatLanguage = z.object({ + "toc-title-document": z.string(), + "toc-title-website": z.string(), + "related-formats-title": z.string(), + "related-notebooks-title": z.string(), + "callout-tip-title": z.string(), + "callout-note-title": z.string(), + "callout-warning-title": z.string(), + "callout-important-title": z.string(), + "callout-caution-title": z.string(), + "section-title-abstract": z.string(), + "section-title-footnotes": z.string(), + "section-title-appendices": z.string(), + "code-summary": z.string(), + "code-tools-menu-caption": z.string(), + "code-tools-show-all-code": z.string(), + "code-tools-hide-all-code": z.string(), + "code-tools-view-source": z.string(), + "code-tools-source-code": z.string(), + "search-no-results-text": z.string(), + "copy-button-tooltip": z.string(), + "copy-button-tooltip-success": z.string(), + "repo-action-links-edit": z.string(), + "repo-action-links-source": z.string(), + "repo-action-links-issue": z.string(), + "search-matching-documents-text": z.string(), + "search-copy-link-title": z.string(), + "search-hide-matches-text": z.string(), + "search-more-match-text": z.string(), + "search-more-matches-text": z.string(), + "search-clear-button-title": z.string(), + "search-text-placeholder": z.string(), + "search-detached-cancel-button-title": z.string(), + "search-submit-button-title": z.string(), + "crossref-fig-title": z.string(), + "crossref-tbl-title": z.string(), + "crossref-lst-title": z.string(), + "crossref-thm-title": z.string(), + "crossref-lem-title": z.string(), + "crossref-cor-title": z.string(), + "crossref-prp-title": z.string(), + "crossref-cnj-title": z.string(), + "crossref-def-title": z.string(), + "crossref-exm-title": z.string(), + "crossref-exr-title": z.string(), + "crossref-fig-prefix": z.string(), + "crossref-tbl-prefix": z.string(), + "crossref-lst-prefix": z.string(), + "crossref-ch-prefix": z.string(), + "crossref-apx-prefix": z.string(), + "crossref-sec-prefix": z.string(), + "crossref-eq-prefix": z.string(), + "crossref-thm-prefix": z.string(), + "crossref-lem-prefix": z.string(), + "crossref-cor-prefix": z.string(), + "crossref-prp-prefix": z.string(), + "crossref-cnj-prefix": z.string(), + "crossref-def-prefix": z.string(), + "crossref-exm-prefix": z.string(), + "crossref-exr-prefix": z.string(), + "crossref-lof-title": z.string(), + "crossref-lot-title": z.string(), + "crossref-lol-title": z.string(), +}).passthrough().partial(); + +export const ZodWebsiteAbout = z.object({ + id: z.string(), + template: z.union([ + z.enum( + [ + '"jolla"', + '"trestles"', + '"solana"', + '"marquee"', + '"broadside"', + ] as const, + ), + z.string(), + ]), + image: z.string(), + "image-alt": z.string(), + "image-title": z.string(), + "image-width": z.string(), + "image-shape": z.enum(['"rectangle"', '"round"', '"rounded"'] as const), + links: z.array(z.lazy(() => ZodNavigationItem)), +}).strict().partial({ template: true }); + +export const ZodWebsiteListing = z.object({ + id: z.string(), + type: z.enum(['"default"', '"table"', '"grid"', '"custom"'] as const), + contents: z.union([ + z.union([z.string(), z.lazy(() => ZodWebsiteListingContentsObject)]), + z.array( + z.union([z.string(), z.lazy(() => ZodWebsiteListingContentsObject)]), + ), + ]), + sort: z.union([z.boolean(), z.union([z.string(), z.array(z.string())])]), + "max-items": z.number(), + "page-size": z.number(), + "sort-ui": z.union([z.boolean(), z.array(z.string())]), + "filter-ui": z.union([z.boolean(), z.array(z.string())]), + categories: z.union([ + z.boolean(), + z.enum(['"numbered"', '"unnumbered"', '"cloud"'] as const), + ]), + feed: z.union([ + z.boolean(), + z.object({ + items: z.number(), + type: z.enum(['"full"', '"partial"', '"metadata"'] as const), + title: z.string(), + image: z.string(), + description: z.string(), + language: z.string(), + categories: z.union([z.string(), z.array(z.string())]), + "xml-stylesheet": z.string(), + }).strict().partial(), + ]), + "date-format": z.string(), + "max-description-length": z.number(), + "image-placeholder": z.string(), + "image-lazy-loading": z.boolean(), + "image-align": z.enum(['"left"', '"right"'] as const), + "image-height": z.string(), + "grid-columns": z.number(), + "grid-item-border": z.boolean(), + "grid-item-align": z.enum(['"left"', '"right"', '"center"'] as const), + "table-striped": z.boolean(), + "table-hover": z.boolean(), + template: z.string(), + "template-params": z.object({}).passthrough(), + fields: z.array(z.string()), + "field-display-names": z.object({}).passthrough().partial(), + "field-types": z.object({}).passthrough().partial(), + "field-links": z.array(z.string()), + "field-required": z.array(z.string()), + include: z.union([ + z.object({}).passthrough(), + z.array(z.object({}).passthrough()), + ]), + exclude: z.union([ + z.object({}).passthrough(), + z.array(z.object({}).passthrough()), + ]), +}).strict().partial(); + +export const ZodWebsiteListingContentsObject = z.object({ + author: z.union([z.string(), z.array(z.string())]), + date: z.string(), + title: z.string(), + subtitle: z.string(), +}).passthrough().partial(); + +export const ZodCslDate = z.union([ + z.string(), + z.union([z.number(), z.array(z.number())]), + z.object({ year: z.number(), month: z.number(), day: z.number() }) + .passthrough().partial(), +]); + +export const ZodCslPerson = z.union([ + z.union([z.string(), z.array(z.string())]), + z.union([ + z.object({ "family-name": z.string(), "given-name": z.string() }) + .passthrough().partial(), + z.array( + z.object({ "family-name": z.string(), "given-name": z.string() }) + .passthrough().partial(), + ), + ]), +]); + +export const ZodCslNumber = z.union([z.number(), z.string()]); + +export const ZodCslItemShared = z.object({ + "abstract-url": z.string(), + accessed: z.lazy(() => ZodCslDate), + annote: z.string(), + archive: z.string(), + "archive-collection": z.string(), + archive_collection: z.string(), + "archive-location": z.string(), + archive_location: z.string(), + "archive-place": z.string(), + authority: z.string(), + "available-date": z.lazy(() => ZodCslDate), + "call-number": z.string(), + chair: z.lazy(() => ZodCslPerson), + "chapter-number": z.lazy(() => ZodCslNumber), + "citation-key": z.string(), + "citation-label": z.string(), + "citation-number": z.lazy(() => ZodCslNumber), + "collection-editor": z.lazy(() => ZodCslPerson), + "collection-number": z.lazy(() => ZodCslNumber), + "collection-title": z.string(), + compiler: z.lazy(() => ZodCslPerson), + composer: z.lazy(() => ZodCslPerson), + "container-author": z.lazy(() => ZodCslPerson), + "container-title": z.string(), + "container-title-short": z.string(), + contributor: z.lazy(() => ZodCslPerson), + curator: z.lazy(() => ZodCslPerson), + dimensions: z.string(), + director: z.lazy(() => ZodCslPerson), + division: z.string(), + DOI: z.string(), + edition: z.lazy(() => ZodCslNumber), + editor: z.lazy(() => ZodCslPerson), + "editorial-director": z.lazy(() => ZodCslPerson), + "editor-translator": z.lazy(() => ZodCslPerson), + event: z.string(), + "event-date": z.lazy(() => ZodCslDate), + "event-title": z.string(), + "event-place": z.string(), + "executive-producer": z.lazy(() => ZodCslPerson), + "first-reference-note-number": z.lazy(() => ZodCslNumber), + "fulltext-url": z.string(), + genre: z.string(), + guest: z.lazy(() => ZodCslPerson), + host: z.lazy(() => ZodCslPerson), + id: z.union([z.string(), z.number()]), + illustrator: z.lazy(() => ZodCslPerson), + interviewer: z.lazy(() => ZodCslPerson), + isbn: z.string(), + ISBN: z.string(), + issn: z.string(), + ISSN: z.string(), + issue: z.lazy(() => ZodCslNumber), + issued: z.lazy(() => ZodCslDate), + jurisdiction: z.string(), + keyword: z.string(), + language: z.string(), + license: z.string(), + locator: z.lazy(() => ZodCslNumber), + medium: z.string(), + narrator: z.lazy(() => ZodCslPerson), + note: z.string(), + number: z.lazy(() => ZodCslNumber), + "number-of-pages": z.lazy(() => ZodCslNumber), + "number-of-volumes": z.lazy(() => ZodCslNumber), + organizer: z.lazy(() => ZodCslPerson), + "original-author": z.lazy(() => ZodCslPerson), + "original-date": z.lazy(() => ZodCslDate), + "original-publisher": z.string(), + "original-publisher-place": z.string(), + "original-title": z.string(), + page: z.lazy(() => ZodCslNumber), + "page-first": z.lazy(() => ZodCslNumber), + "page-last": z.lazy(() => ZodCslNumber), + "part-number": z.lazy(() => ZodCslNumber), + "part-title": z.string(), + "pdf-url": z.string(), + performer: z.lazy(() => ZodCslPerson), + pmcid: z.string(), + PMCID: z.string(), + pmid: z.string(), + PMID: z.string(), + "printing-number": z.lazy(() => ZodCslNumber), + producer: z.lazy(() => ZodCslPerson), + "public-url": z.string(), + publisher: z.string(), + "publisher-place": z.string(), + recipient: z.lazy(() => ZodCslPerson), + "reviewed-author": z.lazy(() => ZodCslPerson), + "reviewed-genre": z.string(), + "reviewed-title": z.string(), + scale: z.string(), + "script-writer": z.lazy(() => ZodCslPerson), + section: z.lazy(() => ZodCslNumber), + "series-creator": z.lazy(() => ZodCslPerson), + source: z.string(), + status: z.string(), + submitted: z.lazy(() => ZodCslDate), + "supplement-number": z.lazy(() => ZodCslNumber), + "title-short": z.string(), + translator: z.lazy(() => ZodCslPerson), + type: z.enum( + [ + '"article"', + '"article-journal"', + '"article-magazine"', + '"article-newspaper"', + '"bill"', + '"book"', + '"broadcast"', + '"chapter"', + '"classic"', + '"collection"', + '"dataset"', + '"document"', + '"entry"', + '"entry-dictionary"', + '"entry-encyclopedia"', + '"event"', + '"figure"', + '"graphic"', + '"hearing"', + '"interview"', + '"legal_case"', + '"legislation"', + '"manuscript"', + '"map"', + '"motion_picture"', + '"musical_score"', + '"pamphlet"', + '"paper-conference"', + '"patent"', + '"performance"', + '"periodical"', + '"personal_communication"', + '"post"', + '"post-weblog"', + '"regulation"', + '"report"', + '"review"', + '"review-book"', + '"software"', + '"song"', + '"speech"', + '"standard"', + '"thesis"', + '"treaty"', + '"webpage"', + ] as const, + ), + url: z.string(), + URL: z.string(), + version: z.lazy(() => ZodCslNumber), + volume: z.lazy(() => ZodCslNumber), + "volume-title": z.string(), + "year-suffix": z.string(), +}).passthrough().partial(); + +export const ZodCslItem = z.object({ + "abstract-url": z.string(), + accessed: z.lazy(() => ZodCslDate), + annote: z.string(), + archive: z.string(), + "archive-collection": z.string(), + archive_collection: z.string(), + "archive-location": z.string(), + archive_location: z.string(), + "archive-place": z.string(), + authority: z.string(), + "available-date": z.lazy(() => ZodCslDate), + "call-number": z.string(), + chair: z.lazy(() => ZodCslPerson), + "chapter-number": z.lazy(() => ZodCslNumber), + "citation-key": z.string(), + "citation-label": z.string(), + "citation-number": z.lazy(() => ZodCslNumber), + "collection-editor": z.lazy(() => ZodCslPerson), + "collection-number": z.lazy(() => ZodCslNumber), + "collection-title": z.string(), + compiler: z.lazy(() => ZodCslPerson), + composer: z.lazy(() => ZodCslPerson), + "container-author": z.lazy(() => ZodCslPerson), + "container-title": z.string(), + "container-title-short": z.string(), + contributor: z.lazy(() => ZodCslPerson), + curator: z.lazy(() => ZodCslPerson), + dimensions: z.string(), + director: z.lazy(() => ZodCslPerson), + division: z.string(), + DOI: z.string(), + edition: z.lazy(() => ZodCslNumber), + editor: z.lazy(() => ZodCslPerson), + "editorial-director": z.lazy(() => ZodCslPerson), + "editor-translator": z.lazy(() => ZodCslPerson), + event: z.string(), + "event-date": z.lazy(() => ZodCslDate), + "event-title": z.string(), + "event-place": z.string(), + "executive-producer": z.lazy(() => ZodCslPerson), + "first-reference-note-number": z.lazy(() => ZodCslNumber), + "fulltext-url": z.string(), + genre: z.string(), + guest: z.lazy(() => ZodCslPerson), + host: z.lazy(() => ZodCslPerson), + id: z.union([z.string(), z.number()]), + illustrator: z.lazy(() => ZodCslPerson), + interviewer: z.lazy(() => ZodCslPerson), + isbn: z.string(), + ISBN: z.string(), + issn: z.string(), + ISSN: z.string(), + issue: z.lazy(() => ZodCslNumber), + issued: z.lazy(() => ZodCslDate), + jurisdiction: z.string(), + keyword: z.string(), + language: z.string(), + license: z.string(), + locator: z.lazy(() => ZodCslNumber), + medium: z.string(), + narrator: z.lazy(() => ZodCslPerson), + note: z.string(), + number: z.lazy(() => ZodCslNumber), + "number-of-pages": z.lazy(() => ZodCslNumber), + "number-of-volumes": z.lazy(() => ZodCslNumber), + organizer: z.lazy(() => ZodCslPerson), + "original-author": z.lazy(() => ZodCslPerson), + "original-date": z.lazy(() => ZodCslDate), + "original-publisher": z.string(), + "original-publisher-place": z.string(), + "original-title": z.string(), + page: z.lazy(() => ZodCslNumber), + "page-first": z.lazy(() => ZodCslNumber), + "page-last": z.lazy(() => ZodCslNumber), + "part-number": z.lazy(() => ZodCslNumber), + "part-title": z.string(), + "pdf-url": z.string(), + performer: z.lazy(() => ZodCslPerson), + pmcid: z.string(), + PMCID: z.string(), + pmid: z.string(), + PMID: z.string(), + "printing-number": z.lazy(() => ZodCslNumber), + producer: z.lazy(() => ZodCslPerson), + "public-url": z.string(), + publisher: z.string(), + "publisher-place": z.string(), + recipient: z.lazy(() => ZodCslPerson), + "reviewed-author": z.lazy(() => ZodCslPerson), + "reviewed-genre": z.string(), + "reviewed-title": z.string(), + scale: z.string(), + "script-writer": z.lazy(() => ZodCslPerson), + section: z.lazy(() => ZodCslNumber), + "series-creator": z.lazy(() => ZodCslPerson), + source: z.string(), + status: z.string(), + submitted: z.lazy(() => ZodCslDate), + "supplement-number": z.lazy(() => ZodCslNumber), + "title-short": z.string(), + translator: z.lazy(() => ZodCslPerson), + type: z.enum( + [ + '"article"', + '"article-journal"', + '"article-magazine"', + '"article-newspaper"', + '"bill"', + '"book"', + '"broadcast"', + '"chapter"', + '"classic"', + '"collection"', + '"dataset"', + '"document"', + '"entry"', + '"entry-dictionary"', + '"entry-encyclopedia"', + '"event"', + '"figure"', + '"graphic"', + '"hearing"', + '"interview"', + '"legal_case"', + '"legislation"', + '"manuscript"', + '"map"', + '"motion_picture"', + '"musical_score"', + '"pamphlet"', + '"paper-conference"', + '"patent"', + '"performance"', + '"periodical"', + '"personal_communication"', + '"post"', + '"post-weblog"', + '"regulation"', + '"report"', + '"review"', + '"review-book"', + '"software"', + '"song"', + '"speech"', + '"standard"', + '"thesis"', + '"treaty"', + '"webpage"', + ] as const, + ), + url: z.string(), + URL: z.string(), + version: z.lazy(() => ZodCslNumber), + volume: z.lazy(() => ZodCslNumber), + "volume-title": z.string(), + "year-suffix": z.string(), + abstract: z.string(), + author: z.lazy(() => ZodCslPerson), + doi: z.string(), + references: z.string(), + title: z.string(), +}).strict().partial(); + +export const ZodCitationItem = z.object({ + abstract: z.string(), + author: z.lazy(() => ZodCslPerson), + doi: z.string(), + references: z.string(), + title: z.string(), + id: z.union([z.string(), z.number()]), + "article-id": z.union([ + z.union([ + z.string(), + z.object({ type: z.string(), value: z.string() }).passthrough().partial(), + ]), + z.array( + z.union([ + z.string(), + z.object({ type: z.string(), value: z.string() }).passthrough() + .partial(), + ]), + ), + ]), + "elocation-id": z.string(), + eissn: z.string(), + pissn: z.string(), + "art-access-id": z.string(), + "publisher-location": z.string(), + subject: z.string(), + categories: z.union([z.string(), z.array(z.string())]), + "container-id": z.union([ + z.union([ + z.string(), + z.object({ type: z.string(), value: z.string() }).passthrough().partial(), + ]), + z.array( + z.union([ + z.string(), + z.object({ type: z.string(), value: z.string() }).passthrough() + .partial(), + ]), + ), + ]), + "jats-type": z.string(), +}).strict().partial(); + +export const ZodSmartInclude = z.union([ + z.object({ text: z.string() }).strict(), + z.object({ file: z.string() }).strict(), +]); + +export const ZodSemver = z.string().regex( + new RegExp( + "^(0|[1-9]\\d*)\\.(0|[1-9]\\d*)\\.(0|[1-9]\\d*)(?:-((?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\\.(?:0|[1-9]\\d*|\\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\\+([0-9a-zA-Z-]+(?:\\.[0-9a-zA-Z-]+)*))?$", + ), +); + +export const ZodQuartoDate = z.union([ + z.string(), + z.object({ format: z.string(), value: z.string() }).strict().partial({ + value: true, + }), +]); + +export const ZodProjectProfile = z.object({ + default: z.union([z.string(), z.array(z.string())]), + group: z.union([z.array(z.string()), z.array(z.array(z.string()))]), +}).strict().partial(); + +export const ZodBadParseSchema = z.object({}).passthrough().partial(); + +export const ZodQuartoDevSchema = z.object({ + _quarto: z.object({ + "trace-filters": z.string(), + tests: z.object({}).passthrough(), + }).passthrough().partial(), +}).passthrough().partial(); + +export const ZodNotebookViewSchema = z.object({ + notebook: z.string(), + title: z.union([z.string(), z.boolean()]), + url: z.string(), + "download-url": z.string(), +}).passthrough().partial({ notebook: true }); + +export const ZodCodeLinksSchema = z.union([ + z.boolean(), + z.union([ + z.union([ + z.object({ + icon: z.string(), + text: z.string(), + href: z.string(), + rel: z.string(), + target: z.string(), + }).passthrough().partial(), + z.enum(['"repo"', '"binder"', '"devcontainer"'] as const), + ]), + z.array( + z.union([ + z.object({ + icon: z.string(), + text: z.string(), + href: z.string(), + rel: z.string(), + target: z.string(), + }).passthrough().partial(), + z.enum(['"repo"', '"binder"', '"devcontainer"'] as const), + ]), + ), + ]), +]); + +export const ZodManuscriptSchema = z.object({ + article: z.string(), + "code-links": z.lazy(() => ZodCodeLinksSchema), + "manuscript-url": z.string(), + "meca-bundle": z.union([z.boolean(), z.string()]), + notebooks: z.array( + z.union([z.string(), z.lazy(() => ZodNotebookViewSchema)]), + ), + resources: z.union([z.string(), z.array(z.string())]), + environment: z.union([z.string(), z.array(z.string())]), +}).strict().partial(); + +export const ZodBrandMeta = z.object({ + name: z.union([ + z.string(), + z.object({ full: z.string(), short: z.string() }).passthrough().partial(), + ]), + link: z.union([ + z.string(), + z.object({ + home: z.string(), + mastodon: z.string(), + bluesky: z.string(), + github: z.string(), + linkedin: z.string(), + twitter: z.string(), + facebook: z.string(), + }).passthrough().partial(), + ]), +}).passthrough().partial(); + +export const ZodBrandStringLightDark = z.union([ + z.string(), + z.object({ light: z.string(), dark: z.string() }).strict().partial(), +]); + +export const ZodBrandLogoExplicitResource = z.object({ + path: z.string(), + alt: z.string(), +}).strict().partial({ path: true }); + +export const ZodBrandLogoResource = z.union([ + z.string(), + z.lazy(() => ZodBrandLogoExplicitResource), +]); + +export const ZodBrandLogo = z.object({ + images: z.record(z.lazy(() => ZodBrandLogoResource)).and( + z.object({}).passthrough().partial(), + ), + small: z.lazy(() => ZodBrandStringLightDark), + medium: z.lazy(() => ZodBrandStringLightDark), + large: z.lazy(() => ZodBrandStringLightDark), +}).strict().partial(); + +export const ZodBrandNamedLogo = z.enum( + ['"small"', '"medium"', '"large"'] as const, +); + +export const ZodBrandColorValue = z.string(); + +export const ZodBrandColor = z.object({ + palette: z.record(z.lazy(() => ZodBrandColorValue)).and( + z.object({}).passthrough().partial(), + ), + foreground: z.lazy(() => ZodBrandColorValue), + background: z.lazy(() => ZodBrandColorValue), + primary: z.lazy(() => ZodBrandColorValue), + secondary: z.lazy(() => ZodBrandColorValue), + tertiary: z.lazy(() => ZodBrandColorValue), + success: z.lazy(() => ZodBrandColorValue), + info: z.lazy(() => ZodBrandColorValue), + warning: z.lazy(() => ZodBrandColorValue), + danger: z.lazy(() => ZodBrandColorValue), + light: z.lazy(() => ZodBrandColorValue), + dark: z.lazy(() => ZodBrandColorValue), + link: z.lazy(() => ZodBrandColorValue), +}).strict().partial(); + +export const ZodBrandMaybeNamedColor = z.union([ + z.lazy(() => ZodBrandNamedThemeColor), + z.string(), +]); + +export const ZodBrandNamedThemeColor = z.enum( + [ + '"foreground"', + '"background"', + '"primary"', + '"secondary"', + '"tertiary"', + '"success"', + '"info"', + '"warning"', + '"danger"', + '"light"', + '"dark"', + '"link"', + ] as const, +); + +export const ZodBrandTypography = z.object({ + fonts: z.array(z.lazy(() => ZodBrandFont)), + base: z.lazy(() => ZodBrandTypographyOptionsBase), + headings: z.lazy(() => ZodBrandTypographyOptionsHeadings), + monospace: z.lazy(() => ZodBrandTypographyOptionsMonospace), + "monospace-inline": z.lazy(() => ZodBrandTypographyOptionsMonospaceInline), + "monospace-block": z.lazy(() => ZodBrandTypographyOptionsMonospaceBlock), + link: z.lazy(() => ZodBrandTypographyOptionsLink), +}).strict().partial(); + +export const ZodBrandTypographyOptionsBase = z.union([ + z.string(), + z.object({ + family: z.string(), + size: z.string(), + weight: z.lazy(() => ZodBrandFontWeight), + "line-height": z.lazy(() => ZodLineHeightNumberString), + }).strict().partial(), +]); + +export const ZodBrandTypographyOptionsHeadings = z.union([ + z.string(), + z.object({ + family: z.string(), + weight: z.lazy(() => ZodBrandFontWeight), + style: z.lazy(() => ZodBrandFontStyle), + color: z.lazy(() => ZodBrandMaybeNamedColor), + "line-height": z.lazy(() => ZodLineHeightNumberString), + }).strict().partial(), +]); + +export const ZodBrandTypographyOptionsMonospace = z.union([ + z.string(), + z.object({ + family: z.string(), + size: z.string(), + weight: z.lazy(() => ZodBrandFontWeight), + color: z.lazy(() => ZodBrandMaybeNamedColor), + "background-color": z.lazy(() => ZodBrandMaybeNamedColor), + }).strict().partial(), +]); + +export const ZodBrandTypographyOptionsMonospaceInline = z.union([ + z.string(), + z.object({ + family: z.string(), + size: z.string(), + weight: z.lazy(() => ZodBrandFontWeight), + color: z.lazy(() => ZodBrandMaybeNamedColor), + "background-color": z.lazy(() => ZodBrandMaybeNamedColor), + }).strict().partial(), +]); + +export const ZodLineHeightNumberString = z.union([z.number(), z.string()]); + +export const ZodBrandTypographyOptionsMonospaceBlock = z.union([ + z.string(), + z.object({ + family: z.string(), + size: z.string(), + weight: z.lazy(() => ZodBrandFontWeight), + color: z.lazy(() => ZodBrandMaybeNamedColor), + "background-color": z.lazy(() => ZodBrandMaybeNamedColor), + "line-height": z.lazy(() => ZodLineHeightNumberString), + }).strict().partial(), +]); + +export const ZodBrandTypographyOptionsLink = z.union([ + z.string(), + z.object({ + weight: z.lazy(() => ZodBrandFontWeight), + color: z.lazy(() => ZodBrandMaybeNamedColor), + "background-color": z.lazy(() => ZodBrandMaybeNamedColor), + decoration: z.string(), + }).strict().partial(), +]); + +export const ZodBrandNamedFont = z.enum( + ['"base"', '"headings"', '"monospace"'] as const, +); + +export const ZodBrandFont = z.union([ + z.lazy(() => ZodBrandFontGoogle), + z.lazy(() => ZodBrandFontBunny), + z.lazy(() => ZodBrandFontFile), + z.lazy(() => ZodBrandFontSystem), + z.lazy(() => ZodBrandFontCommon), +]); + +export const ZodBrandFontWeight = z.enum( + [ + "100", + "200", + "300", + "400", + "500", + "600", + "700", + "800", + "900", + '"thin"', + '"extra-light"', + '"ultra-light"', + '"light"', + '"normal"', + '"regular"', + '"medium"', + '"semi-bold"', + '"demi-bold"', + '"bold"', + '"extra-bold"', + '"ultra-bold"', + '"black"', + ] as const, +); + +export const ZodBrandFontStyle = z.enum( + ['"normal"', '"italic"', '"oblique"'] as const, +); + +export const ZodBrandFontCommon = z.object({ + family: z.string(), + weight: z.union([ + z.lazy(() => ZodBrandFontWeight), + z.array(z.lazy(() => ZodBrandFontWeight)), + ]), + style: z.union([ + z.lazy(() => ZodBrandFontStyle), + z.array(z.lazy(() => ZodBrandFontStyle)), + ]), + display: z.enum( + ['"auto"', '"block"', '"swap"', '"fallback"', '"optional"'] as const, + ), +}).strict().partial(); + +export const ZodBrandFontSystem = z.object({ + family: z.string(), + weight: z.union([ + z.lazy(() => ZodBrandFontWeight), + z.array(z.lazy(() => ZodBrandFontWeight)), + ]), + style: z.union([ + z.lazy(() => ZodBrandFontStyle), + z.array(z.lazy(() => ZodBrandFontStyle)), + ]), + display: z.enum( + ['"auto"', '"block"', '"swap"', '"fallback"', '"optional"'] as const, + ), + source: z.enum(['"system"'] as const), +}).strict().partial(); + +export const ZodBrandFontGoogle = z.object({ + family: z.string(), + weight: z.union([ + z.lazy(() => ZodBrandFontWeight), + z.array(z.lazy(() => ZodBrandFontWeight)), + ]), + style: z.union([ + z.lazy(() => ZodBrandFontStyle), + z.array(z.lazy(() => ZodBrandFontStyle)), + ]), + display: z.enum( + ['"auto"', '"block"', '"swap"', '"fallback"', '"optional"'] as const, + ), + source: z.enum(['"google"'] as const), +}).strict().partial(); + +export const ZodBrandFontBunny = z.object({ + family: z.string(), + weight: z.union([ + z.lazy(() => ZodBrandFontWeight), + z.array(z.lazy(() => ZodBrandFontWeight)), + ]), + style: z.union([ + z.lazy(() => ZodBrandFontStyle), + z.array(z.lazy(() => ZodBrandFontStyle)), + ]), + display: z.enum( + ['"auto"', '"block"', '"swap"', '"fallback"', '"optional"'] as const, + ), + source: z.enum(['"bunny"'] as const), +}).strict().partial(); + +export const ZodBrandFontFile = z.object({ + source: z.enum(['"file"'] as const), + family: z.string(), + files: z.array( + z.union([ + z.string(), + z.object({ + path: z.string(), + weight: z.lazy(() => ZodBrandFontWeight), + style: z.lazy(() => ZodBrandFontStyle), + }).passthrough().partial({ path: true }), + ]), + ), +}).strict().partial({ files: true, family: true, source: true }); + +export const ZodBrandFontFamily = z.string(); + +export const ZodBrand = z.object({ + meta: z.lazy(() => ZodBrandMeta), + logo: z.lazy(() => ZodBrandLogo), + color: z.lazy(() => ZodBrandColor), + typography: z.lazy(() => ZodBrandTypography), + defaults: z.lazy(() => ZodBrandDefaults), +}).strict().partial(); + +export const ZodBrandPathBoolLightDark = z.union([ + z.string(), + z.boolean(), + z.object({ + light: z.union([z.string(), z.lazy(() => ZodBrand)]), + dark: z.union([z.string(), z.lazy(() => ZodBrand)]), + }).strict().partial(), + z.lazy(() => ZodBrand), +]); + +export const ZodBrandDefaults = z.object({ + bootstrap: z.lazy(() => ZodBrandDefaultsBootstrap), + quarto: z.object({}).passthrough(), +}).passthrough().partial(); + +export const ZodBrandDefaultsBootstrap = z.object({ + defaults: z.record(z.union([z.string(), z.boolean(), z.number()])).and( + z.object({}).passthrough().partial(), + ), +}).passthrough().partial(); + +export const ZodProjectConfig = z.object({ + title: z.string(), + type: z.string(), + render: z.array(z.string()), + "execute-dir": z.enum(['"file"', '"project"'] as const), + "output-dir": z.string(), + "lib-dir": z.string(), + resources: z.union([z.string(), z.array(z.string())]), + preview: z.lazy(() => ZodProjectPreview), + "pre-render": z.union([z.string(), z.array(z.string())]), + "post-render": z.union([z.string(), z.array(z.string())]), + detect: z.array(z.array(z.string())), +}).strict().partial(); + +export const ZodBookProject = z.object({}).passthrough().partial(); + +export type Date = z.infer; + +export type MathMethods = z.infer; + +export type PandocFormatRequestHeaders = z.infer< + typeof ZodPandocFormatRequestHeaders +>; + +export type PandocFormatOutputFile = z.infer; + +export type PandocFormatFilters = z.infer; + +export type PandocShortcodes = z.infer; + +export type PageColumn = z.infer; + +export type ContentsAuto = z.infer; + +/* export type NavigationItem = z.infer; */ + +/* export type NavigationItemObject = z.infer; */ + +export type GiscusThemes = z.infer; + +export type GiscusConfiguration = z.infer; + +export type DocumentCommentsConfiguration = z.infer< + typeof ZodDocumentCommentsConfiguration +>; + +export type SocialMetadata = z.infer; + +export type PageFooterRegion = z.infer; + +/* export type SidebarContents = z.infer; */ + +export type ProjectPreview = z.infer; + +export type ProjectServe = z.infer; + +export type Publish = z.infer; + +export type PublishRecord = z.infer; + +export type TwitterCardConfig = z.infer; + +export type OpenGraphConfig = z.infer; + +export type PageFooter = z.infer; + +export type BaseWebsite = z.infer; + +export type BookSchema = z.infer; + +export type ChapterItem = z.infer; + +export type ChapterList = z.infer; + +export type OtherLinks = z.infer; + +export type CrossrefLabelsSchema = z.infer; + +export type EpubContributor = z.infer; + +export type FormatLanguage = z.infer; + +export type WebsiteAbout = z.infer; + +export type WebsiteListing = z.infer; + +export type WebsiteListingContentsObject = z.infer< + typeof ZodWebsiteListingContentsObject +>; + +export type CslDate = z.infer; + +export type CslPerson = z.infer; + +export type CslNumber = z.infer; + +export type CslItemShared = z.infer; + +export type CslItem = z.infer; + +export type CitationItem = z.infer; + +export type SmartInclude = z.infer; + +export type Semver = z.infer; + +export type QuartoDate = z.infer; + +export type ProjectProfile = z.infer; + +export type BadParseSchema = z.infer; + +export type QuartoDevSchema = z.infer; + +export type NotebookViewSchema = z.infer; + +export type CodeLinksSchema = z.infer; + +export type ManuscriptSchema = z.infer; + +export type BrandMeta = z.infer; + +export type BrandStringLightDark = z.infer; + +export type BrandLogoExplicitResource = z.infer< + typeof ZodBrandLogoExplicitResource +>; + +export type BrandLogoResource = z.infer; + +export type BrandLogo = z.infer; + +export type BrandNamedLogo = z.infer; + +export type BrandColorValue = z.infer; + +export type BrandColor = z.infer; + +export type BrandMaybeNamedColor = z.infer; + +export type BrandNamedThemeColor = z.infer; + +export type BrandTypography = z.infer; + +export type BrandTypographyOptionsBase = z.infer< + typeof ZodBrandTypographyOptionsBase +>; + +export type BrandTypographyOptionsHeadings = z.infer< + typeof ZodBrandTypographyOptionsHeadings +>; + +export type BrandTypographyOptionsMonospace = z.infer< + typeof ZodBrandTypographyOptionsMonospace +>; + +export type BrandTypographyOptionsMonospaceInline = z.infer< + typeof ZodBrandTypographyOptionsMonospaceInline +>; + +export type LineHeightNumberString = z.infer; + +export type BrandTypographyOptionsMonospaceBlock = z.infer< + typeof ZodBrandTypographyOptionsMonospaceBlock +>; + +export type BrandTypographyOptionsLink = z.infer< + typeof ZodBrandTypographyOptionsLink +>; + +export type BrandNamedFont = z.infer; + +export type BrandFont = z.infer; + +export type BrandFontWeight = z.infer; + +export type BrandFontStyle = z.infer; + +export type BrandFontCommon = z.infer; + +export type BrandFontSystem = z.infer; + +export type BrandFontGoogle = z.infer; + +export type BrandFontBunny = z.infer; + +export type BrandFontFile = z.infer; + +export type BrandFontFamily = z.infer; + +export type Brand = z.infer; + +export type BrandPathBoolLightDark = z.infer; + +export type BrandDefaults = z.infer; + +export type BrandDefaultsBootstrap = z.infer; + +export type ProjectConfig = z.infer; + +export type BookProject = z.infer; From 590d500af439c58930d7aa4062e1d0851fd50d50 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Fri, 9 May 2025 13:16:28 -0400 Subject: [PATCH 02/11] fix enum generation --- src/core/schema/zod-types-from-schema.ts | 12 +- src/resources/types/zod/schema-types.ts | 742 +++++++++++------------ 2 files changed, 360 insertions(+), 394 deletions(-) diff --git a/src/core/schema/zod-types-from-schema.ts b/src/core/schema/zod-types-from-schema.ts index df1aa4da26b..bf9602b2bf6 100644 --- a/src/core/schema/zod-types-from-schema.ts +++ b/src/core/schema/zod-types-from-schema.ts @@ -38,6 +38,7 @@ class ZodDeclarationFileBuilder { // we keep track of the schemas we've seen so we can // process resolve-ref schemas + // deno-lint-ignore no-explicit-any schemas: Record = {}; // deno-lint-ignore no-explicit-any @@ -106,18 +107,13 @@ ${zodInferredTypes}`; convertSchemaEnum(schema: any): string | undefined { // deno-lint-ignore no-explicit-any const zodEnumDeclaration = (values: any[]) => { - // deno-lint-ignore no-explicit-any - const zodEnumValues = values.map((value: any) => { - return JSON.stringify(value); - }); - // first filter out strings - const zodEnumStrings = zodEnumValues.filter((value) => { + const zodEnumStrings = values.filter((value) => { return typeof value === "string"; }).map((value) => { return JSON.stringify(value); }); - const zodLiterals = zodEnumValues.filter((x) => { + const zodLiterals = values.filter((x) => { return typeof x !== "string"; }); const zodTypes: string[] = []; @@ -131,7 +127,7 @@ ${zodInferredTypes}`; zodTypes.push( ...zodLiterals.map((x) => { return `z.literal(${JSON.stringify(x)})`; - }).join(", "), + }), ); } if (zodTypes.length === 0) { diff --git a/src/resources/types/zod/schema-types.ts b/src/resources/types/zod/schema-types.ts index 59ab93ecdcc..0fbb5174144 100644 --- a/src/resources/types/zod/schema-types.ts +++ b/src/resources/types/zod/schema-types.ts @@ -30,22 +30,12 @@ export const ZodDate = z.union([ ]); export const ZodMathMethods = z.enum( - [ - '"plain"', - '"webtex"', - '"gladtex"', - '"mathml"', - '"mathjax"', - '"katex"', - ] as const, + ["plain", "webtex", "gladtex", "mathml", "mathjax", "katex"] as const, ); export const ZodPandocFormatRequestHeaders = z.array(z.array(z.string())); -export const ZodPandocFormatOutputFile = z.union([ - z.string(), - z.enum(["null"] as const), -]); +export const ZodPandocFormatOutputFile = z.union([z.string(), z.literal(null)]); export const ZodPandocFormatFilters = z.array( z.union([ @@ -58,16 +48,16 @@ export const ZodPandocFormatFilters = z.array( path: z.string(), at: z.enum( [ - '"pre-ast"', - '"post-ast"', - '"pre-quarto"', - '"post-quarto"', - '"pre-render"', - '"post-render"', + "pre-ast", + "post-ast", + "pre-quarto", + "post-quarto", + "pre-render", + "post-render", ] as const, ), }).passthrough().partial({ path: true, at: true }), - z.object({ type: z.enum(['"citeproc"'] as const) }).strict(), + z.object({ type: z.enum(["citeproc"] as const) }).strict(), ]), ); @@ -75,24 +65,24 @@ export const ZodPandocShortcodes = z.array(z.string()); export const ZodPageColumn = z.enum( [ - '"body"', - '"body-outset"', - '"body-outset-left"', - '"body-outset-right"', - '"page"', - '"page-left"', - '"page-right"', - '"page-inset"', - '"page-inset-left"', - '"page-inset-right"', - '"screen"', - '"screen-left"', - '"screen-right"', - '"screen-inset"', - '"screen-inset-shaded"', - '"screen-inset-left"', - '"screen-inset-right"', - '"margin"', + "body", + "body-outset", + "body-outset-left", + "body-outset-right", + "page", + "page-left", + "page-right", + "page-inset", + "page-inset-left", + "page-inset-right", + "screen", + "screen-left", + "screen-right", + "screen-inset", + "screen-inset-shaded", + "screen-inset-left", + "screen-inset-right", + "margin", ] as const, ); @@ -106,22 +96,22 @@ export const ZodContentsAuto = z.object({ export const ZodGiscusThemes = z.enum( [ - '"light"', - '"light_high_contrast"', - '"light_protanopia"', - '"light_tritanopia"', - '"dark"', - '"dark_high_contrast"', - '"dark_protanopia"', - '"dark_tritanopia"', - '"dark_dimmed"', - '"transparent_dark"', - '"cobalt"', - '"purple_dark"', - '"noborder_light"', - '"noborder_dark"', - '"noborder_gray"', - '"preferred_color_scheme"', + "light", + "light_high_contrast", + "light_protanopia", + "light_tritanopia", + "dark", + "dark_high_contrast", + "dark_protanopia", + "dark_tritanopia", + "dark_dimmed", + "transparent_dark", + "cobalt", + "purple_dark", + "noborder_light", + "noborder_dark", + "noborder_gray", + "preferred_color_scheme", ] as const, ); @@ -131,12 +121,12 @@ export const ZodGiscusConfiguration = z.object({ category: z.string(), "category-id": z.string(), mapping: z.union([ - z.enum(['"pathname"', '"url"', '"title"', '"og:title"'] as const), + z.enum(["pathname", "url", "title", "og:title"] as const), z.string(), ]), "reactions-enabled": z.boolean(), - loading: z.enum(['"lazy"'] as const), - "input-position": z.enum(['"top"', '"bottom"'] as const), + loading: z.enum(["lazy"] as const), + "input-position": z.enum(["top", "bottom"] as const), theme: z.union([ z.string(), z.lazy(() => ZodGiscusThemes), @@ -149,7 +139,7 @@ export const ZodGiscusConfiguration = z.object({ }).strict().partial({ repo: true }); export const ZodDocumentCommentsConfiguration = z.union([ - z.enum(["false"] as const), + z.literal(false), z.object({ utterances: z.object({ repo: z.string(), @@ -165,9 +155,9 @@ export const ZodDocumentCommentsConfiguration = z.union([ openSidebar: z.boolean(), showHighlights: z.union([ z.boolean(), - z.enum(['"always"', '"whenSidebarOpen"', '"never"'] as const), + z.enum(["always", "whenSidebarOpen", "never"] as const), ]), - theme: z.enum(['"classic"', '"clean"'] as const), + theme: z.enum(["classic", "clean"] as const), enableExperimentalNewNoteButton: z.boolean(), usernameUrl: z.string(), services: z.array( @@ -178,7 +168,7 @@ export const ZodDocumentCommentsConfiguration = z.union([ allowLeavingGroups: z.boolean(), enableShareLinks: z.boolean(), groups: z.union([ - z.enum(['"$rpc:requestGroups"'] as const), + z.enum(["$rpc:requestGroups"] as const), z.array(z.string()), ]), icon: z.string(), @@ -261,7 +251,7 @@ export const ZodTwitterCardConfig = z.object({ "image-alt": z.string(), "image-width": z.number(), "image-height": z.number(), - "card-style": z.enum(['"summary"', '"summary_large_image"'] as const), + "card-style": z.enum(["summary", "summary_large_image"] as const), creator: z.string(), site: z.string(), }).strict().partial(); @@ -299,17 +289,17 @@ export const ZodBaseWebsite = z.object({ "repo-branch": z.string(), "issue-url": z.string(), "repo-actions": z.union([ - z.enum(['"none"', '"edit"', '"source"', '"issue"'] as const), - z.array(z.enum(['"none"', '"edit"', '"source"', '"issue"'] as const)), + z.enum(["none", "edit", "source", "issue"] as const), + z.array(z.enum(["none", "edit", "source", "issue"] as const)), ]), "reader-mode": z.boolean(), "google-analytics": z.union([ z.string(), z.object({ "tracking-id": z.string(), - storage: z.enum(['"cookies"', '"none"'] as const), + storage: z.enum(["cookies", "none"] as const), "anonymize-ip": z.boolean(), - version: z.enum(["3", "4"] as const), + version: z.union([z.literal(3), z.literal(4)]), }).passthrough().partial(), ]), announcement: z.union([ @@ -318,30 +308,30 @@ export const ZodBaseWebsite = z.object({ content: z.string(), dismissable: z.boolean(), icon: z.string(), - position: z.enum(['"above-navbar"', '"below-navbar"'] as const), + position: z.enum(["above-navbar", "below-navbar"] as const), type: z.enum( [ - '"primary"', - '"secondary"', - '"success"', - '"danger"', - '"warning"', - '"info"', - '"light"', - '"dark"', + "primary", + "secondary", + "success", + "danger", + "warning", + "info", + "light", + "dark", ] as const, ), }).passthrough().partial(), ]), "cookie-consent": z.union([ - z.enum(['"express"', '"implied"'] as const), + z.enum(["express", "implied"] as const), z.boolean(), z.object({ - type: z.enum(['"implied"', '"express"'] as const), + type: z.enum(["implied", "express"] as const), style: z.enum( - ['"simple"', '"headline"', '"interstitial"', '"standalone"'] as const, + ["simple", "headline", "interstitial", "standalone"] as const, ), - palette: z.enum(['"light"', '"dark"'] as const), + palette: z.enum(["light", "dark"] as const), "policy-url": z.string(), language: z.string(), "prefs-text": z.string(), @@ -350,15 +340,15 @@ export const ZodBaseWebsite = z.object({ search: z.union([ z.boolean(), z.object({ - location: z.enum(['"navbar"', '"sidebar"'] as const), - type: z.enum(['"overlay"', '"textbox"'] as const), + location: z.enum(["navbar", "sidebar"] as const), + type: z.enum(["overlay", "textbox"] as const), limit: z.number(), "collapse-after": z.number(), "copy-button": z.boolean(), "merge-navbar-crumbs": z.boolean(), "keyboard-shortcut": z.union([z.string(), z.array(z.string())]), "show-item-context": z.union([ - z.enum(['"tree"', '"parent"', '"root"'] as const), + z.enum(["tree", "parent", "root"] as const), z.boolean(), ]), algolia: z.object({ @@ -387,14 +377,14 @@ export const ZodBaseWebsite = z.object({ background: z.union([ z.enum( [ - '"primary"', - '"secondary"', - '"success"', - '"danger"', - '"warning"', - '"info"', - '"light"', - '"dark"', + "primary", + "secondary", + "success", + "danger", + "warning", + "info", + "light", + "dark", ] as const, ), z.string(), @@ -402,14 +392,14 @@ export const ZodBaseWebsite = z.object({ foreground: z.union([ z.enum( [ - '"primary"', - '"secondary"', - '"success"', - '"danger"', - '"warning"', - '"info"', - '"light"', - '"dark"', + "primary", + "secondary", + "success", + "danger", + "warning", + "info", + "light", + "dark", ] as const, ), z.string(), @@ -417,12 +407,10 @@ export const ZodBaseWebsite = z.object({ search: z.boolean(), pinned: z.boolean(), collapse: z.boolean(), - "collapse-below": z.enum( - ['"sm"', '"md"', '"lg"', '"xl"', '"xxl"'] as const, - ), + "collapse-below": z.enum(["sm", "md", "lg", "xl", "xxl"] as const), left: z.array(z.lazy(() => ZodNavigationItem)), right: z.array(z.lazy(() => ZodNavigationItem)), - "toggle-position": z.enum(['"left"', '"right"'] as const), + "toggle-position": z.enum(["left", "right"] as const), "tools-collapse": z.boolean(), }).passthrough().partial(), ]), @@ -438,18 +426,18 @@ export const ZodBaseWebsite = z.object({ search: z.boolean(), tools: z.array(z.lazy(() => ZodNavigationItemObject)), contents: z.lazy(() => ZodSidebarContents), - style: z.enum(['"docked"', '"floating"'] as const), + style: z.enum(["docked", "floating"] as const), background: z.union([ z.enum( [ - '"primary"', - '"secondary"', - '"success"', - '"danger"', - '"warning"', - '"info"', - '"light"', - '"dark"', + "primary", + "secondary", + "success", + "danger", + "warning", + "info", + "light", + "dark", ] as const, ), z.string(), @@ -457,20 +445,20 @@ export const ZodBaseWebsite = z.object({ foreground: z.union([ z.enum( [ - '"primary"', - '"secondary"', - '"success"', - '"danger"', - '"warning"', - '"info"', - '"light"', - '"dark"', + "primary", + "secondary", + "success", + "danger", + "warning", + "info", + "light", + "dark", ] as const, ), z.string(), ]), border: z.boolean(), - alignment: z.enum(['"left"', '"right"', '"center"'] as const), + alignment: z.enum(["left", "right", "center"] as const), "collapse-level": z.number(), pinned: z.boolean(), header: z.union([z.string(), z.array(z.string())]), @@ -486,18 +474,18 @@ export const ZodBaseWebsite = z.object({ search: z.boolean(), tools: z.array(z.lazy(() => ZodNavigationItemObject)), contents: z.lazy(() => ZodSidebarContents), - style: z.enum(['"docked"', '"floating"'] as const), + style: z.enum(["docked", "floating"] as const), background: z.union([ z.enum( [ - '"primary"', - '"secondary"', - '"success"', - '"danger"', - '"warning"', - '"info"', - '"light"', - '"dark"', + "primary", + "secondary", + "success", + "danger", + "warning", + "info", + "light", + "dark", ] as const, ), z.string(), @@ -505,20 +493,20 @@ export const ZodBaseWebsite = z.object({ foreground: z.union([ z.enum( [ - '"primary"', - '"secondary"', - '"success"', - '"danger"', - '"warning"', - '"info"', - '"light"', - '"dark"', + "primary", + "secondary", + "success", + "danger", + "warning", + "info", + "light", + "dark", ] as const, ), z.string(), ]), border: z.boolean(), - alignment: z.enum(['"left"', '"right"', '"center"'] as const), + alignment: z.enum(["left", "right", "center"] as const), "collapse-level": z.number(), pinned: z.boolean(), header: z.union([z.string(), z.array(z.string())]), @@ -543,7 +531,7 @@ export const ZodBaseWebsite = z.object({ "other-links": z.lazy(() => ZodOtherLinks), "code-links": z.union([z.boolean(), z.lazy(() => ZodCodeLinksSchema)]), drafts: z.union([z.string(), z.array(z.string())]), - "draft-mode": z.enum(['"visible"', '"unlinked"', '"gone"'] as const), + "draft-mode": z.enum(["visible", "unlinked", "gone"] as const), }).strict().partial(); export const ZodBookSchema = z.object({ @@ -559,17 +547,17 @@ export const ZodBookSchema = z.object({ "repo-branch": z.string(), "issue-url": z.string(), "repo-actions": z.union([ - z.enum(['"none"', '"edit"', '"source"', '"issue"'] as const), - z.array(z.enum(['"none"', '"edit"', '"source"', '"issue"'] as const)), + z.enum(["none", "edit", "source", "issue"] as const), + z.array(z.enum(["none", "edit", "source", "issue"] as const)), ]), "reader-mode": z.boolean(), "google-analytics": z.union([ z.string(), z.object({ "tracking-id": z.string(), - storage: z.enum(['"cookies"', '"none"'] as const), + storage: z.enum(["cookies", "none"] as const), "anonymize-ip": z.boolean(), - version: z.enum(["3", "4"] as const), + version: z.union([z.literal(3), z.literal(4)]), }).passthrough().partial(), ]), announcement: z.union([ @@ -578,30 +566,30 @@ export const ZodBookSchema = z.object({ content: z.string(), dismissable: z.boolean(), icon: z.string(), - position: z.enum(['"above-navbar"', '"below-navbar"'] as const), + position: z.enum(["above-navbar", "below-navbar"] as const), type: z.enum( [ - '"primary"', - '"secondary"', - '"success"', - '"danger"', - '"warning"', - '"info"', - '"light"', - '"dark"', + "primary", + "secondary", + "success", + "danger", + "warning", + "info", + "light", + "dark", ] as const, ), }).passthrough().partial(), ]), "cookie-consent": z.union([ - z.enum(['"express"', '"implied"'] as const), + z.enum(["express", "implied"] as const), z.boolean(), z.object({ - type: z.enum(['"implied"', '"express"'] as const), + type: z.enum(["implied", "express"] as const), style: z.enum( - ['"simple"', '"headline"', '"interstitial"', '"standalone"'] as const, + ["simple", "headline", "interstitial", "standalone"] as const, ), - palette: z.enum(['"light"', '"dark"'] as const), + palette: z.enum(["light", "dark"] as const), "policy-url": z.string(), language: z.string(), "prefs-text": z.string(), @@ -610,15 +598,15 @@ export const ZodBookSchema = z.object({ search: z.union([ z.boolean(), z.object({ - location: z.enum(['"navbar"', '"sidebar"'] as const), - type: z.enum(['"overlay"', '"textbox"'] as const), + location: z.enum(["navbar", "sidebar"] as const), + type: z.enum(["overlay", "textbox"] as const), limit: z.number(), "collapse-after": z.number(), "copy-button": z.boolean(), "merge-navbar-crumbs": z.boolean(), "keyboard-shortcut": z.union([z.string(), z.array(z.string())]), "show-item-context": z.union([ - z.enum(['"tree"', '"parent"', '"root"'] as const), + z.enum(["tree", "parent", "root"] as const), z.boolean(), ]), algolia: z.object({ @@ -647,14 +635,14 @@ export const ZodBookSchema = z.object({ background: z.union([ z.enum( [ - '"primary"', - '"secondary"', - '"success"', - '"danger"', - '"warning"', - '"info"', - '"light"', - '"dark"', + "primary", + "secondary", + "success", + "danger", + "warning", + "info", + "light", + "dark", ] as const, ), z.string(), @@ -662,14 +650,14 @@ export const ZodBookSchema = z.object({ foreground: z.union([ z.enum( [ - '"primary"', - '"secondary"', - '"success"', - '"danger"', - '"warning"', - '"info"', - '"light"', - '"dark"', + "primary", + "secondary", + "success", + "danger", + "warning", + "info", + "light", + "dark", ] as const, ), z.string(), @@ -677,12 +665,10 @@ export const ZodBookSchema = z.object({ search: z.boolean(), pinned: z.boolean(), collapse: z.boolean(), - "collapse-below": z.enum( - ['"sm"', '"md"', '"lg"', '"xl"', '"xxl"'] as const, - ), + "collapse-below": z.enum(["sm", "md", "lg", "xl", "xxl"] as const), left: z.array(z.lazy(() => ZodNavigationItem)), right: z.array(z.lazy(() => ZodNavigationItem)), - "toggle-position": z.enum(['"left"', '"right"'] as const), + "toggle-position": z.enum(["left", "right"] as const), "tools-collapse": z.boolean(), }).passthrough().partial(), ]), @@ -698,18 +684,18 @@ export const ZodBookSchema = z.object({ search: z.boolean(), tools: z.array(z.lazy(() => ZodNavigationItemObject)), contents: z.lazy(() => ZodSidebarContents), - style: z.enum(['"docked"', '"floating"'] as const), + style: z.enum(["docked", "floating"] as const), background: z.union([ z.enum( [ - '"primary"', - '"secondary"', - '"success"', - '"danger"', - '"warning"', - '"info"', - '"light"', - '"dark"', + "primary", + "secondary", + "success", + "danger", + "warning", + "info", + "light", + "dark", ] as const, ), z.string(), @@ -717,20 +703,20 @@ export const ZodBookSchema = z.object({ foreground: z.union([ z.enum( [ - '"primary"', - '"secondary"', - '"success"', - '"danger"', - '"warning"', - '"info"', - '"light"', - '"dark"', + "primary", + "secondary", + "success", + "danger", + "warning", + "info", + "light", + "dark", ] as const, ), z.string(), ]), border: z.boolean(), - alignment: z.enum(['"left"', '"right"', '"center"'] as const), + alignment: z.enum(["left", "right", "center"] as const), "collapse-level": z.number(), pinned: z.boolean(), header: z.union([z.string(), z.array(z.string())]), @@ -746,18 +732,18 @@ export const ZodBookSchema = z.object({ search: z.boolean(), tools: z.array(z.lazy(() => ZodNavigationItemObject)), contents: z.lazy(() => ZodSidebarContents), - style: z.enum(['"docked"', '"floating"'] as const), + style: z.enum(["docked", "floating"] as const), background: z.union([ z.enum( [ - '"primary"', - '"secondary"', - '"success"', - '"danger"', - '"warning"', - '"info"', - '"light"', - '"dark"', + "primary", + "secondary", + "success", + "danger", + "warning", + "info", + "light", + "dark", ] as const, ), z.string(), @@ -765,20 +751,20 @@ export const ZodBookSchema = z.object({ foreground: z.union([ z.enum( [ - '"primary"', - '"secondary"', - '"success"', - '"danger"', - '"warning"', - '"info"', - '"light"', - '"dark"', + "primary", + "secondary", + "success", + "danger", + "warning", + "info", + "light", + "dark", ] as const, ), z.string(), ]), border: z.boolean(), - alignment: z.enum(['"left"', '"right"', '"center"'] as const), + alignment: z.enum(["left", "right", "center"] as const), "collapse-level": z.number(), pinned: z.boolean(), header: z.union([z.string(), z.array(z.string())]), @@ -803,7 +789,7 @@ export const ZodBookSchema = z.object({ "other-links": z.lazy(() => ZodOtherLinks), "code-links": z.union([z.boolean(), z.lazy(() => ZodCodeLinksSchema)]), drafts: z.union([z.string(), z.array(z.string())]), - "draft-mode": z.enum(['"visible"', '"unlinked"', '"gone"'] as const), + "draft-mode": z.enum(["visible", "unlinked", "gone"] as const), subtitle: z.string(), author: z.union([ z.union([z.string(), z.object({}).passthrough()]), @@ -819,12 +805,12 @@ export const ZodBookSchema = z.object({ "cover-image": z.string(), "cover-image-alt": z.string(), sharing: z.union([ - z.enum(['"twitter"', '"facebook"', '"linkedin"'] as const), - z.array(z.enum(['"twitter"', '"facebook"', '"linkedin"'] as const)), + z.enum(["twitter", "facebook", "linkedin"] as const), + z.array(z.enum(["twitter", "facebook", "linkedin"] as const)), ]), downloads: z.union([ - z.enum(['"pdf"', '"epub"', '"docx"'] as const), - z.array(z.enum(['"pdf"', '"epub"', '"docx"'] as const)), + z.enum(["pdf", "epub", "docx"] as const), + z.array(z.enum(["pdf", "epub", "docx"] as const)), ]), tools: z.array(z.lazy(() => ZodNavigationItem)), doi: z.string(), @@ -932,28 +918,20 @@ export const ZodFormatLanguage = z.object({ export const ZodWebsiteAbout = z.object({ id: z.string(), template: z.union([ - z.enum( - [ - '"jolla"', - '"trestles"', - '"solana"', - '"marquee"', - '"broadside"', - ] as const, - ), + z.enum(["jolla", "trestles", "solana", "marquee", "broadside"] as const), z.string(), ]), image: z.string(), "image-alt": z.string(), "image-title": z.string(), "image-width": z.string(), - "image-shape": z.enum(['"rectangle"', '"round"', '"rounded"'] as const), + "image-shape": z.enum(["rectangle", "round", "rounded"] as const), links: z.array(z.lazy(() => ZodNavigationItem)), }).strict().partial({ template: true }); export const ZodWebsiteListing = z.object({ id: z.string(), - type: z.enum(['"default"', '"table"', '"grid"', '"custom"'] as const), + type: z.enum(["default", "table", "grid", "custom"] as const), contents: z.union([ z.union([z.string(), z.lazy(() => ZodWebsiteListingContentsObject)]), z.array( @@ -967,13 +945,13 @@ export const ZodWebsiteListing = z.object({ "filter-ui": z.union([z.boolean(), z.array(z.string())]), categories: z.union([ z.boolean(), - z.enum(['"numbered"', '"unnumbered"', '"cloud"'] as const), + z.enum(["numbered", "unnumbered", "cloud"] as const), ]), feed: z.union([ z.boolean(), z.object({ items: z.number(), - type: z.enum(['"full"', '"partial"', '"metadata"'] as const), + type: z.enum(["full", "partial", "metadata"] as const), title: z.string(), image: z.string(), description: z.string(), @@ -986,11 +964,11 @@ export const ZodWebsiteListing = z.object({ "max-description-length": z.number(), "image-placeholder": z.string(), "image-lazy-loading": z.boolean(), - "image-align": z.enum(['"left"', '"right"'] as const), + "image-align": z.enum(["left", "right"] as const), "image-height": z.string(), "grid-columns": z.number(), "grid-item-border": z.boolean(), - "grid-item-align": z.enum(['"left"', '"right"', '"center"'] as const), + "grid-item-align": z.enum(["left", "right", "center"] as const), "table-striped": z.boolean(), "table-hover": z.boolean(), template: z.string(), @@ -1142,51 +1120,51 @@ export const ZodCslItemShared = z.object({ translator: z.lazy(() => ZodCslPerson), type: z.enum( [ - '"article"', - '"article-journal"', - '"article-magazine"', - '"article-newspaper"', - '"bill"', - '"book"', - '"broadcast"', - '"chapter"', - '"classic"', - '"collection"', - '"dataset"', - '"document"', - '"entry"', - '"entry-dictionary"', - '"entry-encyclopedia"', - '"event"', - '"figure"', - '"graphic"', - '"hearing"', - '"interview"', - '"legal_case"', - '"legislation"', - '"manuscript"', - '"map"', - '"motion_picture"', - '"musical_score"', - '"pamphlet"', - '"paper-conference"', - '"patent"', - '"performance"', - '"periodical"', - '"personal_communication"', - '"post"', - '"post-weblog"', - '"regulation"', - '"report"', - '"review"', - '"review-book"', - '"software"', - '"song"', - '"speech"', - '"standard"', - '"thesis"', - '"treaty"', - '"webpage"', + "article", + "article-journal", + "article-magazine", + "article-newspaper", + "bill", + "book", + "broadcast", + "chapter", + "classic", + "collection", + "dataset", + "document", + "entry", + "entry-dictionary", + "entry-encyclopedia", + "event", + "figure", + "graphic", + "hearing", + "interview", + "legal_case", + "legislation", + "manuscript", + "map", + "motion_picture", + "musical_score", + "pamphlet", + "paper-conference", + "patent", + "performance", + "periodical", + "personal_communication", + "post", + "post-weblog", + "regulation", + "report", + "review", + "review-book", + "software", + "song", + "speech", + "standard", + "thesis", + "treaty", + "webpage", ] as const, ), url: z.string(), @@ -1301,51 +1279,51 @@ export const ZodCslItem = z.object({ translator: z.lazy(() => ZodCslPerson), type: z.enum( [ - '"article"', - '"article-journal"', - '"article-magazine"', - '"article-newspaper"', - '"bill"', - '"book"', - '"broadcast"', - '"chapter"', - '"classic"', - '"collection"', - '"dataset"', - '"document"', - '"entry"', - '"entry-dictionary"', - '"entry-encyclopedia"', - '"event"', - '"figure"', - '"graphic"', - '"hearing"', - '"interview"', - '"legal_case"', - '"legislation"', - '"manuscript"', - '"map"', - '"motion_picture"', - '"musical_score"', - '"pamphlet"', - '"paper-conference"', - '"patent"', - '"performance"', - '"periodical"', - '"personal_communication"', - '"post"', - '"post-weblog"', - '"regulation"', - '"report"', - '"review"', - '"review-book"', - '"software"', - '"song"', - '"speech"', - '"standard"', - '"thesis"', - '"treaty"', - '"webpage"', + "article", + "article-journal", + "article-magazine", + "article-newspaper", + "bill", + "book", + "broadcast", + "chapter", + "classic", + "collection", + "dataset", + "document", + "entry", + "entry-dictionary", + "entry-encyclopedia", + "event", + "figure", + "graphic", + "hearing", + "interview", + "legal_case", + "legislation", + "manuscript", + "map", + "motion_picture", + "musical_score", + "pamphlet", + "paper-conference", + "patent", + "performance", + "periodical", + "personal_communication", + "post", + "post-weblog", + "regulation", + "report", + "review", + "review-book", + "software", + "song", + "speech", + "standard", + "thesis", + "treaty", + "webpage", ] as const, ), url: z.string(), @@ -1454,7 +1432,7 @@ export const ZodCodeLinksSchema = z.union([ rel: z.string(), target: z.string(), }).passthrough().partial(), - z.enum(['"repo"', '"binder"', '"devcontainer"'] as const), + z.enum(["repo", "binder", "devcontainer"] as const), ]), z.array( z.union([ @@ -1465,7 +1443,7 @@ export const ZodCodeLinksSchema = z.union([ rel: z.string(), target: z.string(), }).passthrough().partial(), - z.enum(['"repo"', '"binder"', '"devcontainer"'] as const), + z.enum(["repo", "binder", "devcontainer"] as const), ]), ), ]), @@ -1526,9 +1504,7 @@ export const ZodBrandLogo = z.object({ large: z.lazy(() => ZodBrandStringLightDark), }).strict().partial(); -export const ZodBrandNamedLogo = z.enum( - ['"small"', '"medium"', '"large"'] as const, -); +export const ZodBrandNamedLogo = z.enum(["small", "medium", "large"] as const); export const ZodBrandColorValue = z.string(); @@ -1557,18 +1533,18 @@ export const ZodBrandMaybeNamedColor = z.union([ export const ZodBrandNamedThemeColor = z.enum( [ - '"foreground"', - '"background"', - '"primary"', - '"secondary"', - '"tertiary"', - '"success"', - '"info"', - '"warning"', - '"danger"', - '"light"', - '"dark"', - '"link"', + "foreground", + "background", + "primary", + "secondary", + "tertiary", + "success", + "info", + "warning", + "danger", + "light", + "dark", + "link", ] as const, ); @@ -1650,7 +1626,7 @@ export const ZodBrandTypographyOptionsLink = z.union([ ]); export const ZodBrandNamedFont = z.enum( - ['"base"', '"headings"', '"monospace"'] as const, + ["base", "headings", "monospace"] as const, ); export const ZodBrandFont = z.union([ @@ -1661,35 +1637,37 @@ export const ZodBrandFont = z.union([ z.lazy(() => ZodBrandFontCommon), ]); -export const ZodBrandFontWeight = z.enum( - [ - "100", - "200", - "300", - "400", - "500", - "600", - "700", - "800", - "900", - '"thin"', - '"extra-light"', - '"ultra-light"', - '"light"', - '"normal"', - '"regular"', - '"medium"', - '"semi-bold"', - '"demi-bold"', - '"bold"', - '"extra-bold"', - '"ultra-bold"', - '"black"', - ] as const, -); +export const ZodBrandFontWeight = z.union([ + z.enum( + [ + "thin", + "extra-light", + "ultra-light", + "light", + "normal", + "regular", + "medium", + "semi-bold", + "demi-bold", + "bold", + "extra-bold", + "ultra-bold", + "black", + ] as const, + ), + z.literal(100), + z.literal(200), + z.literal(300), + z.literal(400), + z.literal(500), + z.literal(600), + z.literal(700), + z.literal(800), + z.literal(900), +]); export const ZodBrandFontStyle = z.enum( - ['"normal"', '"italic"', '"oblique"'] as const, + ["normal", "italic", "oblique"] as const, ); export const ZodBrandFontCommon = z.object({ @@ -1702,9 +1680,7 @@ export const ZodBrandFontCommon = z.object({ z.lazy(() => ZodBrandFontStyle), z.array(z.lazy(() => ZodBrandFontStyle)), ]), - display: z.enum( - ['"auto"', '"block"', '"swap"', '"fallback"', '"optional"'] as const, - ), + display: z.enum(["auto", "block", "swap", "fallback", "optional"] as const), }).strict().partial(); export const ZodBrandFontSystem = z.object({ @@ -1717,10 +1693,8 @@ export const ZodBrandFontSystem = z.object({ z.lazy(() => ZodBrandFontStyle), z.array(z.lazy(() => ZodBrandFontStyle)), ]), - display: z.enum( - ['"auto"', '"block"', '"swap"', '"fallback"', '"optional"'] as const, - ), - source: z.enum(['"system"'] as const), + display: z.enum(["auto", "block", "swap", "fallback", "optional"] as const), + source: z.enum(["system"] as const), }).strict().partial(); export const ZodBrandFontGoogle = z.object({ @@ -1733,10 +1707,8 @@ export const ZodBrandFontGoogle = z.object({ z.lazy(() => ZodBrandFontStyle), z.array(z.lazy(() => ZodBrandFontStyle)), ]), - display: z.enum( - ['"auto"', '"block"', '"swap"', '"fallback"', '"optional"'] as const, - ), - source: z.enum(['"google"'] as const), + display: z.enum(["auto", "block", "swap", "fallback", "optional"] as const), + source: z.enum(["google"] as const), }).strict().partial(); export const ZodBrandFontBunny = z.object({ @@ -1749,14 +1721,12 @@ export const ZodBrandFontBunny = z.object({ z.lazy(() => ZodBrandFontStyle), z.array(z.lazy(() => ZodBrandFontStyle)), ]), - display: z.enum( - ['"auto"', '"block"', '"swap"', '"fallback"', '"optional"'] as const, - ), - source: z.enum(['"bunny"'] as const), + display: z.enum(["auto", "block", "swap", "fallback", "optional"] as const), + source: z.enum(["bunny"] as const), }).strict().partial(); export const ZodBrandFontFile = z.object({ - source: z.enum(['"file"'] as const), + source: z.enum(["file"] as const), family: z.string(), files: z.array( z.union([ @@ -1805,7 +1775,7 @@ export const ZodProjectConfig = z.object({ title: z.string(), type: z.string(), render: z.array(z.string()), - "execute-dir": z.enum(['"file"', '"project"'] as const), + "execute-dir": z.enum(["file", "project"] as const), "output-dir": z.string(), "lib-dir": z.string(), resources: z.union([z.string(), z.array(z.string())]), From 5dffa5fc00e34f40ded26c419f9b63b81adb2168 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Fri, 9 May 2025 13:24:52 -0400 Subject: [PATCH 03/11] improve export naming, add hello world test --- src/core/schema/zod-types-from-schema.ts | 18 +++- src/resources/types/zod/schema-types.ts | 87 +++++++++++++++++++ .../unit/schema-validation/zod/simple.test.ts | 15 ++++ 3 files changed, 119 insertions(+), 1 deletion(-) create mode 100644 tests/unit/schema-validation/zod/simple.test.ts diff --git a/src/core/schema/zod-types-from-schema.ts b/src/core/schema/zod-types-from-schema.ts index bf9602b2bf6..013f06f479a 100644 --- a/src/core/schema/zod-types-from-schema.ts +++ b/src/core/schema/zod-types-from-schema.ts @@ -60,6 +60,19 @@ class ZodDeclarationFileBuilder { fileSource(): string { const zodDeclarations = this.declarations.join("\n"); const zodInferredTypes = this.inferredTypes.join("\n"); + const zodObject = (() => { + const lines = [ + "export const Zod = {", + ]; + Object.keys(this.schemas).forEach((name) => { + const typeName = typeNameFromSchemaName(name); + lines.push( + ` ${typeName}: Zod${typeName},`, + ); + }); + lines.push("};"); + return lines.join("\n"); + })(); return `import { z } from "zod"; import { ZodSidebarContents, ZodNavigationItem, ZodNavigationItemObject } from "./handwritten-schema-types.ts"; @@ -68,7 +81,10 @@ export { ZodSidebarContents, ZodNavigationItem, ZodNavigationItemObject } from " ${generatedSrcMessage} ${zodDeclarations} -${zodInferredTypes}`; +${zodInferredTypes} + +${zodObject} +`; } // deno-lint-ignore no-explicit-any diff --git a/src/resources/types/zod/schema-types.ts b/src/resources/types/zod/schema-types.ts index 0fbb5174144..e4607bd0cc8 100644 --- a/src/resources/types/zod/schema-types.ts +++ b/src/resources/types/zod/schema-types.ts @@ -1972,3 +1972,90 @@ export type BrandDefaultsBootstrap = z.infer; export type ProjectConfig = z.infer; export type BookProject = z.infer; + +export const Zod = { + Date: ZodDate, + MathMethods: ZodMathMethods, + PandocFormatRequestHeaders: ZodPandocFormatRequestHeaders, + PandocFormatOutputFile: ZodPandocFormatOutputFile, + PandocFormatFilters: ZodPandocFormatFilters, + PandocShortcodes: ZodPandocShortcodes, + PageColumn: ZodPageColumn, + ContentsAuto: ZodContentsAuto, + NavigationItem: ZodNavigationItem, + NavigationItemObject: ZodNavigationItemObject, + GiscusThemes: ZodGiscusThemes, + GiscusConfiguration: ZodGiscusConfiguration, + DocumentCommentsConfiguration: ZodDocumentCommentsConfiguration, + SocialMetadata: ZodSocialMetadata, + PageFooterRegion: ZodPageFooterRegion, + SidebarContents: ZodSidebarContents, + ProjectPreview: ZodProjectPreview, + ProjectServe: ZodProjectServe, + Publish: ZodPublish, + PublishRecord: ZodPublishRecord, + TwitterCardConfig: ZodTwitterCardConfig, + OpenGraphConfig: ZodOpenGraphConfig, + PageFooter: ZodPageFooter, + BaseWebsite: ZodBaseWebsite, + BookSchema: ZodBookSchema, + ChapterItem: ZodChapterItem, + ChapterList: ZodChapterList, + OtherLinks: ZodOtherLinks, + CrossrefLabelsSchema: ZodCrossrefLabelsSchema, + EpubContributor: ZodEpubContributor, + FormatLanguage: ZodFormatLanguage, + WebsiteAbout: ZodWebsiteAbout, + WebsiteListing: ZodWebsiteListing, + WebsiteListingContentsObject: ZodWebsiteListingContentsObject, + CslDate: ZodCslDate, + CslPerson: ZodCslPerson, + CslNumber: ZodCslNumber, + CslItemShared: ZodCslItemShared, + CslItem: ZodCslItem, + CitationItem: ZodCitationItem, + SmartInclude: ZodSmartInclude, + Semver: ZodSemver, + QuartoDate: ZodQuartoDate, + ProjectProfile: ZodProjectProfile, + BadParseSchema: ZodBadParseSchema, + QuartoDevSchema: ZodQuartoDevSchema, + NotebookViewSchema: ZodNotebookViewSchema, + CodeLinksSchema: ZodCodeLinksSchema, + ManuscriptSchema: ZodManuscriptSchema, + BrandMeta: ZodBrandMeta, + BrandStringLightDark: ZodBrandStringLightDark, + BrandLogoExplicitResource: ZodBrandLogoExplicitResource, + BrandLogoResource: ZodBrandLogoResource, + BrandLogo: ZodBrandLogo, + BrandNamedLogo: ZodBrandNamedLogo, + BrandColorValue: ZodBrandColorValue, + BrandColor: ZodBrandColor, + BrandMaybeNamedColor: ZodBrandMaybeNamedColor, + BrandNamedThemeColor: ZodBrandNamedThemeColor, + BrandTypography: ZodBrandTypography, + BrandTypographyOptionsBase: ZodBrandTypographyOptionsBase, + BrandTypographyOptionsHeadings: ZodBrandTypographyOptionsHeadings, + BrandTypographyOptionsMonospace: ZodBrandTypographyOptionsMonospace, + BrandTypographyOptionsMonospaceInline: + ZodBrandTypographyOptionsMonospaceInline, + LineHeightNumberString: ZodLineHeightNumberString, + BrandTypographyOptionsMonospaceBlock: ZodBrandTypographyOptionsMonospaceBlock, + BrandTypographyOptionsLink: ZodBrandTypographyOptionsLink, + BrandNamedFont: ZodBrandNamedFont, + BrandFont: ZodBrandFont, + BrandFontWeight: ZodBrandFontWeight, + BrandFontStyle: ZodBrandFontStyle, + BrandFontCommon: ZodBrandFontCommon, + BrandFontSystem: ZodBrandFontSystem, + BrandFontGoogle: ZodBrandFontGoogle, + BrandFontBunny: ZodBrandFontBunny, + BrandFontFile: ZodBrandFontFile, + BrandFontFamily: ZodBrandFontFamily, + Brand: ZodBrand, + BrandPathBoolLightDark: ZodBrandPathBoolLightDark, + BrandDefaults: ZodBrandDefaults, + BrandDefaultsBootstrap: ZodBrandDefaultsBootstrap, + ProjectConfig: ZodProjectConfig, + BookProject: ZodBookProject, +}; diff --git a/tests/unit/schema-validation/zod/simple.test.ts b/tests/unit/schema-validation/zod/simple.test.ts new file mode 100644 index 00000000000..8359516af0a --- /dev/null +++ b/tests/unit/schema-validation/zod/simple.test.ts @@ -0,0 +1,15 @@ +/* +* simple.test.ts +* +* Copyright (C) 2025 Posit Software, PBC +* +*/ + +import { unitTest } from "../../../test.ts"; +import { assertThrows } from "testing/asserts"; +import { Zod } from "../../../../src/resources/types/zod/schema-types.ts"; + +unitTest("zod-schema-basic", async () => { + assertThrows(() => Zod.MathMethods.parse("latex")); + const result = Zod.MathMethods.parse("katex"); +}); \ No newline at end of file From decfcad1dbd5e25e446f1bed5d265dc3d68ad55a Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Fri, 9 May 2025 14:40:22 -0400 Subject: [PATCH 04/11] clean up schemas --- src/resources/editor/tools/vs-code.mjs | 133 ++++++------ src/resources/editor/tools/yaml/web-worker.js | 133 ++++++------ .../yaml/yaml-intelligence-resources.json | 133 ++++++------ src/resources/schema/definitions.yml | 108 +++++----- src/resources/schema/json-schemas.json | 117 +---------- src/resources/types/schema-types.ts | 62 +----- src/resources/types/zod/schema-types.ts | 197 ++---------------- 7 files changed, 257 insertions(+), 626 deletions(-) diff --git a/src/resources/editor/tools/vs-code.mjs b/src/resources/editor/tools/vs-code.mjs index 12091aafe42..39c5a6a23e8 100644 --- a/src/resources/editor/tools/vs-code.mjs +++ b/src/resources/editor/tools/vs-code.mjs @@ -8637,16 +8637,17 @@ var require_yaml_intelligence_resources = __commonJS({ } }, mapping: { - anyOf: [ - { - enum: [ - "pathname", - "url", - "title", - "og:title" - ] - }, - "string" + schema: { + anyOf: [ + "string", + "number" + ] + }, + completions: [ + "pathname", + "url", + "title", + "og:title" ], description: { short: "The mapping between the page and the embedded discussion.", @@ -9699,40 +9700,34 @@ var require_yaml_intelligence_resources = __commonJS({ } }, background: { - anyOf: [ - { - enum: [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark" - ] - }, - "string" - ], - description: "The navbar's background color (named or hex color)." + string: { + description: "The navbar's background color (named or hex color).", + completions: [ + "primary", + "secondary", + "success", + "danger", + "warning", + "info", + "light", + "dark" + ] + } }, foreground: { - anyOf: [ - { - enum: [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark" - ] - }, - "string" - ], - description: "The navbar's foreground color (named or hex color)." + string: { + description: "The navbar's foreground color (named or hex color).", + completions: [ + "primary", + "secondary", + "success", + "danger", + "warning", + "info", + "light", + "dark" + ] + } }, search: { boolean: { @@ -9854,38 +9849,30 @@ var require_yaml_intelligence_resources = __commonJS({ default: "floating" }, background: { - anyOf: [ - { - enum: [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark" - ] - }, - "string" + schema: "string", + completions: [ + "primary", + "secondary", + "success", + "danger", + "warning", + "info", + "light", + "dark" ], description: "The sidebar's background color (named or hex color)." }, foreground: { - anyOf: [ - { - enum: [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark" - ] - }, - "string" + schema: "string", + completions: [ + "primary", + "secondary", + "success", + "danger", + "warning", + "info", + "light", + "dark" ], description: "The sidebar's foreground color (named or hex color)." }, @@ -24345,12 +24332,12 @@ var require_yaml_intelligence_resources = __commonJS({ mermaid: "%%" }, "handlers/mermaid/schema.yml": { - _internalId: 195600, + _internalId: 195566, type: "object", description: "be an object", properties: { "mermaid-format": { - _internalId: 195592, + _internalId: 195558, type: "enum", enum: [ "png", @@ -24366,7 +24353,7 @@ var require_yaml_intelligence_resources = __commonJS({ exhaustiveCompletions: true }, theme: { - _internalId: 195599, + _internalId: 195565, type: "anyOf", anyOf: [ { diff --git a/src/resources/editor/tools/yaml/web-worker.js b/src/resources/editor/tools/yaml/web-worker.js index cc7bcf9ea9d..019b5d5a06f 100644 --- a/src/resources/editor/tools/yaml/web-worker.js +++ b/src/resources/editor/tools/yaml/web-worker.js @@ -8638,16 +8638,17 @@ try { } }, mapping: { - anyOf: [ - { - enum: [ - "pathname", - "url", - "title", - "og:title" - ] - }, - "string" + schema: { + anyOf: [ + "string", + "number" + ] + }, + completions: [ + "pathname", + "url", + "title", + "og:title" ], description: { short: "The mapping between the page and the embedded discussion.", @@ -9700,40 +9701,34 @@ try { } }, background: { - anyOf: [ - { - enum: [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark" - ] - }, - "string" - ], - description: "The navbar's background color (named or hex color)." + string: { + description: "The navbar's background color (named or hex color).", + completions: [ + "primary", + "secondary", + "success", + "danger", + "warning", + "info", + "light", + "dark" + ] + } }, foreground: { - anyOf: [ - { - enum: [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark" - ] - }, - "string" - ], - description: "The navbar's foreground color (named or hex color)." + string: { + description: "The navbar's foreground color (named or hex color).", + completions: [ + "primary", + "secondary", + "success", + "danger", + "warning", + "info", + "light", + "dark" + ] + } }, search: { boolean: { @@ -9855,38 +9850,30 @@ try { default: "floating" }, background: { - anyOf: [ - { - enum: [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark" - ] - }, - "string" + schema: "string", + completions: [ + "primary", + "secondary", + "success", + "danger", + "warning", + "info", + "light", + "dark" ], description: "The sidebar's background color (named or hex color)." }, foreground: { - anyOf: [ - { - enum: [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark" - ] - }, - "string" + schema: "string", + completions: [ + "primary", + "secondary", + "success", + "danger", + "warning", + "info", + "light", + "dark" ], description: "The sidebar's foreground color (named or hex color)." }, @@ -24346,12 +24333,12 @@ try { mermaid: "%%" }, "handlers/mermaid/schema.yml": { - _internalId: 195600, + _internalId: 195566, type: "object", description: "be an object", properties: { "mermaid-format": { - _internalId: 195592, + _internalId: 195558, type: "enum", enum: [ "png", @@ -24367,7 +24354,7 @@ try { exhaustiveCompletions: true }, theme: { - _internalId: 195599, + _internalId: 195565, type: "anyOf", anyOf: [ { diff --git a/src/resources/editor/tools/yaml/yaml-intelligence-resources.json b/src/resources/editor/tools/yaml/yaml-intelligence-resources.json index cc307b696cf..fa31d65d8b4 100644 --- a/src/resources/editor/tools/yaml/yaml-intelligence-resources.json +++ b/src/resources/editor/tools/yaml/yaml-intelligence-resources.json @@ -1609,16 +1609,17 @@ } }, "mapping": { - "anyOf": [ - { - "enum": [ - "pathname", - "url", - "title", - "og:title" - ] - }, - "string" + "schema": { + "anyOf": [ + "string", + "number" + ] + }, + "completions": [ + "pathname", + "url", + "title", + "og:title" ], "description": { "short": "The mapping between the page and the embedded discussion.", @@ -2671,40 +2672,34 @@ } }, "background": { - "anyOf": [ - { - "enum": [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark" - ] - }, - "string" - ], - "description": "The navbar's background color (named or hex color)." + "string": { + "description": "The navbar's background color (named or hex color).", + "completions": [ + "primary", + "secondary", + "success", + "danger", + "warning", + "info", + "light", + "dark" + ] + } }, "foreground": { - "anyOf": [ - { - "enum": [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark" - ] - }, - "string" - ], - "description": "The navbar's foreground color (named or hex color)." + "string": { + "description": "The navbar's foreground color (named or hex color).", + "completions": [ + "primary", + "secondary", + "success", + "danger", + "warning", + "info", + "light", + "dark" + ] + } }, "search": { "boolean": { @@ -2826,38 +2821,30 @@ "default": "floating" }, "background": { - "anyOf": [ - { - "enum": [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark" - ] - }, - "string" + "schema": "string", + "completions": [ + "primary", + "secondary", + "success", + "danger", + "warning", + "info", + "light", + "dark" ], "description": "The sidebar's background color (named or hex color)." }, "foreground": { - "anyOf": [ - { - "enum": [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark" - ] - }, - "string" + "schema": "string", + "completions": [ + "primary", + "secondary", + "success", + "danger", + "warning", + "info", + "light", + "dark" ], "description": "The sidebar's foreground color (named or hex color)." }, @@ -17317,12 +17304,12 @@ "mermaid": "%%" }, "handlers/mermaid/schema.yml": { - "_internalId": 195600, + "_internalId": 195566, "type": "object", "description": "be an object", "properties": { "mermaid-format": { - "_internalId": 195592, + "_internalId": 195558, "type": "enum", "enum": [ "png", @@ -17338,7 +17325,7 @@ "exhaustiveCompletions": true }, "theme": { - "_internalId": 195599, + "_internalId": 195565, "type": "anyOf", "anyOf": [ { diff --git a/src/resources/schema/definitions.yml b/src/resources/schema/definitions.yml index b8b8f9f5f1e..93fccfc4d83 100644 --- a/src/resources/schema/definitions.yml +++ b/src/resources/schema/definitions.yml @@ -218,9 +218,15 @@ You can quickly find this by using the configuration tool at [https://giscus.app](https://giscus.app). If this is not provided, Quarto will attempt to discover it at render time. mapping: - anyOf: - - enum: [pathname, url, title, og:title] - - string + schema: + anyOf: + - string + - number + completions: + - pathname + - url + - title + - og:title description: short: The mapping between the page and the embedded discussion. long: | @@ -937,35 +943,29 @@ string: description: "Target href from navbar logo / title. By default, the logo and title link to the root page of the site (/index.html)." background: - anyOf: - - enum: - [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark", - ] - - string - description: "The navbar's background color (named or hex color)." + string: + description: "The navbar's background color (named or hex color)." + completions: + - primary + - secondary + - success + - danger + - warning + - info + - light + - dark foreground: - anyOf: - - enum: - [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark", - ] - - string - description: "The navbar's foreground color (named or hex color)." + string: + description: "The navbar's foreground color (named or hex color)." + completions: + - primary + - secondary + - success + - danger + - warning + - info + - light + - dark search: boolean: description: "Include a search box in the navbar." @@ -1039,34 +1039,28 @@ description: "The style of sidebar (`docked` or `floating`)." default: "floating" background: - anyOf: - - enum: - [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark", - ] - - string + schema: string + completions: + - primary + - secondary + - success + - danger + - warning + - info + - light + - dark description: "The sidebar's background color (named or hex color)." foreground: - anyOf: - - enum: - [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark", - ] - - string + schema: string + completions: + - primary + - secondary + - success + - danger + - warning + - info + - light + - dark description: "The sidebar's foreground color (named or hex color)." border: boolean: diff --git a/src/resources/schema/json-schemas.json b/src/resources/schema/json-schemas.json index bcc3568bb07..9c6094a07d8 100644 --- a/src/resources/schema/json-schemas.json +++ b/src/resources/schema/json-schemas.json @@ -258,15 +258,10 @@ "mapping": { "anyOf": [ { - "enum": [ - "pathname", - "url", - "title", - "og:title" - ] + "type": "string" }, { - "type": "string" + "type": "number" } ] }, @@ -1045,42 +1040,10 @@ "type": "string" }, "background": { - "anyOf": [ - { - "enum": [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark" - ] - }, - { - "type": "string" - } - ] + "type": "string" }, "foreground": { - "anyOf": [ - { - "enum": [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark" - ] - }, - { - "type": "string" - } - ] + "type": "string" }, "search": { "type": "boolean" @@ -1179,42 +1142,10 @@ ] }, "background": { - "anyOf": [ - { - "enum": [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark" - ] - }, - { - "type": "string" - } - ] + "type": "string" }, "foreground": { - "anyOf": [ - { - "enum": [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark" - ] - }, - { - "type": "string" - } - ] + "type": "string" }, "border": { "type": "boolean" @@ -1306,42 +1237,10 @@ ] }, "background": { - "anyOf": [ - { - "enum": [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark" - ] - }, - { - "type": "string" - } - ] + "type": "string" }, "foreground": { - "anyOf": [ - { - "enum": [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark" - ] - }, - { - "type": "string" - } - ] + "type": "string" }, "border": { "type": "boolean" diff --git a/src/resources/types/schema-types.ts b/src/resources/types/schema-types.ts index 29ed0532749..248cf3f2bbc 100644 --- a/src/resources/types/schema-types.ts +++ b/src/resources/types/schema-types.ts @@ -132,17 +132,7 @@ can only be created by maintainers and giscus. */; loading?: "lazy"; language?: string /* The language that should be used when displaying the commenting interface. */; - mapping?: - | ("pathname" | "url" | "title" | "og:title") - | string /* The mapping between the page and the embedded discussion. - -- `pathname`: The discussion title contains the page path -- `url`: The discussion title contains the page url -- `title`: The discussion title contains the page title -- `og:title`: The discussion title contains the `og:title` metadata value -- any other string or number: Any other strings will be passed through verbatim and a discussion title -containing that value will be used. Numbers will be treated -as a discussion number and automatic discussion creation is not supported. */; + mapping?: string | number; repo: string /* The Github repo that will be used to store comments. In order to work correctly, the repo must be public, with the giscus app installed, and @@ -475,31 +465,11 @@ The user’s cookie preferences will automatically control Google Analytics (if "tools-collapse"?: boolean /* Collapse tools into the navbar menu when the display becomes narrow. */; background?: - | ( - | "primary" - | "secondary" - | "success" - | "danger" - | "warning" - | "info" - | "light" - | "dark" - ) - | string /* The navbar's background color (named or hex color). */; + string /* The navbar's background color (named or hex color). */; collapse?: boolean /* Collapse the navbar into a menu when the display becomes narrow. */; foreground?: - | ( - | "primary" - | "secondary" - | "success" - | "danger" - | "warning" - | "info" - | "light" - | "dark" - ) - | string /* The navbar's foreground color (named or hex color). */; + string /* The navbar's foreground color (named or hex color). */; logo?: string /* Path to a logo image that will be displayed to the left of the title. */; left?: @@ -558,33 +528,11 @@ The user’s cookie preferences will automatically control Google Analytics (if | "left" | "right" | "center" /* Alignment of the items within the sidebar (`left`, `right`, or `center`) */; - background?: - | ( - | "primary" - | "secondary" - | "success" - | "danger" - | "warning" - | "info" - | "light" - | "dark" - ) - | string /* The sidebar's background color (named or hex color). */; + background?: string; border?: boolean /* Whether to show a border on the sidebar (defaults to true for 'docked' sidebars) */; contents?: SidebarContents; - foreground?: - | ( - | "primary" - | "secondary" - | "success" - | "danger" - | "warning" - | "info" - | "light" - | "dark" - ) - | string /* The sidebar's foreground color (named or hex color). */; + foreground?: string; footer?: MaybeArrayOf< string > /* Markdown to place below sidebar content (text or file path) */; diff --git a/src/resources/types/zod/schema-types.ts b/src/resources/types/zod/schema-types.ts index e4607bd0cc8..4684ccecff8 100644 --- a/src/resources/types/zod/schema-types.ts +++ b/src/resources/types/zod/schema-types.ts @@ -120,10 +120,7 @@ export const ZodGiscusConfiguration = z.object({ "repo-id": z.string(), category: z.string(), "category-id": z.string(), - mapping: z.union([ - z.enum(["pathname", "url", "title", "og:title"] as const), - z.string(), - ]), + mapping: z.union([z.string(), z.number()]), "reactions-enabled": z.boolean(), loading: z.enum(["lazy"] as const), "input-position": z.enum(["top", "bottom"] as const), @@ -374,36 +371,8 @@ export const ZodBaseWebsite = z.object({ logo: z.string(), "logo-alt": z.string(), "logo-href": z.string(), - background: z.union([ - z.enum( - [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark", - ] as const, - ), - z.string(), - ]), - foreground: z.union([ - z.enum( - [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark", - ] as const, - ), - z.string(), - ]), + background: z.string(), + foreground: z.string(), search: z.boolean(), pinned: z.boolean(), collapse: z.boolean(), @@ -427,36 +396,8 @@ export const ZodBaseWebsite = z.object({ tools: z.array(z.lazy(() => ZodNavigationItemObject)), contents: z.lazy(() => ZodSidebarContents), style: z.enum(["docked", "floating"] as const), - background: z.union([ - z.enum( - [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark", - ] as const, - ), - z.string(), - ]), - foreground: z.union([ - z.enum( - [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark", - ] as const, - ), - z.string(), - ]), + background: z.string(), + foreground: z.string(), border: z.boolean(), alignment: z.enum(["left", "right", "center"] as const), "collapse-level": z.number(), @@ -475,36 +416,8 @@ export const ZodBaseWebsite = z.object({ tools: z.array(z.lazy(() => ZodNavigationItemObject)), contents: z.lazy(() => ZodSidebarContents), style: z.enum(["docked", "floating"] as const), - background: z.union([ - z.enum( - [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark", - ] as const, - ), - z.string(), - ]), - foreground: z.union([ - z.enum( - [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark", - ] as const, - ), - z.string(), - ]), + background: z.string(), + foreground: z.string(), border: z.boolean(), alignment: z.enum(["left", "right", "center"] as const), "collapse-level": z.number(), @@ -632,36 +545,8 @@ export const ZodBookSchema = z.object({ logo: z.string(), "logo-alt": z.string(), "logo-href": z.string(), - background: z.union([ - z.enum( - [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark", - ] as const, - ), - z.string(), - ]), - foreground: z.union([ - z.enum( - [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark", - ] as const, - ), - z.string(), - ]), + background: z.string(), + foreground: z.string(), search: z.boolean(), pinned: z.boolean(), collapse: z.boolean(), @@ -685,36 +570,8 @@ export const ZodBookSchema = z.object({ tools: z.array(z.lazy(() => ZodNavigationItemObject)), contents: z.lazy(() => ZodSidebarContents), style: z.enum(["docked", "floating"] as const), - background: z.union([ - z.enum( - [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark", - ] as const, - ), - z.string(), - ]), - foreground: z.union([ - z.enum( - [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark", - ] as const, - ), - z.string(), - ]), + background: z.string(), + foreground: z.string(), border: z.boolean(), alignment: z.enum(["left", "right", "center"] as const), "collapse-level": z.number(), @@ -733,36 +590,8 @@ export const ZodBookSchema = z.object({ tools: z.array(z.lazy(() => ZodNavigationItemObject)), contents: z.lazy(() => ZodSidebarContents), style: z.enum(["docked", "floating"] as const), - background: z.union([ - z.enum( - [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark", - ] as const, - ), - z.string(), - ]), - foreground: z.union([ - z.enum( - [ - "primary", - "secondary", - "success", - "danger", - "warning", - "info", - "light", - "dark", - ] as const, - ), - z.string(), - ]), + background: z.string(), + foreground: z.string(), border: z.boolean(), alignment: z.enum(["left", "right", "center"] as const), "collapse-level": z.number(), From 571f100118d274f05b381b9dbcfe4af85f349e06 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Fri, 9 May 2025 15:29:44 -0400 Subject: [PATCH 05/11] fix required vs partial --- src/command/render/pandoc.ts | 3 +- src/core/schema/zod-types-from-schema.ts | 4 ++- src/resources/editor/tools/vs-code.mjs | 6 ++-- src/resources/editor/tools/yaml/web-worker.js | 6 ++-- .../yaml/yaml-intelligence-resources.json | 6 ++-- src/resources/types/schema-schema-types.ts | 4 +-- src/resources/types/zod/schema-types.ts | 32 +++++++++---------- 7 files changed, 32 insertions(+), 29 deletions(-) diff --git a/src/command/render/pandoc.ts b/src/command/render/pandoc.ts index 4d52ca228f4..9186e6bb51b 100644 --- a/src/command/render/pandoc.ts +++ b/src/command/render/pandoc.ts @@ -202,6 +202,7 @@ import { BrandFontFile, BrandFontGoogle, } from "../../resources/types/schema-types.ts"; +import { Zod } from "../../resources/types/zod/schema-types.ts"; import { kFieldCategories } from "../../project/types/website/listing/website-listing-shared.ts"; import { isWindows } from "../../deno_ral/platform.ts"; import { appendToCombinedLuaProfile } from "../../core/performance/perfetto-utils.ts"; @@ -1480,7 +1481,7 @@ async function resolveExtras( // deno-lint-ignore no-explicit-any const source: string = (_font as any).source ?? "google"; if (source === "file") { - const font = _font as BrandFontFile; + const font = Zod.BrandFontFile.parse(_font); for (const file of font.files || []) { const path = typeof file === "object" ? file.path : file; fontdirs.add(dirname(join(brand.brandDir, path))); diff --git a/src/core/schema/zod-types-from-schema.ts b/src/core/schema/zod-types-from-schema.ts index 013f06f479a..25616b571c2 100644 --- a/src/core/schema/zod-types-from-schema.ts +++ b/src/core/schema/zod-types-from-schema.ts @@ -407,13 +407,15 @@ ${zodObject} baseObj = `${baseObj}.passthrough()`; } if (Array.isArray(required)) { - baseObj = `${baseObj}.partial({${ + baseObj = `${baseObj}.required({${ required.map((key: string) => { return `${key}: true`; }).join(", ") }})`; } else if (required === undefined) { baseObj = `${baseObj}.partial()`; + } else if (required === "all") { + baseObj = `${baseObj}.required()`; } if (obj.additionalProperties) { baseObj = `z.record(${ diff --git a/src/resources/editor/tools/vs-code.mjs b/src/resources/editor/tools/vs-code.mjs index 39c5a6a23e8..7e18bf4769f 100644 --- a/src/resources/editor/tools/vs-code.mjs +++ b/src/resources/editor/tools/vs-code.mjs @@ -24332,12 +24332,12 @@ var require_yaml_intelligence_resources = __commonJS({ mermaid: "%%" }, "handlers/mermaid/schema.yml": { - _internalId: 195566, + _internalId: 195562, type: "object", description: "be an object", properties: { "mermaid-format": { - _internalId: 195558, + _internalId: 195554, type: "enum", enum: [ "png", @@ -24353,7 +24353,7 @@ var require_yaml_intelligence_resources = __commonJS({ exhaustiveCompletions: true }, theme: { - _internalId: 195565, + _internalId: 195561, type: "anyOf", anyOf: [ { diff --git a/src/resources/editor/tools/yaml/web-worker.js b/src/resources/editor/tools/yaml/web-worker.js index 019b5d5a06f..54c2aede01a 100644 --- a/src/resources/editor/tools/yaml/web-worker.js +++ b/src/resources/editor/tools/yaml/web-worker.js @@ -24333,12 +24333,12 @@ try { mermaid: "%%" }, "handlers/mermaid/schema.yml": { - _internalId: 195566, + _internalId: 195562, type: "object", description: "be an object", properties: { "mermaid-format": { - _internalId: 195558, + _internalId: 195554, type: "enum", enum: [ "png", @@ -24354,7 +24354,7 @@ try { exhaustiveCompletions: true }, theme: { - _internalId: 195565, + _internalId: 195561, type: "anyOf", anyOf: [ { diff --git a/src/resources/editor/tools/yaml/yaml-intelligence-resources.json b/src/resources/editor/tools/yaml/yaml-intelligence-resources.json index fa31d65d8b4..0d06a1bd673 100644 --- a/src/resources/editor/tools/yaml/yaml-intelligence-resources.json +++ b/src/resources/editor/tools/yaml/yaml-intelligence-resources.json @@ -17304,12 +17304,12 @@ "mermaid": "%%" }, "handlers/mermaid/schema.yml": { - "_internalId": 195566, + "_internalId": 195562, "type": "object", "description": "be an object", "properties": { "mermaid-format": { - "_internalId": 195558, + "_internalId": 195554, "type": "enum", "enum": [ "png", @@ -17325,7 +17325,7 @@ "exhaustiveCompletions": true }, "theme": { - "_internalId": 195565, + "_internalId": 195561, "type": "anyOf", "anyOf": [ { diff --git a/src/resources/types/schema-schema-types.ts b/src/resources/types/schema-schema-types.ts index 2e3737f9823..48356107367 100644 --- a/src/resources/types/schema-schema-types.ts +++ b/src/resources/types/schema-schema-types.ts @@ -1,6 +1,6 @@ -// This file is automatically generated by `quarto build-js`! Do not edit.", +// This file is automatically generated by `quarto dev-call build-artifacts`! Do not edit.", // -// If you find yourself trying to rebuild types and `quarto build-js` won't run because +// If you find yourself trying to rebuild types and `quarto dev-call build-artifacts` won't run because // of bad type definitions, run the following: // $ cd $QUARTO_ROOT // $ ./package/dist/bin/tools/deno run --importmap=./src/import_map.json --allow-all ./package/src/common/create-schema-types.ts ./src/resources diff --git a/src/resources/types/zod/schema-types.ts b/src/resources/types/zod/schema-types.ts index 4684ccecff8..db74fff3005 100644 --- a/src/resources/types/zod/schema-types.ts +++ b/src/resources/types/zod/schema-types.ts @@ -24,7 +24,7 @@ export type JsonObject = { [key: string]: unknown }; export const ZodDate = z.union([ z.string(), - z.object({ value: z.string(), format: z.string() }).passthrough().partial({ + z.object({ value: z.string(), format: z.string() }).passthrough().required({ value: true, }), ]); @@ -40,7 +40,7 @@ export const ZodPandocFormatOutputFile = z.union([z.string(), z.literal(null)]); export const ZodPandocFormatFilters = z.array( z.union([ z.string(), - z.object({ type: z.string(), path: z.string() }).passthrough().partial({ + z.object({ type: z.string(), path: z.string() }).passthrough().required({ path: true, }), z.object({ @@ -56,7 +56,7 @@ export const ZodPandocFormatFilters = z.array( "post-render", ] as const, ), - }).passthrough().partial({ path: true, at: true }), + }).passthrough().required({ path: true, at: true }), z.object({ type: z.enum(["citeproc"] as const) }).strict(), ]), ); @@ -133,7 +133,7 @@ export const ZodGiscusConfiguration = z.object({ }).strict().partial(), ]), language: z.string(), -}).strict().partial({ repo: true }); +}).strict().required({ repo: true }); export const ZodDocumentCommentsConfiguration = z.union([ z.literal(false), @@ -143,7 +143,7 @@ export const ZodDocumentCommentsConfiguration = z.union([ label: z.string(), theme: z.string(), "issue-term": z.string(), - }).strict().partial({ repo: true }), + }).strict().required({ repo: true }), giscus: z.lazy(() => ZodGiscusConfiguration), hypothesis: z.union([ z.boolean(), @@ -169,7 +169,7 @@ export const ZodDocumentCommentsConfiguration = z.union([ z.array(z.string()), ]), icon: z.string(), - }).passthrough().partial({ + }).passthrough().required({ apiUrl: true, authority: true, grantToken: true, @@ -189,7 +189,7 @@ export const ZodDocumentCommentsConfiguration = z.union([ userid: z.string(), displayName: z.string(), }).passthrough().partial(), - }).passthrough().partial({ user: true }), + }).passthrough().required({ user: true }), requestConfigFromFrame: z.object({ origin: z.string(), ancestorLevel: z.number(), @@ -232,7 +232,7 @@ export const ZodProjectServe = z.object({ args: z.string(), env: z.object({}).passthrough().partial(), ready: z.string(), -}).strict().partial({ cmd: true, ready: true }); +}).strict().required({ cmd: true, ready: true }); export const ZodPublish = z.object({ netlify: z.array(z.lazy(() => ZodPublishRecord)), @@ -650,7 +650,7 @@ export const ZodChapterItem = z.union([ z.object({ part: z.string(), chapters: z.array(z.lazy(() => ZodNavigationItem)), - }).passthrough().partial({ part: true }), + }).passthrough().required({ part: true }), ]); export const ZodChapterList = z.array(z.lazy(() => ZodChapterItem)); @@ -662,7 +662,7 @@ export const ZodOtherLinks = z.array( icon: z.string(), rel: z.string(), target: z.string(), - }).passthrough().partial({ text: true, href: true }), + }).passthrough().required({ text: true, href: true }), ); export const ZodCrossrefLabelsSchema = z.string(); @@ -756,7 +756,7 @@ export const ZodWebsiteAbout = z.object({ "image-width": z.string(), "image-shape": z.enum(["rectangle", "round", "rounded"] as const), links: z.array(z.lazy(() => ZodNavigationItem)), -}).strict().partial({ template: true }); +}).strict().required({ template: true }); export const ZodWebsiteListing = z.object({ id: z.string(), @@ -1224,7 +1224,7 @@ export const ZodSemver = z.string().regex( export const ZodQuartoDate = z.union([ z.string(), - z.object({ format: z.string(), value: z.string() }).strict().partial({ + z.object({ format: z.string(), value: z.string() }).strict().required({ value: true, }), ]); @@ -1248,7 +1248,7 @@ export const ZodNotebookViewSchema = z.object({ title: z.union([z.string(), z.boolean()]), url: z.string(), "download-url": z.string(), -}).passthrough().partial({ notebook: true }); +}).passthrough().required({ notebook: true }); export const ZodCodeLinksSchema = z.union([ z.boolean(), @@ -1317,7 +1317,7 @@ export const ZodBrandStringLightDark = z.union([ export const ZodBrandLogoExplicitResource = z.object({ path: z.string(), alt: z.string(), -}).strict().partial({ path: true }); +}).strict().required({ path: true }); export const ZodBrandLogoResource = z.union([ z.string(), @@ -1564,10 +1564,10 @@ export const ZodBrandFontFile = z.object({ path: z.string(), weight: z.lazy(() => ZodBrandFontWeight), style: z.lazy(() => ZodBrandFontStyle), - }).passthrough().partial({ path: true }), + }).passthrough().required({ path: true }), ]), ), -}).strict().partial({ files: true, family: true, source: true }); +}).strict().required({ files: true, family: true, source: true }); export const ZodBrandFontFamily = z.string(); From bc53b0d62bd0b8e7db157828d58915ac761fb664 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Fri, 9 May 2025 15:43:35 -0400 Subject: [PATCH 06/11] move a few types to zod validator --- src/command/render/pandoc.ts | 9 ++---- src/core/brand/brand.ts | 17 +++++++--- src/core/schema/zod-types-from-schema.ts | 2 +- src/resources/types/zod/schema-types.ts | 41 +++++++++++------------- 4 files changed, 34 insertions(+), 35 deletions(-) diff --git a/src/command/render/pandoc.ts b/src/command/render/pandoc.ts index 9186e6bb51b..76dbf458b1a 100644 --- a/src/command/render/pandoc.ts +++ b/src/command/render/pandoc.ts @@ -197,11 +197,6 @@ import { MarkdownPipelineHandler, } from "../../core/markdown-pipeline.ts"; import { getenv } from "../../core/env.ts"; -import { - BrandFontBunny, - BrandFontFile, - BrandFontGoogle, -} from "../../resources/types/schema-types.ts"; import { Zod } from "../../resources/types/zod/schema-types.ts"; import { kFieldCategories } from "../../project/types/website/listing/website-listing-shared.ts"; import { isWindows } from "../../deno_ral/platform.ts"; @@ -1487,13 +1482,13 @@ async function resolveExtras( fontdirs.add(dirname(join(brand.brandDir, path))); } } else if (source === "bunny") { - const font = _font as BrandFontBunny; + const font = Zod.BrandFontBunny.parse(_font); console.log( "Font bunny is not yet supported for Typst, skipping", font.family, ); } else if (source === "google" /* || font.source === "bunny" */) { - const font = _font as BrandFontGoogle; + const font = Zod.BrandFontGoogle.parse(_font); let { family, style, weight } = font; const parts = [family!]; if (style) { diff --git a/src/core/brand/brand.ts b/src/core/brand/brand.ts index 226fb8a42c2..11e48512317 100644 --- a/src/core/brand/brand.ts +++ b/src/core/brand/brand.ts @@ -7,14 +7,19 @@ */ import { - Brand as BrandJson, + // Brand as BrandJson, BrandFont, - BrandLogoExplicitResource, BrandNamedThemeColor, BrandTypography, BrandTypographyOptionsBase, BrandTypographyOptionsHeadings, } from "../../resources/types/schema-types.ts"; + +import { + Brand as BrandJson, + BrandLogoExplicitResource, + Zod, +} from "../../resources/types/zod/schema-types.ts"; import { InternalError } from "../lib/error.ts"; import { join, relative } from "../../deno_ral/path.ts"; @@ -68,7 +73,7 @@ export class Brand { processedData: ProcessedBrandData; constructor(readonly brand: BrandJson, brandDir: string, projectDir: string) { - this.data = brand; + this.data = Zod.Brand.parse(brand); this.brandDir = brandDir; this.projectDir = projectDir; this.processedData = this.processData(brand); @@ -261,8 +266,10 @@ export class Brand { if (typeof entry === "string") { return { path: join(pathPrefix, entry) }; } - entry.path = join(pathPrefix, entry.path); - return entry; + return { + ...entry, + path: join(pathPrefix, entry.path), + }; } getLogo(name: "small" | "medium" | "large"): CanonicalLogoInfo | undefined { diff --git a/src/core/schema/zod-types-from-schema.ts b/src/core/schema/zod-types-from-schema.ts index 25616b571c2..b4afc6ab132 100644 --- a/src/core/schema/zod-types-from-schema.ts +++ b/src/core/schema/zod-types-from-schema.ts @@ -407,7 +407,7 @@ ${zodObject} baseObj = `${baseObj}.passthrough()`; } if (Array.isArray(required)) { - baseObj = `${baseObj}.required({${ + baseObj = `${baseObj}.partial().required({${ required.map((key: string) => { return `${key}: true`; }).join(", ") diff --git a/src/resources/types/zod/schema-types.ts b/src/resources/types/zod/schema-types.ts index db74fff3005..ab506b21214 100644 --- a/src/resources/types/zod/schema-types.ts +++ b/src/resources/types/zod/schema-types.ts @@ -24,9 +24,8 @@ export type JsonObject = { [key: string]: unknown }; export const ZodDate = z.union([ z.string(), - z.object({ value: z.string(), format: z.string() }).passthrough().required({ - value: true, - }), + z.object({ value: z.string(), format: z.string() }).passthrough().partial() + .required({ value: true }), ]); export const ZodMathMethods = z.enum( @@ -40,9 +39,8 @@ export const ZodPandocFormatOutputFile = z.union([z.string(), z.literal(null)]); export const ZodPandocFormatFilters = z.array( z.union([ z.string(), - z.object({ type: z.string(), path: z.string() }).passthrough().required({ - path: true, - }), + z.object({ type: z.string(), path: z.string() }).passthrough().partial() + .required({ path: true }), z.object({ type: z.string(), path: z.string(), @@ -56,7 +54,7 @@ export const ZodPandocFormatFilters = z.array( "post-render", ] as const, ), - }).passthrough().required({ path: true, at: true }), + }).passthrough().partial().required({ path: true, at: true }), z.object({ type: z.enum(["citeproc"] as const) }).strict(), ]), ); @@ -133,7 +131,7 @@ export const ZodGiscusConfiguration = z.object({ }).strict().partial(), ]), language: z.string(), -}).strict().required({ repo: true }); +}).strict().partial().required({ repo: true }); export const ZodDocumentCommentsConfiguration = z.union([ z.literal(false), @@ -143,7 +141,7 @@ export const ZodDocumentCommentsConfiguration = z.union([ label: z.string(), theme: z.string(), "issue-term": z.string(), - }).strict().required({ repo: true }), + }).strict().partial().required({ repo: true }), giscus: z.lazy(() => ZodGiscusConfiguration), hypothesis: z.union([ z.boolean(), @@ -169,7 +167,7 @@ export const ZodDocumentCommentsConfiguration = z.union([ z.array(z.string()), ]), icon: z.string(), - }).passthrough().required({ + }).passthrough().partial().required({ apiUrl: true, authority: true, grantToken: true, @@ -189,7 +187,7 @@ export const ZodDocumentCommentsConfiguration = z.union([ userid: z.string(), displayName: z.string(), }).passthrough().partial(), - }).passthrough().required({ user: true }), + }).passthrough().partial().required({ user: true }), requestConfigFromFrame: z.object({ origin: z.string(), ancestorLevel: z.number(), @@ -232,7 +230,7 @@ export const ZodProjectServe = z.object({ args: z.string(), env: z.object({}).passthrough().partial(), ready: z.string(), -}).strict().required({ cmd: true, ready: true }); +}).strict().partial().required({ cmd: true, ready: true }); export const ZodPublish = z.object({ netlify: z.array(z.lazy(() => ZodPublishRecord)), @@ -650,7 +648,7 @@ export const ZodChapterItem = z.union([ z.object({ part: z.string(), chapters: z.array(z.lazy(() => ZodNavigationItem)), - }).passthrough().required({ part: true }), + }).passthrough().partial().required({ part: true }), ]); export const ZodChapterList = z.array(z.lazy(() => ZodChapterItem)); @@ -662,7 +660,7 @@ export const ZodOtherLinks = z.array( icon: z.string(), rel: z.string(), target: z.string(), - }).passthrough().required({ text: true, href: true }), + }).passthrough().partial().required({ text: true, href: true }), ); export const ZodCrossrefLabelsSchema = z.string(); @@ -756,7 +754,7 @@ export const ZodWebsiteAbout = z.object({ "image-width": z.string(), "image-shape": z.enum(["rectangle", "round", "rounded"] as const), links: z.array(z.lazy(() => ZodNavigationItem)), -}).strict().required({ template: true }); +}).strict().partial().required({ template: true }); export const ZodWebsiteListing = z.object({ id: z.string(), @@ -1224,9 +1222,8 @@ export const ZodSemver = z.string().regex( export const ZodQuartoDate = z.union([ z.string(), - z.object({ format: z.string(), value: z.string() }).strict().required({ - value: true, - }), + z.object({ format: z.string(), value: z.string() }).strict().partial() + .required({ value: true }), ]); export const ZodProjectProfile = z.object({ @@ -1248,7 +1245,7 @@ export const ZodNotebookViewSchema = z.object({ title: z.union([z.string(), z.boolean()]), url: z.string(), "download-url": z.string(), -}).passthrough().required({ notebook: true }); +}).passthrough().partial().required({ notebook: true }); export const ZodCodeLinksSchema = z.union([ z.boolean(), @@ -1317,7 +1314,7 @@ export const ZodBrandStringLightDark = z.union([ export const ZodBrandLogoExplicitResource = z.object({ path: z.string(), alt: z.string(), -}).strict().required({ path: true }); +}).strict().partial().required({ path: true }); export const ZodBrandLogoResource = z.union([ z.string(), @@ -1564,10 +1561,10 @@ export const ZodBrandFontFile = z.object({ path: z.string(), weight: z.lazy(() => ZodBrandFontWeight), style: z.lazy(() => ZodBrandFontStyle), - }).passthrough().required({ path: true }), + }).passthrough().partial().required({ path: true }), ]), ), -}).strict().required({ files: true, family: true, source: true }); +}).strict().partial().required({ files: true, family: true, source: true }); export const ZodBrandFontFamily = z.string(); From a41de5100f02446b5ec9c41ec056190373b299ce Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Fri, 9 May 2025 16:18:39 -0400 Subject: [PATCH 07/11] more type strictness and enforcement --- deno.jsonc | 3 +++ src/command/render/project.ts | 8 +++++--- src/core/brand/brand.ts | 16 +++++++-------- src/core/sass/brand.ts | 38 +++++++++++++++++++---------------- src/project/project-shared.ts | 12 +++++------ 5 files changed, 43 insertions(+), 34 deletions(-) diff --git a/deno.jsonc b/deno.jsonc index 7b6b69472b1..a5fbd9f889b 100644 --- a/deno.jsonc +++ b/deno.jsonc @@ -1,6 +1,9 @@ // auto-generated by package/src/common/create-deno-config.ts // see dev-docs/update-deno_jsonc.md { + "compilerOptions": { + "noErrorTruncation": true + }, "lint": { "include": ["src/"], "exclude": [ diff --git a/src/command/render/project.ts b/src/command/render/project.ts index 514884ea44c..65e66820058 100644 --- a/src/command/render/project.ts +++ b/src/command/render/project.ts @@ -80,7 +80,7 @@ import { Format } from "../../config/types.ts"; import { fileExecutionEngine } from "../../execute/engine.ts"; import { projectContextForDirectory } from "../../project/project-context.ts"; import { ProjectType } from "../../project/types/types.ts"; -import { ProjectConfig as ProjectConfig_Project } from "../../resources/types/schema-types.ts"; +import { Zod } from "../../resources/types/zod/schema-types.ts"; const noMutationValidations = ( projType: ProjectType, @@ -255,9 +255,11 @@ const mergeExtensionMetadata = async ( context.isSingleFile ? undefined : context.dir, { builtIn: false }, ); - const projectMetadata = extensions.map((extension) => + const projectMetadata = extensions.filter((extension) => extension.contributes.metadata?.project - ).filter((project) => project) as ProjectConfig_Project[]; + ).map((extension) => { + return Zod.ProjectConfig.parse(extension.contributes.metadata!.project); + }); context.config.project = mergeProjectMetadata( context.config.project, ...projectMetadata, diff --git a/src/core/brand/brand.ts b/src/core/brand/brand.ts index 11e48512317..94fc0edd425 100644 --- a/src/core/brand/brand.ts +++ b/src/core/brand/brand.ts @@ -7,17 +7,13 @@ */ import { - // Brand as BrandJson, + Brand as BrandJson, BrandFont, + BrandLogoExplicitResource, BrandNamedThemeColor, BrandTypography, BrandTypographyOptionsBase, BrandTypographyOptionsHeadings, -} from "../../resources/types/schema-types.ts"; - -import { - Brand as BrandJson, - BrandLogoExplicitResource, Zod, } from "../../resources/types/zod/schema-types.ts"; import { InternalError } from "../lib/error.ts"; @@ -72,11 +68,15 @@ export class Brand { projectDir: string; processedData: ProcessedBrandData; - constructor(readonly brand: BrandJson, brandDir: string, projectDir: string) { + constructor( + readonly brand: unknown, + brandDir: string, + projectDir: string, + ) { this.data = Zod.Brand.parse(brand); this.brandDir = brandDir; this.projectDir = projectDir; - this.processedData = this.processData(brand); + this.processedData = this.processData(this.data); } processData(data: BrandJson): ProcessedBrandData { diff --git a/src/core/sass/brand.ts b/src/core/sass/brand.ts index 9455023a9b3..69ab57f4599 100644 --- a/src/core/sass/brand.ts +++ b/src/core/sass/brand.ts @@ -16,11 +16,12 @@ import { import { ProjectContext } from "../../project/types.ts"; import { BrandFont, - BrandFontBunny, + // BrandFontBunny, BrandFontCommon, BrandFontGoogle, BrandFontWeight, -} from "../../resources/types/schema-types.ts"; + Zod, +} from "../../resources/types/zod/schema-types.ts"; import { Brand } from "../brand/brand.ts"; import { darkModeDefault } from "../../format/html/format-html-info.ts"; @@ -323,10 +324,11 @@ const brandTypographyLayer = ( ): string | undefined => { let googleFamily = ""; for (const _resolvedFont of font) { - const resolvedFont = _resolvedFont as (BrandFontGoogle | BrandFontBunny); - if (resolvedFont.source !== "google") { + const safeResolvedFont = Zod.BrandFontGoogle.safeParse(_resolvedFont); + if (!safeResolvedFont.success) { return undefined; } + const resolvedFont = safeResolvedFont.data; const thisFamily = resolvedFont.family; if (!thisFamily) { continue; @@ -351,17 +353,15 @@ const brandTypographyLayer = ( ): string | undefined => { let googleFamily = ""; for (const _resolvedFont of font) { - const resolvedFont = - _resolvedFont as (BrandFont | BrandFontGoogle | BrandFontBunny); + const safeResolvedFont = Zod.BrandFontBunny.safeParse(_resolvedFont); + if (!safeResolvedFont.success) { + return undefined; + } + const resolvedFont = safeResolvedFont.data; // Typescript's type checker doesn't understand that it's ok to attempt // to access a property that might not exist on a type when you're // only testing for its existence. - // deno-lint-ignore no-explicit-any - const source = (resolvedFont as any).source; - if (source && source !== "bunny") { - return undefined; - } const thisFamily = resolvedFont.family; if (!thisFamily) { continue; @@ -384,6 +384,7 @@ const brandTypographyLayer = ( type HTMLFontInformation = { [key: string]: unknown }; type FontKind = + | "link" | "base" | "headings" | "monospace" @@ -398,9 +399,14 @@ const brandTypographyLayer = ( } else if (typeof resolvedFontOptions === "string") { resolvedFontOptions = { family: resolvedFontOptions }; } - const family = resolvedFontOptions.family; - const font = getFontFamilies(family); const result: HTMLFontInformation = {}; + // This is an ugly hack: + // resolvedFontOptions doesn't always have 'family', but at this + // point in the code we know resolvedFontOptions is an object + // that we can attempt to extract the family from. + const family = + (resolvedFontOptions as Record).family; + const font = getFontFamilies(family); result.family = resolveGoogleFontFamily(font) ?? resolveBunnyFontFamily(font) ?? // resolveFilesFontFamily(font) ?? @@ -524,11 +530,9 @@ const brandTypographyLayer = ( "monospace", "headings", "base", - ] + ] as const ) { - const fontInformation = resolveHTMLFontInformation( - kind as FontKind, - ); + const fontInformation = resolveHTMLFontInformation(kind); if (!fontInformation) { continue; } diff --git a/src/project/project-shared.ts b/src/project/project-shared.ts index bef495a34dd..1cbbe4e0de5 100644 --- a/src/project/project-shared.ts +++ b/src/project/project-shared.ts @@ -48,9 +48,9 @@ import { DirectiveCell } from "../core/lib/break-quarto-md-types.ts"; import { QuartoJSONSchema, readYamlFromMarkdown } from "../core/yaml.ts"; import { refSchema } from "../core/lib/yaml-schema/common.ts"; import { - Brand as BrandJson, BrandPathBoolLightDark, -} from "../resources/types/schema-types.ts"; + Zod, +} from "../resources/types/zod/schema-types.ts"; import { Brand } from "../core/brand/brand.ts"; import { assert } from "testing/asserts"; @@ -524,7 +524,7 @@ export async function projectResolveBrand( brandPath, refSchema("brand", "Format-independent brand configuration."), "Brand validation failed for " + brandPath + ".", - ) as BrandJson; + ); return new Brand(brand, dirname(brandPath), project.dir); } async function loadRelativeBrand( @@ -582,7 +582,7 @@ export async function projectResolveBrand( return project.brandCache.brand; } else { const metadata = await project.fileMetadata(fileName); - const brand = metadata.brand as BrandPathBoolLightDark; + const brand = Zod.BrandPathBoolLightDark.parse(metadata.brand); // as BrandPathBoolLightDark; if (brand === false) { return undefined; } @@ -611,7 +611,7 @@ export async function projectResolveBrand( } if (typeof brand.dark === "string") { dark = await loadRelativeBrand(brand.dark); - } else if(brand.dark) { + } else if (brand.dark) { dark = new Brand( brand.dark, dirname(fileName), @@ -622,7 +622,7 @@ export async function projectResolveBrand( } else { fileInformation.brand = { light: new Brand( - brand as BrandJson, + brand, dirname(fileName), project.dir, ), From 2ac7de7b26305f3ccdf466d54a30491e5783042c Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Fri, 9 May 2025 16:28:01 -0400 Subject: [PATCH 08/11] more zod types --- src/project/project-shared.ts | 9 +++------ src/project/serve/serve.ts | 2 +- src/project/types.ts | 9 ++++++--- 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/src/project/project-shared.ts b/src/project/project-shared.ts index 1cbbe4e0de5..c1f4c8e3af7 100644 --- a/src/project/project-shared.ts +++ b/src/project/project-shared.ts @@ -47,10 +47,7 @@ import { normalizeNewlines } from "../core/lib/text.ts"; import { DirectiveCell } from "../core/lib/break-quarto-md-types.ts"; import { QuartoJSONSchema, readYamlFromMarkdown } from "../core/yaml.ts"; import { refSchema } from "../core/lib/yaml-schema/common.ts"; -import { - BrandPathBoolLightDark, - Zod, -} from "../resources/types/zod/schema-types.ts"; +import { Zod } from "../resources/types/zod/schema-types.ts"; import { Brand } from "../core/brand/brand.ts"; import { assert } from "testing/asserts"; @@ -547,7 +544,7 @@ export async function projectResolveBrand( let fileNames = ["_brand.yml", "_brand.yaml"].map((file) => join(project.dir, file) ); - let brand = project?.config?.brand as Boolean | string | { + const brand = project?.config?.brand as boolean | string | { light?: string; dark?: string; }; @@ -582,7 +579,7 @@ export async function projectResolveBrand( return project.brandCache.brand; } else { const metadata = await project.fileMetadata(fileName); - const brand = Zod.BrandPathBoolLightDark.parse(metadata.brand); // as BrandPathBoolLightDark; + const brand = Zod.BrandPathBoolLightDark.parse(metadata.brand); if (brand === false) { return undefined; } diff --git a/src/project/serve/serve.ts b/src/project/serve/serve.ts index 7d96caca82e..3a5034cc5a0 100644 --- a/src/project/serve/serve.ts +++ b/src/project/serve/serve.ts @@ -109,7 +109,7 @@ import { exitWithCleanup, onCleanup } from "../../core/cleanup.ts"; import { projectExtensionDirs } from "../../extension/extension.ts"; import { findOpenPort } from "../../core/port.ts"; import { kLocalhost } from "../../core/port-consts.ts"; -import { ProjectServe } from "../../resources/types/schema-types.ts"; +import { ProjectServe } from "../../resources/types/zod/schema-types.ts"; import { handleHttpRequests } from "../../core/http-server.ts"; import { touch } from "../../core/file.ts"; import { staticResource } from "../../preview/preview-static.ts"; diff --git a/src/project/types.ts b/src/project/types.ts index 3bc67290dab..8892d59c38f 100644 --- a/src/project/types.ts +++ b/src/project/types.ts @@ -14,7 +14,6 @@ import { ExecutionEngine, ExecutionTarget } from "../execute/types.ts"; import { InspectedMdCell } from "../quarto-core/inspect-types.ts"; import { NotebookContext } from "../render/notebook/notebook-types.ts"; import { - Brand as BrandJson, NavigationItem as NavItem, NavigationItemObject, NavigationItemObject as SidebarTool, @@ -70,8 +69,12 @@ export interface ProjectContext { fileInformationCache: Map; // This is a cache of _brand.yml for a project - brandCache?: { brand?: LightDarkBrand}; - resolveBrand: (fileName?: string) => Promise; + brandCache?: { brand?: LightDarkBrand }; + resolveBrand: ( + fileName?: string, + ) => Promise< + undefined | { light?: Brand | undefined; dark?: Brand | undefined } + >; // expands markdown for a file // input file doesn't have to be markdown; it can be, for example, a knitr spin file From 6a9d8354ce2c621ef8ae4c33b01e30fae5348f8f Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Fri, 9 May 2025 16:31:31 -0400 Subject: [PATCH 09/11] handle undefined vs false vs validation correctly --- src/project/project-shared.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/project/project-shared.ts b/src/project/project-shared.ts index c1f4c8e3af7..0d43d0b90ad 100644 --- a/src/project/project-shared.ts +++ b/src/project/project-shared.ts @@ -579,11 +579,14 @@ export async function projectResolveBrand( return project.brandCache.brand; } else { const metadata = await project.fileMetadata(fileName); + if (metadata.brand === undefined) { + return project.resolveBrand(); + } const brand = Zod.BrandPathBoolLightDark.parse(metadata.brand); if (brand === false) { return undefined; } - if (brand === true || brand === undefined) { + if (brand === true) { return project.resolveBrand(); } const fileInformation = ensureFileInformationCache(project, fileName); From 15be26bee4c9dc10271302f103ea65fa45f55456 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Fri, 9 May 2025 16:55:04 -0400 Subject: [PATCH 10/11] more parsing --- src/project/types/book/book-config.ts | 21 +++++++++------------ 1 file changed, 9 insertions(+), 12 deletions(-) diff --git a/src/project/types/book/book-config.ts b/src/project/types/book/book-config.ts index 8e4a4d14c85..656af9d91f1 100644 --- a/src/project/types/book/book-config.ts +++ b/src/project/types/book/book-config.ts @@ -118,10 +118,8 @@ import { RenderFlags } from "../../../command/render/types.ts"; import { formatLanguage } from "../../../core/language.ts"; import { kComments } from "../../../format/html/format-html-shared.ts"; import { resolvePageFooter } from "../website/website-shared.ts"; -import { - NavigationItemObject, - PageFooterRegion, -} from "../../../resources/types/schema-types.ts"; +import { Zod } from "../../../resources/types/zod/schema-types.ts"; +import { PageFooterRegion } from "../../../resources/types/schema-types.ts"; import { projectType } from "../project-types.ts"; import { BookRenderItem, BookRenderItemType } from "./book-types.ts"; import { isAbsoluteRef } from "../../../core/http.ts"; @@ -145,7 +143,7 @@ export async function bookProjectConfig( if (!site[kSiteFavicon]) { const brand = await project.resolveBrand(); if (brand?.light) { - site[kSiteFavicon] = getFavicon(brand.light); // + site[kSiteFavicon] = getFavicon(brand.light); // } } site[kSiteUrl] = book[kSiteUrl]; @@ -259,13 +257,12 @@ export async function bookProjectConfig( const footerFiles: string[] = []; const pageFooter = resolvePageFooter(config); const addFooterItems = (region?: PageFooterRegion) => { - if (region) { - for (const item of region) { - if (typeof item !== "string") { - const navItem = item as NavigationItemObject; - if (navItem.href && !isAbsoluteRef(navItem.href)) { - footerFiles.push(navItem.href); - } + if (!region) return; + for (const item of region) { + if (typeof item !== "string") { + const navItem = Zod.NavigationItemObject.parse(item); + if (navItem.href && !isAbsoluteRef(navItem.href)) { + footerFiles.push(navItem.href); } } } From aee23eaaca42cb12ab55560f6e61d840563e35e8 Mon Sep 17 00:00:00 2001 From: Carlos Scheidegger Date: Fri, 9 May 2025 17:15:41 -0400 Subject: [PATCH 11/11] fix types --- src/project/types/book/book-config.ts | 10 ++++------ src/resources/types/zod/handwritten-schema-types.ts | 4 ++-- 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/project/types/book/book-config.ts b/src/project/types/book/book-config.ts index 656af9d91f1..7dfed26800f 100644 --- a/src/project/types/book/book-config.ts +++ b/src/project/types/book/book-config.ts @@ -257,13 +257,11 @@ export async function bookProjectConfig( const footerFiles: string[] = []; const pageFooter = resolvePageFooter(config); const addFooterItems = (region?: PageFooterRegion) => { - if (!region) return; + if (!region || typeof region === "string") return; for (const item of region) { - if (typeof item !== "string") { - const navItem = Zod.NavigationItemObject.parse(item); - if (navItem.href && !isAbsoluteRef(navItem.href)) { - footerFiles.push(navItem.href); - } + const navItem = Zod.NavigationItemObject.parse(item); + if (navItem.href && !isAbsoluteRef(navItem.href)) { + footerFiles.push(navItem.href); } } }; diff --git a/src/resources/types/zod/handwritten-schema-types.ts b/src/resources/types/zod/handwritten-schema-types.ts index 1ea74e0032a..4d7f2834fb6 100644 --- a/src/resources/types/zod/handwritten-schema-types.ts +++ b/src/resources/types/zod/handwritten-schema-types.ts @@ -109,10 +109,10 @@ export const Base_ZodNavigationItemObject = z.object({ }).strict().partial(); type NavigationItemObject = z.infer & { - menu: NavigationItem[]; + menu?: NavigationItem[]; }; export const ZodNavigationItemObject: z.ZodType = Base_ZodNavigationItemObject.extend({ menu: z.array(z.lazy(() => ZodNavigationItem)), - }); + }).strict().partial();