@zudojs/database 1.3.2 → 1.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -81,6 +81,42 @@ repository passes (`RepositoryDelegateOperations`).
81
81
  `adapter` (in which case it constructs the `PrismaClient` for you). It throws a
82
82
  `DatabaseError` if neither is supplied.
83
83
 
84
+ ### Typed transaction clients
85
+
86
+ The transaction client handed to `transaction()` callbacks is inferred from the
87
+ `prisma` instance you pass, so model delegates keep the types of your generated
88
+ client. This works with Prisma 7's `prisma-client` generator (client generated
89
+ into your application, e.g. `src/generated/prisma`) and with the legacy
90
+ `prisma-client-js` generator alike; the package's published types never import
91
+ `@prisma/client`, so they compile with `skipLibCheck: false`.
92
+
93
+ ```typescript
94
+ import { PrismaClient } from "./generated/prisma/client.js";
95
+
96
+ const client = createDatabaseClient({ prisma: new PrismaClient({ adapter }) });
97
+
98
+ await client.transaction(async (tx) => {
99
+ await tx.user.create({ data: { email: "bob@example.com", name: "Bob" } }); // fully typed
100
+ });
101
+ ```
102
+
103
+ `withTransaction`, `withTransactionRetry`, `TransactionManager`,
104
+ `DatabaseUnitOfWork`, the `Database` facade, the lock manager and the
105
+ migration and seed runners all take the type from the client they wrap.
106
+
107
+ When the concrete client type is unknown (`new DatabaseClient(options)`,
108
+ `createDatabaseClient({ adapter })`, `getDatabase()`), callbacks receive
109
+ `DatabaseTransactionContext`: the raw-query surface (`$queryRaw`,
110
+ `$executeRaw`, `$queryRawUnsafe`, `$executeRawUnsafe`) without model
111
+ delegates. Pass the client type to opt in, for example
112
+ `createDatabaseClient<PrismaClient>({ adapter })`. `TransactionClientOf<typeof
113
+ prisma>` names the inferred type if you need to annotate it.
114
+
115
+ The adapter-only form constructs `PrismaClient` from `@prisma/client` at
116
+ runtime, which only exists with the `prisma-client-js` generator. With the
117
+ `prisma-client` generator, construct the client yourself and pass it as
118
+ `prisma`.
119
+
84
120
  ## Querying
85
121
 
86
122
  ```typescript
@@ -1,4 +1,4 @@
1
- import { DatabaseClient, type DatabaseClientOptions, type DatabaseTransactionContext, type PrismaClientLike } from "../databaseClient/databaseClient.core.js";
1
+ import { DatabaseClient, type DatabaseClientOptions, type DatabaseTransactionContext, type PrismaClientLike, type TransactionClientOf } from "../databaseClient/databaseClient.core.js";
2
2
  import type { DatabaseHealth, DatabaseStatus, TransactionCallback, TransactionOptions } from "../databaseType/databaseType.type.js";
3
3
  /**
4
4
  * Database facade used by the application layer.
@@ -6,14 +6,18 @@ import type { DatabaseHealth, DatabaseStatus, TransactionCallback, TransactionOp
6
6
  * This module provides a single database lifecycle entry point while
7
7
  * keeping the underlying Prisma client implementation inside the
8
8
  * database package.
9
+ *
10
+ * `TTransaction` is the transaction client handed to `transaction()`
11
+ * callbacks. {@link createDatabase} infers it from the wrapped client or
12
+ * from `options.prisma`.
9
13
  */
10
- export declare class Database {
14
+ export declare class Database<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext> {
11
15
  private readonly client;
12
16
  /**
13
17
  * @param options Client options, or an existing {@link DatabaseClient}
14
18
  * to wrap so a single client is shared by the facade and other managers.
15
19
  */
16
- constructor(options?: DatabaseClientOptions | DatabaseClient);
20
+ constructor(options?: DatabaseClientOptions | DatabaseClient<TTransaction>);
17
21
  /**
18
22
  * Initializes the database connection.
19
23
  */
@@ -41,14 +45,14 @@ export declare class Database {
41
45
  /**
42
46
  * Executes work inside a database transaction.
43
47
  */
44
- transaction<TResult>(callback: TransactionCallback<DatabaseTransactionContext, TResult>, options?: TransactionOptions): Promise<TResult>;
48
+ transaction<TResult>(callback: TransactionCallback<TTransaction, TResult>, options?: TransactionOptions): Promise<TResult>;
45
49
  /**
46
50
  * Returns the underlying database client.
47
51
  *
48
52
  * This should primarily be used by repository and infrastructure
49
53
  * implementations that require direct Prisma access.
50
54
  */
51
- getClient(): DatabaseClient;
55
+ getClient(): DatabaseClient<TTransaction>;
52
56
  /**
53
57
  * Returns the underlying Prisma client.
54
58
  */
@@ -59,7 +63,17 @@ export declare class Database {
59
63
  destroy(): Promise<void>;
60
64
  }
61
65
  /**
62
- * Creates a database facade.
66
+ * Creates a database facade over an existing client, keeping its
67
+ * transaction client type.
68
+ */
69
+ export declare function createDatabase<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext>(client: DatabaseClient<TTransaction>): Database<TTransaction>;
70
+ /**
71
+ * Creates a database facade from client options. The transaction client
72
+ * type is inferred from `options.prisma`.
73
+ */
74
+ export declare function createDatabase<TClient extends PrismaClientLike = PrismaClientLike>(options?: DatabaseClientOptions<TClient>): Database<TransactionClientOf<TClient>>;
75
+ /**
76
+ * Creates a database facade from client options or an existing client.
63
77
  */
64
78
  export declare function createDatabase(options?: DatabaseClientOptions | DatabaseClient): Database;
65
79
  /**
@@ -1,10 +1,14 @@
1
- import { DatabaseClient, createDatabaseClient, } from "../databaseClient/databaseClient.core.js";
1
+ import { DatabaseClient, } from "../databaseClient/databaseClient.core.js";
2
2
  /**
3
3
  * Database facade used by the application layer.
4
4
  *
5
5
  * This module provides a single database lifecycle entry point while
6
6
  * keeping the underlying Prisma client implementation inside the
7
7
  * database package.
8
+ *
9
+ * `TTransaction` is the transaction client handed to `transaction()`
10
+ * callbacks. {@link createDatabase} infers it from the wrapped client or
11
+ * from `options.prisma`.
8
12
  */
9
13
  export class Database {
10
14
  client;
@@ -14,7 +18,9 @@ export class Database {
14
18
  */
15
19
  constructor(options = {}) {
16
20
  this.client =
17
- options instanceof DatabaseClient ? options : createDatabaseClient(options);
21
+ options instanceof DatabaseClient
22
+ ? options
23
+ : new DatabaseClient(options);
18
24
  }
19
25
  /**
20
26
  * Initializes the database connection.
@@ -80,9 +86,6 @@ export class Database {
80
86
  await this.client.destroy();
81
87
  }
82
88
  }
83
- /**
84
- * Creates a database facade.
85
- */
86
89
  export function createDatabase(options = {}) {
87
90
  return new Database(options);
88
91
  }
@@ -11,14 +11,10 @@
11
11
  * Only PostgreSQL is exercised by the runners, locks and health helpers in
12
12
  * this package.
13
13
  */
14
- import type { Prisma } from "@prisma/client";
15
14
  import { DatabaseError } from "@zudojs/errors";
16
15
  import type { DatabaseClient as DatabaseClientContract, DatabaseConnectionOptions, DatabaseHealth, DatabaseLogger, DatabaseOperationOptions, DatabaseStatus, TransactionCallback, TransactionIsolationLevel, TransactionOptions } from "../databaseType/databaseType.type.js";
17
- /**
18
- * Transaction client handed to callbacks. This is Prisma's interactive
19
- * transaction client (model delegates plus raw query helpers).
20
- */
21
- export type DatabaseTransactionContext = Prisma.TransactionClient;
16
+ import type { DatabaseTransactionContext, PrismaSqlLike, TransactionClientOf } from "./databaseClient.type.js";
17
+ export type { DatabaseTransactionContext, PrismaSqlLike, TransactionClientOf, } from "./databaseClient.type.js";
22
18
  /**
23
19
  * Isolation levels accepted by Prisma's interactive transactions. The
24
20
  * values are identical to the string names, so no namespace lookup is
@@ -79,12 +75,15 @@ export interface PrismaDriverAdapterLike {
79
75
  * Prisma 7 requires either a driver adapter or an already constructed
80
76
  * client; connection URLs, pool sizes and SSL flags are configured on the
81
77
  * adapter and are therefore not accepted here.
78
+ *
79
+ * `TClient` is the type of `prisma`; {@link createDatabaseClient} infers
80
+ * it to type the transaction client handed to callbacks.
82
81
  */
83
- export interface DatabaseClientOptions extends Pick<DatabaseConnectionOptions, "connectionTimeoutMs" | "logging"> {
82
+ export interface DatabaseClientOptions<TClient extends PrismaClientLike = PrismaClientLike> extends Pick<DatabaseConnectionOptions, "connectionTimeoutMs" | "logging"> {
84
83
  /**
85
84
  * Pre-built Prisma client. Takes precedence over `adapter`.
86
85
  */
87
- readonly prisma?: PrismaClientLike;
86
+ readonly prisma?: TClient;
88
87
  /**
89
88
  * Prisma driver adapter used to construct a client when `prisma` is
90
89
  * not supplied.
@@ -102,8 +101,15 @@ export type RawQueryOptions = DatabaseOperationOptions;
102
101
  * `connect()` de-duplicates concurrent calls through a shared in-flight
103
102
  * promise, and `disconnect()` waits for an in-flight connect before
104
103
  * tearing the client down.
104
+ *
105
+ * `TTransaction` is the client handed to `transaction()` callbacks.
106
+ * {@link createDatabaseClient} infers it from the Prisma client passed as
107
+ * `prisma`, so `tx.user.create(...)` is typed by the generated client. The
108
+ * constructor cannot infer it; `new DatabaseClient(options)` uses the
109
+ * structural {@link DatabaseTransactionContext} unless a type argument is
110
+ * given.
105
111
  */
106
- export declare class DatabaseClient implements DatabaseClientContract<DatabaseTransactionContext> {
112
+ export declare class DatabaseClient<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext> implements DatabaseClientContract<TTransaction> {
107
113
  private readonly prisma;
108
114
  private readonly logger;
109
115
  private readonly options;
@@ -151,7 +157,7 @@ export declare class DatabaseClient implements DatabaseClientContract<DatabaseTr
151
157
  * being logged as a database failure. Driver and database failures, and
152
158
  * any other thrown value, are normalised to a `DatabaseError` and logged.
153
159
  */
154
- transaction<TResult>(callback: TransactionCallback<DatabaseTransactionContext, TResult>, options?: TransactionOptions): Promise<TResult>;
160
+ transaction<TResult>(callback: TransactionCallback<TTransaction, TResult>, options?: TransactionOptions): Promise<TResult>;
155
161
  /**
156
162
  * Executes a raw statement with positional parameters and returns the
157
163
  * affected row count.
@@ -166,11 +172,11 @@ export declare class DatabaseClient implements DatabaseClientContract<DatabaseTr
166
172
  *
167
173
  * Only available when the underlying client supports `$executeRaw`.
168
174
  */
169
- executeRaw(query: Prisma.Sql, options?: RawQueryOptions): Promise<number>;
175
+ executeRaw(query: PrismaSqlLike, options?: RawQueryOptions): Promise<number>;
170
176
  /**
171
177
  * Executes a `Prisma.sql` tagged query.
172
178
  */
173
- queryRaw<TResult = unknown>(query: Prisma.Sql, options?: RawQueryOptions): Promise<TResult>;
179
+ queryRaw<TResult = unknown>(query: PrismaSqlLike, options?: RawQueryOptions): Promise<TResult>;
174
180
  ensureConnected(): Promise<void>;
175
181
  destroy(): Promise<void>;
176
182
  private registerQueryLogging;
@@ -209,6 +215,16 @@ export declare function createAbortError(signal?: AbortSignal): DatabaseAbortErr
209
215
  * running. The abort listener is removed once the operation settles.
210
216
  */
211
217
  export declare function raceAbort<T>(promise: Promise<T>, signal: AbortSignal | undefined): Promise<T>;
212
- /** Creates a database client. */
213
- export declare function createDatabaseClient(options?: DatabaseClientOptions): DatabaseClient;
218
+ /**
219
+ * Creates a database client.
220
+ *
221
+ * The transaction client handed to `transaction()` callbacks is inferred
222
+ * from `options.prisma`: with a generated client it is that client's own
223
+ * interactive transaction client, so model delegates are fully typed.
224
+ * Without `prisma` (adapter only) it is the structural
225
+ * {@link DatabaseTransactionContext}; pass the generated client type as a
226
+ * type argument (`createDatabaseClient<PrismaClient>({ adapter })`) to type
227
+ * the delegates.
228
+ */
229
+ export declare function createDatabaseClient<TClient extends PrismaClientLike = PrismaClientLike>(options?: DatabaseClientOptions<TClient>): DatabaseClient<TransactionClientOf<TClient>>;
214
230
  //# sourceMappingURL=databaseClient.core.d.ts.map
@@ -32,6 +32,13 @@ export const SUPPORTED_ISOLATION_LEVELS = Object.freeze([
32
32
  * `connect()` de-duplicates concurrent calls through a shared in-flight
33
33
  * promise, and `disconnect()` waits for an in-flight connect before
34
34
  * tearing the client down.
35
+ *
36
+ * `TTransaction` is the client handed to `transaction()` callbacks.
37
+ * {@link createDatabaseClient} infers it from the Prisma client passed as
38
+ * `prisma`, so `tx.user.create(...)` is typed by the generated client. The
39
+ * constructor cannot infer it; `new DatabaseClient(options)` uses the
40
+ * structural {@link DatabaseTransactionContext} unless a type argument is
41
+ * given.
35
42
  */
36
43
  export class DatabaseClient {
37
44
  prisma;
@@ -358,7 +365,7 @@ let cachedPrismaClientConstructor;
358
365
  function resolvePrismaClientConstructor() {
359
366
  if (cachedPrismaClientConstructor)
360
367
  return cachedPrismaClientConstructor;
361
- const guidance = "Install it and run `prisma generate`, or pass an already-constructed client as `prisma` in the DatabaseClient options.";
368
+ const guidance = "Install it and run `prisma generate`, or pass an already-constructed client as `prisma` in the DatabaseClient options (required with the `prisma-client` generator, whose client is generated into your application rather than into @prisma/client).";
362
369
  let module;
363
370
  try {
364
371
  module = requirePeer("@prisma/client");
@@ -489,7 +496,17 @@ export function raceAbort(promise, signal) {
489
496
  });
490
497
  });
491
498
  }
492
- /** Creates a database client. */
499
+ /**
500
+ * Creates a database client.
501
+ *
502
+ * The transaction client handed to `transaction()` callbacks is inferred
503
+ * from `options.prisma`: with a generated client it is that client's own
504
+ * interactive transaction client, so model delegates are fully typed.
505
+ * Without `prisma` (adapter only) it is the structural
506
+ * {@link DatabaseTransactionContext}; pass the generated client type as a
507
+ * type argument (`createDatabaseClient<PrismaClient>({ adapter })`) to type
508
+ * the delegates.
509
+ */
493
510
  export function createDatabaseClient(options = {}) {
494
511
  return new DatabaseClient(options);
495
512
  }
@@ -0,0 +1,53 @@
1
+ /**
2
+ * @zudojs/database — Database Client Types
3
+ *
4
+ * Transaction-client types owned by this package. They are structural on
5
+ * purpose: nothing here imports `@prisma/client`, so the published
6
+ * declarations resolve whether the consumer's generated client lives in
7
+ * `node_modules/.prisma/client` (`prisma-client-js`) or in an application
8
+ * directory such as `src/generated/prisma` (`prisma-client`, the Prisma 7
9
+ * default).
10
+ */
11
+ /**
12
+ * A `Prisma.sql` tagged query. Any `Prisma.Sql` instance satisfies it.
13
+ */
14
+ export interface PrismaSqlLike {
15
+ readonly strings: readonly string[];
16
+ readonly values: readonly unknown[];
17
+ readonly sql: string;
18
+ }
19
+ /**
20
+ * The raw-query surface every Prisma interactive transaction client
21
+ * exposes. This is the transaction type used when the concrete client type
22
+ * is not known: the default type argument of `DatabaseClient`,
23
+ * `TransactionManager`, `DatabaseUnitOfWork` and the other transaction
24
+ * helpers, and the type the migration, seed and lock helpers require.
25
+ *
26
+ * `createDatabaseClient({ prisma })` infers the real transaction client
27
+ * (model delegates included) from the client passed in; see
28
+ * {@link TransactionClientOf}.
29
+ *
30
+ * Declared as a type alias rather than an interface so it keeps an
31
+ * implicit index signature and stays assignable to
32
+ * `BaseRepository#withTransaction`'s `TransactionClientLike`.
33
+ */
34
+ export type DatabaseTransactionContext = {
35
+ $queryRawUnsafe<TResult = unknown>(query: string, ...values: unknown[]): Promise<TResult>;
36
+ $executeRawUnsafe(query: string, ...values: unknown[]): Promise<number>;
37
+ $queryRaw<TResult = unknown>(query: TemplateStringsArray | PrismaSqlLike, ...values: unknown[]): Promise<TResult>;
38
+ $executeRaw(query: TemplateStringsArray | PrismaSqlLike, ...values: unknown[]): Promise<number>;
39
+ };
40
+ /**
41
+ * The interactive-transaction callback parameter of a Prisma client: for a
42
+ * generated client this is its own `Omit<PrismaClient, ITXClientDenyList>`
43
+ * (or the extended equivalent after `$extends`), so model delegates such
44
+ * as `tx.user` keep their generated types.
45
+ *
46
+ * Falls back to {@link DatabaseTransactionContext} for clients whose
47
+ * transaction callback cannot be read (hand-written stubs,
48
+ * `PrismaClientLike` itself) or does not expose the raw-query surface.
49
+ */
50
+ export type TransactionClientOf<TClient> = TClient extends {
51
+ $transaction(callback: (transaction: infer TTransaction) => never, ...rest: never[]): unknown;
52
+ } ? [TTransaction] extends [never] ? DatabaseTransactionContext : [TTransaction] extends [DatabaseTransactionContext] ? TTransaction : DatabaseTransactionContext : DatabaseTransactionContext;
53
+ //# sourceMappingURL=databaseClient.type.d.ts.map
@@ -0,0 +1,12 @@
1
+ /**
2
+ * @zudojs/database — Database Client Types
3
+ *
4
+ * Transaction-client types owned by this package. They are structural on
5
+ * purpose: nothing here imports `@prisma/client`, so the published
6
+ * declarations resolve whether the consumer's generated client lives in
7
+ * `node_modules/.prisma/client` (`prisma-client-js`) or in an application
8
+ * directory such as `src/generated/prisma` (`prisma-client`, the Prisma 7
9
+ * default).
10
+ */
11
+ export {};
12
+ //# sourceMappingURL=databaseClient.type.js.map
@@ -3,6 +3,6 @@
3
3
  *
4
4
  * Prisma-backed database client and connection infrastructure.
5
5
  */
6
- export { DatabaseClient, DatabaseAbortError, createDatabaseClient, buildPrismaTransactionOptions, createAbortError, raceAbort, throwIfAborted, SUPPORTED_ISOLATION_LEVELS, type DatabaseClientOptions, type DatabaseTransactionContext, type PrismaClientLike, type PrismaDriverAdapterLike, type PrismaQueryEvent, type PrismaTransactionOptions, type RawQueryOptions, } from "./databaseClient.core.js";
6
+ export { DatabaseClient, DatabaseAbortError, createDatabaseClient, buildPrismaTransactionOptions, createAbortError, raceAbort, throwIfAborted, SUPPORTED_ISOLATION_LEVELS, type DatabaseClientOptions, type DatabaseTransactionContext, type PrismaClientLike, type PrismaDriverAdapterLike, type PrismaQueryEvent, type PrismaTransactionOptions, type PrismaSqlLike, type RawQueryOptions, type TransactionClientOf, } from "./databaseClient.core.js";
7
7
  export { normalizeDatabaseError, withDatabaseErrorMetadata, isPrismaError, isRetryableTransactionError, isConflictError, isNotFoundError, getDatabaseErrorCode, getDatabaseErrorKind, isDatabaseErrorLike, isNonDatabaseBaseError, toDatabaseErrorInfo, RETRYABLE_DATABASE_CODES, type DatabaseErrorKind, type NormalizeDatabaseErrorOptions, type PrismaErrorLike, } from "./databaseClient.errors.js";
8
8
  //# sourceMappingURL=index.d.ts.map
package/dist/index.d.ts CHANGED
@@ -18,7 +18,7 @@
18
18
  */
19
19
  export type { DatabaseOperationOptions, DatabaseStatus, TransactionIsolationLevel, DatabaseOperation, DatabaseConnectionOptions, DatabaseClientHealth, DatabaseHealth as DatabaseHealthInfo, TransactionOptions, TransactionCallback, Repository, SoftDeletableRepository, PaginationInput, PaginationMeta, PaginatedResult, SortDirection, SortInput, QueryOptions, DatabaseEntity, SoftDeletableEntity, AuditableEntity, DatabaseErrorInfo, DatabaseLogger, } from "./databaseType/index.js";
20
20
  export { noopDatabaseLogger } from "./databaseType/index.js";
21
- export { DatabaseClient, DatabaseAbortError, createDatabaseClient, buildPrismaTransactionOptions, createAbortError, raceAbort, throwIfAborted, SUPPORTED_ISOLATION_LEVELS, normalizeDatabaseError, withDatabaseErrorMetadata, isPrismaError, isRetryableTransactionError, isConflictError, isNotFoundError, getDatabaseErrorCode, getDatabaseErrorKind, isDatabaseErrorLike, isNonDatabaseBaseError, toDatabaseErrorInfo, RETRYABLE_DATABASE_CODES, type DatabaseClientOptions, type DatabaseTransactionContext, type PrismaClientLike, type PrismaDriverAdapterLike, type PrismaQueryEvent, type PrismaTransactionOptions, type RawQueryOptions, type DatabaseErrorKind, type NormalizeDatabaseErrorOptions, type PrismaErrorLike, } from "./databaseClient/index.js";
21
+ export { DatabaseClient, DatabaseAbortError, createDatabaseClient, buildPrismaTransactionOptions, createAbortError, raceAbort, throwIfAborted, SUPPORTED_ISOLATION_LEVELS, normalizeDatabaseError, withDatabaseErrorMetadata, isPrismaError, isRetryableTransactionError, isConflictError, isNotFoundError, getDatabaseErrorCode, getDatabaseErrorKind, isDatabaseErrorLike, isNonDatabaseBaseError, toDatabaseErrorInfo, RETRYABLE_DATABASE_CODES, type DatabaseClientOptions, type DatabaseTransactionContext, type PrismaClientLike, type PrismaDriverAdapterLike, type PrismaQueryEvent, type PrismaTransactionOptions, type PrismaSqlLike, type RawQueryOptions, type TransactionClientOf, type DatabaseErrorKind, type NormalizeDatabaseErrorOptions, type PrismaErrorLike, } from "./databaseClient/index.js";
22
22
  export { DatabaseConnectionManager, createConnectionManager, type DatabaseConnectionEvent, type DatabaseConnectionListener, type DatabaseConnectionEventDetails, type DatabaseConnectionManagerOptions, type DatabaseReconnectOptions, } from "./databaseConnection/index.js";
23
23
  export { Database, createDatabase, getDatabase, connectDatabase, disconnectDatabase, resetDatabase, } from "./database/index.js";
24
24
  export { BaseRepository, mapRepositoryError, isPrismaErrorLike, toDatabaseOperation, type RepositoryDelegate, type RepositoryDelegateOperations, type BaseRepositoryOptions, type SoftDeleteOptions, type CursorQueryOptions, type TransactionClientLike, type RepositoryOperation, type RepositoryErrorContext, } from "./repository/index.js";
@@ -62,31 +62,33 @@ export interface DatabaseLockResult {
62
62
  * Application-level lock abstraction for PostgreSQL.
63
63
  *
64
64
  * Locks are acquired inside a transaction and released when it ends.
65
+ * `TTransaction` is the transaction client handed to callbacks, taken from
66
+ * the database client.
65
67
  */
66
- export declare class DatabaseLockManager {
68
+ export declare class DatabaseLockManager<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext> {
67
69
  private readonly client;
68
- constructor(client: DatabaseClient);
70
+ constructor(client: DatabaseClient<TTransaction>);
69
71
  /**
70
72
  * Executes work inside a transaction after acquiring a PostgreSQL
71
73
  * advisory transaction lock.
72
74
  */
73
- withAdvisoryLock<TResult>(lockKey: string, callback: (transaction: DatabaseTransactionContext) => Promise<TResult>, options?: DatabaseLockOptions): Promise<TResult>;
75
+ withAdvisoryLock<TResult>(lockKey: string, callback: (transaction: TTransaction) => Promise<TResult>, options?: DatabaseLockOptions): Promise<TResult>;
74
76
  /**
75
77
  * Acquires a row-level lock and executes work while holding it.
76
78
  *
77
79
  * @throws {DatabaseError} when the row does not exist or was skipped
78
80
  * because another transaction holds it (`skipLocked`).
79
81
  */
80
- withRowLock<TResult>(tableName: string, id: string | number, callback: (transaction: DatabaseTransactionContext) => Promise<TResult>, options?: DatabaseLockOptions): Promise<TResult>;
82
+ withRowLock<TResult>(tableName: string, id: string | number, callback: (transaction: TTransaction) => Promise<TResult>, options?: DatabaseLockOptions): Promise<TResult>;
81
83
  /**
82
84
  * Returns the underlying database client.
83
85
  */
84
- getClient(): DatabaseClient;
86
+ getClient(): DatabaseClient<TTransaction>;
85
87
  }
86
88
  /**
87
89
  * Creates a lock manager.
88
90
  */
89
- export declare function createLockManager(client: DatabaseClient): DatabaseLockManager;
91
+ export declare function createLockManager<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext>(client: DatabaseClient<TTransaction>): DatabaseLockManager<TTransaction>;
90
92
  /**
91
93
  * Acquires a PostgreSQL advisory transaction lock.
92
94
  *
@@ -5,6 +5,8 @@ import { fnv1a64, hashLockKey, SQL_IDENTIFIER_PATTERN, } from "../migration/migr
5
5
  * Application-level lock abstraction for PostgreSQL.
6
6
  *
7
7
  * Locks are acquired inside a transaction and released when it ends.
8
+ * `TTransaction` is the transaction client handed to callbacks, taken from
9
+ * the database client.
8
10
  */
9
11
  export class DatabaseLockManager {
10
12
  client;
@@ -1,3 +1,4 @@
1
+ import type { DatabaseTransactionContext } from "../databaseClient/databaseClient.type.js";
1
2
  import type { Migration, MigrationRecord } from "./migration.types.js";
2
3
  /**
3
4
  * Default migration table.
@@ -14,15 +15,15 @@ export declare const SQL_IDENTIFIER_PATTERN: RegExp;
14
15
  /**
15
16
  * Normalizes and validates migrations.
16
17
  */
17
- export declare function normalizeMigrations(migrations: readonly Migration[]): readonly Migration[];
18
+ export declare function normalizeMigrations<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext>(migrations: readonly Migration<TTransaction>[]): readonly Migration<TTransaction>[];
18
19
  /**
19
20
  * Validates one migration.
20
21
  */
21
- export declare function validateMigration(migration: Migration): void;
22
+ export declare function validateMigration<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext>(migration: Migration<TTransaction>): void;
22
23
  /**
23
24
  * Returns the highest registered migration version.
24
25
  */
25
- export declare function getLatestVersion(migrations: readonly Migration[]): number;
26
+ export declare function getLatestVersion(migrations: readonly Pick<Migration, "version">[]): number;
26
27
  /**
27
28
  * Returns the highest applied migration version.
28
29
  */
@@ -9,7 +9,7 @@ export { DEFAULT_MIGRATION_TABLE, DEFAULT_MIGRATION_LOCK, normalizeMigrations, g
9
9
  * lock before deciding what to execute, so two runners started together
10
10
  * never apply or revert the same migration twice.
11
11
  */
12
- export declare class MigrationRunner {
12
+ export declare class MigrationRunner<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext> {
13
13
  private readonly client;
14
14
  private readonly migrations;
15
15
  private readonly tableName;
@@ -17,11 +17,11 @@ export declare class MigrationRunner {
17
17
  private readonly dialect;
18
18
  private readonly transactionOptions;
19
19
  private readonly perItemTransaction;
20
- constructor(client: DatabaseClient, migrations: readonly Migration[], options?: MigrationRunnerOptions);
20
+ constructor(client: DatabaseClient<TTransaction>, migrations: readonly Migration<TTransaction>[], options?: MigrationRunnerOptions);
21
21
  /**
22
22
  * Returns the migration status without executing anything.
23
23
  */
24
- status(): Promise<MigrationStatus>;
24
+ status(): Promise<MigrationStatus<TTransaction>>;
25
25
  /**
26
26
  * Applies every pending migration in version order.
27
27
  */
@@ -71,5 +71,5 @@ export declare class MigrationRunner {
71
71
  /**
72
72
  * Creates a migration runner.
73
73
  */
74
- export declare function createMigrationRunner(client: DatabaseClient, migrations: readonly Migration[], options?: MigrationRunnerOptions): MigrationRunner;
74
+ export declare function createMigrationRunner<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext>(client: DatabaseClient<TTransaction>, migrations: readonly Migration<TTransaction>[], options?: MigrationRunnerOptions): MigrationRunner<TTransaction>;
75
75
  //# sourceMappingURL=migration.runner.d.ts.map
@@ -7,18 +7,21 @@ import type { SqlDialectName } from "./migration.dialect.js";
7
7
  * Each migration must have a unique, monotonically ordered version. The
8
8
  * version is stored in a BIGINT column, so timestamp-style versions such as
9
9
  * `20260908120000` are supported up to `Number.MAX_SAFE_INTEGER`.
10
+ *
11
+ * `TTransaction` is the transaction client handed to `up` and `down`;
12
+ * `createMigrationRunner` infers it from the database client.
10
13
  */
11
- export interface Migration {
14
+ export interface Migration<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext> {
12
15
  readonly version: number;
13
16
  readonly name: string;
14
17
  /**
15
18
  * Applies the migration.
16
19
  */
17
- readonly up: (database: DatabaseTransactionContext) => Promise<void>;
20
+ readonly up: (database: TTransaction) => Promise<void>;
18
21
  /**
19
22
  * Reverts the migration.
20
23
  */
21
- readonly down?: (database: DatabaseTransactionContext) => Promise<void>;
24
+ readonly down?: (database: TTransaction) => Promise<void>;
22
25
  }
23
26
  /**
24
27
  * Persisted migration record.
@@ -38,10 +41,10 @@ export interface MigrationResult {
38
41
  /**
39
42
  * Migration status.
40
43
  */
41
- export interface MigrationStatus {
44
+ export interface MigrationStatus<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext> {
42
45
  readonly currentVersion: number;
43
46
  readonly latestVersion: number;
44
- readonly pending: readonly Migration[];
47
+ readonly pending: readonly Migration<TTransaction>[];
45
48
  readonly applied: readonly MigrationRecord[];
46
49
  }
47
50
  /**
@@ -3,8 +3,11 @@ import { type SqlDialectName } from "../migration/migration.dialect.js";
3
3
  import type { RunnerTransactionOptions } from "../migration/migration.types.js";
4
4
  /**
5
5
  * Defines a database seed operation.
6
+ *
7
+ * `TTransaction` is the transaction client handed to `run` and `rollback`;
8
+ * {@link createSeedRunner} infers it from the database client.
6
9
  */
7
- export interface Seed {
10
+ export interface Seed<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext> {
8
11
  /**
9
12
  * Unique seed name.
10
13
  */
@@ -16,11 +19,11 @@ export interface Seed {
16
19
  /**
17
20
  * Executes the seed.
18
21
  */
19
- readonly run: (database: DatabaseTransactionContext) => Promise<void>;
22
+ readonly run: (database: TTransaction) => Promise<void>;
20
23
  /**
21
24
  * Optional cleanup operation.
22
25
  */
23
- readonly rollback?: (database: DatabaseTransactionContext) => Promise<void>;
26
+ readonly rollback?: (database: TTransaction) => Promise<void>;
24
27
  }
25
28
  /**
26
29
  * Persisted seed execution record.
@@ -45,8 +48,8 @@ export interface SeedResult {
45
48
  /**
46
49
  * Seed runner status.
47
50
  */
48
- export interface SeedStatus {
49
- readonly pending: readonly Seed[];
51
+ export interface SeedStatus<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext> {
52
+ readonly pending: readonly Seed<TTransaction>[];
50
53
  readonly applied: readonly SeedRecord[];
51
54
  }
52
55
  /**
@@ -91,7 +94,7 @@ export declare const DEFAULT_SEED_LOCK = "database:seeds";
91
94
  * lock before deciding what to execute, so two runners started together
92
95
  * never apply or revert the same seed twice.
93
96
  */
94
- export declare class SeedRunner {
97
+ export declare class SeedRunner<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext> {
95
98
  private readonly client;
96
99
  private readonly seeds;
97
100
  private readonly tableName;
@@ -99,11 +102,11 @@ export declare class SeedRunner {
99
102
  private readonly dialect;
100
103
  private readonly transactionOptions;
101
104
  private readonly perItemTransaction;
102
- constructor(client: DatabaseClient, seeds: readonly Seed[], options?: SeedRunnerOptions);
105
+ constructor(client: DatabaseClient<TTransaction>, seeds: readonly Seed<TTransaction>[], options?: SeedRunnerOptions);
103
106
  /**
104
107
  * Returns the seed runner status.
105
108
  */
106
- status(): Promise<SeedStatus>;
109
+ status(): Promise<SeedStatus<TTransaction>>;
107
110
  /**
108
111
  * Executes every pending seed in order.
109
112
  */
@@ -148,13 +151,13 @@ export declare class SeedRunner {
148
151
  /**
149
152
  * Creates a seed runner.
150
153
  */
151
- export declare function createSeedRunner(client: DatabaseClient, seeds: readonly Seed[], options?: SeedRunnerOptions): SeedRunner;
154
+ export declare function createSeedRunner<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext>(client: DatabaseClient<TTransaction>, seeds: readonly Seed<TTransaction>[], options?: SeedRunnerOptions): SeedRunner<TTransaction>;
152
155
  /**
153
156
  * Validates and sorts seed definitions (stable sort on `order`).
154
157
  */
155
- export declare function normalizeSeeds(seeds: readonly Seed[]): readonly Seed[];
158
+ export declare function normalizeSeeds<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext>(seeds: readonly Seed<TTransaction>[]): readonly Seed<TTransaction>[];
156
159
  /**
157
160
  * Validates one seed definition.
158
161
  */
159
- export declare function validateSeed(seed: Seed): void;
162
+ export declare function validateSeed<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext>(seed: Seed<TTransaction>): void;
160
163
  //# sourceMappingURL=seed.runner.d.ts.map
@@ -64,15 +64,18 @@ export interface TransactionRetryOptions extends ManagedTransactionOptions {
64
64
  export declare function createTransactionId(): string;
65
65
  /**
66
66
  * Manages transaction execution and lifecycle metadata.
67
+ *
68
+ * `TTransaction` is the transaction client handed to callbacks, taken from
69
+ * the {@link DatabaseClient} the manager wraps.
67
70
  */
68
- export declare class TransactionManager {
71
+ export declare class TransactionManager<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext> {
69
72
  private readonly client;
70
- constructor(client: DatabaseClient);
73
+ constructor(client: DatabaseClient<TTransaction>);
71
74
  /**
72
75
  * Executes a callback inside a managed transaction and returns its
73
76
  * result.
74
77
  */
75
- execute<TResult>(callback: (transaction: DatabaseTransactionContext, context: TransactionContext) => Promise<TResult>, options?: ManagedTransactionOptions): Promise<TResult>;
78
+ execute<TResult>(callback: (transaction: TTransaction, context: TransactionContext) => Promise<TResult>, options?: ManagedTransactionOptions): Promise<TResult>;
76
79
  /**
77
80
  * Executes a callback inside a managed transaction and returns the
78
81
  * result together with the final context (`status: "committed"`).
@@ -86,16 +89,16 @@ export declare class TransactionManager {
86
89
  * `NotFoundError` or `DomainError`) rolls the transaction back and is
87
90
  * rethrown as the same, unmodified instance.
88
91
  */
89
- run<TResult>(callback: (transaction: DatabaseTransactionContext, context: TransactionContext) => Promise<TResult>, options?: ManagedTransactionOptions): Promise<TransactionOutcome<TResult>>;
92
+ run<TResult>(callback: (transaction: TTransaction, context: TransactionContext) => Promise<TResult>, options?: ManagedTransactionOptions): Promise<TransactionOutcome<TResult>>;
90
93
  /**
91
94
  * Returns the database client used by the manager.
92
95
  */
93
- getClient(): DatabaseClient;
96
+ getClient(): DatabaseClient<TTransaction>;
94
97
  }
95
98
  /**
96
99
  * Creates a transaction manager.
97
100
  */
98
- export declare function createTransactionManager(client: DatabaseClient): TransactionManager;
101
+ export declare function createTransactionManager<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext>(client: DatabaseClient<TTransaction>): TransactionManager<TTransaction>;
99
102
  /**
100
103
  * Executes a managed database transaction.
101
104
  *
@@ -104,7 +107,7 @@ export declare function createTransactionManager(client: DatabaseClient): Transa
104
107
  * validation, not-found, ...) propagates unchanged; driver and database
105
108
  * failures and any other thrown value become a `DatabaseError`.
106
109
  */
107
- export declare function withTransaction<TResult>(client: DatabaseClient, callback: (transaction: DatabaseTransactionContext, context: TransactionContext) => Promise<TResult>, options?: ManagedTransactionOptions): Promise<TResult>;
110
+ export declare function withTransaction<TResult, TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext>(client: DatabaseClient<TTransaction>, callback: (transaction: TTransaction, context: TransactionContext) => Promise<TResult>, options?: ManagedTransactionOptions): Promise<TResult>;
108
111
  /**
109
112
  * Executes a transaction with retry support.
110
113
  *
@@ -112,7 +115,7 @@ export declare function withTransaction<TResult>(client: DatabaseClient, callbac
112
115
  * callback must be idempotent with respect to any side effects performed
113
116
  * outside the transaction client (for example, sending emails).
114
117
  */
115
- export declare function withTransactionRetry<TResult>(client: DatabaseClient, callback: (transaction: DatabaseTransactionContext, context: TransactionContext) => Promise<TResult>, options?: TransactionRetryOptions): Promise<TResult>;
118
+ export declare function withTransactionRetry<TResult, TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext>(client: DatabaseClient<TTransaction>, callback: (transaction: TTransaction, context: TransactionContext) => Promise<TResult>, options?: TransactionRetryOptions): Promise<TResult>;
116
119
  /**
117
120
  * Creates an immutable transaction context.
118
121
  */
@@ -12,6 +12,9 @@ export function createTransactionId() {
12
12
  }
13
13
  /**
14
14
  * Manages transaction execution and lifecycle metadata.
15
+ *
16
+ * `TTransaction` is the transaction client handed to callbacks, taken from
17
+ * the {@link DatabaseClient} the manager wraps.
15
18
  */
16
19
  export class TransactionManager {
17
20
  client;
@@ -8,37 +8,39 @@ import type { TransactionCallback, TransactionOptions } from "../databaseType/da
8
8
  * Repositories must be rebound to the transaction client handed to the
9
9
  * callback (see `BaseRepository.withTransaction`); repositories built from
10
10
  * the root client run outside the transaction.
11
+ *
12
+ * `TTransaction` is the transaction client handed to the callback.
11
13
  */
12
- export interface UnitOfWork {
13
- execute<TResult>(callback: TransactionCallback<DatabaseTransactionContext, TResult>, options?: TransactionOptions): Promise<TResult>;
14
+ export interface UnitOfWork<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext> {
15
+ execute<TResult>(callback: TransactionCallback<TTransaction, TResult>, options?: TransactionOptions): Promise<TResult>;
14
16
  }
15
17
  /**
16
18
  * Configuration for a unit of work.
17
19
  */
18
- export interface UnitOfWorkOptions {
19
- readonly client: DatabaseClient;
20
+ export interface UnitOfWorkOptions<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext> {
21
+ readonly client: DatabaseClient<TTransaction>;
20
22
  }
21
23
  /**
22
24
  * Prisma-backed unit of work.
23
25
  */
24
- export declare class DatabaseUnitOfWork implements UnitOfWork {
26
+ export declare class DatabaseUnitOfWork<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext> implements UnitOfWork<TTransaction> {
25
27
  private readonly client;
26
- constructor(options: UnitOfWorkOptions);
28
+ constructor(options: UnitOfWorkOptions<TTransaction>);
27
29
  /**
28
30
  * Executes a callback inside a transaction.
29
31
  */
30
- execute<TResult>(callback: TransactionCallback<DatabaseTransactionContext, TResult>, options?: TransactionOptions): Promise<TResult>;
32
+ execute<TResult>(callback: TransactionCallback<TTransaction, TResult>, options?: TransactionOptions): Promise<TResult>;
31
33
  /**
32
34
  * Returns the database client used by this unit of work.
33
35
  */
34
- getClient(): DatabaseClient;
36
+ getClient(): DatabaseClient<TTransaction>;
35
37
  }
36
38
  /**
37
39
  * Creates a database unit of work.
38
40
  */
39
- export declare function createUnitOfWork(client: DatabaseClient): DatabaseUnitOfWork;
41
+ export declare function createUnitOfWork<TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext>(client: DatabaseClient<TTransaction>): DatabaseUnitOfWork<TTransaction>;
40
42
  /**
41
43
  * Executes a callback as a single database transaction.
42
44
  */
43
- export declare function executeUnitOfWork<TResult>(client: DatabaseClient, callback: TransactionCallback<DatabaseTransactionContext, TResult>, options?: TransactionOptions): Promise<TResult>;
45
+ export declare function executeUnitOfWork<TResult, TTransaction extends DatabaseTransactionContext = DatabaseTransactionContext>(client: DatabaseClient<TTransaction>, callback: TransactionCallback<TTransaction, TResult>, options?: TransactionOptions): Promise<TResult>;
44
46
  //# sourceMappingURL=unitOfWork.core.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zudojs/database",
3
- "version": "1.3.2",
3
+ "version": "1.4.0",
4
4
  "description": "Database abstraction layer with clients, repositories, transactions, and query building for Zudojs applications.",
5
5
  "license": "MIT",
6
6
  "author": {
@@ -41,8 +41,8 @@
41
41
  "!dist/.tsbuildinfo"
42
42
  ],
43
43
  "dependencies": {
44
- "@zudojs/errors": "1.3.1",
45
- "@zudojs/logger": "1.4.2",
44
+ "@zudojs/errors": "1.3.2",
45
+ "@zudojs/logger": "1.4.3",
46
46
  "@zudojs/types": "1.2.0"
47
47
  },
48
48
  "peerDependencies": {