@zudojs/database 0.0.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (134) hide show
  1. package/README.md +223 -17
  2. package/dist/cache/cache.memory.d.ts +167 -0
  3. package/dist/cache/cache.memory.js +186 -40
  4. package/dist/cache/index.d.ts +2 -2
  5. package/dist/cache/index.js +2 -2
  6. package/dist/database/database.core.d.ts +91 -0
  7. package/dist/database/database.core.js +150 -0
  8. package/dist/databaseClient/databaseClient.core.d.ts +165 -14
  9. package/dist/databaseClient/databaseClient.core.js +304 -70
  10. package/dist/databaseClient/databaseClient.errors.d.ts +112 -0
  11. package/dist/databaseClient/databaseClient.errors.js +425 -0
  12. package/dist/databaseClient/index.d.ts +2 -1
  13. package/dist/databaseClient/index.js +2 -1
  14. package/dist/databaseConnection/databaseConnection.manager.d.ts +163 -0
  15. package/dist/databaseConnection/databaseConnection.manager.js +138 -76
  16. package/dist/databaseConnection/index.d.ts +1 -1
  17. package/dist/databaseType/databaseType.type.d.ts +224 -0
  18. package/dist/databaseType/databaseType.type.js +10 -0
  19. package/dist/health/health.check.d.ts +74 -0
  20. package/dist/health/health.check.js +78 -47
  21. package/dist/health/index.d.ts +1 -1
  22. package/dist/health/index.js +1 -1
  23. package/dist/index.d.ts +14 -14
  24. package/dist/index.js +11 -11
  25. package/dist/locks/index.d.ts +2 -2
  26. package/dist/locks/index.js +2 -2
  27. package/dist/locks/locks.core.d.ts +128 -0
  28. package/dist/locks/locks.core.js +144 -85
  29. package/dist/migration/index.d.ts +3 -1
  30. package/dist/migration/index.js +2 -0
  31. package/dist/migration/migration.dialect.d.ts +65 -0
  32. package/dist/migration/migration.dialect.js +63 -0
  33. package/dist/migration/migration.helpers.d.ts +65 -0
  34. package/dist/migration/migration.helpers.js +141 -0
  35. package/dist/migration/migration.runner.d.ts +75 -0
  36. package/dist/migration/migration.runner.js +195 -107
  37. package/dist/migration/migration.types.d.ts +85 -0
  38. package/dist/migration/migration.types.js +2 -0
  39. package/dist/pagination/index.d.ts +2 -1
  40. package/dist/pagination/index.js +2 -1
  41. package/dist/pagination/pagination.core.d.ts +163 -0
  42. package/dist/pagination/pagination.core.js +279 -0
  43. package/dist/pagination/pagination.keyset.d.ts +55 -0
  44. package/dist/pagination/pagination.keyset.js +108 -0
  45. package/dist/queryBuilder/index.d.ts +4 -3
  46. package/dist/queryBuilder/index.js +3 -2
  47. package/dist/queryBuilder/queryBuilder.core.d.ts +178 -0
  48. package/dist/queryBuilder/queryBuilder.core.js +420 -0
  49. package/dist/queryBuilder/queryBuilder.factory.d.ts +6 -0
  50. package/dist/queryBuilder/queryBuilder.factory.js +8 -0
  51. package/dist/queryBuilder/queryBuilder.filter.d.ts +170 -0
  52. package/dist/queryBuilder/queryBuilder.filter.js +497 -0
  53. package/dist/queryBuilder/queryBuilder.prisma.d.ts +60 -0
  54. package/dist/queryBuilder/queryBuilder.prisma.js +233 -0
  55. package/dist/queryBuilder/queryBuilder.type.d.ts +48 -0
  56. package/dist/queryBuilder/queryBuilder.type.js +2 -0
  57. package/dist/relations/index.d.ts +1 -1
  58. package/dist/relations/index.js +1 -1
  59. package/dist/relations/relations.definition.d.ts +184 -0
  60. package/dist/relations/relations.definition.js +362 -0
  61. package/dist/repository/index.d.ts +2 -1
  62. package/dist/repository/index.js +1 -0
  63. package/dist/repository/repository.base.d.ts +270 -0
  64. package/dist/repository/repository.base.js +350 -84
  65. package/dist/repository/repository.errors.d.ts +60 -0
  66. package/dist/repository/repository.errors.js +235 -0
  67. package/dist/seed/index.d.ts +2 -2
  68. package/dist/seed/index.js +1 -1
  69. package/dist/seed/seed.runner.d.ts +160 -0
  70. package/dist/seed/seed.runner.js +194 -225
  71. package/dist/transaction/index.d.ts +1 -1
  72. package/dist/transaction/index.js +1 -1
  73. package/dist/transaction/transaction.core.d.ts +117 -0
  74. package/dist/transaction/transaction.core.js +83 -56
  75. package/dist/unitOfWork/unitOfWork.core.d.ts +44 -0
  76. package/dist/unitOfWork/unitOfWork.core.js +9 -20
  77. package/package.json +35 -19
  78. package/dist/cache/cache.memory.d.ts.map +0 -1
  79. package/dist/cache/cache.memory.js.map +0 -1
  80. package/dist/cache/index.d.ts.map +0 -1
  81. package/dist/cache/index.js.map +0 -1
  82. package/dist/database/database.core.d.ts.map +0 -1
  83. package/dist/database/index.d.ts.map +0 -1
  84. package/dist/database/index.js.map +0 -1
  85. package/dist/databaseClient/databaseClient.core.d.ts.map +0 -1
  86. package/dist/databaseClient/databaseClient.core.js.map +0 -1
  87. package/dist/databaseClient/databaseClient.logger.d.ts.map +0 -1
  88. package/dist/databaseClient/databaseClient.logger.js.map +0 -1
  89. package/dist/databaseClient/index.d.ts.map +0 -1
  90. package/dist/databaseClient/index.js.map +0 -1
  91. package/dist/databaseConnection/databaseConnection.manager.d.ts.map +0 -1
  92. package/dist/databaseConnection/databaseConnection.manager.js.map +0 -1
  93. package/dist/databaseConnection/index.d.ts.map +0 -1
  94. package/dist/databaseConnection/index.js.map +0 -1
  95. package/dist/databaseType/index.d.ts.map +0 -1
  96. package/dist/databaseType/index.js.map +0 -1
  97. package/dist/health/health.check.d.ts.map +0 -1
  98. package/dist/health/health.check.js.map +0 -1
  99. package/dist/health/index.d.ts.map +0 -1
  100. package/dist/health/index.js.map +0 -1
  101. package/dist/index.d.ts.map +0 -1
  102. package/dist/index.js.map +0 -1
  103. package/dist/locks/index.d.ts.map +0 -1
  104. package/dist/locks/index.js.map +0 -1
  105. package/dist/locks/locks.core.d.ts.map +0 -1
  106. package/dist/locks/locks.core.js.map +0 -1
  107. package/dist/migration/index.d.ts.map +0 -1
  108. package/dist/migration/index.js.map +0 -1
  109. package/dist/migration/migration.helpers.d.ts.map +0 -1
  110. package/dist/migration/migration.runner.d.ts.map +0 -1
  111. package/dist/migration/migration.runner.js.map +0 -1
  112. package/dist/migration/migration.types.d.ts.map +0 -1
  113. package/dist/pagination/index.d.ts.map +0 -1
  114. package/dist/pagination/index.js.map +0 -1
  115. package/dist/queryBuilder/index.d.ts.map +0 -1
  116. package/dist/queryBuilder/index.js.map +0 -1
  117. package/dist/relations/index.d.ts.map +0 -1
  118. package/dist/relations/index.js.map +0 -1
  119. package/dist/repository/index.d.ts.map +0 -1
  120. package/dist/repository/index.js.map +0 -1
  121. package/dist/repository/repository.base.d.ts.map +0 -1
  122. package/dist/repository/repository.base.js.map +0 -1
  123. package/dist/seed/index.d.ts.map +0 -1
  124. package/dist/seed/index.js.map +0 -1
  125. package/dist/seed/seed.runner.d.ts.map +0 -1
  126. package/dist/seed/seed.runner.js.map +0 -1
  127. package/dist/transaction/index.d.ts.map +0 -1
  128. package/dist/transaction/index.js.map +0 -1
  129. package/dist/transaction/transaction.core.d.ts.map +0 -1
  130. package/dist/transaction/transaction.core.js.map +0 -1
  131. package/dist/unitOfWork/index.d.ts.map +0 -1
  132. package/dist/unitOfWork/index.js.map +0 -1
  133. package/dist/unitOfWork/unitOfWork.core.d.ts.map +0 -1
  134. package/dist/unitOfWork/unitOfWork.core.js.map +0 -1
@@ -1,5 +1,7 @@
1
- import { DatabaseError } from "@zudojs/errors";
1
+ import { DatabaseOperation } from "@zudojs/errors";
2
2
  import { DatabaseClient, } from "../databaseClient/databaseClient.core.js";
3
+ import { normalizeDatabaseError } from "../databaseClient/databaseClient.errors.js";
4
+ import { checkDatabaseHealth, getHealthCheckCause, } from "../health/health.check.js";
3
5
  /**
4
6
  * Manages the database connection lifecycle.
5
7
  *
@@ -12,47 +14,68 @@ export class DatabaseConnectionManager {
12
14
  listeners = new Set();
13
15
  autoConnect;
14
16
  healthCheckIntervalMs;
17
+ healthCheckTimeoutMs;
18
+ reconnect;
15
19
  healthCheckTimer;
16
- connectionPromise;
20
+ healthCheckInFlight;
21
+ consecutiveFailures = 0;
22
+ reconnectPromise;
23
+ lastHealth;
24
+ destroyed = false;
17
25
  constructor(options = {}) {
18
- this.client = new DatabaseClient(options);
19
- this.autoConnect = options.autoConnect ?? false;
20
- this.healthCheckIntervalMs = options.healthCheckIntervalMs;
26
+ const { client, autoConnect, healthCheckIntervalMs, healthCheckTimeoutMs, reconnect, ...clientOptions } = options;
27
+ this.client = client ?? new DatabaseClient(clientOptions);
28
+ this.autoConnect = autoConnect ?? false;
29
+ if (healthCheckIntervalMs !== undefined) {
30
+ if (!Number.isFinite(healthCheckIntervalMs) || healthCheckIntervalMs <= 0) {
31
+ throw new TypeError("healthCheckIntervalMs must be a positive finite number.");
32
+ }
33
+ this.healthCheckIntervalMs = Math.floor(healthCheckIntervalMs);
34
+ }
35
+ const defaultTimeout = Math.min(this.healthCheckIntervalMs ?? 5_000, 5_000);
36
+ this.healthCheckTimeoutMs = healthCheckTimeoutMs ?? defaultTimeout;
37
+ if (!Number.isFinite(this.healthCheckTimeoutMs) || this.healthCheckTimeoutMs <= 0) {
38
+ throw new TypeError("healthCheckTimeoutMs must be a positive finite number.");
39
+ }
40
+ this.reconnect =
41
+ reconnect === false
42
+ ? undefined
43
+ : {
44
+ failureThreshold: Math.max(1, Math.floor(reconnect?.failureThreshold ?? 1)),
45
+ maxAttempts: Math.max(1, Math.floor(reconnect?.maxAttempts ?? 5)),
46
+ baseDelayMs: Math.max(0, reconnect?.baseDelayMs ?? 500),
47
+ maxDelayMs: Math.max(0, reconnect?.maxDelayMs ?? 30_000),
48
+ };
21
49
  }
22
50
  /**
23
51
  * Initializes the connection manager.
24
52
  */
25
53
  async initialize() {
26
- if (!this.autoConnect) {
54
+ if (!this.autoConnect)
27
55
  return;
28
- }
29
56
  await this.connect();
30
57
  }
31
58
  /**
32
- * Opens the database connection.
59
+ * Opens the database connection. Concurrent calls share the client's
60
+ * in-flight attempt.
33
61
  */
34
62
  async connect() {
35
- if (this.client.getStatus() === "connected") {
63
+ if (this.client.getStatus() === "connected")
36
64
  return;
37
- }
38
- if (this.connectionPromise) {
39
- return this.connectionPromise;
40
- }
41
65
  this.emit("connecting");
42
- this.connectionPromise = this.client
43
- .connect()
44
- .then(() => {
66
+ try {
67
+ await this.client.connect();
68
+ this.consecutiveFailures = 0;
45
69
  this.emit("connected");
46
70
  this.startHealthChecks();
47
- })
48
- .catch((error) => {
71
+ }
72
+ catch (error) {
49
73
  this.emit("error", error);
50
- throw this.normalizeError(error, "Database connection failed.");
51
- })
52
- .finally(() => {
53
- this.connectionPromise = undefined;
54
- });
55
- return this.connectionPromise;
74
+ throw normalizeDatabaseError(error, {
75
+ operation: DatabaseOperation.CONNECT,
76
+ fallbackMessage: "Database connection failed.",
77
+ });
78
+ }
56
79
  }
57
80
  /**
58
81
  * Closes the database connection.
@@ -60,9 +83,8 @@ export class DatabaseConnectionManager {
60
83
  async disconnect() {
61
84
  this.stopHealthChecks();
62
85
  const status = this.client.getStatus();
63
- if (status === "disconnected" || status === "disconnecting") {
86
+ if (status === "disconnected" || status === "disconnecting")
64
87
  return;
65
- }
66
88
  this.emit("disconnecting");
67
89
  try {
68
90
  await this.client.disconnect();
@@ -70,7 +92,10 @@ export class DatabaseConnectionManager {
70
92
  }
71
93
  catch (error) {
72
94
  this.emit("error", error);
73
- throw this.normalizeError(error, "Database disconnection failed.");
95
+ throw normalizeDatabaseError(error, {
96
+ operation: DatabaseOperation.DISCONNECT,
97
+ fallbackMessage: "Database disconnection failed.",
98
+ });
74
99
  }
75
100
  }
76
101
  /**
@@ -88,10 +113,16 @@ export class DatabaseConnectionManager {
88
113
  return this.client.getStatus();
89
114
  }
90
115
  /**
91
- * Performs a database health check.
116
+ * Performs a database health check (with the configured timeout).
92
117
  */
93
118
  async healthCheck() {
94
- return this.client.healthCheck();
119
+ return this.client.healthCheck({ timeoutMs: this.healthCheckTimeoutMs });
120
+ }
121
+ /**
122
+ * Returns the result of the most recent scheduled health check.
123
+ */
124
+ getLastHealth() {
125
+ return this.lastHealth;
95
126
  }
96
127
  /**
97
128
  * Returns the underlying database client.
@@ -124,92 +155,123 @@ export class DatabaseConnectionManager {
124
155
  this.listeners.clear();
125
156
  }
126
157
  /**
127
- * Starts periodic database health checks.
158
+ * Starts periodic database health checks. A tick is skipped while a
159
+ * previous check is still in flight, so a hung database never
160
+ * accumulates pending probes.
128
161
  */
129
162
  startHealthChecks() {
130
163
  this.stopHealthChecks();
131
164
  const interval = this.healthCheckIntervalMs;
132
- if (!interval || interval <= 0) {
165
+ if (!interval || this.destroyed)
133
166
  return;
134
- }
135
167
  this.healthCheckTimer = setInterval(() => {
136
168
  void this.runScheduledHealthCheck();
137
169
  }, interval);
138
- this.unrefTimer();
170
+ this.healthCheckTimer.unref?.();
139
171
  }
140
172
  /**
141
173
  * Stops periodic database health checks.
142
174
  */
143
175
  stopHealthChecks() {
144
- if (!this.healthCheckTimer) {
176
+ if (!this.healthCheckTimer)
145
177
  return;
146
- }
147
178
  clearInterval(this.healthCheckTimer);
148
179
  this.healthCheckTimer = undefined;
149
180
  }
181
+ /**
182
+ * Runs one scheduled health check immediately (also used by the timer).
183
+ * Resolves once the check, and any reconnect it triggers, has settled.
184
+ */
185
+ async runScheduledHealthCheck() {
186
+ if (this.healthCheckInFlight)
187
+ return this.healthCheckInFlight;
188
+ this.healthCheckInFlight = this.performScheduledHealthCheck().finally(() => {
189
+ this.healthCheckInFlight = undefined;
190
+ });
191
+ return this.healthCheckInFlight;
192
+ }
150
193
  /**
151
194
  * Releases connection manager resources.
152
195
  */
153
196
  async destroy() {
197
+ this.destroyed = true;
154
198
  this.stopHealthChecks();
155
199
  await this.disconnect();
156
200
  this.removeAllListeners();
157
201
  }
158
- /**
159
- * Emits a connection lifecycle event.
160
- */
161
- emit(event, error) {
202
+ async performScheduledHealthCheck() {
203
+ const health = await checkDatabaseHealth(this.client, {
204
+ timeoutMs: this.healthCheckTimeoutMs,
205
+ });
206
+ this.lastHealth = health;
207
+ if (health.healthy) {
208
+ this.consecutiveFailures = 0;
209
+ return;
210
+ }
211
+ this.consecutiveFailures += 1;
212
+ this.emit("error", getHealthCheckCause(health) ?? health.error);
213
+ if (this.reconnect &&
214
+ !this.destroyed &&
215
+ this.consecutiveFailures >= this.reconnect.failureThreshold) {
216
+ await this.reconnectWithBackoff();
217
+ }
218
+ }
219
+ reconnectWithBackoff() {
220
+ if (this.reconnectPromise)
221
+ return this.reconnectPromise;
222
+ this.reconnectPromise = this.performReconnect().finally(() => {
223
+ this.reconnectPromise = undefined;
224
+ });
225
+ return this.reconnectPromise;
226
+ }
227
+ async performReconnect() {
228
+ const policy = this.reconnect;
229
+ if (!policy)
230
+ return;
231
+ for (let attempt = 1; attempt <= policy.maxAttempts; attempt += 1) {
232
+ if (this.destroyed)
233
+ return;
234
+ this.emit("reconnecting", undefined, attempt);
235
+ try {
236
+ await this.client.disconnect().catch(() => undefined);
237
+ await this.client.connect();
238
+ this.consecutiveFailures = 0;
239
+ this.emit("connected", undefined, attempt);
240
+ return;
241
+ }
242
+ catch (error) {
243
+ this.emit("error", error, attempt);
244
+ if (attempt === policy.maxAttempts)
245
+ return;
246
+ const delay = Math.min(policy.maxDelayMs, policy.baseDelayMs * Math.pow(2, attempt - 1));
247
+ if (delay > 0)
248
+ await sleep(delay);
249
+ }
250
+ }
251
+ }
252
+ emit(event, error, attempt) {
162
253
  const details = Object.freeze({
163
254
  status: this.client.getStatus(),
164
255
  timestamp: new Date(),
165
256
  error,
257
+ attempt,
166
258
  });
167
259
  for (const listener of [...this.listeners]) {
168
260
  try {
169
261
  listener(event, details);
170
262
  }
171
263
  catch {
172
- // Connection lifecycle listeners must never break the
173
- // database connection lifecycle itself.
174
- }
175
- }
176
- }
177
- /**
178
- * Runs a scheduled health check.
179
- */
180
- async runScheduledHealthCheck() {
181
- try {
182
- const health = await this.healthCheck();
183
- if (health.status === "error") {
184
- this.emit("error", health.error);
264
+ // Listeners must never break the connection lifecycle itself.
185
265
  }
186
266
  }
187
- catch (error) {
188
- this.emit("error", error);
189
- }
190
- }
191
- /**
192
- * Prevents the health-check timer from keeping Node alive.
193
- */
194
- unrefTimer() {
195
- const timer = this.healthCheckTimer;
196
- timer?.unref?.();
197
- }
198
- /**
199
- * Normalizes connection failures.
200
- */
201
- normalizeError(error, fallbackMessage) {
202
- if (error instanceof DatabaseError) {
203
- return error;
204
- }
205
- return new DatabaseError(error instanceof Error ? error.message : fallbackMessage, {
206
- cause: error,
207
- metadata: {
208
- status: this.client.getStatus(),
209
- },
210
- });
211
267
  }
212
268
  }
269
+ function sleep(milliseconds) {
270
+ return new Promise((resolve) => {
271
+ const timer = setTimeout(resolve, milliseconds);
272
+ timer.unref?.();
273
+ });
274
+ }
213
275
  /**
214
276
  * Creates a database connection manager.
215
277
  */
@@ -3,5 +3,5 @@
3
3
  *
4
4
  * Connection lifecycle management with health checks.
5
5
  */
6
- export { DatabaseConnectionManager, createConnectionManager, type DatabaseConnectionEvent, type DatabaseConnectionListener, type DatabaseConnectionEventDetails, type DatabaseConnectionManagerOptions, } from "./databaseConnection.manager.js";
6
+ export { DatabaseConnectionManager, createConnectionManager, type DatabaseConnectionEvent, type DatabaseConnectionListener, type DatabaseConnectionEventDetails, type DatabaseConnectionManagerOptions, type DatabaseReconnectOptions, } from "./databaseConnection.manager.js";
7
7
  //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Database connection lifecycle states.
3
+ */
4
+ export type DatabaseStatus = "disconnected" | "connecting" | "connected" | "disconnecting" | "error";
5
+ /**
6
+ * Supported transaction isolation levels.
7
+ */
8
+ export type TransactionIsolationLevel = "ReadUncommitted" | "ReadCommitted" | "RepeatableRead" | "Serializable";
9
+ /**
10
+ * Database operation types.
11
+ */
12
+ export type DatabaseOperation = "create" | "read" | "update" | "delete" | "upsert" | "count" | "aggregate" | "transaction" | "raw";
13
+ /**
14
+ * Options shared by database operations.
15
+ */
16
+ export interface DatabaseOperationOptions {
17
+ /**
18
+ * Aborts the operation from the caller's side. The repository rejects
19
+ * with an `ERR_OPERATION_CANCELLED` DatabaseError as soon as the signal
20
+ * fires; the underlying database query is not cancelled server-side.
21
+ */
22
+ readonly signal?: AbortSignal;
23
+ /**
24
+ * Client-side timeout in milliseconds. When exceeded the caller receives
25
+ * an `ERR_DATABASE_TIMEOUT` DatabaseError; the query itself keeps running
26
+ * on the server until it completes (use a statement timeout for real
27
+ * cancellation).
28
+ */
29
+ readonly timeoutMs?: number;
30
+ readonly metadata?: Readonly<Record<string, unknown>>;
31
+ }
32
+ /**
33
+ * Options used when establishing a database connection.
34
+ *
35
+ * Prisma 7 configures the connection URL, pool size and SSL on the driver
36
+ * adapter, so only options this package can actually honour are declared
37
+ * here.
38
+ */
39
+ export interface DatabaseConnectionOptions {
40
+ /**
41
+ * Client-side deadline for `$connect()` (default 10 000 ms). Non-finite
42
+ * or non-positive values disable the timeout.
43
+ */
44
+ readonly connectionTimeoutMs?: number;
45
+ /**
46
+ * Emits Prisma query events (duration and target only) to the logger.
47
+ */
48
+ readonly logging?: boolean;
49
+ }
50
+ /**
51
+ * Lifecycle-oriented health snapshot returned by `DatabaseClient.healthCheck()`
52
+ * and the `Database` facade. For the richer probe result (healthy /
53
+ * degraded / unhealthy plus an error object) use `checkDatabaseHealth`
54
+ * from the health module.
55
+ */
56
+ export interface DatabaseClientHealth {
57
+ readonly status: DatabaseStatus;
58
+ readonly latencyMs?: number;
59
+ readonly checkedAt: Date;
60
+ readonly error?: string;
61
+ }
62
+ /**
63
+ * @deprecated Use {@link DatabaseClientHealth}. Kept as an alias so the
64
+ * name does not clash with the health module's `DatabaseHealth`.
65
+ */
66
+ export type DatabaseHealth = DatabaseClientHealth;
67
+ /**
68
+ * Transaction configuration.
69
+ */
70
+ export interface TransactionOptions extends DatabaseOperationOptions {
71
+ readonly isolationLevel?: TransactionIsolationLevel;
72
+ readonly timeoutMs?: number;
73
+ readonly maxWaitMs?: number;
74
+ }
75
+ /**
76
+ * Generic transaction callback.
77
+ */
78
+ export type TransactionCallback<TContext, TResult> = (context: TContext) => Promise<TResult>;
79
+ /**
80
+ * Generic database client contract implemented by the concrete
81
+ * `DatabaseClient` class. Kept out of the root barrel to avoid clashing
82
+ * with the class name; prefer the {@link DatabaseClientContract} alias.
83
+ */
84
+ export interface DatabaseClient<TTransactionContext = unknown> {
85
+ connect(): Promise<void>;
86
+ disconnect(): Promise<void>;
87
+ ping(): Promise<void>;
88
+ getStatus(): DatabaseStatus;
89
+ healthCheck(): Promise<DatabaseClientHealth>;
90
+ transaction<TResult>(callback: TransactionCallback<TTransactionContext, TResult>, options?: TransactionOptions): Promise<TResult>;
91
+ }
92
+ /**
93
+ * Alias of the {@link DatabaseClient} contract interface.
94
+ */
95
+ export type DatabaseClientContract<TTransactionContext = unknown> = DatabaseClient<TTransactionContext>;
96
+ /**
97
+ * Generic repository contract.
98
+ */
99
+ export interface Repository<TEntity, TId = string, TCreateInput = Partial<TEntity>, TUpdateInput = Partial<TEntity>, TFilter = unknown> {
100
+ findById(id: TId, options?: DatabaseOperationOptions): Promise<TEntity | null>;
101
+ findOne(filter: TFilter, options?: DatabaseOperationOptions): Promise<TEntity | null>;
102
+ findMany(filter?: TFilter, options?: DatabaseOperationOptions): Promise<readonly TEntity[]>;
103
+ findPaginated(filter?: TFilter, options?: QueryOptions): Promise<PaginatedResult<TEntity>>;
104
+ create(input: TCreateInput, options?: DatabaseOperationOptions): Promise<TEntity>;
105
+ createMany(inputs: readonly TCreateInput[], options?: DatabaseOperationOptions): Promise<number>;
106
+ update(id: TId, input: TUpdateInput, options?: DatabaseOperationOptions): Promise<TEntity>;
107
+ upsert(where: TFilter, create: TCreateInput, update: TUpdateInput, options?: DatabaseOperationOptions): Promise<TEntity>;
108
+ delete(id: TId, options?: DatabaseOperationOptions): Promise<void>;
109
+ deleteMany(filter: TFilter, options?: DatabaseOperationOptions): Promise<number>;
110
+ exists(filter: TFilter, options?: DatabaseOperationOptions): Promise<boolean>;
111
+ count(filter?: TFilter, options?: DatabaseOperationOptions): Promise<number>;
112
+ }
113
+ /**
114
+ * Repository contract for entities that support soft deletion.
115
+ */
116
+ export interface SoftDeletableRepository<TEntity, TId = string, TCreateInput = Partial<TEntity>, TUpdateInput = Partial<TEntity>, TFilter = unknown> extends Repository<TEntity, TId, TCreateInput, TUpdateInput, TFilter> {
117
+ softDelete(id: TId, options?: DatabaseOperationOptions): Promise<TEntity>;
118
+ restore(id: TId, options?: DatabaseOperationOptions): Promise<TEntity>;
119
+ findDeleted(filter?: TFilter, options?: DatabaseOperationOptions): Promise<readonly TEntity[]>;
120
+ }
121
+ /**
122
+ * Pagination request.
123
+ */
124
+ export interface PaginationInput {
125
+ readonly page?: number;
126
+ readonly limit?: number;
127
+ }
128
+ /**
129
+ * Pagination metadata.
130
+ */
131
+ export interface PaginationMeta {
132
+ readonly page: number;
133
+ readonly limit: number;
134
+ readonly total: number;
135
+ readonly totalPages: number;
136
+ readonly hasNextPage: boolean;
137
+ readonly hasPreviousPage: boolean;
138
+ /** Alias of `hasNextPage`. */
139
+ readonly hasNext: boolean;
140
+ /** Alias of `hasPreviousPage`. */
141
+ readonly hasPrev: boolean;
142
+ }
143
+ /**
144
+ * Paginated repository result.
145
+ */
146
+ export interface PaginatedResult<TEntity> {
147
+ readonly data: readonly TEntity[];
148
+ readonly meta: PaginationMeta;
149
+ }
150
+ /**
151
+ * Sorting direction.
152
+ */
153
+ export type SortDirection = "asc" | "desc";
154
+ /**
155
+ * Generic sort definition.
156
+ */
157
+ export interface SortInput<TField extends string = string> {
158
+ readonly field: TField;
159
+ readonly direction: SortDirection;
160
+ }
161
+ /**
162
+ * Generic query options.
163
+ */
164
+ export interface QueryOptions<TField extends string = string> extends DatabaseOperationOptions {
165
+ readonly pagination?: PaginationInput;
166
+ readonly sort?: readonly SortInput<TField>[];
167
+ }
168
+ /**
169
+ * Database entity base contract.
170
+ */
171
+ export interface DatabaseEntity<TId = string> {
172
+ readonly id: TId;
173
+ readonly createdAt: Date;
174
+ readonly updatedAt: Date;
175
+ }
176
+ /**
177
+ * Soft-deletable entity contract.
178
+ */
179
+ export interface SoftDeletableEntity extends DatabaseEntity {
180
+ readonly deletedAt: Date | null;
181
+ }
182
+ /**
183
+ * Auditable entity contract.
184
+ */
185
+ export interface AuditableEntity extends DatabaseEntity {
186
+ readonly createdBy?: string;
187
+ readonly updatedBy?: string;
188
+ }
189
+ /**
190
+ * Plain, serialisable description of a database failure. Produced by
191
+ * `toDatabaseErrorInfo` in the client module.
192
+ */
193
+ export interface DatabaseErrorInfo {
194
+ /**
195
+ * Prisma / driver code (for example `P2002`) when known, otherwise the
196
+ * `DatabaseError.code` (for example `ERR_DATABASE`).
197
+ */
198
+ readonly code?: string;
199
+ readonly message: string;
200
+ /**
201
+ * Operation that failed (a `DatabaseOperation` value from
202
+ * `@zudojs/errors`, or one of the local {@link DatabaseOperation} names).
203
+ */
204
+ readonly operation?: string;
205
+ readonly model?: string;
206
+ readonly field?: string;
207
+ readonly constraint?: string;
208
+ readonly cause?: unknown;
209
+ readonly metadata?: Readonly<Record<string, unknown>>;
210
+ }
211
+ /**
212
+ * Database logger contract.
213
+ */
214
+ export interface DatabaseLogger {
215
+ debug(message: string, metadata?: Readonly<Record<string, unknown>>): void;
216
+ info(message: string, metadata?: Readonly<Record<string, unknown>>): void;
217
+ warn(message: string, metadata?: Readonly<Record<string, unknown>>): void;
218
+ error(message: string, error?: unknown, metadata?: Readonly<Record<string, unknown>>): void;
219
+ }
220
+ /**
221
+ * Default no-op database logger.
222
+ */
223
+ export declare const noopDatabaseLogger: DatabaseLogger;
224
+ //# sourceMappingURL=databaseType.type.d.ts.map
@@ -0,0 +1,10 @@
1
+ /**
2
+ * Default no-op database logger.
3
+ */
4
+ export const noopDatabaseLogger = Object.freeze({
5
+ debug: () => undefined,
6
+ info: () => undefined,
7
+ warn: () => undefined,
8
+ error: () => undefined,
9
+ });
10
+ //# sourceMappingURL=databaseType.type.js.map
@@ -0,0 +1,74 @@
1
+ import { DatabaseError } from "@zudojs/errors";
2
+ import type { DatabaseClient } from "../databaseClient/databaseClient.core.js";
3
+ /**
4
+ * Health status of the database.
5
+ */
6
+ export type DatabaseHealthStatus = "healthy" | "unhealthy" | "degraded";
7
+ /**
8
+ * Detailed database health information.
9
+ */
10
+ export interface DatabaseHealth {
11
+ readonly status: DatabaseHealthStatus;
12
+ readonly healthy: boolean;
13
+ readonly latencyMs: number;
14
+ readonly checkedAt: Date;
15
+ readonly message?: string;
16
+ readonly error?: {
17
+ readonly name: string;
18
+ readonly message: string;
19
+ readonly code?: string;
20
+ readonly databaseCode?: string;
21
+ };
22
+ }
23
+ /**
24
+ * Database health check options.
25
+ */
26
+ export interface DatabaseHealthOptions {
27
+ readonly timeoutMs?: number;
28
+ }
29
+ /**
30
+ * Result of a database readiness check.
31
+ */
32
+ export interface DatabaseReadiness {
33
+ readonly ready: boolean;
34
+ readonly checkedAt: Date;
35
+ readonly latencyMs: number;
36
+ readonly message?: string;
37
+ }
38
+ /**
39
+ * Default database health-check timeout.
40
+ */
41
+ export declare const DEFAULT_HEALTH_TIMEOUT_MS = 5000;
42
+ /**
43
+ * Error raised by {@link assertDatabaseHealth}. The underlying failure is
44
+ * preserved as `cause`.
45
+ */
46
+ export declare class DatabaseUnhealthyError extends DatabaseError {
47
+ readonly health: DatabaseHealth;
48
+ constructor(health: DatabaseHealth, cause: unknown);
49
+ }
50
+ /**
51
+ * Performs a lightweight database health check.
52
+ */
53
+ export declare function checkDatabaseHealth(client: DatabaseClient, options?: DatabaseHealthOptions): Promise<DatabaseHealth>;
54
+ /**
55
+ * Returns the original error that made a health check fail, if any.
56
+ */
57
+ export declare function getHealthCheckCause(health: DatabaseHealth): unknown;
58
+ /**
59
+ * Performs a database readiness check.
60
+ *
61
+ * Readiness is intentionally stricter than health. A degraded
62
+ * connection remains healthy but may still be considered ready.
63
+ */
64
+ export declare function checkDatabaseReadiness(client: DatabaseClient, options?: DatabaseHealthOptions): Promise<DatabaseReadiness>;
65
+ /**
66
+ * Throws a {@link DatabaseUnhealthyError} (with the real failure as
67
+ * `cause`) when the database is not healthy.
68
+ */
69
+ export declare function assertDatabaseHealth(client: DatabaseClient, options?: DatabaseHealthOptions): Promise<DatabaseHealth>;
70
+ /**
71
+ * Checks whether a database is reachable.
72
+ */
73
+ export declare function isDatabaseHealthy(client: DatabaseClient, options?: DatabaseHealthOptions): Promise<boolean>;
74
+ //# sourceMappingURL=health.check.d.ts.map