{"_id":"@app4you/xsd2zod","_rev":"3-bbfe8d1f20329dafae6f3e52d9f7533c","name":"@app4you/xsd2zod","dist-tags":{"latest":"1.0.2"},"versions":{"1.0.1":{"name":"@app4you/xsd2zod","version":"1.0.1","keywords":["xsd","xml-schema","zod","schema","validation","codegen","typescript","cli"],"author":{"name":"Dawid Wiewiórski","email":"biuro@app4you.dev"},"license":"MIT","_id":"@app4you/xsd2zod@1.0.1","maintainers":[{"name":"dawidw5219","email":"wiewiorskidawid@gmail.com"}],"homepage":"https://github.com/Dawidw5219/xsd2zod#readme","bugs":{"url":"https://github.com/Dawidw5219/xsd2zod/issues"},"bin":{"xsd2zod":"dist/cli.js"},"dist":{"shasum":"ef5b16013091dba83657013c702e2db26fb5af09","tarball":"https://registry.npmjs.org/@app4you/xsd2zod/-/xsd2zod-1.0.1.tgz","fileCount":5,"integrity":"sha512-rp9BsmuwgxuaVH6dDu9iDste3kj/idce366h02FjluxiIUv5M6eJWW6z6dKKCfaPJ+yIJZUrSr+eGAhxdXdnFA==","signatures":[{"sig":"MEQCIAeMTQHTZw6rULi9lYgXJdUbJDNZeRTCGoPOSP4vfYSIAiBkFNC/khkFG82w9no9ys5okH2XtXgrg6cpgcXXhXscjA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":75118},"type":"module","engines":{"node":">=18.0.0"},"gitHead":"cf2f2e85573c2a41cb38d51406ac736fd7d2ff46","private":false,"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","deploy":"pnpm typecheck && pnpm test && pnpm build && ghdeploy --npm","prepare":"pnpm build","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"pnpm typecheck && pnpm test && pnpm build"},"_npmUser":{"name":"dawidw5219","email":"wiewiorskidawid@gmail.com"},"repository":{"url":"git+https://github.com/Dawidw5219/xsd2zod.git","type":"git"},"_npmVersion":"10.9.8","description":"Compile XSD (XML Schema) into Zod schemas. CLI tool — one .xsd in, one .ts out","directories":{},"sideEffects":false,"_nodeVersion":"22.23.0","dependencies":{"chalk":"^5.6.2","commander":"^12.1.0","fast-xml-parser":"^5.7.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"zod":"^4.4.3","tsup":"^8.5.1","vitest":"^4.1.2","typescript":"^5.9.3","@types/node":"^22.0.0"},"peerDependencies":{"zod":">=4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/xsd2zod_1.0.1_1784800678399_0.7860996924821506","host":"s3://npm-registry-packages-npm-production"}},"1.0.2":{"name":"@app4you/xsd2zod","version":"1.0.2","keywords":["xsd","xml-schema","zod","schema","validation","codegen","typescript","cli"],"author":{"name":"Dawid Wiewiórski","email":"biuro@app4you.dev"},"license":"MIT","_id":"@app4you/xsd2zod@1.0.2","maintainers":[{"name":"dawidw5219","email":"wiewiorskidawid@gmail.com"}],"homepage":"https://github.com/Dawidw5219/xsd2zod#readme","bugs":{"url":"https://github.com/Dawidw5219/xsd2zod/issues"},"bin":{"xsd2zod":"dist/cli.js"},"dist":{"shasum":"9a02699dc32bf525195dfd03b4a289181118a86b","tarball":"https://registry.npmjs.org/@app4you/xsd2zod/-/xsd2zod-1.0.2.tgz","fileCount":5,"integrity":"sha512-coc/Wa3J8GntUUOE+YVlAZ4kiKUDCkqXVqnU1RA+H2vPMc+ALA8SRaks2tiW5MvO42/kDN3uat+nfg9A99iyWA==","signatures":[{"sig":"MEQCID+PL7iG+M42xIGDsywkecJtOV58Y+VhvcoFBFngzQtQAiAyqlgoJLKVj/u0/Rbbho7vPJbfu+BxA0hHHwi1u435IA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":75118},"type":"module","engines":{"node":">=18.0.0"},"gitHead":"cf2f2e85573c2a41cb38d51406ac736fd7d2ff46","private":false,"scripts":{"dev":"tsup --watch","test":"vitest run","build":"tsup","deploy":"pnpm typecheck && pnpm test && pnpm build && ghdeploy --npm","prepare":"pnpm build","typecheck":"tsc --noEmit","test:watch":"vitest","prepublishOnly":"pnpm typecheck && pnpm test && pnpm build"},"_npmUser":{"name":"dawidw5219","email":"wiewiorskidawid@gmail.com"},"repository":{"url":"git+https://github.com/Dawidw5219/xsd2zod.git","type":"git"},"_npmVersion":"10.9.8","description":"Compile XSD (XML Schema) into Zod schemas. CLI tool — one .xsd in, one .ts out","directories":{},"sideEffects":false,"_nodeVersion":"22.23.0","dependencies":{"chalk":"^5.6.2","commander":"^12.1.0","fast-xml-parser":"^5.7.3"},"publishConfig":{"access":"public"},"_hasShrinkwrap":false,"devDependencies":{"zod":"^4.4.3","tsup":"^8.5.1","vitest":"^4.1.2","typescript":"^5.9.3","@types/node":"^22.0.0"},"peerDependencies":{"zod":">=4.0.0"},"_npmOperationalInternal":{"tmp":"tmp/xsd2zod_1.0.2_1784800874208_0.36459718135145236","host":"s3://npm-registry-packages-npm-production"}}},"time":{"created":"2026-07-23T09:57:58.266Z","modified":"2026-07-23T12:54:49.013Z","1.0.1":"2026-07-23T09:57:58.545Z","1.0.2":"2026-07-23T10:01:14.345Z"},"bugs":{"url":"https://github.com/Dawidw5219/xsd2zod/issues"},"author":{"name":"Dawid Wiewiórski","email":"biuro@app4you.dev"},"license":"MIT","homepage":"https://github.com/Dawidw5219/xsd2zod#readme","keywords":["xsd","xml-schema","zod","schema","validation","codegen","typescript","cli"],"repository":{"url":"git+https://github.com/Dawidw5219/xsd2zod.git","type":"git"},"description":"Compile XSD (XML Schema) into Zod schemas. CLI tool — one .xsd in, one .ts out","maintainers":[{"email":"wiewiorskidawid@gmail.com","name":"dawidw5219"},{"email":"wiktor.slominski@app4you.dev","name":"wiktorslominski"}],"readme":"# xsd2zod\n\n[![npm version](https://img.shields.io/npm/v/%40app4you%2Fxsd2zod.svg?color=cb3837&logo=npm)](https://www.npmjs.com/package/@app4you/xsd2zod)\n[![license](https://img.shields.io/npm/l/xsd2zod.svg?color=blue)](./LICENSE)\n\nCompile XSD (XML Schema) into [Zod](https://zod.dev) schemas. CLI tool — one\n`.xsd` in, one `.ts` out.\n\n```bash\nnpx @app4you/xsd2zod schema.xsd\n# → schema.ts (next to source, with all types as Zod schemas)\n```\n\n## Why\n\nIndustry XML schemas are everywhere — SEPA, HL7, ISO20022, FpML, e-invoice\nformats, SOAP WSDL — and they ship as `.xsd`. To use them safely in TypeScript\nyou need runtime validation that mirrors the schema, and hand-translating\nhundreds of types every time the spec gets republished isn't realistic.\n\nxsd2zod compiles XSD straight into **declarative Zod** — `z.string()`,\n`z.number().int().min(0)`, `z.enum([...])`, `z.object({...}).describe('...')`.\nNo `refine()`, no escape hatches. Annotations from `xs:documentation` become\n`.describe()` calls, so IDE tooltips and downstream form generators get\nhuman-readable labels for free.\n\n## Install\n\n```bash\n# Global (recommended for CLI usage)\nnpm install -g @app4you/xsd2zod\n\n# Or use directly with npx (no install)\nnpx @app4you/xsd2zod schema.xsd\n\n# Or as a dev dependency for codegen scripts\npnpm add -D xsd2zod\n```\n\nThe compiled output uses `zod` as a peer dependency — install it in your project:\n\n```bash\npnpm add zod\n```\n\n## Usage\n\n### Single file → `.ts` next to it\n\n```bash\nxsd2zod order.xsd\n# → order.ts\n```\n\n### Whole folder\n\n```bash\nxsd2zod schemas/\n# → schemas/<each>.ts\n```\n\n### Custom output directory\n\n```bash\nxsd2zod schemas/ --outDir generated/\n# → generated/<each>.ts\n```\n\n### Multiple inputs sharing imports\n\n```bash\nxsd2zod schemas/order.xsd schemas/customer.xsd --outDir generated/\n# → generated/order.ts, generated/customer.ts\n# Both inputs share the same imports map — xs:import / xs:include resolve\n# across the whole input set.\n```\n\nDependency discovery is recursive. If a regulator ships `main.xsd` next to a\n`bazowe/` or `common/` directory, compiling `main.xsd` is enough — nested\n`xs:import`, `xs:include`, `xs:redefine`, and `xs:override` files are indexed\nautomatically, including URL-style `schemaLocation` values used by KSeF FA(3).\n\n### Bundle multi-file XSD into a single self-contained `.xsd`\n\nWhen your schema imports other schemas (e.g. official MF / EU / ISO standards\nthat live in separate files), bundle them into one before committing:\n\n```bash\nxsd2zod bundle main.xsd --outFile main.bundled.xsd\n# main.bundled.xsd has all xs:include / xs:import inlined.\n# No more dependency files to track.\n```\n\nThen compile the bundled schema like any single-file XSD:\n\n```bash\nxsd2zod main.bundled.xsd\n```\n\n## Options\n\n```\nxsd2zod <inputs...> [options]\n\n  --outDir <dir>             output directory (default: next to each input)\n  -o, --out <dir>            alias for --outDir\n  --include-libraries        also emit .ts for library schemas (XSDs without\n                             a root element). Default: skip — their types are\n                             already inlined into the root schemas that\n                             import them.\n  --allow-missing-imports    silently skip xs:include / xs:import refs that\n                             cannot be resolved (default: error out)\n  --silent                   suppress informational output\n  -h, --help                 show help\n\nxsd2zod bundle <input> [options]\n\n  --outFile <file>           output file (default: <stem>.bundled.xsd)\n  --silent                   suppress informational output\n```\n\n## XSD → Zod at a glance\n\nA condensed Rosetta Stone of what you get:\n\n| XSD | Generated Zod |\n|---|---|\n| `<xs:element name=\"age\" type=\"xs:int\" minOccurs=\"0\"/>` | `age: z.number().int().optional()` |\n| `<xs:element name=\"name\" type=\"xs:string\" maxOccurs=\"unbounded\"/>` | `name: z.array(z.string())` |\n| `<xs:simpleType><xs:restriction base=\"xs:string\"><xs:pattern value=\"[A-Z]{2}\"/></xs:restriction></xs:simpleType>` | `z.string().regex(new RegExp(\"[A-Z]{2}\"))` |\n| `<xs:enumeration value=\"A\"/><xs:enumeration value=\"B\"/>` | `z.enum([\"A\", \"B\"])` |\n| `<xs:totalDigits value=\"16\"/><xs:fractionDigits value=\"2\"/>` | `z.number().multipleOf(0.01)` (with bound) |\n| `<xs:complexType><xs:sequence>…</xs:sequence></xs:complexType>` | `z.object({ … })` |\n| `<xs:choice>…</xs:choice>` | `z.union([…])` |\n| `<xs:extension base=\"Base\">…</xs:extension>` | `z.intersection(Base, z.object({…}))` |\n| `<xs:attribute name=\"id\" type=\"xs:string\"/>` | `'@id': z.string()` |\n| `<xs:annotation><xs:documentation>Pierwsze imię</xs:documentation></xs:annotation>` | `.describe(\"Pierwsze imię\")` |\n| `<xs:nillable/>` | `z.union([T, z.null()])` |\n\n### Full example with annotations\n\nGiven an XSD like:\n\n```xml\n<xs:simpleType name=\"TKwota2\">\n  <xs:annotation>\n    <xs:documentation>Wartość kwotowa wykazana w zł i gr</xs:documentation>\n  </xs:annotation>\n  <xs:restriction base=\"xs:decimal\">\n    <xs:totalDigits value=\"16\"/>\n    <xs:fractionDigits value=\"2\"/>\n  </xs:restriction>\n</xs:simpleType>\n```\n\nxsd2zod emits:\n\n```ts\nexport const TKwota2 = z.lazy(() =>\n  z.number().multipleOf(0.01).describe(\"Wartość kwotowa wykazana w zł i gr\")\n);\n```\n\n`xs:annotation/xs:documentation` becomes `.describe()` on every type and field\n— one of the most useful properties of the conversion. Downstream form\ngenerators get human-readable labels for free.\n\n## Coverage\n\n### Supported\n\nPrimitives (all 19): `string`, `boolean`, `decimal`, `float`, `double`,\n`duration`, `dateTime`, `time`, `date`, `gYearMonth`, `gYear`, `gMonthDay`,\n`gDay`, `gMonth`, `hexBinary`, `base64Binary`, `anyURI`, `QName`, `NOTATION`.\n\nDerived datatypes (all 25): `normalizedString`, `token`, `language`, `Name`,\n`NCName`, `ID`, `IDREF`, `IDREFS`, `ENTITY`, `ENTITIES`, `NMTOKEN`,\n`NMTOKENS`, `integer`, `nonPositiveInteger`, `negativeInteger`, `long`, `int`,\n`short`, `byte`, `nonNegativeInteger`, `unsignedLong`, `unsignedInt`,\n`unsignedShort`, `unsignedByte`, `positiveInteger`.\n\nFacets: `length`, `minLength`, `maxLength`, `pattern`, `enumeration`,\n`whiteSpace`, `maxInclusive`, `maxExclusive`, `minInclusive`, `minExclusive`,\n`totalDigits`, `fractionDigits`.\n\nStructural:\n\n- `xs:simpleType` with `xs:restriction` / `xs:list` / `xs:union`\n- `xs:complexType` with `xs:sequence` / `xs:choice` / `xs:all`\n- `xs:complexContent` with `xs:extension` / `xs:restriction`\n- `xs:simpleContent`\n- `xs:attribute`, `xs:attributeGroup`, `xs:group` (definition + reference)\n- `xs:nillable`\n- `xs:annotation` / `xs:documentation` → `.describe()`\n\nModule composition:\n\n- `xs:include` (same namespace) — inlined\n- `xs:import` (cross namespace) — inlined\n- `xs:redefine` — resolved at parse time\n\nWildcards & no-op constructs:\n\n- `xs:any` — parent object emits as `z.object({...}).passthrough()` so unknown\n  keys are preserved. `processContents=\"skip\"` emits `z.any()` (no validation),\n  `lax`/`strict` emit `z.unknown()`. Namespace constraints (`namespace=\"##other\"`\n  etc.) are parsed into the IR but not enforced at the JS-object layer —\n  namespace info doesn't survive XML→JS flattening.\n- `xs:notation` — DTD-era binding for non-XML data formats. Accepted without\n  error, has no validation impact on parsed XML data.\n\n### Not yet supported\n\n- `xs:assert` — XSD 1.1 XPath assertion\n- `xs:alternative` — XSD 1.1 conditional type selection\n- `xs:key` / `xs:keyref` / `xs:unique` — identity constraints\n\nThese all need an XPath evaluator at runtime. Open an issue if you have a\nreal-world schema blocked by one of them.\n\nXSD spec references:\n[Part 1: Structures](https://www.w3.org/TR/xmlschema11-1/) ·\n[Part 2: Datatypes](https://www.w3.org/TR/xmlschema11-2/).\n\n## Behavior notes\n\n### Strict-by-default error handling\n\nIf an `xs:include` / `xs:import` cannot be resolved (file missing, URL\nunreachable), xsd2zod throws with a clear message. Pass\n`--allow-missing-imports` to skip silently — types from the missing schema\nwill resolve to `z.unknown()`.\n\n### All-or-nothing writes\n\nIf any input file fails to compile, **no** output files are written. You'll\nnever end up with a half-emitted `generated/` directory. The compile happens\nfully in memory before anything touches disk.\n\n### Library schemas auto-skipped\n\nXSDs without a root `xs:element` (typed type-definition libraries — common\nfor shared bases like country codes, base types) are recognized as\n\"libraries\" and their `.ts` is skipped by default. Their types are already\ninlined into root schemas that `xs:import` them. Pass `--include-libraries`\nto emit them too.\n\n### Lazy references\n\nEvery named type emits as `z.lazy(() => ...)` so the generated file is\norder-independent and supports cyclic references. Standard `ZodType` methods\n(`.parse()`, `.safeParse()`, `.optional()`, `.nullable()`) work directly on\nthe exported constants. Schema-specific methods (`.extend()` on objects,\n`.min()` on strings/numbers, `.element` on arrays) require unwrapping the\ninner schema:\n\n```ts\nimport { Person } from './person.ts';\n\nPerson.parse(input);                                    // ✓ works directly\nconst Extended = Person.def.getter().extend({ id: z.string() });  // unwrap once\n```\n\n## Alternatives\n\n| Tool | Approach | Why xsd2zod instead |\n|---|---|---|\n| `xsd2jsonschema` + `json-schema-to-zod` | XSD → JSON Schema → Zod (two-stage pipeline) | Loses `xs:documentation`, fails on nested required `xs:sequence`, drops attribute group composition. xsd2zod compiles directly. |\n| `xsd-to-zod` | Subset XSD → Zod | Doesn't handle `xs:choice` mixed with siblings, `xs:complexContent` extension chains, `xs:redefine`. |\n| Hand-rolled types + manual Zod | — | Works for 5 types. Doesn't scale to 113-type schemas with regulator-driven updates. |\n| `zod-from-x` (XML samples → schema) | Infer Zod from XML *instances* | Different problem entirely — only works if you have data, can't capture facets/constraints from sample alone. |\n\n## FAQ\n\n**Q: My XSD has cycles (Type A → Type B → Type A). Does that work?**\nYes. Every named type wraps in `z.lazy(() => ...)` so cycles resolve at runtime\nwithout ordering issues.\n\n**Q: What about `xs:assert` (XSD 1.1 XPath assertions)?**\nNot yet — see the support table. Most real-world schemas don't use them.\nWorkaround: extend the generated schema with `.superRefine()` manually.\n\n**Q: How are `dateTime`, `date`, `time` represented?**\n`xs:dateTime` → `z.iso.datetime()`, `xs:date` → `z.iso.date()`, `xs:time` →\n`z.iso.time()`. Output is the validated string, not a `Date` object — call\n`new Date(result)` if you need it parsed.\n\n**Q: My schema imports `http://www.w3.org/2001/XMLSchema-instance`. Does that break?**\nNo. The `xsi:` namespace is recognized and treated as built-in (used for\n`nil` / `type` attributes).\n\n**Q: How do I debug a parse error?**\nRun with `--silent` removed to see which file/element xsd2zod choked on. Errors\ninclude the XSD location. Open an issue with the smallest reproducer XSD if\nthe error message isn't enough.\n\n**Q: Can I customize identifiers (e.g. strip a prefix)?**\nNot currently — the CLI is intentionally one-shot. PRs welcome if you have a\nconcrete need.\n\n## License\n\nMIT — © Dawid Wiewiórski\n","readmeFilename":"README.md"}