@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.
- package/README.md +223 -17
- package/dist/cache/cache.memory.d.ts +167 -0
- package/dist/cache/cache.memory.js +186 -40
- package/dist/cache/index.d.ts +2 -2
- package/dist/cache/index.js +2 -2
- package/dist/database/database.core.d.ts +91 -0
- package/dist/database/database.core.js +150 -0
- package/dist/databaseClient/databaseClient.core.d.ts +165 -14
- package/dist/databaseClient/databaseClient.core.js +304 -70
- package/dist/databaseClient/databaseClient.errors.d.ts +112 -0
- package/dist/databaseClient/databaseClient.errors.js +425 -0
- package/dist/databaseClient/index.d.ts +2 -1
- package/dist/databaseClient/index.js +2 -1
- package/dist/databaseConnection/databaseConnection.manager.d.ts +163 -0
- package/dist/databaseConnection/databaseConnection.manager.js +138 -76
- package/dist/databaseConnection/index.d.ts +1 -1
- package/dist/databaseType/databaseType.type.d.ts +224 -0
- package/dist/databaseType/databaseType.type.js +10 -0
- package/dist/health/health.check.d.ts +74 -0
- package/dist/health/health.check.js +78 -47
- package/dist/health/index.d.ts +1 -1
- package/dist/health/index.js +1 -1
- package/dist/index.d.ts +14 -14
- package/dist/index.js +11 -11
- package/dist/locks/index.d.ts +2 -2
- package/dist/locks/index.js +2 -2
- package/dist/locks/locks.core.d.ts +128 -0
- package/dist/locks/locks.core.js +144 -85
- package/dist/migration/index.d.ts +3 -1
- package/dist/migration/index.js +2 -0
- package/dist/migration/migration.dialect.d.ts +65 -0
- package/dist/migration/migration.dialect.js +63 -0
- package/dist/migration/migration.helpers.d.ts +65 -0
- package/dist/migration/migration.helpers.js +141 -0
- package/dist/migration/migration.runner.d.ts +75 -0
- package/dist/migration/migration.runner.js +195 -107
- package/dist/migration/migration.types.d.ts +85 -0
- package/dist/migration/migration.types.js +2 -0
- package/dist/pagination/index.d.ts +2 -1
- package/dist/pagination/index.js +2 -1
- package/dist/pagination/pagination.core.d.ts +163 -0
- package/dist/pagination/pagination.core.js +279 -0
- package/dist/pagination/pagination.keyset.d.ts +55 -0
- package/dist/pagination/pagination.keyset.js +108 -0
- package/dist/queryBuilder/index.d.ts +4 -3
- package/dist/queryBuilder/index.js +3 -2
- package/dist/queryBuilder/queryBuilder.core.d.ts +178 -0
- package/dist/queryBuilder/queryBuilder.core.js +420 -0
- package/dist/queryBuilder/queryBuilder.factory.d.ts +6 -0
- package/dist/queryBuilder/queryBuilder.factory.js +8 -0
- package/dist/queryBuilder/queryBuilder.filter.d.ts +170 -0
- package/dist/queryBuilder/queryBuilder.filter.js +497 -0
- package/dist/queryBuilder/queryBuilder.prisma.d.ts +60 -0
- package/dist/queryBuilder/queryBuilder.prisma.js +233 -0
- package/dist/queryBuilder/queryBuilder.type.d.ts +48 -0
- package/dist/queryBuilder/queryBuilder.type.js +2 -0
- package/dist/relations/index.d.ts +1 -1
- package/dist/relations/index.js +1 -1
- package/dist/relations/relations.definition.d.ts +184 -0
- package/dist/relations/relations.definition.js +362 -0
- package/dist/repository/index.d.ts +2 -1
- package/dist/repository/index.js +1 -0
- package/dist/repository/repository.base.d.ts +270 -0
- package/dist/repository/repository.base.js +350 -84
- package/dist/repository/repository.errors.d.ts +60 -0
- package/dist/repository/repository.errors.js +235 -0
- package/dist/seed/index.d.ts +2 -2
- package/dist/seed/index.js +1 -1
- package/dist/seed/seed.runner.d.ts +160 -0
- package/dist/seed/seed.runner.js +194 -225
- package/dist/transaction/index.d.ts +1 -1
- package/dist/transaction/index.js +1 -1
- package/dist/transaction/transaction.core.d.ts +117 -0
- package/dist/transaction/transaction.core.js +83 -56
- package/dist/unitOfWork/unitOfWork.core.d.ts +44 -0
- package/dist/unitOfWork/unitOfWork.core.js +9 -20
- package/package.json +35 -19
- package/dist/cache/cache.memory.d.ts.map +0 -1
- package/dist/cache/cache.memory.js.map +0 -1
- package/dist/cache/index.d.ts.map +0 -1
- package/dist/cache/index.js.map +0 -1
- package/dist/database/database.core.d.ts.map +0 -1
- package/dist/database/index.d.ts.map +0 -1
- package/dist/database/index.js.map +0 -1
- package/dist/databaseClient/databaseClient.core.d.ts.map +0 -1
- package/dist/databaseClient/databaseClient.core.js.map +0 -1
- package/dist/databaseClient/databaseClient.logger.d.ts.map +0 -1
- package/dist/databaseClient/databaseClient.logger.js.map +0 -1
- package/dist/databaseClient/index.d.ts.map +0 -1
- package/dist/databaseClient/index.js.map +0 -1
- package/dist/databaseConnection/databaseConnection.manager.d.ts.map +0 -1
- package/dist/databaseConnection/databaseConnection.manager.js.map +0 -1
- package/dist/databaseConnection/index.d.ts.map +0 -1
- package/dist/databaseConnection/index.js.map +0 -1
- package/dist/databaseType/index.d.ts.map +0 -1
- package/dist/databaseType/index.js.map +0 -1
- package/dist/health/health.check.d.ts.map +0 -1
- package/dist/health/health.check.js.map +0 -1
- package/dist/health/index.d.ts.map +0 -1
- package/dist/health/index.js.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/locks/index.d.ts.map +0 -1
- package/dist/locks/index.js.map +0 -1
- package/dist/locks/locks.core.d.ts.map +0 -1
- package/dist/locks/locks.core.js.map +0 -1
- package/dist/migration/index.d.ts.map +0 -1
- package/dist/migration/index.js.map +0 -1
- package/dist/migration/migration.helpers.d.ts.map +0 -1
- package/dist/migration/migration.runner.d.ts.map +0 -1
- package/dist/migration/migration.runner.js.map +0 -1
- package/dist/migration/migration.types.d.ts.map +0 -1
- package/dist/pagination/index.d.ts.map +0 -1
- package/dist/pagination/index.js.map +0 -1
- package/dist/queryBuilder/index.d.ts.map +0 -1
- package/dist/queryBuilder/index.js.map +0 -1
- package/dist/relations/index.d.ts.map +0 -1
- package/dist/relations/index.js.map +0 -1
- package/dist/repository/index.d.ts.map +0 -1
- package/dist/repository/index.js.map +0 -1
- package/dist/repository/repository.base.d.ts.map +0 -1
- package/dist/repository/repository.base.js.map +0 -1
- package/dist/seed/index.d.ts.map +0 -1
- package/dist/seed/index.js.map +0 -1
- package/dist/seed/seed.runner.d.ts.map +0 -1
- package/dist/seed/seed.runner.js.map +0 -1
- package/dist/transaction/index.d.ts.map +0 -1
- package/dist/transaction/index.js.map +0 -1
- package/dist/transaction/transaction.core.d.ts.map +0 -1
- package/dist/transaction/transaction.core.js.map +0 -1
- package/dist/unitOfWork/index.d.ts.map +0 -1
- package/dist/unitOfWork/index.js.map +0 -1
- package/dist/unitOfWork/unitOfWork.core.d.ts.map +0 -1
- package/dist/unitOfWork/unitOfWork.core.js.map +0 -1
|
@@ -1,5 +1,7 @@
|
|
|
1
|
-
import {
|
|
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
|
-
|
|
20
|
+
healthCheckInFlight;
|
|
21
|
+
consecutiveFailures = 0;
|
|
22
|
+
reconnectPromise;
|
|
23
|
+
lastHealth;
|
|
24
|
+
destroyed = false;
|
|
17
25
|
constructor(options = {}) {
|
|
18
|
-
|
|
19
|
-
this.
|
|
20
|
-
this.
|
|
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
|
-
|
|
43
|
-
.connect()
|
|
44
|
-
.
|
|
66
|
+
try {
|
|
67
|
+
await this.client.connect();
|
|
68
|
+
this.consecutiveFailures = 0;
|
|
45
69
|
this.emit("connected");
|
|
46
70
|
this.startHealthChecks();
|
|
47
|
-
}
|
|
48
|
-
|
|
71
|
+
}
|
|
72
|
+
catch (error) {
|
|
49
73
|
this.emit("error", error);
|
|
50
|
-
throw
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
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
|
|
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 ||
|
|
165
|
+
if (!interval || this.destroyed)
|
|
133
166
|
return;
|
|
134
|
-
}
|
|
135
167
|
this.healthCheckTimer = setInterval(() => {
|
|
136
168
|
void this.runScheduledHealthCheck();
|
|
137
169
|
}, interval);
|
|
138
|
-
this.
|
|
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
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
-
//
|
|
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,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
|