alepha 0.10.6 → 0.11.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/postgres.d.ts CHANGED
@@ -1,80 +1,23 @@
1
1
  import * as _alepha_core1 from "alepha";
2
- import { Alepha, AlephaError, Descriptor, KIND, Service, Static, TArray, TBigInt, TBoolean, TInteger, TNull, TNumber, TNumberOptions, TObject, TObjectOptions, TOptional, TOptionalAdd, TRecord, TSchema as TSchema$1, TString, TStringOptions, TUnion } from "alepha";
3
- import * as drizzle_orm6 from "drizzle-orm";
4
- import { BuildColumns, BuildExtraConfigColumns, SQL, SQLWrapper, TableConfig, sql } from "drizzle-orm";
5
- import * as pg$1 from "drizzle-orm/pg-core";
6
- import { AnyPgColumn, AnyPgTable, LockConfig, LockStrength, PgColumn, PgColumnBuilderBase, PgDatabase, PgInsertValue, PgSequenceOptions, PgTableExtraConfigValue, PgTableWithColumns, PgTransaction, PgTransactionConfig, SelectedFields, TableConfig as TableConfig$1, UpdateDeleteAction } from "drizzle-orm/pg-core";
2
+ import { Alepha, AlephaError, Descriptor, DescriptorArgs, KIND, Static, StaticEncode, TArray, TBigInt, TBoolean, TInteger, TNull, TNumber, TNumberOptions, TObject, TObjectOptions, TOptional, TOptionalAdd, TRecord, TSchema, TString, TStringOptions, TUnion, TUnsafe } from "alepha";
7
3
  import { DateTime, DateTimeProvider } from "alepha/datetime";
8
- import * as _alepha_logger1 from "alepha/logger";
4
+ import * as _alepha_logger0 from "alepha/logger";
9
5
  import * as _alepha_lock0 from "alepha/lock";
10
- import { PostgresJsDatabase } from "drizzle-orm/postgres-js";
11
- import postgres from "postgres";
12
6
  import * as _alepha_retry0 from "alepha/retry";
13
- import * as typebox1 from "typebox";
7
+ import * as drizzle_orm0 from "drizzle-orm";
8
+ import { BuildExtraConfigColumns, SQL, SQLWrapper, sql } from "drizzle-orm";
9
+ import * as drizzle_orm_pg_core0 from "drizzle-orm/pg-core";
10
+ import { LockConfig, LockStrength, PgColumn, PgColumnBuilderBase, PgDatabase, PgInsertValue, PgSchema, PgSelectBase, PgSequenceOptions, PgTableExtraConfigValue, PgTableWithColumns, PgTransaction, PgTransactionConfig, UpdateDeleteAction } from "drizzle-orm/pg-core";
11
+ import * as typebox9 from "typebox";
14
12
  import { PgTransactionConfig as PgTransactionConfig$1 } from "drizzle-orm/pg-core/session";
15
13
  import * as DrizzleKit from "drizzle-kit/api";
16
14
  import { MigrationConfig } from "drizzle-orm/migrator";
15
+ import { PostgresJsDatabase } from "drizzle-orm/postgres-js";
16
+ import postgres from "postgres";
17
+ import * as dayjs0 from "dayjs";
17
18
  import { UpdateDeleteAction as UpdateDeleteAction$1 } from "drizzle-orm/pg-core/foreign-keys";
18
19
  export * from "drizzle-orm/pg-core";
19
20
 
20
- //#region src/constants/PG_SCHEMA.d.ts
21
- declare const PG_SCHEMA: unique symbol;
22
- //#endregion
23
- //#region src/constants/PG_SYMBOLS.d.ts
24
- declare const PG_DEFAULT: unique symbol;
25
- declare const PG_PRIMARY_KEY: unique symbol;
26
- declare const PG_CREATED_AT: unique symbol;
27
- declare const PG_UPDATED_AT: unique symbol;
28
- declare const PG_DELETED_AT: unique symbol;
29
- declare const PG_VERSION: unique symbol;
30
- declare const PG_IDENTITY: unique symbol;
31
- declare const PG_MANY: unique symbol;
32
- declare const PG_ONE: unique symbol;
33
- declare const PG_REF: unique symbol;
34
- /**
35
- * @deprecated Use `PG_IDENTITY` instead.
36
- */
37
- declare const PG_SERIAL: unique symbol;
38
- type PgDefault = typeof PG_DEFAULT;
39
- type PgMany = typeof PG_MANY;
40
- type PgOne = typeof PG_ONE;
41
- type PgRef = typeof PG_REF;
42
- type PgPrimaryKey = typeof PG_PRIMARY_KEY;
43
- type PgSymbols = {
44
- [PG_DEFAULT]: {};
45
- [PG_PRIMARY_KEY]: {};
46
- [PG_CREATED_AT]: {};
47
- [PG_UPDATED_AT]: {};
48
- [PG_DELETED_AT]: {};
49
- [PG_VERSION]: {};
50
- [PG_IDENTITY]: PgIdentityOptions;
51
- [PG_MANY]: PgManyOptions;
52
- [PG_ONE]: PgManyOptions;
53
- [PG_REF]: PgRefOptions;
54
- /**
55
- * @deprecated Use `PG_IDENTITY` instead.
56
- */
57
- [PG_SERIAL]: {};
58
- };
59
- type PgSymbolKeys = keyof PgSymbols;
60
- type PgIdentityOptions = {
61
- mode: "always" | "byDefault";
62
- } & PgSequenceOptions & {
63
- name?: string;
64
- };
65
- interface PgManyOptions {
66
- table: AnyPgTable;
67
- schema: TObject;
68
- foreignKey: string;
69
- }
70
- interface PgRefOptions {
71
- ref: () => AnyPgColumn;
72
- actions?: {
73
- onUpdate?: UpdateDeleteAction;
74
- onDelete?: UpdateDeleteAction;
75
- };
76
- }
77
- //#endregion
78
21
  //#region src/schemas/insertSchema.d.ts
79
22
  /**
80
23
  * Transforms a TObject schema for insert operations.
@@ -104,484 +47,41 @@ declare const insertSchema: <T extends TObject>(obj: T) => TObjectInsert<T>;
104
47
  type TObjectUpdate<T extends TObject> = TObject<{ [K in keyof T["properties"]]: T["properties"][K] extends TOptional<infer U> ? TOptional<TUnion<[U, TNull]>> : T["properties"][K] }>;
105
48
  declare const updateSchema: <T extends TObject>(schema: T) => TObjectUpdate<T>;
106
49
  //#endregion
107
- //#region src/helpers/schemaToPgColumns.d.ts
108
- /**
109
- * Convert a Typebox Schema to Drizzle ORM Postgres columns
110
- */
111
- declare const schemaToPgColumns: <T extends TObject>(schema: T) => FromSchema<T>;
112
- /**
113
- * Map a Typebox field to a PG column.
114
- *
115
- * @param name The key of the field.
116
- * @param value The value of the field.
117
- * @returns The PG column.
118
- */
119
- declare const mapFieldToColumn: (name: string, value: TSchema$1) => pg$1.PgSerialBuilderInitial<string> | pg$1.PgIntegerBuilderInitial<string> | drizzle_orm6.IsIdentity<pg$1.PgBigInt64BuilderInitial<"">, "byDefault"> | drizzle_orm6.IsIdentity<pg$1.PgBigInt64BuilderInitial<"">, "always"> | pg$1.PgBigInt53BuilderInitial<string> | pg$1.PgNumericBuilderInitial<string> | pg$1.PgDateStringBuilderInitial<string> | pg$1.PgUUIDBuilderInitial<string> | pg$1.PgCustomColumnBuilder<{
120
- name: string;
121
- dataType: "custom";
122
- columnType: "PgCustomColumn";
123
- data: Buffer<ArrayBufferLike>;
124
- driverParam: unknown;
125
- enumValues: undefined;
126
- }> | pg$1.PgTimestampStringBuilderInitial<string> | pg$1.PgTextBuilderInitial<string, [string, ...string[]]> | pg$1.PgBooleanBuilderInitial<string> | drizzle_orm6.$Type<pg$1.PgCustomColumnBuilder<{
127
- name: string;
128
- dataType: "custom";
129
- columnType: "PgCustomColumn";
130
- data: {
131
- [x: string]: unknown;
132
- [x: number]: unknown;
133
- [x: symbol]: unknown;
134
- };
135
- driverParam: string;
136
- enumValues: undefined;
137
- }>, {
138
- [x: string]: unknown;
139
- [x: number]: unknown;
140
- [x: symbol]: unknown;
141
- }> | drizzle_orm6.$Type<pg$1.PgCustomColumnBuilder<{
142
- name: string;
143
- dataType: "custom";
144
- columnType: "PgCustomColumn";
145
- data: Record<string, unknown>;
146
- driverParam: string;
147
- enumValues: undefined;
148
- }>, Record<string, unknown>> | drizzle_orm6.$Type<pg$1.PgCustomColumnBuilder<{
149
- name: string;
150
- dataType: "custom";
151
- columnType: "PgCustomColumn";
152
- data: unknown[];
153
- driverParam: string;
154
- enumValues: undefined;
155
- }>, unknown[]> | pg$1.PgArrayBuilder<{
156
- name: string;
157
- dataType: "array";
158
- columnType: "PgArray";
159
- data: string[];
160
- driverParam: string | string[];
161
- enumValues: [string, ...string[]];
162
- size: undefined;
163
- baseBuilder: {
164
- name: string;
165
- dataType: "string";
166
- columnType: "PgText";
167
- data: string;
168
- enumValues: [string, ...string[]];
169
- driverParam: string;
170
- };
171
- }, {
172
- name: string;
173
- dataType: "string";
174
- columnType: "PgText";
175
- data: string;
176
- enumValues: [string, ...string[]];
177
- driverParam: string;
178
- }> | pg$1.PgArrayBuilder<{
179
- name: string;
180
- dataType: "array";
181
- columnType: "PgArray";
182
- data: number[];
183
- driverParam: string | (string | number)[];
184
- enumValues: undefined;
185
- size: undefined;
186
- baseBuilder: {
187
- name: string;
188
- dataType: "number";
189
- columnType: "PgInteger";
190
- data: number;
191
- driverParam: number | string;
192
- enumValues: undefined;
193
- };
194
- }, {
195
- name: string;
196
- dataType: "number";
197
- columnType: "PgInteger";
198
- data: number;
199
- driverParam: number | string;
200
- enumValues: undefined;
201
- }> | pg$1.PgArrayBuilder<{
202
- name: string;
203
- dataType: "array";
204
- columnType: "PgArray";
205
- data: string[];
206
- driverParam: string | string[];
207
- enumValues: undefined;
208
- size: undefined;
209
- baseBuilder: {
210
- name: string;
211
- dataType: "string";
212
- columnType: "PgNumeric";
213
- data: string;
214
- driverParam: string;
215
- enumValues: undefined;
216
- };
217
- }, {
218
- name: string;
219
- dataType: "string";
220
- columnType: "PgNumeric";
221
- data: string;
222
- driverParam: string;
223
- enumValues: undefined;
224
- }> | pg$1.PgArrayBuilder<{
225
- name: string;
226
- dataType: "array";
227
- columnType: "PgArray";
228
- data: boolean[];
229
- driverParam: string | boolean[];
230
- enumValues: undefined;
231
- size: undefined;
232
- baseBuilder: {
233
- name: string;
234
- dataType: "boolean";
235
- columnType: "PgBoolean";
236
- data: boolean;
237
- driverParam: boolean;
238
- enumValues: undefined;
239
- };
240
- }, {
241
- name: string;
242
- dataType: "boolean";
243
- columnType: "PgBoolean";
244
- data: boolean;
245
- driverParam: boolean;
246
- enumValues: undefined;
247
- }>;
248
- /**
249
- * Map a string to a PG column.
250
- *
251
- * @param key The key of the field.
252
- * @param value The value of the field.
253
- */
254
- declare const mapStringToColumn: (key: string, value: TSchema$1) => pg$1.PgDateStringBuilderInitial<string> | pg$1.PgUUIDBuilderInitial<string> | pg$1.PgCustomColumnBuilder<{
255
- name: string;
256
- dataType: "custom";
257
- columnType: "PgCustomColumn";
258
- data: Buffer<ArrayBufferLike>;
259
- driverParam: unknown;
260
- enumValues: undefined;
261
- }> | pg$1.PgTimestampStringBuilderInitial<string> | pg$1.PgTextBuilderInitial<string, [string, ...string[]]>;
262
- declare const camelToSnakeCase: (str: string) => string;
263
- /**
264
- * Convert a schema to columns.
265
- */
266
- type FromSchema<T extends TObject> = { [key in keyof T["properties"]]: PgColumnBuilderBase };
267
- /**
268
- * A table with columns and schema.
269
- */
270
- type PgTableWithColumnsAndSchema<T extends TableConfig, R extends TObject> = PgTableWithColumns<T> & {
271
- get $table(): PgTableWithColumns<T>;
272
- get $schema(): R;
273
- get $insertSchema(): TObjectInsert<R>;
274
- get $updateSchema(): TObjectUpdate<R>;
275
- };
276
- //#endregion
277
50
  //#region src/descriptors/$entity.d.ts
278
51
  /**
279
52
  * Creates a database entity descriptor that defines table structure using TypeBox schemas.
280
53
  *
281
- * This descriptor provides a type-safe way to define database tables using JSON Schema
282
- * syntax while generating the necessary database metadata for migrations and operations.
283
- * It integrates with Drizzle ORM under the hood and works seamlessly with the $repository
284
- * descriptor for complete database functionality.
285
- *
286
- * **Key Features**
287
- *
288
- * - **Type-Safe Schema Definition**: Uses TypeBox for full TypeScript type inference
289
- * - **Automatic Table Generation**: Creates Drizzle ORM table structures automatically
290
- * - **Index Management**: Supports single-column, multi-column, and unique indexes
291
- * - **Constraint Support**: Foreign keys, unique constraints, and check constraints
292
- * - **Audit Fields**: Built-in support for created_at, updated_at, deleted_at, and version fields
293
- * - **Schema Validation**: Automatic insert/update schema generation with validation
294
- *
295
- * **Important Note**:
296
- * This descriptor only defines the table structure - it does not create the physical
297
- * database table. Use it with $repository to perform actual database operations,
298
- * and run migrations to create the tables in your database.
299
- *
300
- * **Use Cases**
301
- *
302
- * Essential for defining database schema in type-safe applications:
303
- * - User management and authentication tables
304
- * - Business domain entities (products, orders, customers)
305
- * - Audit and logging tables
306
- * - Junction tables for many-to-many relationships
307
- * - Configuration and settings tables
308
- *
309
54
  * @example
310
- * **Basic entity with indexes:**
311
55
  * ```ts
56
+ * import { t } from "alepha";
312
57
  * import { $entity } from "alepha/postgres";
313
- * import { pg, t } from "alepha";
314
58
  *
315
- * const User = $entity({
59
+ * const userEntity = $entity({
316
60
  * name: "users",
317
61
  * schema: t.object({
318
- * id: pg.primaryKey(t.uuid()),
319
- * email: t.text({ format: "email" }),
320
- * username: t.text({ minLength: 3, maxLength: 30 }),
321
- * firstName: t.text(),
322
- * lastName: t.text(),
323
- * isActive: t.boolean({ default: true }),
324
- * createdAt: pg.createdAt(),
325
- * updatedAt: pg.updatedAt(),
326
- * deletedAt: pg.deletedAt()
327
- * }),
328
- * indexes: [
329
- * "email", // Simple index on email
330
- * "username", // Simple index on username
331
- * { column: "email", unique: true }, // Unique constraint on email
332
- * { columns: ["firstName", "lastName"] } // Composite index
333
- * ]
334
- * });
335
- * ```
336
- *
337
- * @example
338
- * **E-commerce product entity with relationships:**
339
- * ```ts
340
- * const Product = $entity({
341
- * name: "products",
342
- * schema: t.object({
343
- * id: pg.primaryKey(t.uuid()),
344
- * sku: t.text({ minLength: 3 }),
345
- * name: t.text({ minLength: 1, maxLength: 200 }),
346
- * description: t.optional(t.text()),
347
- * price: t.number({ minimum: 0 }),
348
- * categoryId: t.text({ format: "uuid" }),
349
- * inStock: t.boolean({ default: true }),
350
- * stockQuantity: t.integer({ minimum: 0, default: 0 }),
351
- * tags: t.optional(t.array(t.text())), // PostgreSQL array column
352
- * metadata: t.optional(t.record(t.text(), t.any())), // JSONB column
353
- * version: pg.version(),
354
- * createdAt: pg.createdAt(),
355
- * updatedAt: pg.updatedAt()
356
- * }),
357
- * indexes: [
358
- * { column: "sku", unique: true }, // Unique SKU
359
- * "categoryId", // Foreign key index
360
- * "inStock", // Filter frequently by stock status
361
- * { columns: ["categoryId", "inStock"] }, // Composite for category + stock queries
362
- * "createdAt" // For date-based queries
363
- * ],
364
- * foreignKeys: [
365
- * {
366
- * name: "fk_product_category",
367
- * columns: ["categoryId"],
368
- * foreignColumns: [Category.id] // Reference to Category entity
369
- * }
370
- * ]
371
- * });
372
- * ```
373
- *
374
- * @example
375
- * **Audit log entity with constraints:**
376
- * ```ts
377
- * const AuditLog = $entity({
378
- * name: "audit_logs",
379
- * schema: t.object({
380
- * id: pg.primaryKey(t.uuid()),
381
- * tableName: t.text(),
382
- * recordId: t.text(),
383
- * action: t.enum(["CREATE", "UPDATE", "DELETE"]),
384
- * userId: t.optional(t.text({ format: "uuid" })),
385
- * oldValues: t.optional(t.record(t.text(), t.any())),
386
- * newValues: t.optional(t.record(t.text(), t.any())),
387
- * timestamp: pg.createdAt(),
388
- * ipAddress: t.optional(t.text()),
389
- * userAgent: t.optional(t.text())
390
- * }),
391
- * indexes: [
392
- * "tableName",
393
- * "recordId",
394
- * "userId",
395
- * "action",
396
- * { columns: ["tableName", "recordId"] }, // Find all changes to a record
397
- * { columns: ["userId", "timestamp"] }, // User activity timeline
398
- * "timestamp" // Time-based queries
399
- * ],
400
- * constraints: [
401
- * {
402
- * name: "valid_action_values",
403
- * columns: ["action"],
404
- * check: sql`action IN ('CREATE', 'UPDATE', 'DELETE')`
405
- * }
406
- * ]
407
- * });
408
- * ```
409
- *
410
- * @example
411
- * **Many-to-many junction table:**
412
- * ```ts
413
- * const UserRole = $entity({
414
- * name: "user_roles",
415
- * schema: t.object({
416
- * id: pg.primaryKey(t.uuid()),
417
- * userId: t.text({ format: "uuid" }),
418
- * roleId: t.text({ format: "uuid" }),
419
- * assignedBy: t.text({ format: "uuid" }),
420
- * assignedAt: pg.createdAt(),
421
- * expiresAt: t.optional(t.datetime())
422
- * }),
423
- * indexes: [
424
- * "userId",
425
- * "roleId",
426
- * "assignedBy",
427
- * { columns: ["userId", "roleId"], unique: true }, // Prevent duplicate assignments
428
- * "expiresAt" // For cleanup of expired roles
429
- * ],
430
- * foreignKeys: [
431
- * {
432
- * columns: ["userId"],
433
- * foreignColumns: [User.id]
434
- * },
435
- * {
436
- * columns: ["roleId"],
437
- * foreignColumns: [Role.id]
438
- * },
439
- * {
440
- * columns: ["assignedBy"],
441
- * foreignColumns: [User.id]
442
- * }
443
- * ]
444
- * });
445
- * ```
446
- *
447
- * @example
448
- * **Entity with custom Drizzle configuration:**
449
- * ```ts
450
- * const Order = $entity({
451
- * name: "orders",
452
- * schema: t.object({
453
- * id: pg.primaryKey(t.uuid()),
454
- * orderNumber: t.text(),
455
- * customerId: t.text({ format: "uuid" }),
456
- * status: t.enum(["pending", "processing", "shipped", "delivered"]),
457
- * totalAmount: t.number({ minimum: 0 }),
458
- * currency: t.text({ default: "USD" }),
459
- * notes: t.optional(t.text()),
460
- * createdAt: pg.createdAt(),
461
- * updatedAt: pg.updatedAt(),
462
- * version: pg.version()
62
+ * id: pg.primaryKey(),
63
+ * name: t.text(),
64
+ * email: t.email(),
463
65
  * }),
464
- * indexes: [
465
- * { column: "orderNumber", unique: true },
466
- * "customerId",
467
- * "status",
468
- * "createdAt",
469
- * { columns: ["customerId", "status"] }
470
- * ],
471
- * // Advanced Drizzle ORM configuration
472
- * config: (table) => [
473
- * // Custom index with specific options
474
- * index("idx_orders_amount_status")
475
- * .on(table.totalAmount, table.status)
476
- * .where(sql`status != 'cancelled'`), // Partial index
477
- *
478
- * // Full-text search index (PostgreSQL specific)
479
- * index("idx_orders_search")
480
- * .using("gin", table.notes)
481
- * ]
482
66
  * });
483
67
  * ```
484
- *
485
- * @stability 2
486
68
  */
487
69
  declare const $entity: {
488
- <TTableName extends string, TSchema extends TObject, TColumnsMap extends FromSchema<TSchema>>(options: EntityDescriptorOptions<TTableName, TSchema>): PgTableWithColumnsAndSchema<PgTableConfig<TTableName, TSchema, TColumnsMap>, TSchema>;
489
- [KIND]: string;
70
+ <TSchema$1 extends TObject>(options: EntityDescriptorOptions<TSchema$1>): EntityDescriptor<TSchema$1>;
71
+ [KIND]: typeof EntityDescriptor;
490
72
  };
491
- interface EntityDescriptorOptions<TTableName extends string, T extends TObject, Keys = keyof Static<T>> {
73
+ interface EntityDescriptorOptions<T extends TObject, Keys = keyof Static<T>> {
492
74
  /**
493
75
  * The database table name that will be created for this entity.
494
- *
495
- * This name:
496
- * - Must be unique within your database schema
497
- * - Should follow your database naming conventions (typically snake_case)
498
- * - Will be used in generated SQL queries and migrations
499
- * - Should be descriptive of the entity's purpose
500
- *
501
- * **Naming Guidelines**:
502
- * - Use plural nouns for table names ("users", "products", "orders")
503
- * - Use snake_case for multi-word names ("user_profiles", "order_items")
504
- * - Keep names concise but descriptive
505
- * - Avoid SQL reserved words
506
- *
507
- * @example "users"
508
- * @example "product_categories"
509
- * @example "user_roles"
510
- * @example "audit_logs"
76
+ * If not provided, name will be inferred from the $repository variable name.
511
77
  */
512
- name: TTableName;
78
+ name: string;
513
79
  /**
514
80
  * TypeBox schema defining the table structure and column types.
515
- *
516
- * This schema:
517
- * - Defines all table columns with their types and constraints
518
- * - Provides full TypeScript type inference for the entity
519
- * - Supports validation rules and default values
520
- * - Enables automatic insert/update schema generation
521
- * - Must include exactly one primary key field marked with `pg.primaryKey()`
522
- *
523
- * **Supported PostgreSQL Types**:
524
- * - `pg.primaryKey(t.uuid())` - UUID primary key
525
- * - `t.text()` - VARCHAR column
526
- * - `t.integer()`, `t.number()` - Numeric columns
527
- * - `t.boolean()` - Boolean column
528
- * - `t.array(t.text())` - PostgreSQL array column
529
- * - `t.record(t.text(), t.any())` - JSONB column
530
- * - `pg.createdAt()`, `pg.updatedAt()`, `pg.deletedAt()` - Audit timestamps
531
- * - `pg.version()` - Optimistic locking version field
532
- *
533
- * **Schema Best Practices**:
534
- * - Always include a primary key
535
- * - Use appropriate TypeBox constraints (minLength, format, etc.)
536
- * - Add audit fields for trackability
537
- * - Use optional fields for nullable columns
538
- * - Include foreign key columns for relationships
539
- *
540
- * @example
541
- * ```ts
542
- * t.object({
543
- * id: pg.primaryKey(t.uuid()),
544
- * email: t.text({ format: "email" }),
545
- * firstName: t.text({ minLength: 1, maxLength: 100 }),
546
- * lastName: t.text({ minLength: 1, maxLength: 100 }),
547
- * age: t.optional(t.integer({ minimum: 0, maximum: 150 })),
548
- * isActive: t.boolean({ default: true }),
549
- * preferences: t.optional(t.record(t.text(), t.any())),
550
- * tags: t.optional(t.array(t.text())),
551
- * createdAt: pg.createdAt(),
552
- * updatedAt: pg.updatedAt(),
553
- * version: pg.version()
554
- * })
555
- * ```
556
81
  */
557
82
  schema: T;
558
83
  /**
559
84
  * Database indexes to create for query optimization.
560
- *
561
- * Indexes improve query performance but consume disk space and slow down writes.
562
- * Choose indexes based on your actual query patterns and performance requirements.
563
- *
564
- * **Index Types**:
565
- * - **Simple string**: Creates a single-column index
566
- * - **Single column object**: Creates index on one column with options
567
- * - **Multi-column object**: Creates composite index on multiple columns
568
- *
569
- * **Index Guidelines**:
570
- * - Index frequently queried columns (WHERE, ORDER BY, JOIN conditions)
571
- * - Create unique indexes for business constraints
572
- * - Use composite indexes for multi-column queries
573
- * - Index foreign key columns for join performance
574
- * - Monitor index usage and remove unused indexes
575
- *
576
- * **Performance Considerations**:
577
- * - Each index increases storage requirements
578
- * - Indexes slow down INSERT/UPDATE/DELETE operations
579
- * - PostgreSQL can use multiple indexes in complex queries
580
- * - Partial indexes can be more efficient for filtered queries
581
- *
582
- * @example ["email", "createdAt", { column: "username", unique: true }]
583
- * @example [{ columns: ["userId", "status"], name: "idx_user_status" }]
584
- * @example ["categoryId", { columns: ["price", "inStock"] }]
585
85
  */
586
86
  indexes?: (Keys | {
587
87
  /**
@@ -612,35 +112,6 @@ interface EntityDescriptorOptions<TTableName extends string, T extends TObject,
612
112
  })[];
613
113
  /**
614
114
  * Foreign key constraints to maintain referential integrity.
615
- *
616
- * Foreign keys ensure that values in specified columns must exist in the referenced table.
617
- * They prevent orphaned records and maintain database consistency.
618
- *
619
- * **Foreign Key Benefits**:
620
- * - Prevents invalid references to non-existent records
621
- * - Maintains data integrity automatically
622
- * - Provides clear schema documentation of relationships
623
- * - Enables cascade operations (DELETE, UPDATE)
624
- *
625
- * **Considerations**:
626
- * - Foreign keys can impact performance on large tables
627
- * - They prevent deletion of referenced records
628
- * - Consider cascade options for related data cleanup
629
- *
630
- * @example
631
- * ```ts
632
- * foreignKeys: [
633
- * {
634
- * name: "fk_user_role",
635
- * columns: ["roleId"],
636
- * foreignColumns: [Role.id]
637
- * },
638
- * {
639
- * columns: ["createdBy"],
640
- * foreignColumns: [User.id]
641
- * }
642
- * ]
643
- * ```
644
115
  */
645
116
  foreignKeys?: Array<{
646
117
  /**
@@ -653,8 +124,9 @@ interface EntityDescriptorOptions<TTableName extends string, T extends TObject,
653
124
  columns: Array<keyof Static<T>>;
654
125
  /**
655
126
  * Referenced columns in the foreign table.
127
+ * Must be EntityColumn references from other entities.
656
128
  */
657
- foreignColumns: Array<AnyPgColumn>;
129
+ foreignColumns: Array<() => EntityColumn<any>>;
658
130
  }>;
659
131
  /**
660
132
  * Additional table constraints for data validation.
@@ -666,12 +138,6 @@ interface EntityDescriptorOptions<TTableName extends string, T extends TObject,
666
138
  * - **Unique constraints**: Prevent duplicate values across columns
667
139
  * - **Check constraints**: Enforce custom validation rules with SQL expressions
668
140
  *
669
- * **Use Cases**:
670
- * - Enforce unique combinations of columns
671
- * - Validate value ranges or patterns
672
- * - Ensure consistent data states
673
- * - Implement business rule validation
674
- *
675
141
  * @example
676
142
  * ```ts
677
143
  * constraints: [
@@ -713,52 +179,86 @@ interface EntityDescriptorOptions<TTableName extends string, T extends TObject,
713
179
  }>;
714
180
  /**
715
181
  * Advanced Drizzle ORM configuration for complex table setups.
716
- *
717
- * This allows you to use advanced Drizzle ORM features that aren't covered
718
- * by the simplified options above. Use this for:
719
- * - Custom index types (GIN, GIST, etc.)
720
- * - Partial indexes with WHERE clauses
721
- * - Advanced constraint configurations
722
- * - PostgreSQL-specific features
723
- *
724
- * **When to Use**:
725
- * - Need PostgreSQL-specific index types
726
- * - Require partial indexes for performance
727
- * - Want fine-grained control over table creation
728
- * - Using advanced PostgreSQL features
729
- *
730
- * See Drizzle ORM documentation for complete configuration options.
731
- *
732
- * @param self - The table columns available for configuration
733
- * @returns Array of Drizzle table configuration objects
734
- *
735
- * @example
736
- * ```ts
737
- * config: (table) => [
738
- * // Partial index for active users only
739
- * index("idx_active_users_email")
740
- * .on(table.email)
741
- * .where(sql`is_active = true`),
742
- *
743
- * // GIN index for full-text search
744
- * index("idx_content_search")
745
- * .using("gin", table.searchVector),
746
- *
747
- * // Unique constraint with custom options
748
- * uniqueIndex("idx_unique_slug_per_tenant")
749
- * .on(table.tenantId, table.slug)
750
- * ]
751
- * ```
752
182
  */
753
183
  config?: (self: BuildExtraConfigColumns<string, FromSchema<T>, "pg">) => PgTableExtraConfigValue[];
754
184
  }
755
- type Entity<T extends TObject> = PgTableWithColumnsAndSchema<PgTableConfig<string, T, FromSchema<T>>, T>;
756
- type PgTableConfig<TTableName extends string, TSchema extends TObject, TColumnsMap extends FromSchema<TSchema>> = {
757
- name: TTableName;
758
- schema: any;
759
- columns: BuildColumns<TTableName, TColumnsMap, "pg">;
760
- dialect: "pg";
185
+ declare class EntityDescriptor<T extends TObject = TObject> {
186
+ readonly options: EntityDescriptorOptions<T>;
187
+ constructor(options: EntityDescriptorOptions<T>);
188
+ alias(alias: string): this;
189
+ get cols(): EntityColumns<T>;
190
+ get name(): string;
191
+ get schema(): T;
192
+ get insertSchema(): TObjectInsert<T>;
193
+ get updateSchema(): TObjectUpdate<T>;
194
+ }
195
+ /**
196
+ * Convert a schema to columns.
197
+ */
198
+ type FromSchema<T extends TObject> = { [key in keyof T["properties"]]: PgColumnBuilderBase };
199
+ type SchemaToTableConfig<T extends TObject> = {
200
+ name: string;
201
+ schema: string | undefined;
202
+ columns: { [key in keyof T["properties"]]: PgColumn };
203
+ dialect: string;
204
+ };
205
+ type EntityColumn<T extends TObject> = {
206
+ name: string;
207
+ entity: EntityDescriptor<T>;
208
+ };
209
+ type EntityColumns<T extends TObject> = { [key in keyof T["properties"]]: EntityColumn<T> };
210
+ //#endregion
211
+ //#region src/constants/PG_SYMBOLS.d.ts
212
+ declare const PG_DEFAULT: unique symbol;
213
+ declare const PG_PRIMARY_KEY: unique symbol;
214
+ declare const PG_CREATED_AT: unique symbol;
215
+ declare const PG_UPDATED_AT: unique symbol;
216
+ declare const PG_DELETED_AT: unique symbol;
217
+ declare const PG_VERSION: unique symbol;
218
+ declare const PG_IDENTITY: unique symbol;
219
+ declare const PG_ENUM: unique symbol;
220
+ declare const PG_REF: unique symbol;
221
+ /**
222
+ * @deprecated Use `PG_IDENTITY` instead.
223
+ */
224
+ declare const PG_SERIAL: unique symbol;
225
+ type PgDefault = typeof PG_DEFAULT;
226
+ type PgRef = typeof PG_REF;
227
+ type PgPrimaryKey = typeof PG_PRIMARY_KEY;
228
+ type PgSymbols = {
229
+ [PG_DEFAULT]: {};
230
+ [PG_PRIMARY_KEY]: {};
231
+ [PG_CREATED_AT]: {};
232
+ [PG_UPDATED_AT]: {};
233
+ [PG_DELETED_AT]: {};
234
+ [PG_VERSION]: {};
235
+ [PG_IDENTITY]: PgIdentityOptions;
236
+ [PG_REF]: PgRefOptions;
237
+ [PG_ENUM]: PgEnumOptions;
238
+ /**
239
+ * @deprecated Use `PG_IDENTITY` instead.
240
+ */
241
+ [PG_SERIAL]: {};
242
+ };
243
+ type PgSymbolKeys = keyof PgSymbols;
244
+ type PgIdentityOptions = {
245
+ mode: "always" | "byDefault";
246
+ } & PgSequenceOptions & {
247
+ name?: string;
761
248
  };
249
+ interface PgEnumOptions {
250
+ name?: string;
251
+ }
252
+ interface PgRefOptions {
253
+ ref: () => {
254
+ name: string;
255
+ entity: EntityDescriptor;
256
+ };
257
+ actions?: {
258
+ onUpdate?: UpdateDeleteAction;
259
+ onDelete?: UpdateDeleteAction;
260
+ };
261
+ }
762
262
  //#endregion
763
263
  //#region src/errors/PgError.d.ts
764
264
  declare class PgError extends AlephaError {
@@ -782,7 +282,7 @@ declare class PgError extends AlephaError {
782
282
  * );
783
283
  * ```
784
284
  */
785
- declare const pgAttr: <T extends TSchema$1, Attr extends PgSymbolKeys>(type: T, attr: Attr, value?: PgSymbols[Attr]) => PgAttr<T, Attr>;
285
+ declare const pgAttr: <T extends TSchema, Attr extends PgSymbolKeys>(type: T, attr: Attr, value?: PgSymbols[Attr]) => PgAttr<T, Attr>;
786
286
  /**
787
287
  * Retrieves the fields of a schema that have a specific attribute.
788
288
  */
@@ -790,10 +290,10 @@ declare const getAttrFields: (schema: TObject, name: PgSymbolKeys) => PgAttrFiel
790
290
  /**
791
291
  * Type representation.
792
292
  */
793
- type PgAttr<T extends TSchema$1, TAttr extends PgSymbolKeys> = T & { [K in TAttr]: PgSymbols[K] };
293
+ type PgAttr<T extends TSchema, TAttr extends PgSymbolKeys> = T & { [K in TAttr]: PgSymbols[K] };
794
294
  interface PgAttrField {
795
295
  key: string;
796
- type: TSchema$1;
296
+ type: TSchema;
797
297
  data: any;
798
298
  nested?: any[];
799
299
  one?: boolean;
@@ -1156,7 +656,10 @@ interface FilterOperators<TValue> {
1156
656
  }
1157
657
  //#endregion
1158
658
  //#region src/interfaces/PgQueryWhere.d.ts
1159
- type PgQueryWhere<T extends TObject> = { [Key in keyof Static<T>]?: FilterOperators<Static<T>[Key]> | Static<T>[Key] } & {
659
+ type PgQueryWhere<T extends TObject, Relations extends PgRelationMap<TObject> | undefined = undefined> = (PgQueryWhereOperators<T> & PgQueryWhereConditions<T>) | (PgQueryWhereRelations<Relations> & PgQueryWhereOperators<T> & PgQueryWhereConditions<T, Relations>);
660
+ type PgQueryWhereOrSQL<T extends TObject, Relations extends PgRelationMap<TObject> | undefined = undefined> = SQLWrapper | PgQueryWhere<T, Relations>;
661
+ type PgQueryWhereOperators<T extends TObject> = { [Key in keyof Static<T>]?: FilterOperators<Static<T>[Key]> | Static<T>[Key] | (Static<T>[Key] extends object ? NestedJsonbQuery<Static<T>[Key]> : never) };
662
+ type PgQueryWhereConditions<T extends TObject, Relations extends PgRelationMap<TObject> | undefined = undefined> = {
1160
663
  /**
1161
664
  * Combine a list of conditions with the `and` operator. Conditions
1162
665
  * that are equal `undefined` are automatically ignored.
@@ -1173,7 +676,7 @@ type PgQueryWhere<T extends TObject> = { [Key in keyof Static<T>]?: FilterOperat
1173
676
  * )
1174
677
  * ```
1175
678
  */
1176
- and?: Array<PgQueryWhereOrSQL<T>>;
679
+ and?: Array<PgQueryWhereOrSQL<T, Relations>>;
1177
680
  /**
1178
681
  * Combine a list of conditions with the `or` operator. Conditions
1179
682
  * that are equal `undefined` are automatically ignored.
@@ -1190,7 +693,7 @@ type PgQueryWhere<T extends TObject> = { [Key in keyof Static<T>]?: FilterOperat
1190
693
  * )
1191
694
  * ```
1192
695
  */
1193
- or?: Array<PgQueryWhereOrSQL<T>>;
696
+ or?: Array<PgQueryWhereOrSQL<T, Relations>>;
1194
697
  /**
1195
698
  * Negate the meaning of an expression using the `not` keyword.
1196
699
  *
@@ -1202,7 +705,7 @@ type PgQueryWhere<T extends TObject> = { [Key in keyof Static<T>]?: FilterOperat
1202
705
  * .where(not(inArray(cars.make, ['GM', 'Ford'])))
1203
706
  * ```
1204
707
  */
1205
- not?: PgQueryWhereOrSQL<T>;
708
+ not?: PgQueryWhereOrSQL<T, Relations>;
1206
709
  /**
1207
710
  * Test whether a subquery evaluates to have any rows.
1208
711
  *
@@ -1225,26 +728,11 @@ type PgQueryWhere<T extends TObject> = { [Key in keyof Static<T>]?: FilterOperat
1225
728
  */
1226
729
  exists?: SQLWrapper;
1227
730
  };
1228
- type PgQueryWhereOrSQL<T extends TObject> = SQLWrapper | PgQueryWhere<T>;
1229
- type PgQueryWhereWithManyOrSQL<T extends TObject> = SQLWrapper | PgQueryWhereWithMany<T>;
1230
- type PgQueryWhereWithMany<T extends TObject> = PgQueryWhere<RemoveManyRelations<T>> & ExtractManyRelations<T>;
1231
- type RelField = {
1232
- [PG_MANY]: any;
1233
- } | {
1234
- [PG_ONE]: any;
1235
- };
1236
- type ExtractManyRelations<T extends TObject> = { [K in keyof T["properties"] as T["properties"][K] extends RelField ? T["properties"][K] extends TArray ? T["properties"][K]["items"] extends TObject ? K : never : never : T["properties"][K] extends TObject ? K : never]?: PgQueryWhere<T["properties"][K] extends TArray ? T["properties"][K]["items"] extends TObject ? T["properties"][K]["items"] : TObject : T["properties"][K] extends TObject ? T["properties"][K] : TObject> };
1237
- type RemoveManyRelations<T extends TObject> = TObject<{ [K in keyof T["properties"] as T["properties"][K] extends RelField ? never : K]: T["properties"][K] }>;
1238
- type PgQueryWithMap<T extends TObject> = { [K in keyof T["properties"] as T["properties"][K] extends RelField ? K : never]?: T["properties"][K] extends TArray ? T["properties"][K]["items"] extends TObject ? PgQueryWithMany<T["properties"][K]["items"]> : never : T["properties"][K] extends TObject ? PgQueryWithOne<T["properties"][K]> : T["properties"][K] extends PgAttr<PgAttr<TOptionalAdd<infer U>, PgOne>, PgDefault> ? U extends TObject ? PgQueryWithOne<U> : never : never };
1239
- type PgQueryWithOne<T extends TObject> = true | {
1240
- columns?: (keyof Static<T>)[];
1241
- relations?: PgQueryWithMap<T>;
1242
- };
1243
- type PgQueryWithMany<T extends TObject> = true | {
1244
- limit?: number;
1245
- columns?: (keyof Static<T>)[];
1246
- relations?: PgQueryWithMap<T>;
1247
- };
731
+ type PgQueryWhereRelations<Relations extends PgRelationMap<TObject> | undefined = undefined> = Relations extends PgRelationMap<TObject> ? { [K in keyof Relations]?: PgQueryWhere<Relations[K]["join"]["schema"], Relations[K]["with"]> } : {};
732
+ /**
733
+ * Recursively allow nested queries for JSONB object/array types
734
+ */
735
+ type NestedJsonbQuery<T> = T extends object ? T extends Array<infer U> ? U extends object ? { [K in keyof U]?: FilterOperators<U[K]> | U[K] } : FilterOperators<U> | U : { [K in keyof T]?: FilterOperators<T[K]> | T[K] | (T[K] extends object ? NestedJsonbQuery<T[K]> : never) } : FilterOperators<T> | T;
1248
736
  //#endregion
1249
737
  //#region src/interfaces/PgQuery.d.ts
1250
738
  /**
@@ -1265,32 +753,140 @@ interface OrderByClause<T> {
1265
753
  * 3. Array: orderBy: [{ column: "name", direction: "asc" }, { column: "age", direction: "desc" }]
1266
754
  */
1267
755
  type OrderBy<T> = keyof T | OrderByClause<T> | Array<OrderByClause<T>>;
756
+ /**
757
+ * Generic query interface for PostgreSQL entities
758
+ */
1268
759
  interface PgQuery<T extends TObject = TObject> {
1269
- distinct?: boolean;
1270
- where?: PgQueryWhereWithMany<T> | SQLWrapper;
760
+ distinct?: (keyof Static<T>)[];
761
+ columns?: (keyof Static<T>)[];
762
+ where?: PgQueryWhereOrSQL<T>;
1271
763
  limit?: number;
1272
764
  offset?: number;
1273
765
  orderBy?: OrderBy<Static<T>>;
1274
766
  groupBy?: (keyof Static<T>)[];
1275
- relations?: PgQueryWithMap<T>;
767
+ }
768
+ type PgStatic<T extends TObject, Relations extends PgRelationMap<T>> = Static<T> & { [K in keyof Relations]: Static<Relations[K]["join"]["schema"]> & (Relations[K]["with"] extends PgRelationMap<TObject> ? PgStatic<Relations[K]["join"]["schema"], Relations[K]["with"]> : {}) };
769
+ interface PgQueryRelations<T extends TObject = TObject, Relations extends PgRelationMap<T> | undefined = undefined> extends PgQuery<T> {
770
+ with?: Relations;
771
+ where?: PgQueryWhereOrSQL<T, Relations>;
772
+ }
773
+ type PgRelationMap<Base extends TObject> = Record<string, PgRelation<Base>>;
774
+ type PgRelation<Base extends TObject> = {
775
+ type?: "left" | "inner" | "right";
776
+ join: {
777
+ schema: TObject;
778
+ name: string;
779
+ };
780
+ on: SQLWrapper | [keyof Static<Base>, {
781
+ name: string;
782
+ }];
783
+ with?: PgRelationMap<TObject>;
784
+ };
785
+ //#endregion
786
+ //#region src/descriptors/$sequence.d.ts
787
+ /**
788
+ * Creates a PostgreSQL sequence descriptor for generating unique numeric values.
789
+ */
790
+ declare const $sequence: {
791
+ (options?: SequenceDescriptorOptions): SequenceDescriptor;
792
+ [KIND]: typeof SequenceDescriptor;
793
+ };
794
+ interface SequenceDescriptorOptions extends PgSequenceOptions {
795
+ /**
796
+ * The name of the sequence. If not provided, the property key will be used.
797
+ */
798
+ name?: string;
799
+ provider?: DatabaseProvider;
800
+ }
801
+ declare class SequenceDescriptor extends Descriptor<SequenceDescriptorOptions> {
802
+ readonly provider: DatabaseProvider;
803
+ onInit(): void;
804
+ get name(): string;
805
+ next(): Promise<number>;
806
+ current(): Promise<number>;
807
+ protected $provider(): DatabaseProvider;
1276
808
  }
1277
809
  //#endregion
1278
- //#region src/providers/drivers/PostgresProvider.d.ts
810
+ //#region src/services/ModelBuilder.d.ts
811
+ /**
812
+ * Database-specific table configuration functions
813
+ */
814
+ interface TableConfigBuilders<TConfig> {
815
+ index: (name: string) => {
816
+ on: (...columns: any[]) => TConfig;
817
+ };
818
+ uniqueIndex: (name: string) => {
819
+ on: (...columns: any[]) => TConfig;
820
+ };
821
+ unique: (name: string) => {
822
+ on: (...columns: any[]) => TConfig;
823
+ };
824
+ check: (name: string, sql: SQL) => TConfig;
825
+ foreignKey: (config: {
826
+ name: string;
827
+ columns: any[];
828
+ foreignColumns: any[];
829
+ }) => TConfig;
830
+ }
831
+ /**
832
+ * Abstract base class for transforming Alepha Descriptors (Entity, Sequence, etc...)
833
+ * into drizzle models (tables, enums, sequences, etc...).
834
+ */
835
+ declare abstract class ModelBuilder {
836
+ /**
837
+ * Build a table from an entity descriptor.
838
+ */
839
+ abstract buildTable(entity: EntityDescriptor, options: {
840
+ tables: Map<string, unknown>;
841
+ enums: Map<string, unknown>;
842
+ schema: string;
843
+ }): void;
844
+ /**
845
+ * Build a sequence from a sequence descriptor.
846
+ */
847
+ abstract buildSequence(sequence: SequenceDescriptor, options: {
848
+ sequences: Map<string, unknown>;
849
+ schema: string;
850
+ }): void;
851
+ /**
852
+ * Convert camelCase to snake_case for column names.
853
+ */
854
+ protected toColumnName(str: string): string;
855
+ /**
856
+ * Build the table configuration function for any database.
857
+ * This includes indexes, foreign keys, constraints, and custom config.
858
+ *
859
+ * @param entity - The entity descriptor
860
+ * @param builders - Database-specific builder functions
861
+ * @param tableResolver - Function to resolve entity references to table columns
862
+ * @param customConfigHandler - Optional handler for custom config
863
+ */
864
+ protected buildTableConfig<TConfig, TSelf>(entity: EntityDescriptor, builders: TableConfigBuilders<TConfig>, tableResolver?: (entityName: string) => any, customConfigHandler?: (config: any, self: TSelf) => TConfig[]): ((self: TSelf) => TConfig[]) | undefined;
865
+ }
866
+ //#endregion
867
+ //#region src/providers/drivers/DatabaseProvider.d.ts
1279
868
  type SQLLike = SQLWrapper | string;
1280
- declare abstract class PostgresProvider {
869
+ declare abstract class DatabaseProvider {
1281
870
  protected readonly alepha: Alepha;
1282
- abstract get db(): PgDatabase<any>;
1283
- readonly dialect: "postgres" | "sqlite";
871
+ protected abstract readonly builder: ModelBuilder;
872
+ abstract readonly db: PgDatabase<any>;
873
+ abstract readonly dialect: "postgres" | "sqlite";
874
+ readonly enums: Map<string, unknown>;
875
+ readonly tables: Map<string, unknown>;
876
+ readonly sequences: Map<string, unknown>;
877
+ table<T extends TObject>(entity: EntityDescriptor<T>): PgTableWithColumns<SchemaToTableConfig<T>>;
1284
878
  get schema(): string;
1285
- execute<T extends TObject | undefined>(query: SQLLike, schema?: T): Promise<Array<T extends TObject ? Static<T> : any>>;
1286
- protected mapResult<T extends TObject = any>(result: Array<any>, schema?: T): Array<T extends TObject ? Static<T> : any>;
879
+ registerEntity(entity: EntityDescriptor): void;
880
+ registerSequence(sequence: SequenceDescriptor): void;
881
+ abstract execute(statement: SQLLike): Promise<Record<string, unknown>[]>;
882
+ run<T extends TObject>(statement: SQLLike, schema: T): Promise<Array<Static<T>>>;
1287
883
  }
1288
884
  //#endregion
1289
885
  //#region src/schemas/pageQuerySchema.d.ts
1290
- declare const pageQuerySchema: typebox1.TObject<{
1291
- page: typebox1.TOptional<typebox1.TInteger>;
1292
- size: typebox1.TOptional<typebox1.TInteger>;
1293
- sort: typebox1.TOptional<typebox1.TString>;
886
+ declare const pageQuerySchema: typebox9.TObject<{
887
+ page: typebox9.TOptional<typebox9.TInteger>;
888
+ size: typebox9.TOptional<typebox9.TInteger>;
889
+ sort: typebox9.TOptional<typebox9.TString>;
1294
890
  }>;
1295
891
  type PageQuery = Static<typeof pageQuerySchema>;
1296
892
  //#endregion
@@ -1298,9 +894,13 @@ type PageQuery = Static<typeof pageQuerySchema>;
1298
894
  /**
1299
895
  * Create a pagination schema for the given object schema.
1300
896
  *
897
+ * > use `pg.page` directly.
898
+ *
1301
899
  * @example
900
+ * ```ts
1302
901
  * const userSchema = t.object({ id: t.int(), name: t.text() });
1303
- * const pagedUserSchema = pageSchema(userSchema);
902
+ * const userPageSchema = pageSchema(userSchema);
903
+ * ```
1304
904
  *
1305
905
  * @see {@link $repository#paginate}
1306
906
  */
@@ -1317,106 +917,218 @@ type TPage<T extends TObject | TRecord> = TObject<{
1317
917
  totalElements: TOptionalAdd<TInteger>;
1318
918
  }>;
1319
919
  }>;
920
+ /**
921
+ * Opinionated type definition for a paginated response.
922
+ *
923
+ * @example
924
+ * ```ts
925
+ * const page = {
926
+ * content: [ ... ]
927
+ * can: {
928
+ * next: true,
929
+ * previous: false
930
+ * },
931
+ * page: {
932
+ * number: 0,
933
+ * size: 10,
934
+ * totalElements: 1200
935
+ * }
936
+ * }
937
+ * ```
938
+ */
1320
939
  type Page<T> = {
940
+ /**
941
+ * Array of items on the current page.
942
+ */
1321
943
  content: T[];
1322
944
  can: {
945
+ /**
946
+ * Indicates if there is a next page.
947
+ */
1323
948
  next: boolean;
949
+ /**
950
+ * Indicates if there is a previous page.
951
+ */
1324
952
  previous: boolean;
1325
953
  };
1326
954
  page: {
955
+ /**
956
+ * Page number, starting from 0.
957
+ */
1327
958
  number: number;
959
+ /**
960
+ * Number of items per page.
961
+ */
1328
962
  size: number;
963
+ /**
964
+ * Requires `count: true` in the paginate options.
965
+ */
1329
966
  totalElements?: number;
1330
967
  };
1331
968
  };
1332
969
  //#endregion
970
+ //#region src/services/PgJsonQueryManager.d.ts
971
+ /**
972
+ * Manages JSONB query generation for nested object and array queries in PostgreSQL.
973
+ * This class handles complex nested queries using PostgreSQL's JSONB operators.
974
+ */
975
+ declare class PgJsonQueryManager {
976
+ /**
977
+ * Check if a query contains nested JSONB queries.
978
+ * A nested query is when the value is an object with operator keys.
979
+ */
980
+ hasNestedQuery(where: PgQueryWhere<TObject>): boolean;
981
+ /**
982
+ * Build a JSONB query condition for nested object queries.
983
+ * Supports deep nesting like: { profile: { contact: { email: { eq: "test@example.com" } } } }
984
+ *
985
+ * @param column The JSONB column
986
+ * @param path The path to the nested property (e.g., ['profile', 'contact', 'email'])
987
+ * @param operator The filter operator (e.g., { eq: "test@example.com" })
988
+ * @returns SQL condition
989
+ */
990
+ buildJsonbCondition(column: PgColumn, path: string[], operator: FilterOperators<any>): SQL | undefined;
991
+ /**
992
+ * Build JSONB array query conditions.
993
+ * Supports queries like: { addresses: { city: { eq: "Wonderland" } } }
994
+ * which translates to: EXISTS (SELECT 1 FROM jsonb_array_elements(addresses) elem WHERE elem->>'city' = 'Wonderland')
995
+ */
996
+ buildJsonbArrayCondition(column: PgColumn, path: string[], arrayPath: string, operator: FilterOperators<any>): SQL | undefined;
997
+ /**
998
+ * Apply a filter operator to a JSONB value.
999
+ */
1000
+ private applyOperatorToJsonValue;
1001
+ /**
1002
+ * Parse a nested query object and extract the path and operator.
1003
+ * For example: { profile: { contact: { email: { eq: "test@example.com" } } } }
1004
+ * Returns: { path: ['profile', 'contact', 'email'], operator: { eq: "test@example.com" } }
1005
+ */
1006
+ parseNestedQuery(nestedQuery: any, currentPath?: string[]): Array<{
1007
+ path: string[];
1008
+ operator: FilterOperators<any>;
1009
+ }>;
1010
+ /**
1011
+ * Determine if a property is a JSONB column based on the schema.
1012
+ * A column is JSONB if it's defined as an object or array in the TypeBox schema.
1013
+ */
1014
+ isJsonbColumn(schema: TObject, columnName: string): boolean;
1015
+ /**
1016
+ * Check if an array property contains primitive types (string, number, boolean, etc.)
1017
+ * rather than objects. Primitive arrays should use native Drizzle operators.
1018
+ * @returns true if the array contains primitives, false if it contains objects
1019
+ */
1020
+ isPrimitiveArray(schema: TObject, columnName: string): boolean;
1021
+ /**
1022
+ * Check if a nested path points to an array property.
1023
+ */
1024
+ isArrayProperty(schema: TObject, path: string[]): boolean;
1025
+ }
1026
+ //#endregion
1027
+ //#region src/services/PgQueryManager.d.ts
1028
+ declare class PgQueryManager {
1029
+ protected readonly jsonQueryManager: PgJsonQueryManager;
1030
+ protected readonly alepha: Alepha;
1031
+ /**
1032
+ * Convert a query object to a SQL query.
1033
+ */
1034
+ toSQL(query: PgQueryWhereOrSQL<TObject>, options: {
1035
+ schema: TObject;
1036
+ col: (key: string) => PgColumn;
1037
+ joins?: PgJoin[];
1038
+ }): SQL | undefined;
1039
+ /**
1040
+ * Build a JSONB query for nested object/array queries.
1041
+ */
1042
+ protected buildJsonbQuery(column: PgColumn, nestedQuery: any, schema: TObject, columnName: string): SQL | undefined;
1043
+ /**
1044
+ * Check if an object has any filter operator properties.
1045
+ */
1046
+ protected hasFilterOperatorProperties(obj: any): boolean;
1047
+ /**
1048
+ * Map a filter operator to a SQL query.
1049
+ */
1050
+ mapOperatorToSql(operator: FilterOperators<any> | any, column: PgColumn, columnSchema?: TObject, columnName?: string): SQL | undefined;
1051
+ /**
1052
+ * Parse pagination sort string to orderBy format.
1053
+ * Format: "firstName,-lastName" -> [{ column: "firstName", direction: "asc" }, { column: "lastName", direction: "desc" }]
1054
+ * - Columns separated by comma
1055
+ * - Prefix with '-' for DESC direction
1056
+ *
1057
+ * @param sort Pagination sort string
1058
+ * @returns OrderBy array or single object
1059
+ */
1060
+ parsePaginationSort(sort: string): Array<{
1061
+ column: string;
1062
+ direction: "asc" | "desc";
1063
+ }> | {
1064
+ column: string;
1065
+ direction: "asc" | "desc";
1066
+ };
1067
+ /**
1068
+ * Normalize orderBy parameter to array format.
1069
+ * Supports 3 modes:
1070
+ * 1. String: "name" -> [{ column: "name", direction: "asc" }]
1071
+ * 2. Object: { column: "name", direction: "desc" } -> [{ column: "name", direction: "desc" }]
1072
+ * 3. Array: [{ column: "name" }, { column: "age", direction: "desc" }] -> normalized array
1073
+ *
1074
+ * @param orderBy The orderBy parameter
1075
+ * @returns Normalized array of order by clauses
1076
+ */
1077
+ normalizeOrderBy(orderBy: any): Array<{
1078
+ column: string;
1079
+ direction: "asc" | "desc";
1080
+ }>;
1081
+ /**
1082
+ * Create a pagination object.
1083
+ *
1084
+ * @param entities The entities to paginate.
1085
+ * @param limit The limit of the pagination.
1086
+ * @param offset The offset of the pagination.
1087
+ */
1088
+ createPagination<T>(entities: T[], limit?: number, offset?: number): Page<T>;
1089
+ }
1090
+ interface PgJoin {
1091
+ table: string;
1092
+ schema: TObject;
1093
+ key: string;
1094
+ col: (key: string) => PgColumn;
1095
+ parent?: string;
1096
+ }
1097
+ //#endregion
1098
+ //#region src/services/PgRelationManager.d.ts
1099
+ declare class PgRelationManager {
1100
+ /**
1101
+ * Recursively build joins for the query builder based on the relations map
1102
+ */
1103
+ buildJoins(provider: DatabaseProvider, builder: PgSelectBase<any, any, any>, joins: Array<PgJoin>, withRelations: PgRelationMap<TObject>, table: PgTableWithColumns<any>, parentKey?: string): void;
1104
+ /**
1105
+ * Map a row with its joined relations based on the joins definition
1106
+ */
1107
+ mapRowWithJoins(record: Record<string, unknown>, row: Record<string, unknown>, schema: TObject, joins: PgJoin[], parentKey?: string): Record<string, unknown>;
1108
+ /**
1109
+ * Check if all values in an object are null (indicates a left join with no match)
1110
+ */
1111
+ private isAllNull;
1112
+ /**
1113
+ * Build a schema that includes all join properties recursively
1114
+ */
1115
+ buildSchemaWithJoins(baseSchema: TObject, joins: PgJoin[], parentPath?: string): TObject;
1116
+ }
1117
+ //#endregion
1333
1118
  //#region src/descriptors/$repository.d.ts
1334
1119
  /**
1335
- * Creates a repository descriptor for database operations on a defined entity.
1120
+ * Creates a repository for database operations on a defined entity.
1336
1121
  *
1337
1122
  * This descriptor provides a comprehensive, type-safe interface for performing all
1338
1123
  * database operations on entities defined with $entity. It offers a rich set of
1339
1124
  * CRUD operations, advanced querying capabilities, pagination, transactions, and
1340
1125
  * built-in support for audit trails and soft deletes.
1341
- *
1342
- * **Key Features**
1343
- *
1344
- * - **Complete CRUD Operations**: Create, read, update, delete with full type safety
1345
- * - **Advanced Querying**: Complex WHERE conditions, sorting, pagination, and aggregations
1346
- * - **Transaction Support**: Database transactions for consistency and atomicity
1347
- * - **Soft Delete Support**: Built-in soft delete functionality with `pg.deletedAt()` fields
1348
- * - **Optimistic Locking**: Version-based conflict resolution with `pg.version()` fields
1349
- * - **Audit Trail Integration**: Automatic handling of `createdAt`, `updatedAt` timestamps
1350
- * - **Raw SQL Support**: Execute custom SQL queries when needed
1351
- * - **Pagination**: Built-in pagination with metadata and navigation
1352
- *
1353
- * **Important Requirements**
1354
- * - Must be used with an entity created by $entity
1355
- * - Entity schema must include exactly one primary key field
1356
- * - Database tables must be created via migrations before use
1357
- *
1358
- * **Use Cases**
1359
- *
1360
- * Essential for all database-driven applications:
1361
- * - User management and authentication systems
1362
- * - E-commerce product and order management
1363
- * - Content management and blogging platforms
1364
- * - Financial and accounting applications
1365
- * - Any application requiring persistent data storage
1366
- *
1367
- * @example
1368
- * **Basic repository with CRUD operations:**
1369
- * ```ts
1370
- * import { $entity, $repository } from "alepha/postgres";
1371
- * import { pg, t } from "alepha";
1372
- *
1373
- * // First, define the entity
1374
- * const User = $entity({
1375
- * name: "users",
1376
- * schema: t.object({
1377
- * id: pg.primaryKey(t.uuid()),
1378
- * email: t.text({ format: "email" }),
1379
- * firstName: t.text(),
1380
- * lastName: t.text(),
1381
- * isActive: t.boolean({ default: true }),
1382
- * createdAt: pg.createdAt(),
1383
- * updatedAt: pg.updatedAt()
1384
- * }),
1385
- * indexes: [{ column: "email", unique: true }]
1386
- * });
1387
- *
1388
- * class UserService {
1389
- * users = $repository({ table: User });
1390
- *
1391
- * async createUser(userData: { email: string; firstName: string; lastName: string }) {
1392
- * return await this.users.create({
1393
- * id: generateUUID(),
1394
- * email: userData.email,
1395
- * firstName: userData.firstName,
1396
- * lastName: userData.lastName,
1397
- * isActive: true
1398
- * });
1399
- * }
1400
- *
1401
- * async getUserByEmail(email: string) {
1402
- * return await this.users.findOne({ email });
1403
- * }
1404
- *
1405
- * async updateUser(id: string, updates: { firstName?: string; lastName?: string }) {
1406
- * return await this.users.updateById(id, updates);
1407
- * }
1408
- *
1409
- * async deactivateUser(id: string) {
1410
- * return await this.users.updateById(id, { isActive: false });
1411
- * }
1412
- * }
1413
- * ```
1414
1126
  */
1415
1127
  declare const $repository: {
1416
- <EntityTableConfig extends TableConfig, EntitySchema extends TObject>(optionsOrTable: RepositoryDescriptorOptions<EntityTableConfig, EntitySchema> | PgTableWithColumnsAndSchema<EntityTableConfig, EntitySchema>): RepositoryDescriptor<EntityTableConfig, EntitySchema>;
1128
+ <T extends TObject>(optionsOrEntity: EntityDescriptor<T> | EntityDescriptorOptions<T> | RepositoryDescriptorOptions<T>): RepositoryDescriptor<T>;
1417
1129
  [KIND]: typeof RepositoryDescriptor;
1418
1130
  };
1419
- interface RepositoryDescriptorOptions<EntityTableConfig extends TableConfig, EntitySchema extends TObject> {
1131
+ interface RepositoryDescriptorOptions<T extends TObject> {
1420
1132
  /**
1421
1133
  * The entity table definition created with $entity.
1422
1134
  *
@@ -1451,7 +1163,7 @@ interface RepositoryDescriptorOptions<EntityTableConfig extends TableConfig, Ent
1451
1163
  * const userRepository = $repository({ table: User });
1452
1164
  * ```
1453
1165
  */
1454
- table: PgTableWithColumnsAndSchema<EntityTableConfig, EntitySchema>;
1166
+ entity: EntityDescriptor<T>;
1455
1167
  /**
1456
1168
  * Override the default PostgreSQL database provider.
1457
1169
  *
@@ -1474,14 +1186,16 @@ interface RepositoryDescriptorOptions<EntityTableConfig extends TableConfig, Ent
1474
1186
  * @example TenantSpecificPostgresProvider
1475
1187
  * @example TestDatabaseProvider
1476
1188
  */
1477
- provider?: Service<PostgresProvider>;
1189
+ provider?: DatabaseProvider;
1190
+ name?: string;
1478
1191
  }
1479
- declare class RepositoryDescriptor<EntityTableConfig extends TableConfig, EntitySchema extends TObject> extends Descriptor<RepositoryDescriptorOptions<EntityTableConfig, EntitySchema>> {
1192
+ declare class RepositoryDescriptor<T extends TObject = TObject> extends Descriptor<RepositoryDescriptorOptions<T>> {
1193
+ protected readonly relationManager: PgRelationManager;
1194
+ protected readonly queryManager: PgQueryManager;
1480
1195
  protected readonly dateTimeProvider: DateTimeProvider;
1481
1196
  protected readonly alepha: Alepha;
1482
- readonly provider: PostgresProvider;
1483
- readonly schema: EntitySchema;
1484
- readonly schemaInsert: TObjectInsert<EntitySchema>;
1197
+ readonly provider: DatabaseProvider;
1198
+ constructor(args: DescriptorArgs<RepositoryDescriptorOptions<T>>);
1485
1199
  /**
1486
1200
  * Represents the primary key of the table.
1487
1201
  * - Key is the name of the primary key column.
@@ -1489,15 +1203,15 @@ declare class RepositoryDescriptor<EntityTableConfig extends TableConfig, Entity
1489
1203
  *
1490
1204
  * ID is mandatory. If the table does not have a primary key, it will throw an error.
1491
1205
  */
1492
- readonly id: {
1493
- type: TSchema$1;
1494
- key: keyof EntitySchema["properties"];
1206
+ get id(): {
1207
+ type: TSchema;
1208
+ key: keyof T["properties"];
1495
1209
  col: PgColumn;
1496
1210
  };
1497
1211
  /**
1498
1212
  * Get Drizzle table object.
1499
1213
  */
1500
- get table(): PgTableWithColumns<EntityTableConfig>;
1214
+ get table(): PgTableWithColumns<SchemaToTableConfig<T>>;
1501
1215
  /**
1502
1216
  * Get SQL table name. (from Drizzle table object)
1503
1217
  */
@@ -1505,87 +1219,104 @@ declare class RepositoryDescriptor<EntityTableConfig extends TableConfig, Entity
1505
1219
  /**
1506
1220
  * Getter for the database connection from the database provider.
1507
1221
  */
1508
- protected get db(): PgDatabase<any, Record<string, never>, drizzle_orm6.ExtractTablesWithRelations<Record<string, never>>>;
1222
+ protected get db(): PgDatabase<any>;
1509
1223
  /**
1510
1224
  * Execute a SQL query.
1225
+ *
1226
+ * This method allows executing raw SQL queries against the database.
1227
+ * This is by far the easiest way to run custom queries that are not covered by the repository's built-in methods!
1228
+ *
1229
+ * You must use the `sql` tagged template function from Drizzle ORM to create the query. https://orm.drizzle.team/docs/sql
1230
+ *
1231
+ * @example
1232
+ * ```ts
1233
+ * class App {
1234
+ * repository = $repository({ ... });
1235
+ * async getAdults() {
1236
+ * const users = repository.table; // Drizzle table object
1237
+ * await repository.query(sql`SELECT * FROM ${users} WHERE ${users.age} > ${18}`);
1238
+ * // or better
1239
+ * await repository.query((users) => sql`SELECT * FROM ${users} WHERE ${users.age} > ${18}`);
1240
+ * }
1241
+ * }
1242
+ * ```
1243
+ */
1244
+ query<R extends TObject = T>(query: SQLLike | ((table: PgTableWithColumns<SchemaToTableConfig<T>>, db: PgDatabase<any>) => SQLLike), schema?: R): Promise<Static<R>[]>;
1245
+ /**
1246
+ * Map raw database fields to entity fields. (handles column name differences)
1511
1247
  */
1512
- query<T extends TObject = EntitySchema>(query: SQLLike | ((table: PgTableWithColumns<EntityTableConfig>, db: PgDatabase<any>) => SQLLike), schema?: T): Promise<Static<T>[]>;
1513
- protected mapRawFieldsToEntity(row: any[]): any;
1248
+ protected mapRawFieldsToEntity(row: Record<string, unknown>): any;
1514
1249
  /**
1515
1250
  * Get a Drizzle column from the table by his name.
1516
- *
1517
- * @param name - The name of the column to get.
1518
- * @returns The column from the table.
1519
1251
  */
1520
- protected col(name: keyof PgTableWithColumns<EntityTableConfig>["_"]["columns"]): PgColumn;
1252
+ protected col(name: keyof StaticEncode<T>): PgColumn;
1521
1253
  /**
1522
1254
  * Run a transaction.
1523
- *
1524
- * @param transaction
1525
- * @param config
1526
1255
  */
1527
1256
  transaction<T>(transaction: (tx: PgTransaction<any, Record<string, any>, any>) => Promise<T>, config?: PgTransactionConfig): Promise<T>;
1528
1257
  /**
1529
1258
  * Start a SELECT query on the table.
1530
- *
1531
- * @returns The SELECT query builder.
1532
1259
  */
1533
- protected select(opts?: StatementOptions): pg$1.PgSelectBase<string, Record<string, PgColumn<drizzle_orm6.ColumnBaseConfig<drizzle_orm6.ColumnDataType, string>, {}, {}>>, "single", Record<string, "not-null">, false, never, {
1260
+ protected select(opts?: StatementOptions): drizzle_orm_pg_core0.PgSelectBase<string, Record<string, PgColumn<drizzle_orm0.ColumnBaseConfig<drizzle_orm0.ColumnDataType, string>, {}, {}>>, "single", Record<string, "not-null">, false, never, {
1534
1261
  [x: string]: unknown;
1535
1262
  }[], {
1536
- [x: string]: PgColumn<drizzle_orm6.ColumnBaseConfig<drizzle_orm6.ColumnDataType, string>, {}, {}>;
1263
+ [x: string]: PgColumn<drizzle_orm0.ColumnBaseConfig<drizzle_orm0.ColumnDataType, string>, {}, {}>;
1537
1264
  }>;
1538
- protected selectDistinct(opts: StatementOptions | undefined, fields: SelectedFields): pg$1.PgSelectBase<string, SelectedFields, "partial", Record<string, "not-null">, false, never, {
1539
- [x: string]: unknown;
1265
+ /**
1266
+ * Start a SELECT DISTINCT query on the table.
1267
+ */
1268
+ protected selectDistinct(opts?: StatementOptions, columns?: (keyof Static<T>)[]): drizzle_orm_pg_core0.PgSelectBase<string, Record<string, any>, "partial", Record<string, "not-null">, false, never, {
1269
+ [x: string]: any;
1540
1270
  }[], {
1541
- [x: string]: never;
1271
+ [x: string]: any;
1542
1272
  }>;
1543
1273
  /**
1544
1274
  * Start an INSERT query on the table.
1545
- *
1546
- * @returns The INSERT query builder.
1547
1275
  */
1548
- protected insert(opts?: StatementOptions): pg$1.PgInsertBuilder<PgTableWithColumns<EntityTableConfig>, any, false>;
1276
+ protected insert(opts?: StatementOptions): drizzle_orm_pg_core0.PgInsertBuilder<PgTableWithColumns<SchemaToTableConfig<T>>, any, false>;
1549
1277
  /**
1550
1278
  * Start an UPDATE query on the table.
1551
- *
1552
- * @returns The UPDATE query builder.
1553
1279
  */
1554
- protected update(opts?: StatementOptions): pg$1.PgUpdateBuilder<PgTableWithColumns<EntityTableConfig>, any>;
1280
+ protected update(opts?: StatementOptions): drizzle_orm_pg_core0.PgUpdateBuilder<PgTableWithColumns<SchemaToTableConfig<T>>, any>;
1555
1281
  /**
1556
1282
  * Start a DELETE query on the table.
1557
- *
1558
- * @returns The DELETE query builder.
1559
1283
  */
1560
- protected delete(opts?: StatementOptions): pg$1.PgDeleteBase<PgTableWithColumns<EntityTableConfig>, any, undefined, undefined, false, never>;
1284
+ protected delete(opts?: StatementOptions): drizzle_orm_pg_core0.PgDeleteBase<PgTableWithColumns<SchemaToTableConfig<T>>, any, undefined, undefined, false, never>;
1561
1285
  /**
1562
- * Find entities.
1286
+ * Create a Drizzle `select` query based on a JSON query object.
1563
1287
  *
1564
- * @param query The find query.
1565
- * @param opts The statement options.
1566
- * @returns The found entities.
1288
+ * > This method is the base for `find`, `findOne`, `findById`, and `paginate`.
1567
1289
  */
1568
- find(query?: PgQuery<EntitySchema>, opts?: StatementOptions): Promise<Static<EntitySchema>[]>;
1290
+ find<R extends PgRelationMap<T>>(query?: PgQueryRelations<T, R>, opts?: StatementOptions): Promise<PgStatic<T, R>[]>;
1569
1291
  /**
1570
1292
  * Find a single entity.
1293
+ */
1294
+ findOne<R extends PgRelationMap<T>>(query: Pick<PgQueryRelations<T, R>, "with" | "where">, opts?: StatementOptions): Promise<PgStatic<T, R>>;
1295
+ /**
1296
+ * Find entities with pagination.
1571
1297
  *
1572
- * @param where The where clause.
1573
- * @param opts The statement options.
1574
- * @returns The found entity.
1298
+ * It uses the same parameters as `find()`, but adds pagination metadata to the response.
1299
+ *
1300
+ * > Pagination CAN also do a count query to get the total number of elements.
1575
1301
  */
1576
- findOne<T extends EntitySchema = EntitySchema>(where: PgQueryWhereOrSQL<T>, opts?: StatementOptions): Promise<Static<EntitySchema>>;
1302
+ paginate<R extends PgRelationMap<T>>(pagination?: PageQuery, query?: PgQueryRelations<T, R>, opts?: StatementOptions & {
1303
+ count?: boolean;
1304
+ }): Promise<Page<PgStatic<T, R>>>;
1577
1305
  /**
1578
1306
  * Find an entity by ID.
1307
+ *
1308
+ * This is a convenience method for `findOne` with a where clause on the primary key.
1309
+ * If you need more complex queries, use `findOne` instead.
1579
1310
  */
1580
- findById(id: string | number, opts?: StatementOptions): Promise<Static<EntitySchema>>;
1311
+ findById(id: string | number, opts?: StatementOptions): Promise<Static<T>>;
1581
1312
  /**
1582
- * Paginate entities.
1313
+ * Helper to create a type-safe query object.
1583
1314
  */
1584
- paginate(pagination?: PageQuery, query?: PgQuery<EntitySchema>, opts?: StatementOptions & {
1585
- count?: boolean;
1586
- }): Promise<Page<Static<EntitySchema>>>;
1587
- createQuery(query?: PgQuery<EntitySchema>): PgQuery<EntitySchema>;
1588
- createQueryWhere(where?: PgQueryWhere<EntitySchema>): PgQueryWhere<EntitySchema>;
1315
+ createQuery(): PgQuery<T>;
1316
+ /**
1317
+ * Helper to create a type-safe where clause.
1318
+ */
1319
+ createQueryWhere(): PgQueryWhere<T>;
1589
1320
  /**
1590
1321
  * Create an entity.
1591
1322
  *
@@ -1593,7 +1324,7 @@ declare class RepositoryDescriptor<EntityTableConfig extends TableConfig, Entity
1593
1324
  * @param opts The options for creating the entity.
1594
1325
  * @returns The ID of the created entity.
1595
1326
  */
1596
- create(data: Static<TObjectInsert<EntitySchema>>, opts?: StatementOptions): Promise<Static<EntitySchema>>;
1327
+ create(data: Static<TObjectInsert<T>>, opts?: StatementOptions): Promise<Static<T>>;
1597
1328
  /**
1598
1329
  * Create many entities.
1599
1330
  *
@@ -1601,11 +1332,11 @@ declare class RepositoryDescriptor<EntityTableConfig extends TableConfig, Entity
1601
1332
  * @param opts The statement options.
1602
1333
  * @returns The created entities.
1603
1334
  */
1604
- createMany(values: Array<Static<TObjectInsert<EntitySchema>>>, opts?: StatementOptions): Promise<Static<EntitySchema>[]>;
1335
+ createMany(values: Array<Static<TObjectInsert<T>>>, opts?: StatementOptions): Promise<Static<T>[]>;
1605
1336
  /**
1606
1337
  * Find an entity and update it.
1607
1338
  */
1608
- updateOne(where: PgQueryWhereOrSQL<EntitySchema>, data: Partial<Static<TObjectUpdate<EntitySchema>>>, opts?: StatementOptions): Promise<Static<EntitySchema>>;
1339
+ updateOne(where: PgQueryWhereOrSQL<T>, data: Partial<Static<TObjectUpdate<T>>>, opts?: StatementOptions): Promise<Static<T>>;
1609
1340
  /**
1610
1341
  * Save a given entity.
1611
1342
  *
@@ -1627,132 +1358,90 @@ declare class RepositoryDescriptor<EntityTableConfig extends TableConfig, Entity
1627
1358
  * @see {@link PostgresTypeProvider#version}
1628
1359
  * @see {@link PgVersionMismatchError}
1629
1360
  */
1630
- save(entity: Static<EntitySchema>, opts?: StatementOptions): Promise<Static<EntitySchema>>;
1361
+ save(entity: Static<T>, opts?: StatementOptions): Promise<void>;
1631
1362
  /**
1632
1363
  * Find an entity by ID and update it.
1633
1364
  */
1634
- updateById(id: string | number, data: Partial<Static<TObjectUpdate<EntitySchema>>>, opts?: StatementOptions): Promise<Static<EntitySchema>>;
1365
+ updateById(id: string | number, data: Partial<Static<TObjectUpdate<T>>>, opts?: StatementOptions): Promise<Static<T>>;
1635
1366
  /**
1636
1367
  * Find many entities and update all of them.
1637
1368
  */
1638
- updateMany(where: PgQueryWhereOrSQL<EntitySchema>, data: Partial<Static<TObjectUpdate<EntitySchema>>>, opts?: StatementOptions): Promise<void>;
1369
+ updateMany(where: PgQueryWhereOrSQL<T>, data: Partial<Static<TObjectUpdate<T>>>, opts?: StatementOptions): Promise<Array<number | string>>;
1639
1370
  /**
1640
1371
  * Find many and delete all of them.
1372
+ * @returns Array of deleted entity IDs
1641
1373
  */
1642
- deleteMany(where?: PgQueryWhereOrSQL<EntitySchema>, opts?: StatementOptions): Promise<void>;
1374
+ deleteMany(where?: PgQueryWhereOrSQL<T>, opts?: StatementOptions): Promise<Array<number | string>>;
1643
1375
  /**
1644
1376
  * Delete all entities.
1377
+ * @returns Array of deleted entity IDs
1645
1378
  */
1646
- clear(opts?: StatementOptions): Promise<void>;
1379
+ clear(opts?: StatementOptions): Promise<Array<number | string>>;
1647
1380
  /**
1648
1381
  * Delete the given entity.
1649
1382
  *
1650
1383
  * You must fetch the entity first in order to delete it.
1384
+ * @returns Array containing the deleted entity ID
1651
1385
  */
1652
- destroy(entity: Static<EntitySchema>, opts?: StatementOptions): Promise<void>;
1386
+ destroy(entity: Static<T>, opts?: StatementOptions): Promise<Array<number | string>>;
1653
1387
  /**
1654
1388
  * Find an entity and delete it.
1389
+ * @returns Array of deleted entity IDs (should contain at most one ID)
1655
1390
  */
1656
- deleteOne(where?: PgQueryWhereOrSQL<EntitySchema>, opts?: StatementOptions): Promise<void>;
1391
+ deleteOne(where?: PgQueryWhereOrSQL<T>, opts?: StatementOptions): Promise<Array<number | string>>;
1657
1392
  /**
1658
1393
  * Find an entity by ID and delete it.
1394
+ * @returns Array containing the deleted entity ID
1395
+ * @throws PgEntityNotFoundError if the entity is not found
1659
1396
  */
1660
- deleteById(id: string | number, opts?: StatementOptions): Promise<void>;
1397
+ deleteById(id: string | number, opts?: StatementOptions): Promise<Array<number | string>>;
1661
1398
  /**
1662
1399
  * Count entities.
1663
1400
  */
1664
- count(where?: PgQueryWhereOrSQL<EntitySchema>, opts?: StatementOptions): Promise<number>;
1401
+ count(where?: PgQueryWhereOrSQL<T>, opts?: StatementOptions): Promise<number>;
1665
1402
  protected conflictMessagePattern: string;
1666
1403
  protected handleError(error: unknown, message: string): PgError;
1667
- protected withDeletedAt(where: PgQueryWhereOrSQL<EntitySchema>, opts?: {
1404
+ protected withDeletedAt(where: PgQueryWhereOrSQL<T>, opts?: {
1668
1405
  force?: boolean;
1669
- }): PgQueryWhereOrSQL<EntitySchema>;
1670
- protected get organization(): undefined;
1406
+ }): PgQueryWhereOrSQL<T>;
1671
1407
  protected deletedAt(): PgAttrField | undefined;
1672
- /**
1673
- * Convert a query object to a SQL query.
1674
- *
1675
- * @param query The query object.
1676
- * @param schema The schema to use.
1677
- * @param col The column to use.
1678
- */
1679
- protected jsonQueryToSql(query: PgQueryWhereOrSQL<EntitySchema>, schema?: TObject, col?: (key: string) => PgColumn): SQL | undefined;
1680
- /**
1681
- * Map a filter operator to a SQL query.
1682
- *
1683
- * @param operator
1684
- * @param column
1685
- * @protected
1686
- */
1687
- protected mapOperatorToSql(operator: FilterOperators<any> | any, column: PgColumn): SQL | undefined;
1688
- /**
1689
- * Create a pagination object.
1690
- *
1691
- * @param entities The entities to paginate.
1692
- * @param limit The limit of the pagination.
1693
- * @param offset The offset of the pagination.
1694
- */
1695
- protected createPagination(entities: Static<EntitySchema>[], limit?: number, offset?: number): Page<Static<EntitySchema>>;
1696
1408
  /**
1697
1409
  * Convert something to valid Pg Insert Value.
1698
1410
  */
1699
- protected cast(data: any, insert: boolean): PgInsertValue<PgTableWithColumns<EntityTableConfig>>;
1411
+ protected cast(data: any, insert: boolean): PgInsertValue<PgTableWithColumns<SchemaToTableConfig<T>>>;
1700
1412
  /**
1701
- * Clean a row. Remove all null values.
1413
+ * Transform a row from the database into a clean entity.
1702
1414
  *
1703
- * @param row The row to clean.
1704
- * @param schema The schema to use.
1705
- * @returns The cleaned row.
1415
+ * - Validate against schema
1416
+ * - Replace all null values by undefined
1417
+ * - Fix date-time and date fields to ISO strings
1418
+ * - Cast BigInt to string
1706
1419
  */
1707
- protected clean<T extends TObject = EntitySchema>(row: any, schema?: T): Static<T>;
1420
+ protected clean<T extends TObject>(row: Record<string, unknown>, schema: T): Static<T>;
1708
1421
  /**
1709
- * Parse pagination sort string to orderBy format.
1710
- * Format: "firstName,-lastName" -> [{ column: "firstName", direction: "asc" }, { column: "lastName", direction: "desc" }]
1711
- * - Columns separated by comma
1712
- * - Prefix with '-' for DESC direction
1713
- *
1714
- * @param sort Pagination sort string
1715
- * @returns OrderBy array or single object
1422
+ * Clean a row with joins recursively
1716
1423
  */
1717
- protected parsePaginationSort(sort: string): Array<{
1718
- column: string;
1719
- direction: "asc" | "desc";
1720
- }> | {
1721
- column: string;
1722
- direction: "asc" | "desc";
1723
- };
1424
+ protected cleanWithJoins<T extends TObject>(row: Record<string, unknown>, schema: T, joins: PgJoin[], parentPath?: string): Static<T>;
1724
1425
  /**
1725
- * Normalize orderBy parameter to array format.
1726
- * Supports 3 modes:
1727
- * 1. String: "name" -> [{ column: "name", direction: "asc" }]
1728
- * 2. Object: { column: "name", direction: "desc" } -> [{ column: "name", direction: "desc" }]
1729
- * 3. Array: [{ column: "name" }, { column: "age", direction: "desc" }] -> normalized array
1730
- *
1731
- * @param orderBy The orderBy parameter
1732
- * @returns Normalized array of order by clauses
1426
+ * Convert a where clause to SQL.
1733
1427
  */
1734
- protected normalizeOrderBy(orderBy: any): Array<{
1735
- column: string;
1736
- direction: "asc" | "desc";
1737
- }>;
1428
+ protected toSQL(where: PgQueryWhereOrSQL<T>, joins?: PgJoin[]): SQL | undefined;
1738
1429
  /**
1739
1430
  * Get the where clause for an ID.
1740
1431
  *
1741
1432
  * @param id The ID to get the where clause for.
1742
1433
  * @returns The where clause for the ID.
1743
1434
  */
1744
- protected getWhereId(id: string | number): PgQueryWhereOrSQL<EntitySchema>;
1435
+ protected getWhereId(id: string | number): PgQueryWhere<T>;
1745
1436
  /**
1746
1437
  * Find a primary key in the schema.
1747
- *
1748
- * @param schema
1749
- * @protected
1750
1438
  */
1751
1439
  protected getPrimaryKey(schema: TObject): {
1752
1440
  key: string;
1753
- col: PgColumn<drizzle_orm6.ColumnBaseConfig<drizzle_orm6.ColumnDataType, string>, {}, {}>;
1754
- type: TSchema$1;
1441
+ col: PgColumn<drizzle_orm0.ColumnBaseConfig<drizzle_orm0.ColumnDataType, string>, {}, {}>;
1442
+ type: TSchema;
1755
1443
  };
1444
+ protected $provider(): DatabaseProvider;
1756
1445
  }
1757
1446
  /**
1758
1447
  * The options for a statement.
@@ -1779,374 +1468,6 @@ interface StatementOptions {
1779
1468
  now?: DateTime;
1780
1469
  }
1781
1470
  //#endregion
1782
- //#region src/descriptors/$sequence.d.ts
1783
- /**
1784
- * Creates a PostgreSQL sequence descriptor for generating unique numeric values.
1785
- *
1786
- * This descriptor provides a type-safe interface to PostgreSQL sequences, which are
1787
- * database objects that generate unique numeric identifiers. Sequences are commonly
1788
- * used for primary keys, order numbers, invoice numbers, and other cases where
1789
- * guaranteed unique, incrementing values are needed across concurrent operations.
1790
- *
1791
- * **Key Features**
1792
- *
1793
- * - **Thread-Safe**: PostgreSQL sequences are inherently thread-safe and handle concurrency
1794
- * - **Configurable Parameters**: Start value, increment, min/max bounds, and cycling behavior
1795
- * - **Automatic Creation**: Sequences are created automatically when first used
1796
- * - **Type Safety**: Full TypeScript support with numeric return types
1797
- * - **Performance**: Optimized for high-throughput ID generation
1798
- * - **Schema Support**: Works with PostgreSQL schemas for organization
1799
- *
1800
- * **Use Cases**
1801
- *
1802
- * Perfect for generating unique identifiers in concurrent environments:
1803
- * - Primary key generation (alternative to UUIDs)
1804
- * - Order numbers and invoice sequences
1805
- * - Ticket numbers and reference IDs
1806
- * - Version numbers and revision tracking
1807
- * - Batch numbers for processing workflows
1808
- * - Any scenario requiring guaranteed unique incrementing numbers
1809
- *
1810
- * @example
1811
- * **Basic sequence for order numbers:**
1812
- * ```ts
1813
- * import { $sequence } from "alepha/postgres";
1814
- *
1815
- * class OrderService {
1816
- * orderNumbers = $sequence({
1817
- * name: "order_numbers",
1818
- * start: 1000, // Start from order #1000
1819
- * increment: 1 // Increment by 1 each time
1820
- * });
1821
- *
1822
- * async createOrder(orderData: OrderData) {
1823
- * const orderNumber = await this.orderNumbers.next();
1824
- *
1825
- * return await this.orders.create({
1826
- * id: generateUUID(),
1827
- * orderNumber,
1828
- * ...orderData
1829
- * });
1830
- * }
1831
- *
1832
- * async getCurrentOrderNumber() {
1833
- * // Get the last generated number without incrementing
1834
- * return await this.orderNumbers.current();
1835
- * }
1836
- * }
1837
- * ```
1838
- *
1839
- * @example
1840
- * **Invoice numbering with yearly reset:**
1841
- * ```ts
1842
- * class InvoiceService {
1843
- * // Separate sequence for each year
1844
- * getInvoiceSequence(year: number) {
1845
- * return $sequence({
1846
- * name: `invoice_numbers_${year}`,
1847
- * start: 1,
1848
- * increment: 1
1849
- * });
1850
- * }
1851
- *
1852
- * async generateInvoiceNumber(): Promise<string> {
1853
- * const year = new Date().getFullYear();
1854
- * const sequence = this.getInvoiceSequence(year);
1855
- * const number = await sequence.next();
1856
- *
1857
- * // Format as INV-2024-001, INV-2024-002, etc.
1858
- * return `INV-${year}-${number.toString().padStart(3, '0')}`;
1859
- * }
1860
- * }
1861
- * ```
1862
- *
1863
- * @example
1864
- * **High-performance ID generation with custom increments:**
1865
- * ```ts
1866
- * class TicketService {
1867
- * // Generate ticket numbers in increments of 10 for better distribution
1868
- * ticketSequence = $sequence({
1869
- * name: "ticket_numbers",
1870
- * start: 1000,
1871
- * increment: 10,
1872
- * min: 1000,
1873
- * max: 999999,
1874
- * cycle: false // Don't cycle when max is reached
1875
- * });
1876
- *
1877
- * priorityTicketSequence = $sequence({
1878
- * name: "priority_ticket_numbers",
1879
- * start: 1,
1880
- * increment: 1,
1881
- * min: 1,
1882
- * max: 999,
1883
- * cycle: true // Cycle when reaching max
1884
- * });
1885
- *
1886
- * async generateTicketNumber(isPriority: boolean = false): Promise<number> {
1887
- * if (isPriority) {
1888
- * return await this.priorityTicketSequence.next();
1889
- * }
1890
- * return await this.ticketSequence.next();
1891
- * }
1892
- *
1893
- * async getSequenceStatus() {
1894
- * return {
1895
- * currentTicketNumber: await this.ticketSequence.current(),
1896
- * currentPriorityNumber: await this.priorityTicketSequence.current()
1897
- * };
1898
- * }
1899
- * }
1900
- * ```
1901
- *
1902
- * @example
1903
- * **Batch processing with sequence-based coordination:**
1904
- * ```ts
1905
- * class BatchProcessor {
1906
- * batchSequence = $sequence({
1907
- * name: "batch_numbers",
1908
- * start: 1,
1909
- * increment: 1
1910
- * });
1911
- *
1912
- * async processBatch(items: any[]) {
1913
- * const batchNumber = await this.batchSequence.next();
1914
- *
1915
- * console.log(`Starting batch processing #${batchNumber} with ${items.length} items`);
1916
- *
1917
- * try {
1918
- * // Process items with batch number for tracking
1919
- * for (const item of items) {
1920
- * await this.processItem(item, batchNumber);
1921
- * }
1922
- *
1923
- * await this.auditLogger.log({
1924
- * event: 'batch_completed',
1925
- * batchNumber,
1926
- * itemCount: items.length,
1927
- * timestamp: new Date()
1928
- * });
1929
- *
1930
- * return { batchNumber, processedCount: items.length };
1931
- *
1932
- * } catch (error) {
1933
- * await this.auditLogger.log({
1934
- * event: 'batch_failed',
1935
- * batchNumber,
1936
- * error: error.message,
1937
- * timestamp: new Date()
1938
- * });
1939
- * throw error;
1940
- * }
1941
- * }
1942
- *
1943
- * async processItem(item: any, batchNumber: number) {
1944
- * // Associate item processing with batch number
1945
- * await this.items.update(item.id, {
1946
- * ...item.updates,
1947
- * batchNumber,
1948
- * processedAt: new Date()
1949
- * });
1950
- * }
1951
- * }
1952
- * ```
1953
- *
1954
- * @example
1955
- * **Multi-tenant sequence management:**
1956
- * ```ts
1957
- * class TenantSequenceService {
1958
- * // Create tenant-specific sequences
1959
- * getTenantSequence(tenantId: string, sequenceType: string) {
1960
- * return $sequence({
1961
- * name: `${tenantId}_${sequenceType}_seq`,
1962
- * start: 1,
1963
- * increment: 1
1964
- * });
1965
- * }
1966
- *
1967
- * async generateTenantOrderNumber(tenantId: string): Promise<string> {
1968
- * const sequence = this.getTenantSequence(tenantId, 'orders');
1969
- * const number = await sequence.next();
1970
- *
1971
- * return `${tenantId.toUpperCase()}-ORD-${number.toString().padStart(6, '0')}`;
1972
- * }
1973
- *
1974
- * async generateTenantInvoiceNumber(tenantId: string): Promise<string> {
1975
- * const sequence = this.getTenantSequence(tenantId, 'invoices');
1976
- * const number = await sequence.next();
1977
- *
1978
- * return `${tenantId.toUpperCase()}-INV-${number.toString().padStart(6, '0')}`;
1979
- * }
1980
- *
1981
- * async getTenantSequenceStatus(tenantId: string) {
1982
- * const orderSeq = this.getTenantSequence(tenantId, 'orders');
1983
- * const invoiceSeq = this.getTenantSequence(tenantId, 'invoices');
1984
- *
1985
- * return {
1986
- * tenant: tenantId,
1987
- * sequences: {
1988
- * orders: {
1989
- * current: await orderSeq.current(),
1990
- * next: await orderSeq.next()
1991
- * },
1992
- * invoices: {
1993
- * current: await invoiceSeq.current()
1994
- * }
1995
- * }
1996
- * };
1997
- * }
1998
- * }
1999
- * ```
2000
- *
2001
- * **Important Notes**:
2002
- * - Sequences are created automatically when first used
2003
- * - PostgreSQL sequences are atomic and handle high concurrency
2004
- * - Sequence values are not rolled back in failed transactions
2005
- * - Consider the impact of max values and cycling behavior
2006
- * - Sequences are schema-scoped in PostgreSQL
2007
- *
2008
- * @stability 1
2009
- */
2010
- declare const $sequence: {
2011
- (options?: SequenceDescriptorOptions): SequenceDescriptor;
2012
- [KIND]: typeof SequenceDescriptor;
2013
- };
2014
- interface SequenceDescriptorOptions {
2015
- /**
2016
- * Name of the PostgreSQL sequence to create.
2017
- *
2018
- * This name:
2019
- * - Must be unique within the database schema
2020
- * - Should follow PostgreSQL identifier conventions
2021
- * - Will be used in generated SQL for sequence operations
2022
- * - Should be descriptive of the sequence's purpose
2023
- *
2024
- * If not provided, defaults to the property key where the sequence is declared.
2025
- *
2026
- * **Naming Guidelines**:
2027
- * - Use descriptive names like "order_numbers", "invoice_seq"
2028
- * - Include the purpose or entity type in the name
2029
- * - Consider adding "_seq" suffix for clarity
2030
- * - Use snake_case for consistency with PostgreSQL conventions
2031
- *
2032
- * @example "order_numbers"
2033
- * @example "invoice_sequence"
2034
- * @example "ticket_numbers_seq"
2035
- */
2036
- name?: string;
2037
- /**
2038
- * The starting value for the sequence.
2039
- *
2040
- * This value:
2041
- * - Determines the first number that will be generated
2042
- * - Can be any integer within the sequence's range
2043
- * - Is useful for avoiding conflicts with existing data
2044
- * - Can be set higher for business reasons (e.g., starting invoices at 1000)
2045
- *
2046
- * **Common Patterns**:
2047
- * - Start at 1 for simple counters
2048
- * - Start at 1000+ for professional-looking numbers
2049
- * - Start at current max + 1 when migrating existing data
2050
- *
2051
- * @default 1
2052
- * @example 1 // Simple counter starting at 1
2053
- * @example 1000 // Professional numbering starting at 1000
2054
- * @example 10000 // Large starting number for established businesses
2055
- */
2056
- start?: number;
2057
- /**
2058
- * The increment value for each call to next().
2059
- *
2060
- * This value:
2061
- * - Determines how much the sequence increases each time
2062
- * - Can be any positive or negative integer
2063
- * - Affects the gaps between generated numbers
2064
- * - Can be used for number distribution strategies
2065
- *
2066
- * **Use Cases**:
2067
- * - increment: 1 for consecutive numbering
2068
- * - increment: 10 for distributed numbering (leaves gaps for manual entries)
2069
- * - increment: -1 for countdown sequences
2070
- *
2071
- * @default 1
2072
- * @example 1 // Standard consecutive numbering
2073
- * @example 10 // Leave gaps between numbers
2074
- * @example 100 // Large gaps for special numbering schemes
2075
- */
2076
- increment?: number;
2077
- /**
2078
- * The minimum value the sequence can generate.
2079
- *
2080
- * When the sequence reaches this value:
2081
- * - It cannot generate values below this minimum
2082
- * - Helps prevent negative numbers in contexts where they don't make sense
2083
- * - Works with cycling behavior to define the lower bound
2084
- *
2085
- * **Considerations**:
2086
- * - Set to 1 for positive-only sequences
2087
- * - Set to 0 if zero values are acceptable
2088
- * - Consider business rules about minimum valid numbers
2089
- *
2090
- * @example 1 // No zero or negative values
2091
- * @example 0 // Allow zero values
2092
- * @example 1000 // Maintain minimum professional appearance
2093
- */
2094
- min?: number;
2095
- /**
2096
- * The maximum value the sequence can generate.
2097
- *
2098
- * When the sequence reaches this value:
2099
- * - It cannot generate values above this maximum
2100
- * - Behavior depends on the cycle option
2101
- * - Useful for preventing overflow or limiting number ranges
2102
- *
2103
- * **Planning Considerations**:
2104
- * - Consider the expected volume of your application
2105
- * - Account for business growth over time
2106
- * - Factor in any formatting constraints (e.g., fixed-width displays)
2107
- * - Remember that PostgreSQL sequences can handle very large numbers
2108
- *
2109
- * @example 999999 // Six-digit limit
2110
- * @example 2147483647 // Maximum 32-bit signed integer
2111
- * @example 9999 // Four-digit limit for display purposes
2112
- */
2113
- max?: number;
2114
- /**
2115
- * Whether the sequence should cycle back to the minimum when it reaches the maximum.
2116
- *
2117
- * **cycle: true**:
2118
- * - When max is reached, next value will be the minimum value
2119
- * - Useful for scenarios where number reuse is acceptable
2120
- * - Common for temporary identifiers or rotating references
2121
- *
2122
- * **cycle: false (default)**:
2123
- * - When max is reached, further calls will fail with an error
2124
- * - Prevents unexpected number reuse
2125
- * - Better for permanent identifiers where uniqueness is critical
2126
- *
2127
- * **Use Cases for Cycling**:
2128
- * - Temporary ticket numbers that can be reused
2129
- * - Session IDs with limited lifetime
2130
- * - Batch numbers in rotating systems
2131
- *
2132
- * **Avoid Cycling For**:
2133
- * - Primary keys and permanent identifiers
2134
- * - Invoice numbers and financial references
2135
- * - Audit logs and compliance records
2136
- *
2137
- * @default false
2138
- */
2139
- cycle?: boolean;
2140
- }
2141
- declare class SequenceDescriptor extends Descriptor<SequenceDescriptorOptions> {
2142
- protected readonly provider: PostgresProvider;
2143
- protected created: boolean;
2144
- get name(): string;
2145
- protected create(): Promise<void>;
2146
- next(): Promise<number>;
2147
- current(): Promise<number>;
2148
- }
2149
- //#endregion
2150
1471
  //#region src/descriptors/$transaction.d.ts
2151
1472
  /**
2152
1473
  * Creates a transaction descriptor for database operations requiring atomicity and consistency.
@@ -2298,66 +1619,85 @@ declare class PgVersionMismatchError extends PgError {
2298
1619
  constructor(table: string, id: any);
2299
1620
  }
2300
1621
  //#endregion
2301
- //#region src/providers/RepositoryProvider.d.ts
2302
- declare class RepositoryProvider {
2303
- protected readonly log: _alepha_logger1.Logger;
1622
+ //#region src/providers/DrizzleKitProvider.d.ts
1623
+ declare class DrizzleKitProvider {
1624
+ protected readonly log: _alepha_logger0.Logger;
2304
1625
  protected readonly alepha: Alepha;
2305
- protected get repositories(): RepositoryDescriptor<TableConfig$1, TObject>[];
2306
1626
  /**
2307
- * Get all repositories.
1627
+ * Synchronize database with current schema definitions.
2308
1628
  *
2309
- * @param provider - Filter by provider.
2310
- */
2311
- getRepositories(provider?: PostgresProvider): RepositoryDescriptor<TableConfig$1, TObject>[];
2312
- /**
2313
- * Get all tables from the repositories.
1629
+ * In development mode, it will generate and execute migrations based on the current state.
1630
+ * In testing mode, it will generate migrations from scratch without applying them.
2314
1631
  *
2315
- * @param provider
1632
+ * Does nothing in production mode, you must handle migrations manually.
2316
1633
  */
2317
- getTables(provider?: PostgresProvider): pg$1.PgTableWithColumns<TableConfig$1>[];
1634
+ synchronize(provider: DatabaseProvider): Promise<void>;
2318
1635
  /**
2319
- * Get all pg providers from the repositories.
1636
+ * Mostly used for testing purposes. You can generate SQL migration statements without executing them.
2320
1637
  */
2321
- getProviders(): PostgresProvider[];
2322
- }
2323
- //#endregion
2324
- //#region src/providers/DrizzleKitProvider.d.ts
2325
- declare class DrizzleKitProvider {
2326
- protected readonly log: _alepha_logger1.Logger;
2327
- protected readonly alepha: Alepha;
2328
- protected readonly repositoryProvider: RepositoryProvider;
2329
- push(provider: PostgresProvider, schema?: string): Promise<void>;
2330
- setPgSchema(provider: PostgresProvider): Promise<void>;
1638
+ generateMigration(provider: DatabaseProvider, prevSnapshot?: any): Promise<{
1639
+ statements: string[];
1640
+ models: Record<string, unknown>;
1641
+ snapshot?: any;
1642
+ }>;
2331
1643
  /**
2332
- * Try to generate migrations from scratch based on the models.
2333
- * Then, execute the migrations.
2334
- *
2335
- * This is useful for testing or development purposes.
2336
- *
2337
- * Do not use in production.
2338
- *
2339
- * @param provider - The Postgres provider to use.
2340
- * @param schema - The schema to use.
2341
- * @returns A promise that resolves once the migrations have been executed.
1644
+ * Load all tables, enums, sequences, etc. from the provider's repositories.
2342
1645
  */
2343
- synchronize(provider: PostgresProvider, schema?: string): Promise<void>;
1646
+ getModels(provider: DatabaseProvider): Record<string, unknown>;
2344
1647
  /**
2345
- * Get all tables from the provider's repositories.
1648
+ * Load the migration snapshot from the database.
2346
1649
  */
2347
- protected getTables(provider: PostgresProvider, schema?: string): Promise<Record<string, any>>;
2348
- protected loadMigrationSnapshot(provider: PostgresProvider): Promise<any>;
2349
- protected saveMigrationSnapshot(provider: PostgresProvider, curr: Record<string, any>, entry?: {
2350
- id: number;
2351
- snapshot: string;
2352
- }): Promise<void>;
2353
- protected executeStatements(statements: string[], provider: PostgresProvider, _schema?: string, catchErrors?: boolean): Promise<void>;
2354
- protected prepareSchema(schemaName: string, provider: PostgresProvider, repositories: any[]): Promise<void>;
1650
+ protected loadDevMigrations(provider: DatabaseProvider): Promise<DevMigrations | undefined>;
1651
+ protected saveDevMigrations(provider: DatabaseProvider, curr: Record<string, any>, devMigrations?: DevMigrations): Promise<void>;
1652
+ protected executeStatements(statements: string[], provider: DatabaseProvider, catchErrors?: boolean): Promise<void>;
1653
+ protected createSchemaIfNotExists(provider: DatabaseProvider, schemaName: string): Promise<void>;
2355
1654
  /**
2356
1655
  * Try to load the official Drizzle Kit API.
2357
1656
  * If not available, fallback to the local kit import.
2358
1657
  */
2359
1658
  protected importDrizzleKit(): typeof DrizzleKit;
2360
- synchronizeSqlite(provider: PostgresProvider): Promise<void>;
1659
+ }
1660
+ declare const devMigrationsSchema: typebox9.TObject<{
1661
+ id: typebox9.TNumber;
1662
+ name: typebox9.TString;
1663
+ snapshot: typebox9.TString;
1664
+ created_at: typebox9.TString;
1665
+ }>;
1666
+ type DevMigrations = Static<typeof devMigrationsSchema>;
1667
+ //#endregion
1668
+ //#region src/services/PostgresModelBuilder.d.ts
1669
+ declare class PostgresModelBuilder extends ModelBuilder {
1670
+ protected schemas: Map<string, drizzle_orm_pg_core0.PgSchema<string>>;
1671
+ protected getPgSchema(name: string): any;
1672
+ buildTable(entity: EntityDescriptor<any>, options: {
1673
+ tables: Map<string, unknown>;
1674
+ enums: Map<string, unknown>;
1675
+ schema: string;
1676
+ }): void;
1677
+ buildSequence(sequence: SequenceDescriptor, options: {
1678
+ sequences: Map<string, unknown>;
1679
+ schema: string;
1680
+ }): void;
1681
+ /**
1682
+ * Get PostgreSQL-specific config builder for the table.
1683
+ */
1684
+ protected getTableConfig(entity: EntityDescriptor, tables: Map<string, unknown>): ((self: BuildExtraConfigColumns<string, any, "pg">) => PgTableExtraConfigValue[]) | undefined;
1685
+ schemaToPgColumns: <T extends TObject>(tableName: string, schema: T, nsp: PgSchema, enums: Map<string, unknown>, tables: Map<string, unknown>) => FromSchema<T>;
1686
+ mapFieldToColumn: (tableName: string, fieldName: string, value: TSchema, nsp: PgSchema, enums: Map<string, any>) => any;
1687
+ /**
1688
+ * Map a string to a PG column.
1689
+ *
1690
+ * @param key The key of the field.
1691
+ * @param value The value of the field.
1692
+ */
1693
+ mapStringToColumn: (key: string, value: TSchema) => drizzle_orm_pg_core0.PgUUIDBuilderInitial<string> | drizzle_orm_pg_core0.PgCustomColumnBuilder<{
1694
+ name: string;
1695
+ dataType: "custom";
1696
+ columnType: "PgCustomColumn";
1697
+ data: Buffer<ArrayBufferLike>;
1698
+ driverParam: unknown;
1699
+ enumValues: undefined;
1700
+ }> | drizzle_orm_pg_core0.PgTimestampStringBuilderInitial<string> | drizzle_orm_pg_core0.PgDateStringBuilderInitial<string> | drizzle_orm_pg_core0.PgTextBuilderInitial<string, [string, ...string[]]>;
2361
1701
  }
2362
1702
  //#endregion
2363
1703
  //#region src/providers/drivers/NodePostgresProvider.d.ts
@@ -2385,42 +1725,33 @@ declare const envSchema: _alepha_core1.TObject<{
2385
1725
  * It will generate the migration script and save it to the DB.
2386
1726
  *
2387
1727
  * This is recommended for development and testing purposes only.
2388
- *
2389
- * @default false
2390
1728
  */
2391
1729
  POSTGRES_SYNCHRONIZE: _alepha_core1.TOptional<_alepha_core1.TBoolean>;
2392
- /**
2393
- * Push the schema to the database.
2394
- * It's like `drizzle-kit push` command.
2395
- * It will introspect the models from DB and generate the SQL statements to create or update the tables.
2396
- *
2397
- * @default false
2398
- */
2399
- POSTGRES_PUSH: _alepha_core1.TOptional<_alepha_core1.TBoolean>;
2400
1730
  }>;
2401
1731
  interface NodePostgresProviderOptions {
2402
1732
  migrations: MigrationConfig;
2403
1733
  connection: postgres.Options<any>;
2404
1734
  }
2405
- declare class NodePostgresProvider extends PostgresProvider {
1735
+ declare class NodePostgresProvider extends DatabaseProvider {
1736
+ static readonly SSL_MODES: readonly ["require", "allow", "prefer", "verify-full"];
2406
1737
  readonly dialect = "postgres";
2407
- protected readonly sslModes: readonly ["require", "allow", "prefer", "verify-full"];
2408
- protected readonly log: _alepha_logger1.Logger;
1738
+ protected readonly log: _alepha_logger0.Logger;
2409
1739
  protected readonly env: {
2410
1740
  DATABASE_URL?: string | undefined;
2411
1741
  POSTGRES_SCHEMA?: string | undefined;
2412
1742
  POSTGRES_SYNCHRONIZE?: boolean | undefined;
2413
- POSTGRES_PUSH?: boolean | undefined;
2414
1743
  };
2415
1744
  protected readonly alepha: Alepha;
2416
1745
  protected readonly kit: DrizzleKitProvider;
1746
+ protected readonly builder: PostgresModelBuilder;
2417
1747
  protected client?: postgres.Sql;
2418
1748
  protected pg?: PostgresJsDatabase;
2419
1749
  readonly options: NodePostgresProviderOptions;
2420
1750
  /**
2421
1751
  * In testing mode, the schema name will be generated and deleted after the test.
2422
1752
  */
2423
- protected schemaForTesting?: string;
1753
+ protected schemaForTesting: string | undefined;
1754
+ execute(statement: SQLLike): Promise<Array<Record<string, unknown>>>;
2424
1755
  /**
2425
1756
  * Get Postgres schema.
2426
1757
  */
@@ -2450,7 +1781,7 @@ declare class NodePostgresProvider extends PostgresProvider {
2450
1781
  //#endregion
2451
1782
  //#region src/providers/PostgresTypeProvider.d.ts
2452
1783
  declare class PostgresTypeProvider {
2453
- readonly attr: <T extends TSchema$1, Attr extends PgSymbolKeys>(type: T, attr: Attr, value?: PgSymbols[Attr]) => PgAttr<T, Attr>;
1784
+ readonly attr: <T extends TSchema, Attr extends PgSymbolKeys>(type: T, attr: Attr, value?: PgSymbols[Attr]) => PgAttr<T, Attr>;
2454
1785
  /**
2455
1786
  * Creates a primary key with an identity column.
2456
1787
  */
@@ -2478,7 +1809,7 @@ declare class PostgresTypeProvider {
2478
1809
  * Wrap a schema with "default" attribute.
2479
1810
  * This is used to set a default value for a column in the database.
2480
1811
  */
2481
- readonly default: <T extends TSchema$1>(type: T, value?: Static<T>) => PgAttr<T, PgDefault>;
1812
+ readonly default: <T extends TSchema>(type: T, value?: Static<T>) => PgAttr<T, PgDefault>;
2482
1813
  /**
2483
1814
  * Creates a column 'version'.
2484
1815
  *
@@ -2493,21 +1824,33 @@ declare class PostgresTypeProvider {
2493
1824
  /**
2494
1825
  * Creates a column Created At. So just a datetime column with a default value of the current timestamp.
2495
1826
  */
2496
- readonly createdAt: (options?: TStringOptions) => PgAttr<PgAttr<TString, typeof PG_CREATED_AT>, typeof PG_DEFAULT>;
1827
+ readonly createdAt: (options?: TStringOptions) => PgAttr<PgAttr<typebox9.TCodec<TString, dayjs0.Dayjs>, typeof PG_CREATED_AT>, typeof PG_DEFAULT>;
2497
1828
  /**
2498
1829
  * Creates a column Updated At. Like createdAt, but it is updated on every update of the row.
2499
1830
  */
2500
- readonly updatedAt: (options?: TStringOptions) => PgAttr<PgAttr<TString, typeof PG_UPDATED_AT>, typeof PG_DEFAULT>;
1831
+ readonly updatedAt: (options?: TStringOptions) => PgAttr<PgAttr<typebox9.TCodec<TString, dayjs0.Dayjs>, typeof PG_UPDATED_AT>, typeof PG_DEFAULT>;
2501
1832
  /**
2502
1833
  * Creates a column Deleted At for soft delete functionality.
2503
1834
  * This is used to mark rows as deleted without actually removing them from the database.
2504
1835
  * The column is nullable - NULL means not deleted, timestamp means deleted.
2505
1836
  */
2506
- readonly deletedAt: (options?: TStringOptions) => PgAttr<typebox1.TOptional<TString>, typeof PG_DELETED_AT>;
1837
+ readonly deletedAt: (options?: TStringOptions) => PgAttr<typebox9.TOptional<typebox9.TCodec<TString, dayjs0.Dayjs>>, typeof PG_DELETED_AT>;
1838
+ /**
1839
+ * Creates a Postgres ENUM type.
1840
+ *
1841
+ * > By default, `t.enum()` is mapped to a TEXT column in Postgres.
1842
+ * > Using this method, you can create a real ENUM type in the database.
1843
+ *
1844
+ * @example
1845
+ * ```ts
1846
+ * const statusEnum = pg.enum(["pending", "active", "archived"], { name: "status_enum" });
1847
+ * ```
1848
+ */
1849
+ readonly enum: <T extends string[]>(values: [...T], pgEnumOptions?: PgEnumOptions, typeOptions?: TStringOptions) => PgAttr<TUnsafe<T[number]>, typeof PG_ENUM>;
2507
1850
  /**
2508
1851
  * Creates a reference to another table or schema. Basically a foreign key.
2509
1852
  */
2510
- readonly ref: <T extends TSchema$1>(type: T, ref: () => any, actions?: {
1853
+ readonly ref: <T extends TSchema>(type: T, ref: () => any, actions?: {
2511
1854
  onUpdate?: UpdateDeleteAction$1;
2512
1855
  onDelete?: UpdateDeleteAction$1;
2513
1856
  }) => PgAttr<T, PgRef>;
@@ -2516,8 +1859,6 @@ declare class PostgresTypeProvider {
2516
1859
  * It's used by {@link RepositoryDescriptor#paginate} method.
2517
1860
  */
2518
1861
  readonly page: <T extends TObject>(resource: T, options?: TObjectOptions) => TPage<T>;
2519
- readonly many: <T extends TObject, Config extends TableConfig>(table: PgTableWithColumnsAndSchema<Config, T>, foreignKey: keyof T["properties"]) => TOptionalAdd<PgAttr<PgAttr<TArray<T>, PgMany>, PgDefault>>;
2520
- readonly one: <T extends TObject, Config extends TableConfig>(table: PgTableWithColumnsAndSchema<Config, T>, foreignKey: keyof T["properties"]) => PgAttr<PgAttr<TOptionalAdd<T>, PgOne>, PgDefault>;
2521
1862
  }
2522
1863
  declare const pg: PostgresTypeProvider;
2523
1864
  //#endregion
@@ -2525,13 +1866,13 @@ declare const pg: PostgresTypeProvider;
2525
1866
  /**
2526
1867
  * @deprecated Use `pg.primaryKey()` instead.
2527
1868
  */
2528
- declare const legacyIdSchema: PgAttr<PgAttr<PgAttr<typebox1.TInteger, typeof PG_PRIMARY_KEY>, typeof PG_SERIAL>, typeof PG_DEFAULT>;
1869
+ declare const legacyIdSchema: PgAttr<PgAttr<PgAttr<typebox9.TInteger, typeof PG_PRIMARY_KEY>, typeof PG_SERIAL>, typeof PG_DEFAULT>;
2529
1870
  //#endregion
2530
1871
  //#region src/types/schema.d.ts
2531
1872
  /**
2532
1873
  * Postgres schema type.
2533
1874
  */
2534
- declare const schema: <TDocument extends TSchema$1>(name: string, document: TDocument) => drizzle_orm6.$Type<pg$1.PgCustomColumnBuilder<{
1875
+ declare const schema: <TDocument extends TSchema>(name: string, document: TDocument) => drizzle_orm0.$Type<drizzle_orm_pg_core0.PgCustomColumnBuilder<{
2535
1876
  name: string;
2536
1877
  dataType: "custom";
2537
1878
  columnType: "PgCustomColumn";
@@ -2574,14 +1915,13 @@ declare const schema: <TDocument extends TSchema$1>(name: string, document: TDoc
2574
1915
  *
2575
1916
  * Migrations are supported via Drizzle ORM, you need to use the `drizzle-kit` CLI tool to generate and run migrations.
2576
1917
  *
2577
- * Relations are **NOT SUPPORTED** yet. If you need relations, please use the `drizzle-orm` package directly.
2578
- *
2579
1918
  * @see {@link $entity}
1919
+ * @see {@link $sequence}
2580
1920
  * @see {@link $repository}
2581
1921
  * @see {@link $transaction}
2582
1922
  * @module alepha.postgres
2583
1923
  */
2584
1924
  declare const AlephaPostgres: _alepha_core1.Service<_alepha_core1.Module<{}>>;
2585
1925
  //#endregion
2586
- export { $entity, $repository, $sequence, $transaction, AlephaPostgres, DrizzleKitProvider, Entity, EntityDescriptorOptions, ExtractManyRelations, FilterOperators, FromSchema, NodePostgresProvider, NodePostgresProviderOptions, OrderBy, OrderByClause, OrderDirection, PG_CREATED_AT, PG_DEFAULT, PG_DELETED_AT, PG_IDENTITY, PG_MANY, PG_ONE, PG_PRIMARY_KEY, PG_REF, PG_SCHEMA, PG_SERIAL, PG_UPDATED_AT, PG_VERSION, Page, PageQuery, PgAttr, PgAttrField, PgConflictError, PgDefault, PgEntityNotFoundError, PgError, PgIdentityOptions, PgMany, PgManyOptions, PgMigrationError, PgOne, PgPrimaryKey, PgQuery, PgQueryWhere, PgQueryWhereOrSQL, PgQueryWhereWithMany, PgQueryWhereWithManyOrSQL, PgQueryWithMany, PgQueryWithMap, PgQueryWithOne, PgRef, PgRefOptions, PgSymbolKeys, PgSymbols, PgTableConfig, PgTableWithColumnsAndSchema, PgVersionMismatchError, PostgresProvider, PostgresTypeProvider, RelField, RemoveManyRelations, RepositoryDescriptor, RepositoryDescriptorOptions, RepositoryProvider, SQLLike, SequenceDescriptor, SequenceDescriptorOptions, StatementOptions, TObjectInsert, TObjectUpdate, TPage, TransactionContext, TransactionDescriptorOptions, camelToSnakeCase, drizzle_orm6 as drizzle, getAttrFields, insertSchema, legacyIdSchema, mapFieldToColumn, mapStringToColumn, pageQuerySchema, pageSchema, pg, pgAttr, schema, schemaToPgColumns, sql, updateSchema };
1926
+ export { $entity, $repository, $sequence, $transaction, AlephaPostgres, DatabaseProvider, DrizzleKitProvider, EntityColumn, EntityColumns, EntityDescriptor, EntityDescriptorOptions, FilterOperators, FromSchema, NodePostgresProvider, NodePostgresProviderOptions, OrderBy, OrderByClause, OrderDirection, PG_CREATED_AT, PG_DEFAULT, PG_DELETED_AT, PG_ENUM, PG_IDENTITY, PG_PRIMARY_KEY, PG_REF, PG_SERIAL, PG_UPDATED_AT, PG_VERSION, Page, PageQuery, PgAttr, PgAttrField, PgConflictError, PgDefault, PgEntityNotFoundError, PgEnumOptions, PgError, PgIdentityOptions, PgMigrationError, PgPrimaryKey, PgQuery, PgQueryRelations, PgQueryWhere, PgQueryWhereOrSQL, PgRef, PgRefOptions, PgRelation, PgRelationMap, PgStatic, PgSymbolKeys, PgSymbols, PgVersionMismatchError, PostgresTypeProvider, RepositoryDescriptor, RepositoryDescriptorOptions, SQLLike, SchemaToTableConfig, SequenceDescriptor, SequenceDescriptorOptions, StatementOptions, TObjectInsert, TObjectUpdate, TPage, TransactionContext, TransactionDescriptorOptions, drizzle_orm0 as drizzle, getAttrFields, insertSchema, legacyIdSchema, pageQuerySchema, pageSchema, pg, pgAttr, schema, sql, updateSchema };
2587
1927
  //# sourceMappingURL=index.d.ts.map