@buenojs/bueno 0.8.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.
Files changed (120) hide show
  1. package/.env.example +109 -0
  2. package/.github/workflows/ci.yml +31 -0
  3. package/LICENSE +21 -0
  4. package/README.md +892 -0
  5. package/architecture.md +652 -0
  6. package/bun.lock +70 -0
  7. package/dist/cli/index.js +3233 -0
  8. package/dist/index.js +9014 -0
  9. package/package.json +77 -0
  10. package/src/cache/index.ts +795 -0
  11. package/src/cli/ARCHITECTURE.md +837 -0
  12. package/src/cli/bin.ts +10 -0
  13. package/src/cli/commands/build.ts +425 -0
  14. package/src/cli/commands/dev.ts +248 -0
  15. package/src/cli/commands/generate.ts +541 -0
  16. package/src/cli/commands/help.ts +55 -0
  17. package/src/cli/commands/index.ts +112 -0
  18. package/src/cli/commands/migration.ts +355 -0
  19. package/src/cli/commands/new.ts +804 -0
  20. package/src/cli/commands/start.ts +208 -0
  21. package/src/cli/core/args.ts +283 -0
  22. package/src/cli/core/console.ts +349 -0
  23. package/src/cli/core/index.ts +60 -0
  24. package/src/cli/core/prompt.ts +424 -0
  25. package/src/cli/core/spinner.ts +265 -0
  26. package/src/cli/index.ts +135 -0
  27. package/src/cli/templates/deploy.ts +295 -0
  28. package/src/cli/templates/docker.ts +307 -0
  29. package/src/cli/templates/index.ts +24 -0
  30. package/src/cli/utils/fs.ts +428 -0
  31. package/src/cli/utils/index.ts +8 -0
  32. package/src/cli/utils/strings.ts +197 -0
  33. package/src/config/env.ts +408 -0
  34. package/src/config/index.ts +506 -0
  35. package/src/config/loader.ts +329 -0
  36. package/src/config/merge.ts +285 -0
  37. package/src/config/types.ts +320 -0
  38. package/src/config/validation.ts +441 -0
  39. package/src/container/forward-ref.ts +143 -0
  40. package/src/container/index.ts +386 -0
  41. package/src/context/index.ts +360 -0
  42. package/src/database/index.ts +1142 -0
  43. package/src/database/migrations/index.ts +371 -0
  44. package/src/database/schema/index.ts +619 -0
  45. package/src/frontend/api-routes.ts +640 -0
  46. package/src/frontend/bundler.ts +643 -0
  47. package/src/frontend/console-client.ts +419 -0
  48. package/src/frontend/console-stream.ts +587 -0
  49. package/src/frontend/dev-server.ts +846 -0
  50. package/src/frontend/file-router.ts +611 -0
  51. package/src/frontend/frameworks/index.ts +106 -0
  52. package/src/frontend/frameworks/react.ts +85 -0
  53. package/src/frontend/frameworks/solid.ts +104 -0
  54. package/src/frontend/frameworks/svelte.ts +110 -0
  55. package/src/frontend/frameworks/vue.ts +92 -0
  56. package/src/frontend/hmr-client.ts +663 -0
  57. package/src/frontend/hmr.ts +728 -0
  58. package/src/frontend/index.ts +342 -0
  59. package/src/frontend/islands.ts +552 -0
  60. package/src/frontend/isr.ts +555 -0
  61. package/src/frontend/layout.ts +475 -0
  62. package/src/frontend/ssr/react.ts +446 -0
  63. package/src/frontend/ssr/solid.ts +523 -0
  64. package/src/frontend/ssr/svelte.ts +546 -0
  65. package/src/frontend/ssr/vue.ts +504 -0
  66. package/src/frontend/ssr.ts +699 -0
  67. package/src/frontend/types.ts +2274 -0
  68. package/src/health/index.ts +604 -0
  69. package/src/index.ts +410 -0
  70. package/src/lock/index.ts +587 -0
  71. package/src/logger/index.ts +444 -0
  72. package/src/logger/transports/index.ts +969 -0
  73. package/src/metrics/index.ts +494 -0
  74. package/src/middleware/built-in.ts +360 -0
  75. package/src/middleware/index.ts +94 -0
  76. package/src/modules/filters.ts +458 -0
  77. package/src/modules/guards.ts +405 -0
  78. package/src/modules/index.ts +1256 -0
  79. package/src/modules/interceptors.ts +574 -0
  80. package/src/modules/lazy.ts +418 -0
  81. package/src/modules/lifecycle.ts +478 -0
  82. package/src/modules/metadata.ts +90 -0
  83. package/src/modules/pipes.ts +626 -0
  84. package/src/router/index.ts +339 -0
  85. package/src/router/linear.ts +371 -0
  86. package/src/router/regex.ts +292 -0
  87. package/src/router/tree.ts +562 -0
  88. package/src/rpc/index.ts +1263 -0
  89. package/src/security/index.ts +436 -0
  90. package/src/ssg/index.ts +631 -0
  91. package/src/storage/index.ts +456 -0
  92. package/src/telemetry/index.ts +1097 -0
  93. package/src/testing/index.ts +1586 -0
  94. package/src/types/index.ts +236 -0
  95. package/src/types/optional-deps.d.ts +219 -0
  96. package/src/validation/index.ts +276 -0
  97. package/src/websocket/index.ts +1004 -0
  98. package/tests/integration/cli.test.ts +1016 -0
  99. package/tests/integration/fullstack.test.ts +234 -0
  100. package/tests/unit/cache.test.ts +174 -0
  101. package/tests/unit/cli-commands.test.ts +892 -0
  102. package/tests/unit/cli.test.ts +1258 -0
  103. package/tests/unit/container.test.ts +279 -0
  104. package/tests/unit/context.test.ts +221 -0
  105. package/tests/unit/database.test.ts +183 -0
  106. package/tests/unit/linear-router.test.ts +280 -0
  107. package/tests/unit/lock.test.ts +336 -0
  108. package/tests/unit/middleware.test.ts +184 -0
  109. package/tests/unit/modules.test.ts +142 -0
  110. package/tests/unit/pubsub.test.ts +257 -0
  111. package/tests/unit/regex-router.test.ts +265 -0
  112. package/tests/unit/router.test.ts +373 -0
  113. package/tests/unit/rpc.test.ts +1248 -0
  114. package/tests/unit/security.test.ts +174 -0
  115. package/tests/unit/telemetry.test.ts +371 -0
  116. package/tests/unit/test-cache.test.ts +110 -0
  117. package/tests/unit/test-database.test.ts +282 -0
  118. package/tests/unit/tree-router.test.ts +325 -0
  119. package/tests/unit/validation.test.ts +794 -0
  120. package/tsconfig.json +27 -0
@@ -0,0 +1,619 @@
1
+ /**
2
+ * Database Schema Definition and Type System
3
+ *
4
+ * Provides utilities for defining database schemas and generating
5
+ * TypeScript types from them.
6
+ */
7
+
8
+ // ============= Column Types =============
9
+
10
+ export type ColumnType =
11
+ | "serial"
12
+ | "integer"
13
+ | "bigint"
14
+ | "decimal"
15
+ | "varchar"
16
+ | "text"
17
+ | "boolean"
18
+ | "date"
19
+ | "timestamp"
20
+ | "timestamptz"
21
+ | "json"
22
+ | "jsonb"
23
+ | "uuid"
24
+ | "blob"
25
+ | "enum";
26
+
27
+ // ============= Column Options =============
28
+
29
+ export interface ColumnOptions {
30
+ type: ColumnType;
31
+ length?: number;
32
+ precision?: number;
33
+ scale?: number;
34
+ nullable?: boolean;
35
+ default?: unknown;
36
+ primaryKey?: boolean;
37
+ autoIncrement?: boolean;
38
+ unique?: boolean;
39
+ references?: {
40
+ table: string;
41
+ column: string;
42
+ onDelete?: "CASCADE" | "SET NULL" | "RESTRICT" | "NO ACTION";
43
+ onUpdate?: "CASCADE" | "SET NULL" | "RESTRICT" | "NO ACTION";
44
+ };
45
+ check?: string;
46
+ enum?: string[];
47
+ comment?: string;
48
+ }
49
+
50
+ // ============= Table Schema =============
51
+
52
+ export interface TableSchema {
53
+ name: string;
54
+ columns: Record<string, ColumnOptions>;
55
+ indexes?: IndexDefinition[];
56
+ constraints?: ConstraintDefinition[];
57
+ comment?: string;
58
+ }
59
+
60
+ export interface IndexDefinition {
61
+ name?: string;
62
+ columns: string[];
63
+ unique?: boolean;
64
+ type?: "btree" | "hash" | "gin" | "gist";
65
+ }
66
+
67
+ export interface ConstraintDefinition {
68
+ name?: string;
69
+ type: "unique" | "check" | "foreign";
70
+ columns: string[];
71
+ reference?: {
72
+ table: string;
73
+ columns: string[];
74
+ onDelete?: "CASCADE" | "SET NULL" | "RESTRICT" | "NO ACTION";
75
+ onUpdate?: "CASCADE" | "SET NULL" | "RESTRICT" | "NO ACTION";
76
+ };
77
+ check?: string;
78
+ }
79
+
80
+ // ============= Column Builder =============
81
+
82
+ class ColumnBuilder {
83
+ column: ColumnOptions;
84
+
85
+ constructor(type: ColumnType) {
86
+ this.column = { type, nullable: false };
87
+ }
88
+
89
+ nullable(): this {
90
+ this.column.nullable = true;
91
+ return this;
92
+ }
93
+
94
+ notNull(): this {
95
+ this.column.nullable = false;
96
+ return this;
97
+ }
98
+
99
+ default(value: unknown): this {
100
+ this.column.default = value;
101
+ return this;
102
+ }
103
+
104
+ primaryKey(): this {
105
+ this.column.primaryKey = true;
106
+ return this;
107
+ }
108
+
109
+ unique(): this {
110
+ this.column.unique = true;
111
+ return this;
112
+ }
113
+
114
+ length(len: number): this {
115
+ this.column.length = len;
116
+ return this;
117
+ }
118
+
119
+ precision(p: number, s?: number): this {
120
+ this.column.precision = p;
121
+ if (s !== undefined) this.column.scale = s;
122
+ return this;
123
+ }
124
+
125
+ references(
126
+ table: string,
127
+ column: string,
128
+ options?: {
129
+ onDelete?: "CASCADE" | "SET NULL" | "RESTRICT" | "NO ACTION";
130
+ onUpdate?: "CASCADE" | "SET NULL" | "RESTRICT" | "NO ACTION";
131
+ },
132
+ ): this {
133
+ this.column.references = {
134
+ table,
135
+ column,
136
+ ...options,
137
+ };
138
+ return this;
139
+ }
140
+
141
+ check(expression: string): this {
142
+ this.column.check = expression;
143
+ return this;
144
+ }
145
+
146
+ comment(text: string): this {
147
+ this.column.comment = text;
148
+ return this;
149
+ }
150
+
151
+ build(): ColumnOptions {
152
+ return { ...this.column };
153
+ }
154
+ }
155
+
156
+ // ============= Schema Builder =============
157
+
158
+ export class SchemaBuilder {
159
+ private name: string;
160
+ private columns: Record<string, ColumnOptions> = {};
161
+ private indexes: IndexDefinition[] = [];
162
+ private constraints: ConstraintDefinition[] = [];
163
+ private tableComment?: string;
164
+
165
+ constructor(tableName: string) {
166
+ this.name = tableName;
167
+ }
168
+
169
+ // Column type helpers
170
+ serial(name: string): ColumnBuilder {
171
+ const builder = new ColumnBuilder("serial");
172
+ builder.primaryKey();
173
+ this.columns[name] = builder.build();
174
+ return builder;
175
+ }
176
+
177
+ integer(name: string): ColumnBuilder {
178
+ const builder = new ColumnBuilder("integer");
179
+ this.columns[name] = builder.build();
180
+ return builder;
181
+ }
182
+
183
+ bigint(name: string): ColumnBuilder {
184
+ const builder = new ColumnBuilder("bigint");
185
+ this.columns[name] = builder.build();
186
+ return builder;
187
+ }
188
+
189
+ decimal(name: string): ColumnBuilder {
190
+ const builder = new ColumnBuilder("decimal");
191
+ this.columns[name] = builder.build();
192
+ return builder;
193
+ }
194
+
195
+ varchar(name: string, length = 255): ColumnBuilder {
196
+ const builder = new ColumnBuilder("varchar");
197
+ builder.length(length);
198
+ this.columns[name] = builder.build();
199
+ return builder;
200
+ }
201
+
202
+ text(name: string): ColumnBuilder {
203
+ const builder = new ColumnBuilder("text");
204
+ this.columns[name] = builder.build();
205
+ return builder;
206
+ }
207
+
208
+ boolean(name: string): ColumnBuilder {
209
+ const builder = new ColumnBuilder("boolean");
210
+ this.columns[name] = builder.build();
211
+ return builder;
212
+ }
213
+
214
+ date(name: string): ColumnBuilder {
215
+ const builder = new ColumnBuilder("date");
216
+ this.columns[name] = builder.build();
217
+ return builder;
218
+ }
219
+
220
+ timestamp(name: string): ColumnBuilder {
221
+ const builder = new ColumnBuilder("timestamp");
222
+ this.columns[name] = builder.build();
223
+ return builder;
224
+ }
225
+
226
+ timestamptz(name: string): ColumnBuilder {
227
+ const builder = new ColumnBuilder("timestamptz");
228
+ this.columns[name] = builder.build();
229
+ return builder;
230
+ }
231
+
232
+ json(name: string): ColumnBuilder {
233
+ const builder = new ColumnBuilder("json");
234
+ this.columns[name] = builder.build();
235
+ return builder;
236
+ }
237
+
238
+ jsonb(name: string): ColumnBuilder {
239
+ const builder = new ColumnBuilder("jsonb");
240
+ this.columns[name] = builder.build();
241
+ return builder;
242
+ }
243
+
244
+ uuid(name: string): ColumnBuilder {
245
+ const builder = new ColumnBuilder("uuid");
246
+ this.columns[name] = builder.build();
247
+ return builder;
248
+ }
249
+
250
+ blob(name: string): ColumnBuilder {
251
+ const builder = new ColumnBuilder("blob");
252
+ this.columns[name] = builder.build();
253
+ return builder;
254
+ }
255
+
256
+ enum(name: string, values: string[]): ColumnBuilder {
257
+ const builder = new ColumnBuilder("enum");
258
+ builder.column.enum = values;
259
+ this.columns[name] = builder.build();
260
+ return builder;
261
+ }
262
+
263
+ // Custom column
264
+ column(name: string, options: ColumnOptions): this {
265
+ this.columns[name] = options;
266
+ return this;
267
+ }
268
+
269
+ // Indexes
270
+ index(
271
+ columns: string[],
272
+ options?: {
273
+ name?: string;
274
+ unique?: boolean;
275
+ type?: "btree" | "hash" | "gin" | "gist";
276
+ },
277
+ ): this {
278
+ this.indexes.push({
279
+ columns,
280
+ ...options,
281
+ });
282
+ return this;
283
+ }
284
+
285
+ unique(columns: string[], name?: string): this {
286
+ this.constraints.push({
287
+ type: "unique",
288
+ columns,
289
+ name,
290
+ });
291
+ return this;
292
+ }
293
+
294
+ foreignKey(
295
+ columns: string[],
296
+ reference: {
297
+ table: string;
298
+ columns: string[];
299
+ onDelete?: "CASCADE" | "SET NULL" | "RESTRICT" | "NO ACTION";
300
+ onUpdate?: "CASCADE" | "SET NULL" | "RESTRICT" | "NO ACTION";
301
+ },
302
+ name?: string,
303
+ ): this {
304
+ this.constraints.push({
305
+ type: "foreign",
306
+ columns,
307
+ reference,
308
+ name,
309
+ });
310
+ return this;
311
+ }
312
+
313
+ check(expression: string, name?: string): this {
314
+ this.constraints.push({
315
+ type: "check",
316
+ columns: [],
317
+ check: expression,
318
+ name,
319
+ });
320
+ return this;
321
+ }
322
+
323
+ comment(text: string): this {
324
+ this.tableComment = text;
325
+ return this;
326
+ }
327
+
328
+ build(): TableSchema {
329
+ return {
330
+ name: this.name,
331
+ columns: { ...this.columns },
332
+ indexes: [...this.indexes],
333
+ constraints: [...this.constraints],
334
+ comment: this.tableComment,
335
+ };
336
+ }
337
+ }
338
+
339
+ // ============= Migration Helpers =============
340
+
341
+ /**
342
+ * Generate CREATE TABLE SQL from schema
343
+ */
344
+ export function generateCreateTable(
345
+ schema: TableSchema,
346
+ driver: "postgresql" | "mysql" | "sqlite" = "postgresql",
347
+ ): string {
348
+ const columnDefs: string[] = [];
349
+ const primaryKeys: string[] = [];
350
+
351
+ for (const [name, col] of Object.entries(schema.columns)) {
352
+ const parts: string[] = [name, mapColumnType(col, driver)];
353
+
354
+ if (col.primaryKey) {
355
+ primaryKeys.push(name);
356
+ if (driver === "sqlite" && col.type === "serial") {
357
+ parts[1] = "INTEGER PRIMARY KEY AUTOINCREMENT";
358
+ }
359
+ }
360
+
361
+ if (!col.nullable && !col.primaryKey) {
362
+ parts.push("NOT NULL");
363
+ }
364
+
365
+ if (col.unique && !col.primaryKey) {
366
+ parts.push("UNIQUE");
367
+ }
368
+
369
+ if (col.default !== undefined) {
370
+ parts.push(`DEFAULT ${formatDefault(col.default, driver)}`);
371
+ }
372
+
373
+ if (col.references) {
374
+ const ref = col.references;
375
+ parts.push(`REFERENCES ${ref.table}(${ref.column})`);
376
+ if (ref.onDelete) parts.push(`ON DELETE ${ref.onDelete}`);
377
+ if (ref.onUpdate) parts.push(`ON UPDATE ${ref.onUpdate}`);
378
+ }
379
+
380
+ columnDefs.push(parts.join(" "));
381
+ }
382
+
383
+ // Add primary key constraint if multiple
384
+ if (primaryKeys.length > 1) {
385
+ columnDefs.push(`PRIMARY KEY (${primaryKeys.join(", ")})`);
386
+ }
387
+
388
+ // Add constraints
389
+ for (const constraint of schema.constraints ?? []) {
390
+ if (constraint.type === "unique") {
391
+ const name =
392
+ constraint.name || `uq_${schema.name}_${constraint.columns.join("_")}`;
393
+ columnDefs.push(
394
+ `CONSTRAINT ${name} UNIQUE (${constraint.columns.join(", ")})`,
395
+ );
396
+ } else if (constraint.type === "check" && constraint.check) {
397
+ const name = constraint.name || `chk_${schema.name}`;
398
+ columnDefs.push(`CONSTRAINT ${name} CHECK (${constraint.check})`);
399
+ }
400
+ }
401
+
402
+ let sql = `CREATE TABLE ${schema.name} (\n ${columnDefs.join(",\n ")}\n)`;
403
+
404
+ if (driver === "postgresql" && schema.comment) {
405
+ sql += `;\nCOMMENT ON TABLE ${schema.name} IS '${schema.comment}'`;
406
+ }
407
+
408
+ return `${sql};`;
409
+ }
410
+
411
+ /**
412
+ * Generate DROP TABLE SQL
413
+ */
414
+ export function generateDropTable(tableName: string, ifExists = true): string {
415
+ if (ifExists) {
416
+ return `DROP TABLE IF EXISTS ${tableName};`;
417
+ }
418
+ return `DROP TABLE ${tableName};`;
419
+ }
420
+
421
+ /**
422
+ * Generate CREATE INDEX SQL
423
+ */
424
+ export function generateCreateIndex(
425
+ tableName: string,
426
+ index: IndexDefinition,
427
+ driver: "postgresql" | "mysql" | "sqlite" = "postgresql",
428
+ ): string {
429
+ const name = index.name || `idx_${tableName}_${index.columns.join("_")}`;
430
+ const unique = index.unique ? "UNIQUE " : "";
431
+ const type =
432
+ index.type && driver === "postgresql" ? `USING ${index.type} ` : "";
433
+
434
+ return `CREATE ${unique}INDEX ${name} ON ${tableName} ${type}(${index.columns.join(", ")});`;
435
+ }
436
+
437
+ // ============= Type Mappers =============
438
+
439
+ function mapColumnType(
440
+ col: ColumnOptions,
441
+ driver: "postgresql" | "mysql" | "sqlite",
442
+ ): string {
443
+ const typeMap: Record<ColumnType, Record<string, string>> = {
444
+ serial: {
445
+ postgresql: "SERIAL",
446
+ mysql: "INT AUTO_INCREMENT",
447
+ sqlite: "INTEGER",
448
+ },
449
+ integer: {
450
+ postgresql: "INTEGER",
451
+ mysql: "INT",
452
+ sqlite: "INTEGER",
453
+ },
454
+ bigint: {
455
+ postgresql: "BIGINT",
456
+ mysql: "BIGINT",
457
+ sqlite: "INTEGER",
458
+ },
459
+ decimal: {
460
+ postgresql: col.precision
461
+ ? `DECIMAL(${col.precision}, ${col.scale ?? 0})`
462
+ : "DECIMAL",
463
+ mysql: col.precision
464
+ ? `DECIMAL(${col.precision}, ${col.scale ?? 0})`
465
+ : "DECIMAL",
466
+ sqlite: "REAL",
467
+ },
468
+ varchar: {
469
+ postgresql: col.length ? `VARCHAR(${col.length})` : "VARCHAR",
470
+ mysql: col.length ? `VARCHAR(${col.length})` : "VARCHAR(255)",
471
+ sqlite: "TEXT",
472
+ },
473
+ text: {
474
+ postgresql: "TEXT",
475
+ mysql: "TEXT",
476
+ sqlite: "TEXT",
477
+ },
478
+ boolean: {
479
+ postgresql: "BOOLEAN",
480
+ mysql: "TINYINT(1)",
481
+ sqlite: "INTEGER",
482
+ },
483
+ date: {
484
+ postgresql: "DATE",
485
+ mysql: "DATE",
486
+ sqlite: "TEXT",
487
+ },
488
+ timestamp: {
489
+ postgresql: "TIMESTAMP",
490
+ mysql: "DATETIME",
491
+ sqlite: "TEXT",
492
+ },
493
+ timestamptz: {
494
+ postgresql: "TIMESTAMPTZ",
495
+ mysql: "DATETIME",
496
+ sqlite: "TEXT",
497
+ },
498
+ json: {
499
+ postgresql: "JSON",
500
+ mysql: "JSON",
501
+ sqlite: "TEXT",
502
+ },
503
+ jsonb: {
504
+ postgresql: "JSONB",
505
+ mysql: "JSON",
506
+ sqlite: "TEXT",
507
+ },
508
+ uuid: {
509
+ postgresql: "UUID",
510
+ mysql: "CHAR(36)",
511
+ sqlite: "TEXT",
512
+ },
513
+ blob: {
514
+ postgresql: "BYTEA",
515
+ mysql: "BLOB",
516
+ sqlite: "BLOB",
517
+ },
518
+ enum: {
519
+ postgresql: col.enum
520
+ ? `ENUM(${col.enum.map((e) => `'${e}'`).join(", ")})`
521
+ : "VARCHAR",
522
+ mysql: col.enum
523
+ ? `ENUM(${col.enum.map((e) => `'${e}'`).join(", ")})`
524
+ : "VARCHAR",
525
+ sqlite: "TEXT",
526
+ },
527
+ };
528
+
529
+ return typeMap[col.type][driver];
530
+ }
531
+
532
+ function formatDefault(
533
+ value: unknown,
534
+ driver: "postgresql" | "mysql" | "sqlite",
535
+ ): string {
536
+ if (value === null) return "NULL";
537
+ if (value === true) return driver === "sqlite" ? "1" : "TRUE";
538
+ if (value === false) return driver === "sqlite" ? "0" : "FALSE";
539
+ if (typeof value === "number") return String(value);
540
+ if (typeof value === "string") {
541
+ // Check for SQL functions
542
+ if (
543
+ value.toUpperCase() === "NOW()" ||
544
+ value.toUpperCase() === "CURRENT_TIMESTAMP"
545
+ ) {
546
+ return driver === "mysql" ? "CURRENT_TIMESTAMP" : value.toUpperCase();
547
+ }
548
+ return `'${value.replace(/'/g, "''")}'`;
549
+ }
550
+ return String(value);
551
+ }
552
+
553
+ // ============= Type Inference Helpers =============
554
+
555
+ /**
556
+ * TypeScript type mapping from column types
557
+ * This is for documentation/type generation purposes
558
+ */
559
+ export type TypeScriptType<T extends ColumnType> = T extends
560
+ | "serial"
561
+ | "integer"
562
+ ? number
563
+ : T extends "bigint"
564
+ ? bigint | string
565
+ : T extends "decimal"
566
+ ? number | string
567
+ : T extends "varchar" | "text" | "uuid" | "enum"
568
+ ? string
569
+ : T extends "boolean"
570
+ ? boolean
571
+ : T extends "date" | "timestamp" | "timestamptz"
572
+ ? Date | string
573
+ : T extends "json" | "jsonb"
574
+ ? Record<string, unknown> | unknown[]
575
+ : T extends "blob"
576
+ ? Buffer | ArrayBuffer
577
+ : unknown;
578
+
579
+ /**
580
+ * Infer TypeScript type from schema
581
+ * Usage: type User = InferType<typeof userSchema>
582
+ */
583
+ export type InferType<S extends TableSchema> = {
584
+ [K in keyof S["columns"]]: S["columns"][K] extends { nullable: true }
585
+ ? TypeScriptType<S["columns"][K]["type"]> | null
586
+ : TypeScriptType<S["columns"][K]["type"]>;
587
+ };
588
+
589
+ /**
590
+ * Infer insert type (optional fields with defaults)
591
+ */
592
+ export type InferInsertType<S extends TableSchema> = {
593
+ [K in keyof S["columns"]]: S["columns"][K] extends { default: unknown }
594
+ ? TypeScriptType<S["columns"][K]["type"]> | undefined
595
+ : S["columns"][K] extends { nullable: true }
596
+ ? TypeScriptType<S["columns"][K]["type"]> | null | undefined
597
+ : TypeScriptType<S["columns"][K]["type"]>;
598
+ };
599
+
600
+ // ============= Schema Factory =============
601
+
602
+ /**
603
+ * Create a new schema builder
604
+ */
605
+ export function createSchema(tableName: string): SchemaBuilder {
606
+ return new SchemaBuilder(tableName);
607
+ }
608
+
609
+ /**
610
+ * Define a table schema
611
+ */
612
+ export function defineTable(
613
+ name: string,
614
+ define: (builder: SchemaBuilder) => void,
615
+ ): TableSchema {
616
+ const builder = new SchemaBuilder(name);
617
+ define(builder);
618
+ return builder.build();
619
+ }