Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions .changeset/quiet-rockets-juggle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
'typesync-cli': minor
---

Add `generate-zod`, a new generator that emits Zod schemas from Typesync definitions with support for Zod v3 and v4 output.
Generated schemas include `.describe(...)` metadata, Firestore SDK target handling shared with `generate-ts`, optional `z.infer` type emission, and integration coverage for runtime validation.
12 changes: 2 additions & 10 deletions docs/cli/generate-ts.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import DefinitionOption from '/snippets/cli-option-definition.mdx';
import IndentationOption from '/snippets/cli-option-indentation.mdx';
import OutFileOption from '/snippets/cli-option-out-file.mdx';
import TargetOption from '/snippets/cli-option-target.mdx';
import TSTargetsList from '/snippets/ts-targets-list.mdx';

Generates TypeScript type definitions for the specified schema and writes them to the specified file.

Expand Down Expand Up @@ -36,13 +37,4 @@ Controls how objects are defined in the TypeScript output. Object types can be r

## Targets

- `firebase-admin@13`: For backend projects that use [Firebase Admin Node.js SDK (v13)](https://www.npmjs.com/package/firebase-admin).
- `firebase-admin@12`: For backend projects that use [Firebase Admin Node.js SDK (v12)](https://www.npmjs.com/package/firebase-admin).
- `firebase-admin@11`: For backend projects that use [Firebase Admin Node.js SDK (v11)](https://www.npmjs.com/package/firebase-admin).
- `firebase-admin@10`: For backend projects that use [Firebase Admin Node.js SDK (v10)](https://www.npmjs.com/package/firebase-admin).
- `firebase@11`: For frontend projects that use [Firebase Javascript SDK (v11)](https://www.npmjs.com/package/firebase).
- `firebase@10`: For frontend projects that use [Firebase Javascript SDK (v10)](https://www.npmjs.com/package/firebase).
- `firebase@9`: For frontend projects that use [Firebase Javascript SDK (v9)](https://www.npmjs.com/package/firebase).
- `react-native-firebase@21`: For React Native projects that use [React Native Firebase (v21)](https://www.npmjs.com/package/@react-native-firebase/firestore).
- `react-native-firebase@20`: For React Native projects that use [React Native Firebase (v20)](https://www.npmjs.com/package/@react-native-firebase/firestore).
- `react-native-firebase@19`: For React Native projects that use [React Native Firebase (v19)](https://www.npmjs.com/package/@react-native-firebase/firestore).
<TSTargetsList />
99 changes: 99 additions & 0 deletions docs/cli/generate-zod.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
---
title: 'generate-zod'
icon: 'rectangle-terminal'
---

import DebugOption from '/snippets/cli-option-debug.mdx';
import DefinitionOption from '/snippets/cli-option-definition.mdx';
import IndentationOption from '/snippets/cli-option-indentation.mdx';
import OutFileOption from '/snippets/cli-option-out-file.mdx';
import TargetOption from '/snippets/cli-option-target.mdx';
import TSTargetsList from '/snippets/ts-targets-list.mdx';

Generates [Zod](https://zod.dev) schemas for the specified Typesync schema and writes them to the specified file. The
generated schemas mirror the same mapping rules used internally by `typesync validate-data`, so the structure and
constraints stay in lockstep across runtime validation and code generation.

Documentation captured in your schema definition is preserved in the output: model-level docs are emitted as JSDoc
comments and as `.describe(...)` calls on the schema, and field-level docs are emitted as `.describe(...)` calls on the
field's value schema.

## Usage

```bash
typesync generate-zod --definition <filePathOrPattern> --target <targetEnvironment> --outFile <filePath> --variant <v3|v4> --schemaNamePattern <schemaNamePattern> --emitInferredTypes --inferredTypeNamePattern <inferredTypeNamePattern> --indentation <indentation> --debug <debug>
```

## Options

<DefinitionOption />
<TargetOption />
<OutFileOption />

<ParamField type='"v3" | "v4"' path="variant" default="v4">

Which Zod major release the generated code should target. Pick the variant that matches the `zod` version installed in
the consuming project. The two majors differ in a couple of API surface points (the `z.record` key argument and the
strict / loose object factories) and the generator emits the right shape for each.

</ParamField>

<ParamField type="string" path="schemaNamePattern" default="{modelName}Schema">

The pattern that controls how the generated Zod schema constants are named. The pattern must be a string that contains
the `"{modelName}"` substring (this is a literal value).

Example values:

- `"{modelName}Schema"` &rarr; produces exports like `UserSchema`, `PostSchema`, `AccountSchema` etc.
- `"z{modelName}"` &rarr; produces exports like `zUser`, `zPost`, `zAccount` etc.

</ParamField>

<ParamField type="boolean" path="emitInferredTypes" default={false}>

When enabled, the generator emits an inferred TypeScript type alongside each Zod schema. For a model named `User` with
the default patterns, the output becomes:

```ts
export const UserSchema = z.strictObject({
/* ... */
});
export type User = z.infer<typeof UserSchema>;
```

</ParamField>

<ParamField type="string" path="inferredTypeNamePattern" default="{modelName}">

The pattern that controls how the inferred TypeScript types are named when `--emitInferredTypes` is set. The pattern
must contain the literal substring `"{modelName}"`. Ignored when `--emitInferredTypes` is not set.

Example values:

- `"{modelName}"` &rarr; produces types like `User`, `Post`, `Account` etc.
- `"{modelName}Type"` &rarr; produces types like `UserType`, `PostType`, `AccountType` etc.
- `"I{modelName}"` &rarr; produces types like `IUser`, `IPost`, `IAccount` etc.

</ParamField>

<IndentationOption />
<DebugOption />

## Targets

`generate-zod` reuses the `generate-ts` target list. The `--target` option determines the runtime classes that the
generated `z.instanceof(...)` checks resolve to — pick the target that matches the Firestore SDK you decode documents
with.

<TSTargetsList />

## Notes

- References between models are emitted as `z.lazy(() => OtherSchema)`. Using `z.lazy` defers identifier resolution until
validation time, which keeps mutually recursive schemas working regardless of declaration order in the generated file.
- Strict / loose objects map to `z.strictObject` / `z.looseObject` for v4 and to `z.object(...).strict()` /
`z.object(...).passthrough()` for v3. Whether an object accepts additional fields is controlled by the
`additionalFields` flag in the schema definition.
- The Firestore SDK is only imported when at least one model uses `timestamp` or `bytes`. For the admin targets, `bytes`
resolves to the Node global `Buffer` and does not trigger an SDK import on its own.
1 change: 1 addition & 0 deletions docs/docs.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"cli/generate-ts",
"cli/generate-swift",
"cli/generate-py",
"cli/generate-zod",
"cli/generate-rules",
"cli/generate-graph",
"cli/validate-data",
Expand Down
10 changes: 10 additions & 0 deletions docs/snippets/ts-targets-list.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
- `firebase-admin@13`: For backend projects that use [Firebase Admin Node.js SDK (v13)](https://www.npmjs.com/package/firebase-admin).
- `firebase-admin@12`: For backend projects that use [Firebase Admin Node.js SDK (v12)](https://www.npmjs.com/package/firebase-admin).
- `firebase-admin@11`: For backend projects that use [Firebase Admin Node.js SDK (v11)](https://www.npmjs.com/package/firebase-admin).
- `firebase-admin@10`: For backend projects that use [Firebase Admin Node.js SDK (v10)](https://www.npmjs.com/package/firebase-admin).
- `firebase@11`: For frontend projects that use [Firebase Javascript SDK (v11)](https://www.npmjs.com/package/firebase).
- `firebase@10`: For frontend projects that use [Firebase Javascript SDK (v10)](https://www.npmjs.com/package/firebase).
- `firebase@9`: For frontend projects that use [Firebase Javascript SDK (v9)](https://www.npmjs.com/package/firebase).
- `react-native-firebase@21`: For React Native projects that use [React Native Firebase (v21)](https://www.npmjs.com/package/@react-native-firebase/firestore).
- `react-native-firebase@20`: For React Native projects that use [React Native Firebase (v20)](https://www.npmjs.com/package/@react-native-firebase/firestore).
- `react-native-firebase@19`: For React Native projects that use [React Native Firebase (v19)](https://www.npmjs.com/package/@react-native-firebase/firestore).
5 changes: 4 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@
"test:integration:python": "tsx scripts/integration-test.ts python",
"test:integration:swift": "tsx scripts/integration-test.ts swift",
"test:integration:typescript": "tsx scripts/integration-test.ts typescript",
"test:integration:zod": "tsx scripts/integration-test.ts zod",
"test:rules": "tsx src/cli/index.tsx generate-rules --definition tests/security/definition.yml --outFile tests/security/firestore.rules && firebase emulators:exec --project demo-rules --only firestore 'vitest run --config vitest.security.config.ts'",
"test:src": "vitest run",
"test:watch": "vitest"
Expand Down Expand Up @@ -99,6 +100,8 @@
"tsup": "^8.0.2",
"tsx": "^4.7.2",
"typescript": "5.7.2",
"vitest": "^4.1.5"
"vitest": "^4.1.5",
"zod-v3": "npm:zod@^3.23.8",
"zod-v4": "npm:zod@^4.3.6"
}
}
Loading
Loading