{"_id":"zodline","_rev":"3-999193dad1116a6366e8cadebda25e5c","name":"zodline","dist-tags":{"latest":"0.3.1"},"versions":{"0.2.0":{"name":"zodline","version":"0.2.0","keywords":["cli","parser","zod","typescript","command-line"],"author":{"name":"Robin Genz","email":"mail@robingenz.dev"},"license":"MIT","_id":"zodline@0.2.0","maintainers":[{"name":"robingenz","email":"mail@robingenz.dev"}],"homepage":"https://github.com/robingenz/zodline#readme","bugs":{"url":"https://github.com/robingenz/zodline/issues"},"bin":{"zodline-example":"example.js"},"dist":{"shasum":"c62f6c35a5e3c78bb2d20f98f530a3f3b2540dab","tarball":"https://registry.npmjs.org/zodline/-/zodline-0.2.0.tgz","fileCount":16,"integrity":"sha512-VsqC71PUxo7ZVyZF7eK7VlieT8yIVXKwENSub9YW96+XP8+w1YdoeNthOjjAPAAceqeqLutQ8NTqsovBQ9LNQw==","signatures":[{"sig":"MEQCIAdslt408U8+e/GSK2xT1K+1tkejA10xJ4ybge2ygHk5AiBbUAKT0xFwD6iPNmr592MqTC9L9a2fsAQcGy4dbYFmdw==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":52097},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=16.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"a49cf497a4b915fa4eca0f08b76d104bab43900b","scripts":{"dev":"tsc --watch","fmt":"prettier --write \"src/**/*.{ts,js,json}\"","lint":"prettier --check \"src/**/*.{ts,js,json}\"","test":"vitest run","build":"tsc","clean":"rimraf dist","release":"commit-and-tag-version","test:ui":"vitest --ui","test:run":"vitest run","test:coverage":"vitest run --coverage","prepublishOnly":"npm run clean && npm run build","release:dry-run":"npm run release -- --dry-run"},"_npmUser":{"name":"robingenz","email":"mail@robingenz.dev"},"repository":{"url":"git+https://github.com/robingenz/zodline.git","type":"git"},"_npmVersion":"11.13.0","description":"A CLI parser built with Zod.","directories":{},"_nodeVersion":"24.16.0","_hasShrinkwrap":false,"devDependencies":{"zod":"^4.0.17","rimraf":"^5.0.5","vitest":"^3.2.4","prettier":"^3.1.1","@vitest/ui":"^3.2.4","typescript":"^5.3.3","@types/node":"^20.10.0","@vitest/coverage-v8":"^3.2.4","commit-and-tag-version":"^12.5.2"},"peerDependencies":{"zod":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/zodline_0.2.0_1786001214608_0.17256118281220423","host":"s3://npm-registry-packages-npm-production"}},"0.3.0":{"name":"zodline","version":"0.3.0","keywords":["cli","parser","zod","typescript","command-line"],"author":{"name":"Robin Genz","email":"mail@robingenz.dev"},"license":"MIT","_id":"zodline@0.3.0","maintainers":[{"name":"robingenz","email":"mail@robingenz.dev"}],"homepage":"https://github.com/capawesome-team/zodline#readme","bugs":{"url":"https://github.com/capawesome-team/zodline/issues"},"bin":{"zodline-example":"example.js"},"dist":{"shasum":"32b20c3664c1c17247de82e37827a0c703f84a1e","tarball":"https://registry.npmjs.org/zodline/-/zodline-0.3.0.tgz","fileCount":16,"integrity":"sha512-9F3EY7xp9DIztEaM1Jnz+rN9aBuCt3RZaPGpOEEIx3nak2ISRvBDplPBfjVWkWs+ZV68+ZP90qRGUDzW/vZxDQ==","signatures":[{"sig":"MEUCIAakZw2oLLHPVYmDJuO42lsLEF98wONr3ff9gwICC0YhAiEA7YWa1+g9X4MJCk2pYBpQGqM12T/O5oRYS2dROc8ddZ8=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":52109},"main":"dist/index.js","type":"module","types":"dist/index.d.ts","engines":{"node":">=16.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"6f410b5289fe3123cf33b7a13121bbb9988708b9","scripts":{"dev":"tsc --watch","fmt":"prettier --write \"src/**/*.{ts,js,json}\"","lint":"prettier --check \"src/**/*.{ts,js,json}\"","test":"vitest run","build":"tsc","clean":"rimraf dist","release":"commit-and-tag-version","test:ui":"vitest --ui","test:run":"vitest run","test:coverage":"vitest run --coverage","prepublishOnly":"npm run clean && npm run build","release:dry-run":"npm run release -- --dry-run"},"_npmUser":{"name":"robingenz","email":"mail@robingenz.dev"},"repository":{"url":"git+https://github.com/capawesome-team/zodline.git","type":"git"},"_npmVersion":"11.13.0","description":"A CLI parser built with Zod.","directories":{},"_nodeVersion":"24.16.0","_hasShrinkwrap":false,"devDependencies":{"zod":"^4.4.3","rimraf":"^5.0.5","vitest":"^3.2.7","prettier":"^3.9.6","@vitest/ui":"^3.2.7","typescript":"^5.9.3","@types/node":"^20.19.43","@vitest/coverage-v8":"^3.2.7","commit-and-tag-version":"^12.7.3"},"peerDependencies":{"zod":"^4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/zodline_0.3.0_1786001576910_0.606733618847975","host":"s3://npm-registry-packages-npm-production"}},"0.3.1":{"_id":"zodline@0.3.1","bugs":{"url":"https://github.com/capawesome-team/zodline/issues"},"dist":{"shasum":"dfa79259650359578ca760e6dfd52e2313148040","tarball":"https://registry.npmjs.org/zodline/-/zodline-0.3.1.tgz","fileCount":15,"integrity":"sha512-hN8qqGgQUhA2f7PbuO0HtLXiu+41CkAod89JzkQk2Fao63kf9GRBcCI2ocgjKZzEe+hWFCbj9iRyZ3PyoVJfMQ==","signatures":[{"sig":"MEQCIBSOA/9FaZJrcpdTGBifWpoB8WssoTDE1hY0yJq3F0DAAiB2nfEnO0FSPPPb8wy9EcUX6dwITTPlUW1g5AKwQZN9xg==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"},{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEQCICUWZdSt19kfXBr5bnTJIgnjOWBLlpWcFcmIoONlXKq0AiB5vbN0NlMjH65SfxiXK+W+ug3qATfOq1EtYffcMbzK2g=="}],"attestations":{"url":"https://registry.npmjs.org/-/npm/v1/attestations/zodline@0.3.1","provenance":{"predicateType":"https://slsa.dev/provenance/v1"}},"unpackedSize":57832},"main":"dist/index.js","name":"zodline","type":"module","types":"dist/index.d.ts","author":{"name":"Robin Genz","email":"mail@robingenz.dev"},"engines":{"node":">=16.0.0"},"exports":{".":{"types":"./dist/index.d.ts","import":"./dist/index.js"}},"gitHead":"ab8f75b41ce5aa97ef9971e1a453ebb3e238e93d","license":"MIT","scripts":{"dev":"tsc --watch","fmt":"prettier --write \"src/**/*.{ts,js,json}\"","lint":"prettier --check \"src/**/*.{ts,js,json}\"","test":"vitest run","build":"tsc","clean":"rimraf dist","test:ui":"vitest --ui","docs:dev":"blume dev","test:run":"vitest run","docs:build":"blume build && rm -rf .blume-dist && cp -R dist .blume-dist","docs:check":"blume check","docs:preview":"blume preview","test:coverage":"vitest run --coverage","prepublishOnly":"npm run clean && npm run build"},"version":"0.3.1","_npmUser":{"name":"GitHub Actions","email":"npm-oidc-no-reply@github.com","trustedPublisher":{"id":"github","oidcConfigId":"oidc:b96ee89b-4130-412c-83e0-de4763807e54"}},"homepage":"https://zodline.dev","keywords":["cli","parser","cli-parser","command-line","command-line-parser","argv","arguments","args","flags","options","zod","zod-cli","typescript","type-safe","validation","esm","commander-alternative","yargs-alternative"],"repository":{"url":"git+https://github.com/capawesome-team/zodline.git","type":"git"},"_npmVersion":"12.0.2","description":"Type-safe CLI argument parser powered by Zod schemas. Zero dependencies, inferred TypeScript types, and automatic help generation.","directories":{},"maintainers":[{"name":"robingenz","email":"mail@robingenz.dev"}],"_nodeVersion":"24.20.0","_hasShrinkwrap":false,"devDependencies":{"zod":"^4.4.3","astro":"^7.1.6","blume":"^1.3.1","rimraf":"^5.0.5","vitest":"^3.2.7","prettier":"^3.9.6","@vitest/ui":"^3.2.7","typescript":"^5.9.3","@types/node":"^20.19.43","@vitest/coverage-v8":"^3.2.7"},"peerDependencies":{"zod":"^4.0.0"},"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/zodline_0.3.1_1789893894257_0.46668141838979227"}}},"time":{"created":"2026-08-06T07:26:54.459Z","modified":"2026-09-20T08:44:54.669Z","0.2.0":"2026-08-06T07:26:54.772Z","0.3.0":"2026-08-06T07:32:57.066Z","0.3.1":"2026-09-20T08:44:54.343Z"},"bugs":{"url":"https://github.com/capawesome-team/zodline/issues"},"author":{"name":"Robin Genz","email":"mail@robingenz.dev"},"license":"MIT","homepage":"https://zodline.dev","keywords":["cli","parser","cli-parser","command-line","command-line-parser","argv","arguments","args","flags","options","zod","zod-cli","typescript","type-safe","validation","esm","commander-alternative","yargs-alternative"],"repository":{"url":"git+https://github.com/capawesome-team/zodline.git","type":"git"},"description":"Type-safe CLI argument parser powered by Zod schemas. Zero dependencies, inferred TypeScript types, and automatic help generation.","maintainers":[{"name":"robingenz","email":"mail@robingenz.dev"}],"readme":"# zodline\n\n[![npm version](https://img.shields.io/npm/v/zodline)](https://www.npmjs.com/package/zodline)\n[![npm downloads](https://img.shields.io/npm/dm/zodline)](https://www.npmjs.com/package/zodline)\n[![license](https://img.shields.io/npm/l/zodline)](https://github.com/capawesome-team/zodline/blob/main/LICENSE)\n\nBuild type-safe command-line interfaces with Zod. Declare your commands, options and arguments as schemas — validation, TypeScript types and help output come for free.\n\n📚 **[Documentation](https://zodline.dev/docs)** · [Quickstart](https://zodline.dev/docs/quickstart) · [API reference](https://zodline.dev/docs/reference/api)\n\n## Features\n\n- 🛡️ **Type-safe**: `options` and `args` are inferred from your schemas. Rename a field and every call site fails to compile.\n- 📋 **Declarative**: A command is a plain object — description, schemas, action. No builder chains.\n- ✅ **Validation included**: Coercion, defaults, refinements, unions — anything Zod can express, your CLI can accept.\n- 🚫 **Strict by default**: Unknown flags are errors, not silently ignored keys. Typos surface immediately.\n- ❓ **Generated help**: `--help` and `--version` are handled for you, including per-command help.\n- 🚀 **Zero dependencies**: Only Zod, which you already have.\n\n## Requirements\n\n- Node.js 16 or later\n- Zod 4 (peer dependency)\n- An ESM project — `zodline` ships no CommonJS build\n\n## Installation\n\n```bash\nnpm install zodline zod\n```\n\n## Quickstart\n\n```ts\nimport { z } from 'zod';\nimport { defineCommand, defineConfig, defineOptions, processConfig } from 'zodline';\n\nconst greet = defineCommand({\n  description: 'Greet someone',\n  options: defineOptions(\n    z.object({\n      name: z.string().describe('Name to greet'),\n      loud: z.boolean().default(false).describe('Use uppercase'),\n    }),\n    { n: 'name', l: 'loud' }, // Short aliases\n  ),\n  action: async (options) => {\n    // options is typed as { name: string; loud: boolean }\n    const greeting = `Hello, ${options.name}!`;\n    console.log(options.loud ? greeting.toUpperCase() : greeting);\n  },\n});\n\nconst config = defineConfig({\n  meta: {\n    name: 'my-cli',\n    version: '1.0.0',\n    description: 'A simple CLI example',\n  },\n  commands: { greet },\n});\n\nconst result = processConfig(config, process.argv.slice(2));\nawait result.command.action(result.options, result.args);\n```\n\nRun it:\n\n```bash\n$ my-cli greet --name Alice\nHello, Alice!\n\n$ my-cli greet -n Bob --loud\nHELLO, BOB!\n```\n\nHelp is generated from the same schemas:\n\n```\n$ my-cli --help\n\nA simple CLI example (my-cli v1.0.0)\n\nUSAGE my-cli <command>\n\nCOMMANDS\n\n  greet    Greet someone\n\nUse my-cli <command> --help for more information about a command.\n```\n\n```\n$ my-cli greet --help\n\nGreet someone (my-cli greet v1.0.0)\n\nUSAGE my-cli greet [OPTIONS]\n\nOPTIONS\n\n  --name, -n    Name to greet\n  --loud, -l    Use uppercase (default: false)\n```\n\n## Usage\n\n### Commands\n\nCommands are a flat record of name to definition. Group related commands with a separator in the name:\n\n```ts\nconst config = defineConfig({\n  meta: { name: 'my-app', version: '1.0.0' },\n  commands: {\n    start: startCommand,\n    'apps:list': appsListCommand,\n    'apps:create': appsCreateCommand,\n  },\n});\n```\n\nUse a space as the separator to get subcommands. The longest matching name wins and the remaining positional\narguments become `args`:\n\n```ts\nconst config = defineConfig({\n  meta: { name: 'my-app', version: '1.0.0' },\n  commands: {\n    'config set': configSetCommand,\n    'config get': configGetCommand,\n  },\n});\n```\n\n- `my-app config set theme dark` runs `configSetCommand` with the args `['theme', 'dark']`\n- `my-app config --help` lists only the `config` commands\n\nSet `defaultCommand` to run a command when none is given:\n\n```ts\nconst config = defineConfig({\n  meta: { name: 'my-app', version: '1.0.0' },\n  commands: { start: startCommand, build: buildCommand },\n  defaultCommand: startCommand,\n});\n```\n\n- `my-app` runs `startCommand`\n- `my-app build` runs `buildCommand`\n- `my-app --help` still shows the help message\n\n### Options\n\n`defineOptions` takes a Zod object schema and an optional map from short alias to schema key. The `.describe()` text becomes the option's help line, and `.default()` is shown in help.\n\n```ts\nconst options = defineOptions(\n  z.object({\n    port: z.coerce.number().min(1).max(65535).default(3000).describe('Port to listen on'),\n    files: z.array(z.string()).describe('Input files'),\n    tags: z.array(z.string()).optional().describe('Tags to apply'),\n  }),\n  { p: 'port', f: 'files' },\n);\n```\n\nEverything from the command line arrives as a string, so use `z.coerce` for numbers and other non-string types.\n\nA single value for an array field is wrapped automatically, so both of these produce `['a.txt']`:\n\n```bash\n--files a.txt\n--files a.txt --files b.txt   # ['a.txt', 'b.txt']\n```\n\n### Arguments\n\nPositional arguments are validated by a single schema that receives the whole array. Use `z.tuple` for a fixed shape and `z.array` for a variable number:\n\n```ts\nconst copy = defineCommand({\n  description: 'Copy a file',\n  args: z.tuple([z.string().describe('Source file'), z.string().describe('Destination file')]),\n  options: defineOptions(z.object({ verbose: z.boolean().default(false) }), { v: 'verbose' }),\n  action: async (options, args) => {\n    const [source, destination] = args; // [string, string]\n    console.log(`Copying ${source} to ${destination}`);\n  },\n});\n```\n\n### Flag syntax\n\n| Form | Example | Result |\n| --- | --- | --- |\n| Long flag | `--verbose` | `verbose: true` |\n| Long flag with value | `--port 3000`, `--port=3000` | `port: '3000'` |\n| Short flag | `-v`, `-p 3000` | resolved through the alias map |\n| Clustered short flags | `-abc` | `a: true, b: true, c: true` |\n| Kebab-case | `--max-retries` | matches the schema key `maxRetries` |\n| Repeated flag | `--file a.txt --file b.txt` | `file: ['a.txt', 'b.txt']` |\n\nA flag whose next argument starts with `-`, or which is last, becomes `true`.\n\n### Help and version\n\n- `<cli> --help` prints the command list, `<cli> <command> --help` prints that command's options.\n- `<cli> --version` prints `meta.version`. It is only handled when no command is given and `meta.version` is set.\n\nBoth paths print to stdout and call `process.exit(0)`. Keep that in mind when calling `processConfig` from tests.\n\n### Error handling\n\n`processConfig` validates and returns — it never invokes your action. That keeps parsing and execution separate, so commands stay testable.\n\n```ts\nimport { z } from 'zod';\nimport { processConfig, ZodlineError } from 'zodline';\n\ntry {\n  const result = processConfig(config, process.argv.slice(2));\n  await result.command.action(result.options, result.args);\n} catch (error) {\n  if (error instanceof ZodlineError) {\n    // Unknown command, unknown option, or no command specified\n    console.error(error.message);\n  } else if (error instanceof z.ZodError) {\n    // An option failed schema validation\n    console.error(z.prettifyError(error));\n  } else {\n    throw error;\n  }\n  process.exit(1);\n}\n```\n\nPositional argument failures are currently thrown as a plain `Error` prefixed with `Argument validation failed:`.\n\n## API\n\n| Export | Description |\n| --- | --- |\n| `defineOptions(schema, aliases?)` | Pairs a Zod object schema with an optional short-alias map. |\n| `defineCommand(definition)` | Defines a command from a `description`, `examples`, `options`, `args` and `action`. |\n| `defineConfig(config)` | Defines the CLI from `meta`, `commands` and an optional `defaultCommand`. |\n| `processConfig(config, argv)` | Parses and validates `argv`, returning `{ command, options, args }`. |\n| `ZodlineError` | Thrown for unknown commands and unknown options. |\n\nSee the [API reference](https://zodline.dev/docs/reference/api) for full signatures and types.\n\n## Comparison\n\n|  | zodline | commander | yargs |\n| --- | --- | --- | --- |\n| Validation | Zod schemas | custom parser functions | built-in coercions |\n| Option types | inferred from the schema | manual type annotations | inferred from the builder chain |\n| Runtime dependencies | 0 | 0 | 6 |\n| Module format | ESM only | CJS and ESM | CJS and ESM |\n| Parsing and execution | separate | coupled | coupled |\n\nDependency counts as of commander 15 and yargs 18. Both cover more surface than `zodline` — nested subcommands, shell completion, i18n. `zodline` is deliberately smaller and leans on Zod for everything it can.\n\n## Used By\n\n- [Capawesome Team CLI](https://github.com/capawesome-team/cli) — the Capawesome Cloud CLI to manage Live Updates and more.\n\n## Contributing\n\n```bash\nnpm install\nnpm test\nnpm run build\nnpm run lint\n```\n\nBug reports and feature requests are welcome in the [issue tracker](https://github.com/capawesome-team/zodline/issues).\n\n## Migration\n\n`zodline` was previously published as `@robingenz/zli`. To migrate:\n\n1. Replace the dependency:\n\n   ```bash\n   npm uninstall @robingenz/zli\n   npm install zodline\n   ```\n\n2. Update import specifiers:\n\n   ```diff\n   - import { ... } from '@robingenz/zli';\n   + import { ... } from 'zodline';\n   ```\n\n3. Rename the `ZliError` export to `ZodlineError` (the public API is otherwise unchanged):\n\n   ```diff\n   - import { ZliError } from '@robingenz/zli';\n   + import { ZodlineError } from 'zodline';\n   ```\n\n## Changelog\n\nSee [CHANGELOG.md](./CHANGELOG.md).\n\n## License\n\nSee [LICENSE](./LICENSE).\n","readmeFilename":"README.md"}