npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@qninhdt/typespec-zod

v0.7.0

Published

TypeSpec emitter that generates Zod schemas from TypeSpec types.

Readme

@qninhdt/typespec-zod

TypeSpec emitter that generates namespace-grouped Zod schemas from default form models.

This emitter is intentionally focused on form and DTO shapes. It follows the same namespace and selector rules as the ORM-backed emitters, but it does not emit @table models.

What This Emitter Is For

Use this emitter when you want:

  • Zod schemas generated from TypeSpec default form models
  • inferred TypeScript types beside the schemas
  • stable namespace-derived output layout
  • rich field metadata for frontend forms

Installation

pnpm add -D \
  @typespec/compiler \
  @typespec/emitter-framework \
  @alloy-js/core \
  @alloy-js/typescript \
  @qninhdt/typespec-orm \
  @qninhdt/typespec-zod \
  zod

Runtime Expectations

Generated Zod output is intended to drop into TypeScript projects cleanly.

  • standalone mode writes package.json, tsconfig.json, and src/index.ts
  • generated code targets ESM-style package output
  • runtime validation depends on zod
  • inferred types are emitted in the same pass as the schemas, so no post-processing step is required

Configuration Reference

emit:

- "@qninhdt/typespec-zod"

options:
"@qninhdt/typespec-zod":
output-dir: "./outputs/zod"
standalone: true
library-name: "@acme/forms"
include: - "Demo.Platform.Forms"

Supported options:

| Option | Type | Meaning | | -------------- | ---------- | ------------------------------------------------- | | output-dir | string | target directory handled by the TypeSpec compiler | | standalone | boolean | write package metadata and emit under src/ | | library-name | string | package name for standalone output | | include | string[] | namespace or declaration selectors to keep | | exclude | string[] | namespace or declaration selectors to drop |

Not supported:

  • filename
  • package-name
  • legacy post-write alias patching

Selector Behavior

Zod uses the same selector behavior as the ORM-backed emitters.

Examples:

include:

- "Demo.GamePlatform.Forms"
  exclude:
- "Demo.GamePlatform.Forms.Internal"

Behavior:

  • selectors are dotted names, not glob patterns
  • exclude wins over include
  • excluding a dependency required by a selected default form model fails emission

Output Layout

Given:

typescript namespace App.Forms.Public;

Standalone output looks like:

text outputs/zod/ package.json tsconfig.json src/ app/ forms/ public/ CreateInvitationForm.ts index.ts

Non-standalone mode writes directly under the namespace folders and skips package metadata files.

Generated Package Contract

For each emitted default form model, the emitter writes:

  • a ModelSchema
  • type Model = z.infer<typeof ModelSchema>
  • ModelMeta when field metadata is available

Standalone output also writes:

  • package.json
  • tsconfig.json
  • a root src/index.ts barrel that re-exports every generated data model

This means consumers can either import from the root barrel or from specific namespace paths.

Schema Example

`typescript import "@qninhdt/typespec-orm";

using Qninhdt.Orm;

namespace Demo.Accounts;

@table model User { @key id: uuid;

@maxLength(320) @format("email") email: string;

@maxLength(100) displayName: string; }

namespace Demo.Forms; model CreateInvitationForm { @title("Invitee Email") @placeholder("[email protected]") inviteeEmail: Demo.Accounts.User.email;

@title("Message") message?: text; }

@@inputType(CreateInvitationForm.message::type, "textarea"); `

Generated Behavior

For each default form model, the emitter generates:

  • ModelSchema
  • type Model = z.infer<typeof ModelSchema>
  • optional ModelMeta

Example shape:

`ts import { z } from "zod";

export const CreateInvitationFormSchema = z.object({ inviteeEmail: z.email().max(320), message: z.string().optional(), });

export type CreateInvitationForm = z.infer;

export const CreateInvitationFormMeta = { inviteeEmail: { title: "Invitee Email", placeholder: "[email protected]", inputType: "email", }, message: { title: "Message", inputType: "textarea", }, } as const; `

Form Metadata

The metadata export is built from:

  • @title
  • @placeholder
  • @@inputType
  • inferred input type from some formats such as email and url

This gives frontend teams a single generated source for both validation and display hints.

Lookup Types And Constraint Inheritance

Zod generation works especially well with lookup types:

typescript model PublicUser { email: Demo.GamePlatform.Accounts.User.email; }

That pattern lets a form or DTO model inherit scalar constraints from the source property, such as:

  • string length bounds
  • format-derived validators like email and url
  • titles and placeholders when modeled on the default form field

If the public shape should diverge from the persistence model, define a dedicated default form property explicitly instead of chaining more lookup reuse.

Frontend Integration Pattern

The intended usage pattern is:

  1. model public-facing input shapes as default form models
  2. reuse field constraints with lookup types where it helps
  3. generate Zod output
  4. import ModelSchema, Model, and ModelMeta in the frontend

That keeps validation, TypeScript inference, and form hints sourced from one schema without forcing frontend code to consume table models directly.

Supported Features

  • namespace-first layout
  • standalone package scaffolding via library-name
  • root index.ts barrel
  • lookup-type constraint inheritance
  • inferred TypeScript aliases emitted in the same render pass
  • form metadata export
  • shared filtering with include and exclude

Important Boundaries

  • only default form models and @tableMixin schemas are emitted
  • relation-heavy @table models are not the target of this emitter
  • foreign-key lookups to a non-id column (e.g. organizationCode: Organization.code) are emitted as a plain scalar reference matching the column's underlying scalar; the relationship is not preserved in the Zod output
  • if a default form model references a shape that cannot be represented cleanly as Zod output, emission fails; fix the source schema instead of expecting silent fallback behavior

Common Diagnostics And Gotchas

  • standalone-requires-library-name Standalone package generation requires library-name.
  • filtered dependency failures A selected default form model still needs every required dependency included by the selector set.
  • unsupported-type The TypeSpec field could not be mapped to a Zod schema and emission fails.
  • table-only shapes leaking into forms If a public form model starts to mirror a full persistence model, prefer writing an explicit default form model rather than reusing relation-heavy table shapes directly.

Practical guidance:

  • keep default form models intentionally public-facing
  • use lookup types for individual fields more often than for whole object graphs
  • treat the root barrel as a convenience export, not a forced import style

Verification

The repo verifies generated Zod output with:

sh pnpm run compile-examples pnpm --dir outputs/file-vault/zod exec tsc -p tsconfig.json pnpm --dir outputs/game-platform/zod exec tsc -p tsconfig.json

Related Docs


Made with heart by @qninhdt, with GPT-5.4 and Claude Opus 4.6.