{"_id":"@douglance/stdb-zod-bridge","name":"@douglance/stdb-zod-bridge","dist-tags":{"latest":"1.0.0"},"versions":{"1.0.0":{"name":"@douglance/stdb-zod-bridge","version":"1.0.0","description":"Auto-generate Zod validation schemas from SpacetimeDB TypeScript modules","type":"module","main":"./dist/generator.js","types":"./dist/generator.d.ts","exports":{".":{"types":"./dist/generator.d.ts","import":"./dist/generator.js"},"./cli":{"types":"./dist/cli.d.ts","import":"./dist/cli.js"}},"bin":{"zod-bridge":"dist/cli.js"},"scripts":{"build":"tsc --build","dev":"tsc --build --watch","clean":"rm -rf dist .tsbuildinfo","typecheck":"tsc --noEmit","lint":"biome check .","lint:fix":"biome check --write .","test":"vitest run --passWithNoTests","test:watch":"vitest","test:coverage":"vitest run --coverage"},"dependencies":{"chokidar":"^4.0.3","prettier":"^3.6.2","ts-morph":"^24.0.0","typescript":"^5.7.0","yargs":"^17.7.2","zod":"^4.1.12"},"devDependencies":{"@biomejs/biome":"^1.9.4","@types/node":"^20.17.10","@types/yargs":"^17.0.33","@vitest/coverage-v8":"^3.2.4","spacetimedb":"^1.6.0","vitest":"^3.2.4"},"peerDependencies":{"spacetimedb":">=1.6.0","zod":">=4.0.0"},"peerDependenciesMeta":{"spacetimedb":{"optional":false},"zod":{"optional":false}},"keywords":["spacetimedb","zod","validation","schema","codegen","typescript"],"author":"","license":"MIT","_id":"@douglance/stdb-zod-bridge@1.0.0","gitHead":"d95209c60e55e862b01f3a505241d5059eacf573","_nodeVersion":"23.10.0","_npmVersion":"10.9.2","dist":{"integrity":"sha512-ltoPm/oiSp2h9R2g3LhEReJ5fUoLZb/mFOIRlTDPek+z2M94LH+zoZTVpedociQZO8RrBcTglRF0ifDS3hI3XQ==","shasum":"ef2726aa801cd85ee5f7a6f6671aea719299ed14","tarball":"https://registry.npmjs.org/@douglance/stdb-zod-bridge/-/stdb-zod-bridge-1.0.0.tgz","fileCount":14,"unpackedSize":40467,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEYCIQDddDaPRnu575YuPmSG3dP6dMKjP04nmgwlqjgpVAoWFAIhALtVaCxHDKWyi1W/MW8XWAD9TZ7rFXZhL/wXfc+vD1hy"}]},"_npmUser":{"name":"douglance","email":"douglance@gmail.com"},"directories":{},"maintainers":[{"name":"douglance","email":"douglance@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/stdb-zod-bridge_1.0.0_1761473561643_0.16813301219743315"},"_hasShrinkwrap":false}},"time":{"created":"2025-10-26T10:12:41.555Z","1.0.0":"2025-10-26T10:12:41.869Z","modified":"2025-10-26T10:12:42.175Z"},"maintainers":[{"name":"douglance","email":"douglance@gmail.com"}],"description":"Auto-generate Zod validation schemas from SpacetimeDB TypeScript modules","keywords":["spacetimedb","zod","validation","schema","codegen","typescript"],"license":"MIT","readme":"# @spacetimedb/zod-bridge\n\nAuto-generate [Zod](https://zod.dev) validation schemas from SpacetimeDB TypeScript module schemas for runtime type validation across the WASM boundary.\n\n## Why Use This?\n\nSpacetimeDB modules define tables and reducers with TypeScript types, but client code often needs runtime validation:\n\n- Validate user input before sending to reducers\n- Catch type errors before they reach the server\n- Get helpful error messages for invalid data\n- Auto-generate validation from your existing schema (single source of truth)\n\n## Installation\n\n```bash\nnpm install @spacetimedb/zod-bridge @spacetimedb/zod-bridge-runtime zod\n```\n\n## Quick Start\n\n### 1. Generate Schemas\n\nGiven a SpacetimeDB module:\n\n```typescript\n// src/schema.ts\nimport { schema, table, t } from 'spacetimedb/server';\n\nconst Position = table({ name: 'Position', public: true }, {\n  entity_id: t.identity().primaryKey(),\n  x: t.f32(),\n  y: t.f32(),\n});\n\nconst gameSchema = schema(Position);\n\ngameSchema.reducer('join_game', { name: t.string() }, (ctx, { name }) => {\n  // ...\n});\n\nexport default gameSchema;\n```\n\nGenerate Zod schemas:\n\n```bash\nnpx zod-bridge src/schema.ts src/generated/zod-schemas.ts\n```\n\n### 2. Use Generated Schemas\n\n```typescript\n// src/client.ts\nimport { PositionSchema, JoinGameArgsSchema } from './generated/zod-schemas';\nimport { z } from 'zod';\n\n// Validate reducer arguments\nfunction safeJoinGame(input: unknown) {\n  try {\n    const args = JoinGameArgsSchema.parse(input);\n    conn.reducers.joinGame(args); // Guaranteed valid\n  } catch (error) {\n    if (error instanceof z.ZodError) {\n      console.error('Validation failed:', error.errors);\n      // Show user-friendly error message\n    }\n  }\n}\n\n// Validate table data\nconst position = PositionSchema.parse(rawData);\n```\n\n## CLI Usage\n\n### Basic Generation\n\n```bash\nnpx zod-bridge <input> <output>\n```\n\nExample:\n```bash\nnpx zod-bridge src/schema.ts src/generated/zod-schemas.ts\n```\n\n### Watch Mode\n\nAuto-regenerate when schema changes:\n\n```bash\nnpx zod-bridge src/schema.ts src/generated/zod-schemas.ts --watch\n```\n\n### Options\n\n- `--watch`, `-w`: Watch input file and regenerate on changes\n- `--no-variants`: Skip generating Create/Update variant schemas\n- `--help`, `-h`: Show help\n- `--version`, `-v`: Show version\n\n## Generated Output\n\nFor each table, the generator creates:\n\n1. **Base Schema**: Validates the full table row\n2. **Type Definition**: TypeScript type from Zod schema\n3. **Create Schema** (optional): Omits auto-increment fields\n4. **Update Schema** (optional): Makes all fields except primary key optional\n\nExample:\n\n```typescript\n// Generated from: table Position\nexport const PositionSchema = z.object({\n  entity_id: zodSpacetime.identity(),\n  x: z.number(),\n  y: z.number(),\n});\n\nexport type Position = z.infer<typeof PositionSchema>;\n\n// For reducer: join_game(name: string)\nexport const JoinGameArgsSchema = z.object({\n  name: z.string().min(1).max(20),\n});\n\nexport type JoinGameArgs = z.infer<typeof JoinGameArgsSchema>;\n```\n\n## Type Mapping\n\n| SpacetimeDB Type | Zod Schema | Notes |\n|------------------|------------|-------|\n| `t.string()` | `z.string()` | |\n| `t.bool()` | `z.boolean()` | |\n| `t.f32()`, `t.f64()` | `zodNumeric.f64()` | Range-validated number |\n| `t.u8()` | `zodNumeric.u8()` | 0-255 |\n| `t.u16()` | `zodNumeric.u16()` | 0-65535 |\n| `t.u32()` | `zodNumeric.u32()` | 0-4294967295 |\n| `t.u64()` | `zodNumeric.u64()` | BigInt 0-18446744073709551615n |\n| `t.i8()` | `zodNumeric.i8()` | -128-127 |\n| `t.i32()` | `zodNumeric.i32()` | -2147483648-2147483647 |\n| `t.identity()` | `zodSpacetime.identity()` | Accepts Identity, hex string, or BigInt |\n| `t.timestamp()` | `zodSpacetime.timestamp()` | Accepts Date, ISO string, or Unix ms |\n| `t.connectionId()` | `zodSpacetime.connectionId()` | Accepts ConnectionId or string |\n| `t.scheduleAt()` | `zodSpacetime.scheduleAt()` | Accepts ScheduleAt or BigInt (microseconds) |\n\nSee [@spacetimedb/zod-bridge-runtime](../zod-bridge-runtime) for numeric and SpacetimeDB type validators.\n\n## Before vs After\n\n### Before (No Validation)\n\n```typescript\n// Hope userInput is valid, crash if not\nconn.reducers.joinGame({ name: userInput });\n```\n\n**Problems:**\n- No validation until server receives data\n- Unclear error messages\n- User sees \"Internal Server Error\"\n- Debugging requires reading server logs\n\n### After (Zod Validation)\n\n```typescript\nimport { JoinGameArgsSchema } from './generated/zod-schemas';\nimport { z } from 'zod';\n\nfunction safeJoinGame(input: unknown) {\n  try {\n    const args = JoinGameArgsSchema.parse(input);\n    conn.reducers.joinGame(args); // Guaranteed valid\n  } catch (error) {\n    if (error instanceof z.ZodError) {\n      console.error('Validation failed:', error.errors);\n      // [{\n      //   code: \"too_small\",\n      //   minimum: 1,\n      //   path: [\"name\"],\n      //   message: \"String must contain at least 1 character(s)\"\n      // }]\n    }\n  }\n}\n```\n\n**Benefits:**\n- Catch errors before sending to server\n- Clear, actionable error messages\n- Show user-friendly validation feedback\n- Type-safe client code\n\n## Programmatic API\n\n```typescript\nimport { generate } from '@spacetimedb/zod-bridge';\n\nawait generate({\n  inputFile: './src/schema.ts',\n  outputFile: './src/generated/zod-schemas.ts',\n  includeVariants: true, // Generate Create/Update schemas\n});\n```\n\n## Integration with Build Tools\n\n### Package.json Script\n\n```json\n{\n  \"scripts\": {\n    \"generate:schemas\": \"zod-bridge src/schema.ts src/generated/zod-schemas.ts\",\n    \"dev\": \"zod-bridge src/schema.ts src/generated/zod-schemas.ts --watch & vite dev\"\n  }\n}\n```\n\n### Pre-commit Hook\n\n```bash\n#!/bin/bash\n# .git/hooks/pre-commit\nnpx zod-bridge src/schema.ts src/generated/zod-schemas.ts\ngit add src/generated/zod-schemas.ts\n```\n\n## Limitations\n\nCurrent version supports primitive types and SpacetimeDB built-in types. Complex types planned for future releases:\n\n- `t.array()` - Coming soon\n- `t.option()` - Coming soon\n- `t.object()` - Coming soon\n- `t.enum()` - Coming soon\n\n## Error Messages\n\nThe generator provides detailed error messages:\n\n```\nError: No table definitions found in src/schema.ts.\n\nExpected to find tables declared as:\n  const TableName = table({ name: 'TableName', public: true }, { ... });\n\nMake sure your schema file imports the 'table' function from 'spacetimedb/server'.\n```\n\n## License\n\nMIT\n\n## Contributing\n\nIssues and PRs welcome at [github.com/yourusername/spacetimedb-utils](https://github.com/yourusername/spacetimedb-utils)\n","readmeFilename":"README.md","_rev":"1-c564f12f048fd5750c3d571117dd5890"}