pg2zod 2.1.1 → 2.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -2,11 +2,14 @@
2
2
 
3
3
  > **Introspect PostgreSQL databases and generate strict, comprehensive Zod v4 schemas**
4
4
 
5
- A modern TypeScript package that automatically generates high-quality, strict Zod schemas from your PostgreSQL database schema. Supports all PostgreSQL types including advanced features like enums, composite types, domains, ranges, arrays, and geometric types.
5
+ A modern TypeScript package that automatically generates high-quality, strict Zod schemas from your PostgreSQL database
6
+ schema. Supports all PostgreSQL types including advanced features like enums, composite types, domains, ranges, arrays,
7
+ and geometric types.
6
8
 
7
9
  ## Features
8
10
 
9
11
  ✨ **Comprehensive Type Coverage**
12
+
10
13
  - All built-in PostgreSQL types (numeric, text, date/time, boolean, JSON, UUID, etc.)
11
14
  - Custom types: enums, domains, composite types, range types
12
15
  - Database views (read-only schemas)
@@ -18,6 +21,7 @@ A modern TypeScript package that automatically generates high-quality, strict Zo
18
21
  - Bit strings, XML, and more
19
22
 
20
23
  🔒 **Strict & Safe**
24
+
21
25
  - Length constraints (`varchar(n)` → `.max(n)`)
22
26
  - Precision/scale validation for numeric types
23
27
  - Format validations (UUID, IP, MAC addresses, etc.)
@@ -26,16 +30,19 @@ A modern TypeScript package that automatically generates high-quality, strict Zo
26
30
  - NOT NULL awareness
27
31
 
28
32
  🎯 **Smart Code Generation**
33
+
29
34
  - Read schemas (reflect actual DB structure)
30
35
  - Insert schemas (intelligent optional field detection based on defaults/auto-generation)
31
36
  - Update schemas (all fields optional but maintain validation)
32
37
  - TypeScript type inference support with `z.infer<>`
38
+ - **Database type interface** (organizes all types by schema, similar to Supabase)
33
39
  - Schema-prefixed naming to avoid collisions (e.g., `PublicUsersSchema`)
34
40
  - Optional camelCase conversion
35
41
  - CHECK constraint parsing and implementation
36
42
  - Comprehensive comments
37
43
 
38
44
  🚀 **Modern Stack**
45
+
39
46
  - ESM-first
40
47
  - TypeScript with strict mode
41
48
  - Zod v4 (latest beta)
@@ -56,26 +63,21 @@ yarn add pg2zod
56
63
  ### CLI Usage
57
64
 
58
65
  ```bash
59
- # Generate schemas from a local database (includes input schemas by default)
66
+ # Generate schemas from a local database
67
+ # By default includes: tables, views, functions, composite types, and Database interface
60
68
  pg2zod --database mydb --output src/db/schema.ts
61
69
 
62
70
  # Use a connection URL
63
71
  pg2zod --url postgresql://user:pass@localhost:5432/mydb -o schema.ts
64
72
 
65
- # Skip input schemas if you only need read schemas
66
- pg2zod --database mydb --no-input-schemas --output schema.ts
67
-
68
- # Include composite types (skipped by default)
69
- pg2zod --database mydb --composite-types --output schema.ts
73
+ # Skip views, routines, or composite types if you don't need them
74
+ pg2zod --database mydb --no-views --no-routines --output schema.ts
70
75
 
71
- # Include database views
72
- pg2zod --database mydb --views --output schema.ts
76
+ # Include SECURITY INVOKER functions (only SECURITY DEFINER by default)
77
+ pg2zod --database mydb --security-invoker --output schema.ts
73
78
 
74
- # Include functions/procedures (SECURITY DEFINER only by default)
75
- pg2zod --database mydb --routines --output schema.ts
76
-
77
- # Include all functions including SECURITY INVOKER
78
- pg2zod --database mydb --routines --security-invoker --output schema.ts
79
+ # Skip input schemas if you only need read schemas
80
+ pg2zod --database mydb --no-input-schemas --output schema.ts
79
81
 
80
82
  # Use camelCase for field names
81
83
  pg2zod --database mydb --camel-case -o schema.ts
@@ -90,22 +92,25 @@ pg2zod --database mydb --schemas public,auth,api -o schema.ts
90
92
  ### Programmatic API
91
93
 
92
94
  ```typescript
93
- import { generateZodSchemasString } from 'pg2zod';
95
+ import {generateZodSchemasString} from 'pg2zod';
94
96
 
95
97
  const schemas = await generateZodSchemasString(
96
- {
97
- host: 'localhost',
98
- port: 5432,
99
- database: 'mydb',
100
- user: 'postgres',
101
- password: 'password',
102
- },
103
- {
104
- schemas: ['public'],
105
- generateInputSchemas: true,
106
- includeComments: true,
107
- strictMode: false,
108
- }
98
+ {
99
+ host: 'localhost',
100
+ port: 5432,
101
+ database: 'mydb',
102
+ user: 'postgres',
103
+ password: 'password',
104
+ },
105
+ {
106
+ schemas: ['public'],
107
+ generateInputSchemas: true,
108
+ includeViews: true, // Default: true
109
+ includeRoutines: true, // Default: true
110
+ includeCompositeTypes: true, // Default: true
111
+ includeComments: true,
112
+ strictMode: false,
113
+ }
109
114
  );
110
115
 
111
116
  console.log(schemas);
@@ -115,120 +120,143 @@ console.log(schemas);
115
120
 
116
121
  ### Built-in Types
117
122
 
118
- | PostgreSQL Type | Zod Schema |
119
- |----------------|------------|
120
- | `smallint`, `integer` | `z.number().int()` |
121
- | `bigint` | `z.bigint()` |
122
- | `numeric(p,s)`, `decimal` | `z.number()` with precision/scale comment |
123
- | `real`, `double precision` | `z.number()` |
124
- | `varchar(n)` | `z.string().max(n)` |
125
- | `char(n)` | `z.string().length(n)` |
126
- | `text` | `z.string()` |
127
- | `boolean` | `z.boolean()` |
128
- | `date`, `timestamp` | `z.date()` |
129
- | `time` | `z.iso.time()` |
130
- | `interval` | `z.iso.duration()` |
131
- | `uuid` | `z.uuid()` |
132
- | `json`, `jsonb` | `z.record(z.string(), z.unknown())` |
133
- | `inet` | `z.union([z.ipv4(), z.ipv6()])` |
134
- | `cidr` | `z.union([z.cidrv4(), z.cidrv6()])` |
135
- | `macaddr` | `z.mac()` |
136
- | `point` | `z.tuple([z.number(), z.number()])` |
137
- | `circle` | `z.object({ center: ..., radius: ... })` |
138
- | `polygon` | `z.array(z.tuple([z.number(), z.number()]))` |
139
- | Arrays | `z.array(...)` (nested for multi-dimensional) |
123
+ | PostgreSQL Type | Zod Schema |
124
+ |----------------------------|-----------------------------------------------|
125
+ | `smallint`, `integer` | `z.number().int()` |
126
+ | `bigint` | `z.bigint()` |
127
+ | `numeric(p,s)`, `decimal` | `z.number()` with precision/scale comment |
128
+ | `real`, `double precision` | `z.number()` |
129
+ | `varchar(n)` | `z.string().max(n)` |
130
+ | `char(n)` | `z.string().length(n)` |
131
+ | `text` | `z.string()` |
132
+ | `boolean` | `z.boolean()` |
133
+ | `date`, `timestamp` | `z.date()` |
134
+ | `time` | `z.iso.time()` |
135
+ | `interval` | `z.iso.duration()` |
136
+ | `uuid` | `z.uuid()` |
137
+ | `json`, `jsonb` | `z.record(z.string(), z.unknown())` |
138
+ | `inet` | `z.union([z.ipv4(), z.ipv6()])` |
139
+ | `cidr` | `z.union([z.cidrv4(), z.cidrv6()])` |
140
+ | `macaddr` | `z.mac()` |
141
+ | `point` | `z.tuple([z.number(), z.number()])` |
142
+ | `circle` | `z.object({ center: ..., radius: ... })` |
143
+ | `polygon` | `z.array(z.tuple([z.number(), z.number()]))` |
144
+ | Arrays | `z.array(...)` (nested for multi-dimensional) |
140
145
 
141
146
  ### Custom Types
142
147
 
143
148
  **Enums:**
149
+
144
150
  ```sql
145
151
  CREATE TYPE status AS ENUM ('pending', 'active', 'inactive');
146
152
  ```
153
+
147
154
 
155
+
148
156
  ```typescript
149
157
  export const StatusSchema = z.enum(['pending', 'active', 'inactive']);
150
158
  export type Status = z.infer<typeof StatusSchema>;
151
159
  ```
152
160
 
153
161
  **Domains:**
162
+
154
163
  ```sql
155
164
  CREATE DOMAIN email AS TEXT CHECK (VALUE ~ '^[^@]+@[^@]+$');
156
165
  ```
166
+
157
167
 
168
+
158
169
  ```typescript
159
170
  export const EmailSchema = z.string().regex(/^[^@]+@[^@]+$/);
160
171
  export type Email = z.infer<typeof EmailSchema>;
161
172
  ```
162
173
 
163
174
  **Composite Types:**
175
+
164
176
  ```sql
165
177
  CREATE TYPE address AS (street TEXT, city TEXT, zip VARCHAR(10));
166
178
  ```
179
+
167
180
 
181
+
168
182
  ```typescript
169
183
  export const AddressSchema = z.object({
170
- street: z.string(),
171
- city: z.string(),
172
- zip: z.string().max(10),
184
+ street: z.string(),
185
+ city: z.string(),
186
+ zip: z.string().max(10),
173
187
  });
174
188
  export type Address = z.infer<typeof AddressSchema>;
175
189
  ```
176
190
 
177
191
  **Range Types:**
192
+
178
193
  ```sql
179
194
  -- int4range, daterange, tstzrange, etc.
180
195
  ```
196
+
181
197
 
198
+
182
199
  ```typescript
183
200
  export const Int4rangeSchema = z.tuple([z.number().int().nullable(), z.number().int().nullable()]);
184
201
  export type Int4range = z.infer<typeof Int4rangeSchema>;
185
202
  ```
186
203
 
187
204
  **Views:**
205
+
188
206
  ```sql
189
207
  CREATE VIEW user_stats AS
190
- SELECT
191
- u.id,
192
- u.username,
193
- COUNT(o.id) as order_count
208
+ SELECT u.id,
209
+ u.username,
210
+ COUNT(o.id) as order_count
194
211
  FROM users u
195
- LEFT JOIN orders o ON u.id = o.user_id
212
+ LEFT JOIN orders o ON u.id = o.user_id
196
213
  GROUP BY u.id, u.username;
197
214
  ```
215
+
198
216
 
217
+
199
218
  ```typescript
200
219
  /** View: public.user_stats (read-only) */
201
220
  export const PublicUserStatsSchema = z.object({
202
- id: z.number().int(),
203
- username: z.string(),
204
- order_count: z.number().int(),
205
- });
221
+ id: z.number().int(),
222
+ username: z.string(),
223
+ order_count: z.number().int(),
224
+ });
206
225
  export type PublicUserStats = z.infer<typeof PublicUserStatsSchema>;
207
226
  ```
208
227
 
209
228
  **Functions/Procedures:**
229
+
210
230
  ```sql
211
231
  CREATE FUNCTION get_user_by_id(user_id INTEGER)
212
- RETURNS TABLE(id INTEGER, username VARCHAR, email VARCHAR) AS $$
232
+ RETURNS TABLE
233
+ (
234
+ id INTEGER,
235
+ username VARCHAR,
236
+ email VARCHAR
237
+ ) AS $$
213
238
  BEGIN
214
- RETURN QUERY SELECT u.id, u.username, u.email FROM users u WHERE u.id = user_id;
239
+ RETURN QUERY SELECT u.id, u.username, u.email FROM users u WHERE u.id = user_id;
215
240
  END;
216
- $$ LANGUAGE plpgsql SECURITY DEFINER;
241
+ $$
242
+ LANGUAGE plpgsql SECURITY DEFINER;
217
243
  ```
244
+
218
245
 
246
+
219
247
  ```typescript
220
248
  /** FUNCTION: public.get_user_by_id */
221
249
  export const PublicGetUserByIdParamsSchema = z.object({
222
- /** integer (IN) */
223
- user_id: z.number().int(),
224
- });
250
+ /** integer (IN) */
251
+ user_id: z.number().int(),
252
+ });
225
253
  export type PublicGetUserByIdParams = z.infer<typeof PublicGetUserByIdParamsSchema>;
226
254
 
227
255
  /** Returns: record */
228
256
  export const PublicGetUserByIdReturnSchema = z.array(z.object({
229
- id: z.number().int(),
230
- username: z.string(),
231
- email: z.string(),
257
+ id: z.number().int(),
258
+ username: z.string(),
259
+ email: z.string(),
232
260
  }));
233
261
  export type PublicGetUserByIdReturn = z.infer<typeof PublicGetUserByIdReturnSchema>;
234
262
  ```
@@ -238,33 +266,109 @@ export type PublicGetUserByIdReturn = z.infer<typeof PublicGetUserByIdReturnSche
238
266
  CHECK constraints are automatically parsed and translated to Zod validations:
239
267
 
240
268
  ```sql
241
- CREATE TABLE products (
242
- price NUMERIC CHECK (price > 0),
243
- quantity INTEGER CHECK (quantity >= 0 AND quantity <= 1000),
244
- code VARCHAR(20) CHECK (code ~ '^[A-Z]{3}-\d{4}$'),
269
+ CREATE TABLE products
270
+ (
271
+ price NUMERIC CHECK (price > 0),
272
+ quantity INTEGER CHECK (quantity >= 0 AND quantity <= 1000),
273
+ code VARCHAR(20) CHECK (code ~ '^[A-Z]{3}-\d{4}$'
274
+ ) ,
245
275
  status TEXT CHECK (status = ANY (ARRAY['draft', 'published', 'archived']))
246
276
  );
247
277
  ```
278
+
248
279
 
280
+
249
281
  ```typescript
250
282
  export const PublicProductsSchema = z.object({
251
- price: z.number().min(0.00000000000001),
252
- quantity: z.number().int().min(0).max(1000),
253
- code: z.string().regex(/^[A-Z]{3}-\d{4}$/),
254
- status: z.enum(['draft', 'published', 'archived']),
283
+ price: z.number().min(0.00000000000001),
284
+ quantity: z.number().int().min(0).max(1000),
285
+ code: z.string().regex(/^[A-Z]{3}-\d{4}$/),
286
+ status: z.enum(['draft', 'published', 'archived']),
255
287
  });
256
288
  ```
257
289
 
258
290
  **Supported CHECK constraint patterns:**
291
+
259
292
  - Numeric comparisons: `>, <, >=, <=`
260
293
  - BETWEEN: `value BETWEEN min AND max`
261
294
  - IN/ANY(ARRAY): `value = ANY (ARRAY['a', 'b'])` → `z.enum(['a', 'b'])`
262
295
  - Regex: `value ~ 'pattern'` → `z.string().regex(/pattern/)`
263
296
  - Length: `length(value) >= n` → `z.string().min(n)`
264
297
 
298
+ ## Database Type Interface
299
+
300
+ pg2zod generates a `Database` TypeScript interface that organizes all your database types by schema, similar to Supabase's type generator. This provides a structured way to access all your types:
301
+
302
+ ```typescript
303
+ export interface Database {
304
+ public: {
305
+ Tables: {
306
+ users: {
307
+ Row: PublicUsers;
308
+ Insert: PublicUsersInsert;
309
+ Update: PublicUsersUpdate;
310
+ };
311
+ posts: {
312
+ Row: PublicPosts;
313
+ Insert: PublicPostsInsert;
314
+ Update: PublicPostsUpdate;
315
+ };
316
+ };
317
+ Views: {
318
+ user_stats: {
319
+ Row: PublicUserStatsView;
320
+ };
321
+ };
322
+ Functions: {
323
+ get_user_by_id: {
324
+ Args: PublicGetUserByIdParams;
325
+ Returns: PublicGetUserByIdReturn;
326
+ };
327
+ log_action: {
328
+ Args: Record<string, never>; // No parameters
329
+ Returns: void; // No return value
330
+ };
331
+ };
332
+ Enums: {
333
+ user_role: PublicUserRole;
334
+ status: PublicStatus;
335
+ };
336
+ CompositeTypes: {
337
+ address: PublicAddressComposite;
338
+ };
339
+ };
340
+ auth: {
341
+ Tables: {
342
+ // auth schema tables...
343
+ };
344
+ };
345
+ }
346
+ ```
347
+
348
+ **Usage:**
349
+
350
+ ```typescript
351
+ import { Database } from './schema';
352
+
353
+ // Access table types
354
+ type User = Database['public']['Tables']['users']['Row'];
355
+ type UserInsert = Database['public']['Tables']['users']['Insert'];
356
+
357
+ // Access view types
358
+ type UserStats = Database['public']['Views']['user_stats']['Row'];
359
+
360
+ // Access function types
361
+ type GetUserByIdArgs = Database['public']['Functions']['get_user_by_id']['Args'];
362
+ type GetUserByIdReturn = Database['public']['Functions']['get_user_by_id']['Returns'];
363
+
364
+ // Access enums
365
+ type UserRole = Database['public']['Enums']['user_role'];
366
+ ```
367
+
265
368
  ## CLI Options
266
369
 
267
370
  ### Connection Options
371
+
268
372
  ```
269
373
  --url <url> PostgreSQL connection URL
270
374
  --host <host> Database host (default: localhost)
@@ -276,14 +380,15 @@ export const PublicProductsSchema = z.object({
276
380
  ```
277
381
 
278
382
  ### Generation Options
383
+
279
384
  ```
280
385
  --schemas <schemas> Comma-separated list of schemas (default: public)
281
386
  --tables <tables> Include only these tables
282
387
  --exclude-tables <tables> Exclude these tables
283
388
  --no-input-schemas Skip input schemas (generated by default)
284
- --composite-types Include composite types (skipped by default)
285
- --views Include database views (skipped by default)
286
- --routines Include functions/procedures (skipped by default)
389
+ --no-composite-types Skip composite types (generated by default)
390
+ --no-views Skip database views (generated by default)
391
+ --no-routines Skip functions/procedures (generated by default)
287
392
  --security-invoker Include SECURITY INVOKER routines (default: DEFINER only)
288
393
  --branded-types Use branded types for IDs (future)
289
394
  --strict Fail on unmapped types
@@ -292,6 +397,7 @@ export const PublicProductsSchema = z.object({
292
397
  ```
293
398
 
294
399
  ### Output Options
400
+
295
401
  ```
296
402
  --output <file> Output file path (default: schema.ts)
297
403
  -o <file> Short form of --output
@@ -303,11 +409,11 @@ export const PublicProductsSchema = z.object({
303
409
 
304
410
  ```typescript
305
411
  import {
306
- generateZodSchemas,
307
- generateZodSchemasString,
308
- introspectDatabase,
309
- generateSchemas,
310
- formatOutput,
412
+ generateZodSchemas,
413
+ generateZodSchemasString,
414
+ introspectDatabase,
415
+ generateSchemas,
416
+ formatOutput,
311
417
  } from 'pg2zod';
312
418
 
313
419
  // Complete flow: introspect + generate + format
@@ -318,36 +424,36 @@ const schemaString = await generateZodSchemasString(config, options);
318
424
 
319
425
  // Step-by-step
320
426
  const metadata = await introspectDatabase(config, options);
321
- const result = generateSchemas(metadata, options);
322
- const output = formatOutput(result);
427
+ const result2 = generateSchemas(metadata, options);
428
+ const output = formatOutput(result2);
323
429
  ```
324
430
 
325
431
  ### Types
326
432
 
327
433
  ```typescript
328
434
  interface DatabaseConfig {
329
- host: string;
330
- port: number;
331
- database: string;
332
- user: string;
333
- password: string;
334
- ssl?: boolean | { rejectUnauthorized: boolean };
435
+ host: string;
436
+ port: number;
437
+ database: string;
438
+ user: string;
439
+ password: string;
440
+ ssl?: boolean | { rejectUnauthorized: boolean };
335
441
  }
336
442
 
337
443
  interface SchemaGenerationOptions {
338
- schemas?: string[]; // Default: ['public']
339
- tables?: string[]; // Include only these
340
- excludeTables?: string[]; // Exclude these
341
- generateInputSchemas?: boolean; // Generate Insert/Update schemas (default: true)
342
- includeCompositeTypes?: boolean; // Include composite types (default: false)
343
- includeViews?: boolean; // Include database views (default: false)
344
- includeRoutines?: boolean; // Include functions/procedures (default: false)
345
- includeSecurityInvoker?: boolean; // Include SECURITY INVOKER routines (default: false)
346
- useBrandedTypes?: boolean; // Use branded types (future)
347
- strictMode?: boolean; // Fail on unknown types
348
- includeComments?: boolean; // Include comments (default: true)
349
- useCamelCase?: boolean; // Convert to camelCase
350
- customTypeMappings?: Record<string, string>; // Custom mappings
444
+ schemas?: string[]; // Default: ['public']
445
+ tables?: string[]; // Include only these
446
+ excludeTables?: string[]; // Exclude these
447
+ generateInputSchemas?: boolean; // Generate Insert/Update schemas (default: true)
448
+ includeCompositeTypes?: boolean; // Include composite types (default: true)
449
+ includeViews?: boolean; // Include database views (default: true)
450
+ includeRoutines?: boolean; // Include functions/procedures (default: true)
451
+ includeSecurityInvoker?: boolean; // Include SECURITY INVOKER routines (default: false)
452
+ useBrandedTypes?: boolean; // Use branded types (future)
453
+ strictMode?: boolean; // Fail on unknown types
454
+ includeComments?: boolean; // Include comments (default: true)
455
+ useCamelCase?: boolean; // Convert to camelCase
456
+ customTypeMappings?: Record<string, string>; // Custom mappings
351
457
  }
352
458
  ```
353
459
 
@@ -360,19 +466,20 @@ interface SchemaGenerationOptions {
360
466
  CREATE TYPE user_role AS ENUM ('admin', 'user', 'guest');
361
467
 
362
468
  -- Create domain
363
- CREATE DOMAIN email AS VARCHAR(255)
364
- CHECK (VALUE ~ '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$');
469
+ CREATE DOMAIN email AS VARCHAR(255)
470
+ CHECK (VALUE ~ '^[A-Za-z0-9._%+-]+@[A-Za-z0-9.-]+\.[A-Za-z]{2,}$');
365
471
 
366
472
  -- Create table
367
- CREATE TABLE users (
368
- id SERIAL PRIMARY KEY,
369
- username VARCHAR(50) NOT NULL UNIQUE,
370
- email email NOT NULL,
371
- role user_role DEFAULT 'user',
372
- age INTEGER CHECK (age >= 18 AND age <= 120),
373
- tags TEXT[],
374
- metadata JSONB,
375
- created_at TIMESTAMPTZ DEFAULT NOW()
473
+ CREATE TABLE users
474
+ (
475
+ id SERIAL PRIMARY KEY,
476
+ username VARCHAR(50) NOT NULL UNIQUE,
477
+ email email NOT NULL,
478
+ role user_role DEFAULT 'user',
479
+ age INTEGER CHECK (age >= 18 AND age <= 120),
480
+ tags TEXT[],
481
+ metadata JSONB,
482
+ created_at TIMESTAMPTZ DEFAULT NOW()
376
483
  );
377
484
  ```
378
485
 
@@ -382,7 +489,7 @@ CREATE TABLE users (
382
489
  // Generated by pg2zod
383
490
  // Do not edit manually
384
491
 
385
- import { z } from 'zod';
492
+ import {z} from 'zod';
386
493
 
387
494
  // ============================================
388
495
  // Enums
@@ -406,39 +513,39 @@ export type PublicEmail = z.infer<typeof PublicEmailSchema>;
406
513
 
407
514
  /** Table: public.users - Read schema */
408
515
  export const PublicUsersSchema = z.object({
409
- id: z.number().int(),
410
- username: z.string().max(50),
411
- email: PublicEmailSchema,
412
- role: PublicUserRoleSchema,
413
- age: z.number().int().min(18).max(120).nullable(),
414
- tags: z.array(z.string()).nullable(),
415
- metadata: z.record(z.string(), z.unknown()).nullable(),
416
- created_at: z.date(),
516
+ id: z.number().int(),
517
+ username: z.string().max(50),
518
+ email: PublicEmailSchema,
519
+ role: PublicUserRoleSchema,
520
+ age: z.number().int().min(18).max(120).nullable(),
521
+ tags: z.array(z.string()).nullable(),
522
+ metadata: z.record(z.string(), z.unknown()).nullable(),
523
+ created_at: z.date(),
417
524
  });
418
525
  export type PublicUsers = z.infer<typeof PublicUsersSchema>;
419
526
 
420
527
  /** Insert schema for users - only auto-generated fields and fields with defaults are optional */
421
528
  export const PublicUsersInsertSchema = z.object({
422
- id: z.number().int().optional(), // auto-generated: SERIAL/identity
423
- username: z.string().max(50), // required: no default
424
- email: PublicEmailSchema, // required: no default
425
- role: PublicUserRoleSchema.optional(), // optional: has DEFAULT 'user'
426
- age: z.number().int().min(18).max(120).nullable(), // nullable but no default, so required
427
- tags: z.array(z.string()).nullable(), // nullable but no default, so required
428
- metadata: z.record(z.string(), z.unknown()).nullable(), // nullable but no default, so required
429
- created_at: z.date().optional(), // optional: has DEFAULT NOW()
529
+ id: z.number().int().optional(), // auto-generated: SERIAL/identity
530
+ username: z.string().max(50), // required: no default
531
+ email: PublicEmailSchema, // required: no default
532
+ role: PublicUserRoleSchema.optional(), // optional: has DEFAULT 'user'
533
+ age: z.number().int().min(18).max(120).nullable(), // nullable but no default, so required
534
+ tags: z.array(z.string()).nullable(), // nullable but no default, so required
535
+ metadata: z.record(z.string(), z.unknown()).nullable(), // nullable but no default, so required
536
+ created_at: z.date().optional(), // optional: has DEFAULT NOW()
430
537
  });
431
538
  export type PublicUsersInsert = z.infer<typeof PublicUsersInsertSchema>;
432
539
 
433
540
  /** Update schema for users - all fields optional, primary keys excluded, validation preserved */
434
541
  export const PublicUsersUpdateSchema = z.object({
435
- username: z.string().max(50).optional(),
436
- email: PublicEmailSchema.optional(),
437
- role: PublicUserRoleSchema.optional(),
438
- age: z.number().int().min(18).max(120).optional().nullable(),
439
- tags: z.array(z.string()).optional().nullable(),
440
- metadata: z.record(z.string(), z.unknown()).optional().nullable(),
441
- created_at: z.date().optional(),
542
+ username: z.string().max(50).optional(),
543
+ email: PublicEmailSchema.optional(),
544
+ role: PublicUserRoleSchema.optional(),
545
+ age: z.number().int().min(18).max(120).optional().nullable(),
546
+ tags: z.array(z.string()).optional().nullable(),
547
+ metadata: z.record(z.string(), z.unknown()).optional().nullable(),
548
+ created_at: z.date().optional(),
442
549
  });
443
550
  export type PublicUsersUpdate = z.infer<typeof PublicUsersUpdateSchema>;
444
551
  ```
@@ -466,5 +573,6 @@ MIT
466
573
  ## Credits
467
574
 
468
575
  Built with:
576
+
469
577
  - [pg](https://github.com/brianc/node-postgres) - PostgreSQL client
470
578
  - [zod](https://github.com/colinhacks/zod) - TypeScript-first schema validation