diff --git a/package-lock.json b/package-lock.json index 2b737aa..ba10f83 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,8 +13,7 @@ "@clack/prompts": "^0.6.2", "configstore": "^5.0.1", "picocolors": "^1.0.0", - "zod": "^3.21.3", - "zod-validation-error": "^1.0.1" + "valibot": "^0.30.0" }, "bin": { "bcommits": "dist/index.js", @@ -7758,6 +7757,11 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "node_modules/valibot": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.30.0.tgz", + "integrity": "sha512-5POBdbSkM+3nvJ6ZlyQHsggisfRtyT4tVTo1EIIShs6qCdXJnyWU5TJ68vr8iTg5zpOLjXLRiBqNx+9zwZz/rA==" + }, "node_modules/validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -7928,25 +7932,6 @@ "funding": { "url": "https://github.com/sponsors/sindresorhus" } - }, - "node_modules/zod": { - "version": "3.21.3", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.3.tgz", - "integrity": "sha512-tz1QgJomEhMTQhOBvQxnnrTo8q77EjpGLlaWicCoEEkMzScuONzDkJKIHr83CS89fGY24iGCStj2ZOCHpPOEyA==", - "funding": { - "url": "https://github.com/sponsors/colinhacks" - } - }, - "node_modules/zod-validation-error": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-1.0.1.tgz", - "integrity": "sha512-QRk2AtHLJg8sCZAbEjXSs7E0n4/mSdX5caoh6eOUvDSdcIQz03i0xoNN1Qx6UZT+ADVHRK6+ZXRtldzW6nnltA==", - "engines": { - "node": "^14.17 || >=16.0.0" - }, - "peerDependencies": { - "zod": "^3.18.0" - } } }, "dependencies": { @@ -13456,6 +13441,11 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, + "valibot": { + "version": "0.30.0", + "resolved": "https://registry.npmjs.org/valibot/-/valibot-0.30.0.tgz", + "integrity": "sha512-5POBdbSkM+3nvJ6ZlyQHsggisfRtyT4tVTo1EIIShs6qCdXJnyWU5TJ68vr8iTg5zpOLjXLRiBqNx+9zwZz/rA==" + }, "validate-npm-package-license": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", @@ -13589,17 +13579,6 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.0.0.tgz", "integrity": "sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==", "dev": true - }, - "zod": { - "version": "3.21.3", - "resolved": "https://registry.npmjs.org/zod/-/zod-3.21.3.tgz", - "integrity": "sha512-tz1QgJomEhMTQhOBvQxnnrTo8q77EjpGLlaWicCoEEkMzScuONzDkJKIHr83CS89fGY24iGCStj2ZOCHpPOEyA==" - }, - "zod-validation-error": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/zod-validation-error/-/zod-validation-error-1.0.1.tgz", - "integrity": "sha512-QRk2AtHLJg8sCZAbEjXSs7E0n4/mSdX5caoh6eOUvDSdcIQz03i0xoNN1Qx6UZT+ADVHRK6+ZXRtldzW6nnltA==", - "requires": {} } } } diff --git a/package.json b/package.json index b23e0a7..3c3b655 100644 --- a/package.json +++ b/package.json @@ -24,8 +24,7 @@ "@clack/prompts": "^0.6.2", "configstore": "^5.0.1", "picocolors": "^1.0.0", - "zod": "^3.21.3", - "zod-validation-error": "^1.0.1" + "valibot": "^0.30.0" }, "scripts": { "start": "jiti ./src/index.ts", diff --git a/src/branch.ts b/src/branch.ts index 9a56f20..6539e79 100644 --- a/src/branch.ts +++ b/src/branch.ts @@ -1,29 +1,31 @@ #! /usr/bin/env node -import { CommitState, Config } from "./zod-state"; +import * as p from "@clack/prompts"; +import { execSync } from "child_process"; +import Configstore from "configstore"; +import color from "picocolors"; +import { chdir } from "process"; +import { Output, parse } from "valibot"; + +// This must be imported before ./utils 🤦 +import { BranchState, CommitState, Config } from "./vali-state"; + import { BRANCH_ACTION_OPTIONS, CACHE_PROMPT, - load_setup, OPTIONAL_PROMPT, - Z_BRANCH_ACTIONS, - Z_BRANCH_CONFIG_FIELDS, - Z_BRANCH_FIELDS, + V_BRANCH_ACTIONS, + V_BRANCH_CONFIG_FIELDS, + V_BRANCH_FIELDS, + load_setup, } from "./utils"; -import { BranchState } from "./zod-state"; -import * as p from "@clack/prompts"; -import Configstore from "configstore"; -import { z } from "zod"; -import { execSync } from "child_process"; -import color from "picocolors"; -import { chdir } from "process"; main(load_setup(" better-branch ")); -async function main(config: z.infer) { - const branch_state = BranchState.parse({}); +async function main(config: Output) { + const branch_state = parse(BranchState, {}); - let checkout_type: z.infer = "branch"; + let checkout_type: Output = "branch"; if (config.enable_worktrees) { const branch_or_worktree = await p.select({ message: `Checkout a branch or create a worktree?`, @@ -178,12 +180,12 @@ async function main(config: z.infer) { } function build_branch( - branch: z.infer, - config: z.infer, + branch: Output, + config: Output, ) { let res = ""; - config.branch_order.forEach((b: z.infer) => { - const config_key: z.infer = `branch_${b}`; + config.branch_order.forEach((b: Output) => { + const config_key: Output = `branch_${b}`; if (branch[b]) res += branch[b] + config[config_key].separator; }); if (res.endsWith("-") || res.endsWith("/") || res.endsWith("_")) { diff --git a/src/index.ts b/src/index.ts index e274157..d885889 100644 --- a/src/index.ts +++ b/src/index.ts @@ -4,8 +4,8 @@ import * as p from "@clack/prompts"; import color from "picocolors"; import { execSync } from "child_process"; import { chdir } from "process"; -import { z } from "zod"; -import { CommitState, Config } from "./zod-state"; +import { Output, parse } from "valibot"; +import { CommitState, Config } from "./vali-state"; import { load_setup, addNewLine, @@ -18,7 +18,7 @@ import { clean_commit_title, COMMIT_FOOTER_OPTIONS, infer_type_from_branch, - Z_FOOTER_OPTIONS, + V_FOOTER_OPTIONS, CUSTOM_SCOPE_KEY, get_git_root, REGEX_SLASH_UND, @@ -28,8 +28,8 @@ import { git_add, git_status } from "./git"; main(load_setup()); -export async function main(config: z.infer) { - let commit_state = CommitState.parse({}); +export async function main(config: Output) { + let commit_state = parse(CommitState, {}); chdir(get_git_root()); if (config.check_status) { @@ -211,7 +211,7 @@ export async function main(config: z.infer) { message: `Select optional footers ${SPACE_TO_SELECT}`, initialValues: config.commit_footer.initial_value, options: COMMIT_FOOTER_OPTIONS as { - value: z.infer; + value: Output; label: string; hint: string; }[], @@ -324,8 +324,8 @@ export async function main(config: z.infer) { } function build_commit_string( - commit_state: z.infer, - config: z.infer, + commit_state: Output, + config: Output, colorize: boolean = false, escape_quotes: boolean = false, include_trailer: boolean = false, diff --git a/src/init.ts b/src/init.ts index f605f43..eaad6d1 100644 --- a/src/init.ts +++ b/src/init.ts @@ -1,17 +1,18 @@ #! /usr/bin/env node -import { Config } from "./zod-state"; -import color from "picocolors"; -import fs from "fs"; import * as p from "@clack/prompts"; -import { CONFIG_FILE_NAME, get_git_root, load_setup } from "./utils"; +import fs from "fs"; +import color from "picocolors"; +import { parse } from "valibot"; +import { CONFIG_FILE_NAME, get_git_root } from "./utils"; +import { Config } from "./vali-state"; try { console.clear(); p.intro(`${color.bgCyan(color.black(" better-commits-init "))}`); const root = get_git_root(); const root_path = `${root}/${CONFIG_FILE_NAME}`; - const default_config = Config.parse({}); + const default_config = parse(Config, {}); fs.writeFileSync(root_path, JSON.stringify(default_config, null, 4)); p.log.success(`${color.green("Successfully created .better-commits.json")}`); p.outro( diff --git a/src/utils.ts b/src/utils.ts index 8c31d49..55b135c 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,11 +1,10 @@ -import { homedir } from "os"; -import { z } from "zod"; -import color from "picocolors"; -import { execSync } from "child_process"; import * as p from "@clack/prompts"; +import { execSync } from "child_process"; import fs from "fs"; -import { fromZodError } from "zod-validation-error"; -import { Config } from "./zod-state"; +import { homedir } from "os"; +import color from "picocolors"; +import { Output, ValiError, parse, picklist } from "valibot"; +import { Config } from "./vali-state"; export const CONFIG_FILE_NAME = ".better-commits.json"; export const SPACE_TO_SELECT = `${color.dim("( to select)")}`; @@ -120,36 +119,36 @@ export const COMMIT_FOOTER_OPTIONS = [ ]; export const CUSTOM_SCOPE_KEY: "custom" = "custom"; -export const Z_FOOTER_OPTIONS = z.enum([ +export const V_FOOTER_OPTIONS = picklist([ "closes", "trailer", "breaking-change", "deprecated", "custom", ]); -export const Z_BRANCH_FIELDS = z.enum([ +export const V_BRANCH_FIELDS = picklist([ "user", "version", "type", "ticket", "description", ]); -export const Z_BRANCH_CONFIG_FIELDS = z.enum([ +export const V_BRANCH_CONFIG_FIELDS = picklist([ "branch_user", "branch_version", "branch_type", "branch_ticket", "branch_description", ]); -export const BRANCH_ORDER_DEFAULTS: z.infer[] = [ +export const BRANCH_ORDER_DEFAULTS: Output[] = [ "user", "version", "type", "ticket", "description", ]; -export const Z_BRANCH_ACTIONS = z.enum(["branch", "worktree"]); -export const FOOTER_OPTION_VALUES: z.infer[] = [ +export const V_BRANCH_ACTIONS = picklist(["branch", "worktree"]); +export const FOOTER_OPTION_VALUES: Output[] = [ "closes", "trailer", "breaking-change", @@ -157,7 +156,7 @@ export const FOOTER_OPTION_VALUES: z.infer[] = [ "custom", ]; export const BRANCH_ACTION_OPTIONS: { - value: z.infer; + value: Output; label: string; hint?: string; }[] = [ @@ -168,7 +167,7 @@ export const BRANCH_ACTION_OPTIONS: { /* LOAD */ export function load_setup( cli_name = " better-commits ", -): z.infer { +): Output { console.clear(); p.intro(`${color.bgCyan(color.black(cli_name))}`); @@ -197,7 +196,7 @@ export function load_setup( if (global_config) return global_config; - const default_config = Config.parse({}); + const default_config = parse(Config, {}); p.log.step( "Config not found. Generating default .better-commit.json at $HOME", ); @@ -217,13 +216,17 @@ function read_config_from_path(config_path: string) { return validate_config(res); } -function validate_config( - config: z.infer, -): z.infer { +function validate_config(config: Output): Output { try { - return Config.parse(config); + return parse(Config, config); } catch (err: any) { - console.log(fromZodError(err).message); + if (err instanceof ValiError) { + const first_issue_path = err.issues[0].path ?? []; + const dot_path = first_issue_path.map((item) => item.key).join("."); + p.log.error( + `Invalid Configuration: ${color.red(dot_path)}\n` + err.message, + ); + } process.exit(0); } } diff --git a/src/vali-state.ts b/src/vali-state.ts new file mode 100644 index 0000000..2a6dc89 --- /dev/null +++ b/src/vali-state.ts @@ -0,0 +1,253 @@ +import { + array, + boolean, + custom, + emoji, + minValue, + number, + object, + optional, + picklist, + string, + transform, +} from "valibot"; +import { + BRANCH_ORDER_DEFAULTS, + CUSTOM_SCOPE_KEY, + DEFAULT_SCOPE_OPTIONS, + DEFAULT_TYPE_OPTIONS, + FOOTER_OPTION_VALUES, + V_BRANCH_ACTIONS, + V_BRANCH_FIELDS, + V_FOOTER_OPTIONS, +} from "./utils"; + +export const Config = object({ + check_status: optional(boolean(), true), + commit_type: transform( + optional( + object( + { + enable: optional(boolean(), true), + initial_value: optional(string(), "feat"), + infer_type_from_branch: optional(boolean(), true), + append_emoji_to_label: optional(boolean(), false), + append_emoji_to_commit: optional(boolean(), false), + options: optional( + array( + object({ + value: string(), + label: optional(string()), + hint: optional(string()), + emoji: optional(string([emoji()]), undefined), + trailer: optional(string()), + }), + ), + DEFAULT_TYPE_OPTIONS, + ), + }, + [ + custom( + (val) => + val.options.map((v) => v.value).includes(val.initial_value), + (val) => { + const input = val.input as { initial_value: string }; + return `Type: initial_value "${input.initial_value}" must exist in options`; + }, + ), + ], + ), + {}, + ), + (val) => { + const options = val.options.map((v) => ({ + ...v, + label: + v.emoji && val.append_emoji_to_label + ? `${v.emoji} ${v.label}` + : v.label, + })); + return { ...val, options }; + }, + ), + commit_scope: transform( + optional( + object( + { + enable: optional(boolean(), true), + custom_scope: optional(boolean(), false), + initial_value: optional(string(), "app"), + options: optional( + array( + object({ + value: string(), + label: optional(string()), + hint: optional(string()), + }), + ), + DEFAULT_SCOPE_OPTIONS, + ), + }, + [ + custom( + (val) => { + const options = val.options.map((v) => v.value); + return options.includes(val.initial_value); + }, + (val) => { + const input = val.input as { initial_value: string }; + return `Scope: initial_value "${input.initial_value}" must exist in options`; + }, + ), + ], + ), + {}, + ), + (val) => { + const options = val.options.map((v) => v.value); + if (val.custom_scope && !options.includes(CUSTOM_SCOPE_KEY)) { + return { + ...val, + options: [ + ...val.options, + { + label: CUSTOM_SCOPE_KEY, + value: CUSTOM_SCOPE_KEY, + hint: "Write a custom scope", + }, + ], + }; + } + return val; + }, + ), + check_ticket: optional( + object({ + infer_ticket: optional(boolean(), true), + confirm_ticket: optional(boolean(), true), + add_to_title: optional(boolean(), true), + append_hashtag: optional(boolean(), false), + prepend_hashtag: optional( + picklist(["Never", "Always", "Prompt"]), + "Never", + ), + surround: optional(picklist(["", "()", "[]", "{}"]), ""), + title_position: optional( + picklist(["start", "end", "before-colon", "beginning"]), + "start", + ), + }), + {}, + ), + commit_title: optional( + object({ + max_size: optional(number([minValue(1)]), 70), + }), + {}, + ), + commit_body: optional( + object({ + enable: optional(boolean(), true), + required: optional(boolean(), false), + }), + {}, + ), + commit_footer: optional( + object({ + enable: optional(boolean(), true), + initial_value: optional(array(V_FOOTER_OPTIONS), []), + options: optional(array(V_FOOTER_OPTIONS), FOOTER_OPTION_VALUES), + }), + {}, + ), + breaking_change: optional( + object({ + add_exclamation_to_title: optional(boolean(), false), + }), + {}, + ), + confirm_with_editor: optional(boolean(), false), + confirm_commit: optional(boolean(), true), + print_commit_output: optional(boolean(), true), + branch_pre_commands: optional(array(string()), []), + branch_post_commands: optional(array(string()), []), + worktree_pre_commands: optional(array(string()), []), + worktree_post_commands: optional(array(string()), []), + branch_user: optional( + object({ + enable: optional(boolean(), true), + required: optional(boolean(), false), + separator: optional(picklist(["/", "-", "_"]), "/"), + }), + {}, + ), + branch_type: optional( + object({ + enable: optional(boolean(), true), + separator: optional(picklist(["/", "-", "_"]), "/"), + }), + {}, + ), + branch_version: optional( + object({ + enable: optional(boolean(), true), + required: optional(boolean(), false), + separator: optional(picklist(["/", "-", "_"]), "/"), + }), + {}, + ), + branch_ticket: optional( + object({ + enable: optional(boolean(), true), + required: optional(boolean(), false), + separator: optional(picklist(["/", "-", "_"]), "/"), + }), + {}, + ), + branch_description: optional( + object({ + max_length: optional(number([minValue(1)]), 70), + separator: optional(picklist(["", "/", "-", "_"]), ""), + }), + {}, + ), + branch_action_default: optional(V_BRANCH_ACTIONS, "branch"), + branch_order: optional(array(V_BRANCH_FIELDS), BRANCH_ORDER_DEFAULTS), + enable_worktrees: optional(boolean(), true), + overrides: optional( + object({ + shell: optional(string()), + }), + {}, + ), +}); + +export const CommitState = optional( + object({ + type: optional(string(), ""), + scope: optional(string(), ""), + title: optional(string(), ""), + body: optional(string(), ""), + closes: optional(string(), ""), + ticket: optional(string(), ""), + breaking_title: optional(string(), ""), + breaking_body: optional(string(), ""), + deprecates: optional(string(), ""), + deprecates_title: optional(string(), ""), + deprecates_body: optional(string(), ""), + custom_footer: optional(string(), ""), + trailer: optional(string(), ""), + }), + {}, +); + +export const BranchState = optional( + object({ + user: optional(string(), ""), + type: optional(string(), ""), + ticket: optional(string(), ""), + description: optional(string(), ""), + version: optional(string(), ""), + }), + {}, +); diff --git a/src/zod-state.ts b/src/zod-state.ts deleted file mode 100644 index a150f64..0000000 --- a/src/zod-state.ts +++ /dev/null @@ -1,203 +0,0 @@ -import { z } from "zod"; -import { - BRANCH_ORDER_DEFAULTS, - CUSTOM_SCOPE_KEY, - DEFAULT_SCOPE_OPTIONS, - DEFAULT_TYPE_OPTIONS, - FOOTER_OPTION_VALUES, - Z_BRANCH_ACTIONS, - Z_BRANCH_FIELDS, - Z_FOOTER_OPTIONS, -} from "./utils"; - -export const Config = z - .object({ - check_status: z.boolean().default(true), - commit_type: z - .object({ - enable: z.boolean().default(true), - initial_value: z.string().default("feat"), - infer_type_from_branch: z.boolean().default(true), - append_emoji_to_label: z.boolean().default(false), - append_emoji_to_commit: z.boolean().default(false), - options: z - .array( - z.object({ - value: z.string(), - label: z.string().optional(), - hint: z.string().optional(), - emoji: z.string().emoji().optional(), - trailer: z.string().optional(), - }), - ) - .default(DEFAULT_TYPE_OPTIONS), - }) - .default({}) - .transform((val) => { - const options = val.options.map((v) => ({ - ...v, - label: - v.emoji && val.append_emoji_to_label - ? `${v.emoji} ${v.label}` - : v.label, - })); - return { ...val, options }; - }) - .refine( - (val) => val.options.map((v) => v.value).includes(val.initial_value), - (val) => ({ - message: `Type: initial_value "${val.initial_value}" must exist in options`, - }), - ), - commit_scope: z - .object({ - enable: z.boolean().default(true), - custom_scope: z.boolean().default(false), - initial_value: z.string().default("app"), - options: z - .array( - z.object({ - value: z.string(), - label: z.string().optional(), - hint: z.string().optional(), - }), - ) - .default(DEFAULT_SCOPE_OPTIONS), - }) - .default({}) - .transform((val) => { - const options = val.options.map((v) => v.value); - if (val.custom_scope && !options.includes(CUSTOM_SCOPE_KEY)) { - return { - ...val, - options: [ - ...val.options, - { - label: CUSTOM_SCOPE_KEY, - value: CUSTOM_SCOPE_KEY, - hint: "Write a custom scope", - }, - ], - }; - } - return val; - }) - .refine( - (val) => { - const options = val.options.map((v) => v.value); - return options.includes(val.initial_value); - }, - (val) => ({ - message: `Scope: initial_value "${val.initial_value}" must exist in options`, - }), - ), - check_ticket: z - .object({ - infer_ticket: z.boolean().default(true), - confirm_ticket: z.boolean().default(true), - add_to_title: z.boolean().default(true), - append_hashtag: z.boolean().default(false), - prepend_hashtag: z.enum(["Never", "Always", "Prompt"]).default("Never"), - surround: z.enum(["", "()", "[]", "{}"]).default(""), - title_position: z - .enum(["start", "end", "before-colon", "beginning"]) - .default("start"), - }) - .default({}), - commit_title: z - .object({ - max_size: z.number().positive().default(70), - }) - .default({}), - commit_body: z - .object({ - enable: z.boolean().default(true), - required: z.boolean().default(false), - }) - .default({}), - commit_footer: z - .object({ - enable: z.boolean().default(true), - initial_value: z.array(Z_FOOTER_OPTIONS).default([]), - options: z.array(Z_FOOTER_OPTIONS).default(FOOTER_OPTION_VALUES), - }) - .default({}), - breaking_change: z - .object({ - add_exclamation_to_title: z.boolean().default(true), - }) - .default({}), - confirm_with_editor: z.boolean().default(false), - confirm_commit: z.boolean().default(true), - print_commit_output: z.boolean().default(true), - branch_pre_commands: z.array(z.string()).default([]), - branch_post_commands: z.array(z.string()).default([]), - worktree_pre_commands: z.array(z.string()).default([]), - worktree_post_commands: z.array(z.string()).default([]), - branch_user: z - .object({ - enable: z.boolean().default(true), - required: z.boolean().default(false), - separator: z.enum(["/", "-", "_"]).default("/"), - }) - .default({}), - branch_type: z - .object({ - enable: z.boolean().default(true), - separator: z.enum(["/", "-", "_"]).default("/"), - }) - .default({}), - branch_version: z - .object({ - enable: z.boolean().default(false), - required: z.boolean().default(false), - separator: z.enum(["/", "-", "_"]).default("/"), - }) - .default({}), - branch_ticket: z - .object({ - enable: z.boolean().default(true), - required: z.boolean().default(false), - separator: z.enum(["/", "-", "_"]).default("-"), - }) - .default({}), - branch_description: z - .object({ - max_length: z.number().positive().default(70), - separator: z.enum(["", "/", "-", "_"]).default(""), - }) - .default({}), - branch_action_default: Z_BRANCH_ACTIONS.default("branch"), - branch_order: z.array(Z_BRANCH_FIELDS).default(BRANCH_ORDER_DEFAULTS), - enable_worktrees: z.boolean().default(true), - overrides: z.object({ shell: z.string().optional() }).default({}), - }) - .default({}); - -export const CommitState = z - .object({ - type: z.string().default(""), - scope: z.string().default(""), - title: z.string().default(""), - body: z.string().default(""), - closes: z.string().default(""), - ticket: z.string().default(""), - breaking_title: z.string().default(""), - breaking_body: z.string().default(""), - deprecates: z.string().default(""), - deprecates_title: z.string().default(""), - deprecates_body: z.string().default(""), - custom_footer: z.string().default(""), - trailer: z.string().default(""), - }) - .default({}); - -export const BranchState = z - .object({ - user: z.string().default(""), - type: z.string().default(""), - ticket: z.string().default(""), - description: z.string().default(""), - version: z.string().default(""), - }) - .default({});