npm package: @libar-dev/zod-convex-gen
Description: Build-time Zod to Convex validator generator for eliminating runtime bundle size
Author: Libar AI
Latest version: 0.1.0

# @libar-dev/zod-convex-gen

> Build-time Zod to Convex validator generator - eliminates Zod from runtime bundle

[![npm version](https://img.shields.io/npm/v/@libar-dev/zod-convex-gen.svg)](https://www.npmjs.com/package/@libar-dev/zod-convex-gen)
[![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)

## The Problem

Convex applications using Zod at runtime can exceed the **64MB memory limit**. The Zod library adds ~55-62MB of overhead, leaving minimal room for business logic.

## The Solution

This package converts Zod schemas to pure Convex validators at **build time**, eliminating Zod from the runtime bundle entirely.

**Results**: 90% memory reduction (62MB → 10-15MB) with 100% type safety maintained.

## Quick Start

### 1. Install

```bash
npm install --save-dev @libar-dev/zod-convex-gen tsx
# or: pnpm add -D @libar-dev/zod-convex-gen tsx
```

> **Requires**: Zod 4.x, Node.js 20+

### 2. Create Schema

```typescript
// src/table-schemas/users.ts
import { z } from 'zod';

export const UsersTableSchema = z.object({
  email: z.string().email(),
  name: z.string(),
  role: z.enum(['admin', 'user', 'guest']),
  createdAt: z.number(),
}).strict();

// Export type alias for hybrid pattern
export type User = z.infer<typeof UsersTableSchema>;
```

### 3. Generate Validators

```bash
npx zod-convex-gen
```

Creates `convex/generatedValidators/users.ts`:

```typescript
// AUTO-GENERATED - DO NOT EDIT
import { v } from 'convex/values';

export const usersFields = {
  email: v.string(),
  name: v.string(),
  createdAt: v.float64(),
};

export const usersEnums = {
  role: v.union(v.literal('admin'), v.literal('user'), v.literal('guest')),
};

export const usersObject = v.object({ ...usersFields, ...usersEnums });
export const usersArray = v.array(usersObject);
```

### 4. Use in Convex Schema

```typescript
// convex/schema.ts
import { defineSchema, defineTable } from 'convex/server';
import { usersFields, usersEnums } from './generatedValidators/users';

export default defineSchema({
  users: defineTable({ ...usersFields, ...usersEnums })
    .index('by_email', ['email']),
});
```

## CLI Reference

### Commands

| Command | Description |
|---------|-------------|
| `generate` | Generate validators (default) |
| `clean` | Remove generated files |
| `info` | Show configuration and loaders |
| `init` | Create sample config file |

### Options

| Option | Description | Default |
|--------|-------------|---------|
| `-i, --input <path>` | Input directory | `src/table-schemas` |
| `-o, --output <path>` | Output directory | `convex/generatedValidators` |
| `-w, --watch` | Watch mode | `false` |
| `-p, --pattern <pattern>` | Schema export pattern | `*TableSchema` |
| `--loader <name>` | TypeScript loader | `tsx` |
| `--dry-run` | Validate without writing | `false` |
| `--no-format` | Skip prettier formatting | `false` |
| `-v, --verbose` | Verbose output | `false` |

### Examples

```bash
# Development with auto-regeneration
npx zod-convex-gen --watch

# Custom directories
npx zod-convex-gen -i src/schemas -o convex/validators

# Validation only (CI)
npx zod-convex-gen --dry-run

# Clean stale files
npx zod-convex-gen clean --stale --force
```

## Configuration

### Zero Configuration

Works out of the box with these conventions:
- Input: `src/table-schemas/`
- Output: `convex/generatedValidators/`
- Schema pattern: exports ending with `TableSchema`

### Config File

Create `zod-convex-gen.json` or use `npx zod-convex-gen init`:

```json
{
  "input": ["src/table-schemas", "src/validation-schemas"],
  "output": "convex/generatedValidators",
  "schemaPattern": "*TableSchema",
  "tableNameTransform": "camelCase",
  "format": true,
  "preserveDirectoryStructure": true,
  "maxInlineDepth": 2,
  "extractUnions": true
}
```

### All Options

| Option | Type | Default | Description |
|--------|------|---------|-------------|
| `input` | `string \| string[]` | `src/table-schemas` | Input directories |
| `output` | `string` | `convex/generatedValidators` | Output directory |
| `schemaPattern` | `string` | `*TableSchema` | Export name pattern |
| `tableNameTransform` | `string` | `camelCase` | Name transform (`camelCase`, `kebab-case`, `snake_case`, `none`) |
| `format` | `boolean` | `true` | Prettier formatting |
| `outputMode` | `string` | `multiple` | `multiple` or `single` file |
| `preserveDirectoryStructure` | `boolean` | `true` | Mirror input structure |
| `maxInlineDepth` | `number` | `2` | Max nesting before extraction |
| `extractUnions` | `boolean` | `true` | Extract nested unions |
| `includeTimestamp` | `boolean` | `false` | Add timestamp to headers |
| `exclude` | `string[]` | `['*.test.ts', '*.spec.ts']` | Files to exclude |
| `minFieldsForExtraction` | `number` | `3` | Min fields to trigger extraction |
| `loader` | `string` | `tsx` | TypeScript loader |
| `verbosity` | `string` | `quiet` | `silent`, `quiet`, `normal`, `verbose` |

## Troubleshooting

### "No schemas found"

1. Ensure schemas end with `TableSchema`:
   ```typescript
   export const UsersTableSchema = z.object({...}); // ✅
   export const UserSchema = z.object({...});       // ❌
   ```

2. Verify input directory exists: `ls src/table-schemas/`

3. Check exports are named (not default):
   ```typescript
   export const UsersTableSchema = ...  // ✅
   const UsersTableSchema = ...         // ❌ (missing export)
   ```

### "Cannot load TypeScript file"

Install a TypeScript loader:

```bash
npm install --save-dev tsx  # Recommended
# or: ts-node, esbuild, @swc/core
```

### Files not updating

Use watch mode during development:

```bash
npx zod-convex-gen --watch
```

### Type errors after generation

Follow build order:

```bash
npx zod-convex-gen        # 1. Generate validators
npx convex codegen        # 2. Generate Convex types
npx tsc --noEmit          # 3. TypeScript validation
```

### TS2589 "Type instantiation excessively deep"

This generator **prevents** most TS2589 errors by:
1. Generating pre-composed validators (`*Array`, `*Record`, `*OrNull`)
2. Auto-extracting deeply nested structures

**To use correctly:**

```typescript
// ❌ WRONG - inline construction causes TS2589
returns: v.array(v.object(mySchemaFields))

// ✅ CORRECT - use pre-composed validator from generated output
returns: mySchemaArray

// ❌ WRONG - explicit Promise type conflicts with returns validator
handler: async (ctx, args): Promise<MyType> => { ... }

// ✅ CORRECT - let validator infer type
handler: async (ctx, args) => { ... }

// ❌ WRONG - variable annotation conflicts
const result: MyType = await ctx.runMutation(...);

// ✅ CORRECT - no annotation
const result = await ctx.runMutation(...);
```

If errors persist, reduce `maxInlineDepth` in config:

```json
{ "maxInlineDepth": 1 }
```

See **[TS2589 Usage Guide](docs/ts2589-usage.md)** for complete patterns including helper function anti-patterns, test helpers, and diagnostic commands.

## Documentation

- [Architecture & Two-Stage Conversion](docs/architecture.md)
- [TS2589 Usage Guide](docs/ts2589-usage.md)
- [Advanced Features](docs/advanced.md)
- [Patterns & Best Practices](docs/patterns.md)
- [API Reference](docs/api.md)

## License

MIT