@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
@@ -0,0 +1,425 @@
1
+ /**
2
+ * @zudojs/database — Error normalisation
3
+ *
4
+ * Converts Prisma (and arbitrary) failures into `DatabaseError` instances
5
+ * that carry the Prisma error code as `databaseCode`, the operation that
6
+ * failed, and an HTTP-friendly status/code so callers can distinguish a
7
+ * unique violation from a missing row from an outage without reaching into
8
+ * `cause`.
9
+ *
10
+ * Prisma errors are detected structurally (a `code` such as `P2002` plus a
11
+ * `clientVersion`), so the mapping works regardless of which copy of
12
+ * `@prisma/client` produced the error.
13
+ */
14
+ import { DatabaseError, DatabaseOperation, ErrorCategory, isDatabaseError, ErrorCode, } from "@zudojs/errors";
15
+ /**
16
+ * Structural `DatabaseError` guard.
17
+ *
18
+ * `@zudojs/errors` resolves from the npm registry (exact `0.1.0` pins are a
19
+ * repo-wide convention), so a consumer may hold a second copy of the
20
+ * package. `instanceof` would then fail and errors would be double-wrapped;
21
+ * this guard accepts any `Error` that carries the `DatabaseError` shape
22
+ * (`category: "database"` plus a string `code`).
23
+ */
24
+ export function isDatabaseErrorLike(value) {
25
+ if (isDatabaseError(value))
26
+ return true;
27
+ if (!(value instanceof Error))
28
+ return false;
29
+ const candidate = value;
30
+ return (candidate.category === ErrorCategory.DATABASE &&
31
+ typeof candidate.code === "string" &&
32
+ typeof candidate.statusCode === "number");
33
+ }
34
+ const CONNECTION_MESSAGE = "Database connection failed.";
35
+ const PRISMA_CODE_MAP = Object.freeze({
36
+ // Query engine errors
37
+ P2000: {
38
+ kind: "validation",
39
+ statusCode: 400,
40
+ code: ErrorCode.DATABASE_QUERY,
41
+ message: "A value is too long for its column.",
42
+ expose: true,
43
+ },
44
+ P2002: {
45
+ kind: "conflict",
46
+ statusCode: 409,
47
+ code: ErrorCode.CONFLICT,
48
+ message: "A record with the same unique value already exists.",
49
+ expose: true,
50
+ },
51
+ P2003: {
52
+ kind: "constraint",
53
+ statusCode: 409,
54
+ code: ErrorCode.CONFLICT,
55
+ message: "A foreign key constraint was violated.",
56
+ expose: true,
57
+ },
58
+ P2004: {
59
+ kind: "constraint",
60
+ statusCode: 409,
61
+ code: ErrorCode.CONFLICT,
62
+ message: "A database constraint was violated.",
63
+ expose: true,
64
+ },
65
+ P2011: {
66
+ kind: "constraint",
67
+ statusCode: 400,
68
+ code: ErrorCode.DATABASE_QUERY,
69
+ message: "A required value was null.",
70
+ expose: true,
71
+ },
72
+ P2014: {
73
+ kind: "constraint",
74
+ statusCode: 409,
75
+ code: ErrorCode.CONFLICT,
76
+ message: "A required relation would be violated.",
77
+ expose: true,
78
+ },
79
+ P2015: {
80
+ kind: "not-found",
81
+ statusCode: 404,
82
+ code: ErrorCode.RESOURCE_NOT_FOUND,
83
+ message: "A related record could not be found.",
84
+ expose: true,
85
+ },
86
+ P2018: {
87
+ kind: "not-found",
88
+ statusCode: 404,
89
+ code: ErrorCode.RESOURCE_NOT_FOUND,
90
+ message: "Required connected records were not found.",
91
+ expose: true,
92
+ },
93
+ P2024: {
94
+ kind: "timeout",
95
+ statusCode: 503,
96
+ code: ErrorCode.DATABASE_TIMEOUT,
97
+ message: "Timed out acquiring a database connection from the pool.",
98
+ expose: false,
99
+ },
100
+ P2025: {
101
+ kind: "not-found",
102
+ statusCode: 404,
103
+ code: ErrorCode.RESOURCE_NOT_FOUND,
104
+ message: "The requested record was not found.",
105
+ expose: true,
106
+ },
107
+ P2028: {
108
+ kind: "timeout",
109
+ statusCode: 503,
110
+ code: ErrorCode.DATABASE_TRANSACTION,
111
+ message: "The database transaction timed out or was closed.",
112
+ expose: false,
113
+ },
114
+ P2034: {
115
+ kind: "serialization",
116
+ statusCode: 409,
117
+ code: ErrorCode.DATABASE_TRANSACTION,
118
+ message: "The transaction failed due to a write conflict or deadlock.",
119
+ expose: false,
120
+ },
121
+ // Connection / engine errors
122
+ P1000: {
123
+ kind: "connection",
124
+ statusCode: 503,
125
+ code: ErrorCode.DATABASE_CONNECTION,
126
+ message: CONNECTION_MESSAGE,
127
+ expose: false,
128
+ },
129
+ P1001: {
130
+ kind: "connection",
131
+ statusCode: 503,
132
+ code: ErrorCode.DATABASE_CONNECTION,
133
+ message: CONNECTION_MESSAGE,
134
+ expose: false,
135
+ },
136
+ P1002: {
137
+ kind: "timeout",
138
+ statusCode: 503,
139
+ code: ErrorCode.DATABASE_TIMEOUT,
140
+ message: "The database server did not respond in time.",
141
+ expose: false,
142
+ },
143
+ P1003: {
144
+ kind: "connection",
145
+ statusCode: 503,
146
+ code: ErrorCode.DATABASE_CONNECTION,
147
+ message: CONNECTION_MESSAGE,
148
+ expose: false,
149
+ },
150
+ P1008: {
151
+ kind: "timeout",
152
+ statusCode: 503,
153
+ code: ErrorCode.DATABASE_TIMEOUT,
154
+ message: "The database operation timed out.",
155
+ expose: false,
156
+ },
157
+ P1010: {
158
+ kind: "connection",
159
+ statusCode: 503,
160
+ code: ErrorCode.DATABASE_CONNECTION,
161
+ message: CONNECTION_MESSAGE,
162
+ expose: false,
163
+ },
164
+ P1011: {
165
+ kind: "connection",
166
+ statusCode: 503,
167
+ code: ErrorCode.DATABASE_CONNECTION,
168
+ message: CONNECTION_MESSAGE,
169
+ expose: false,
170
+ },
171
+ P1017: {
172
+ kind: "connection",
173
+ statusCode: 503,
174
+ code: ErrorCode.DATABASE_CONNECTION,
175
+ message: "The database server closed the connection.",
176
+ expose: false,
177
+ },
178
+ });
179
+ /**
180
+ * Codes that indicate a transient transaction failure worth retrying.
181
+ */
182
+ export const RETRYABLE_DATABASE_CODES = new Set([
183
+ "P2034", // Prisma: write conflict / deadlock
184
+ "P2028", // Prisma: transaction closed / timed out
185
+ "P1017", // Prisma: server closed the connection
186
+ "40001", // PostgreSQL: serialization_failure
187
+ "40P01", // PostgreSQL: deadlock_detected
188
+ ]);
189
+ /**
190
+ * Detects a Prisma error structurally.
191
+ */
192
+ export function isPrismaError(value) {
193
+ if (!value || typeof value !== "object")
194
+ return false;
195
+ const candidate = value;
196
+ const code = candidate.code ?? candidate.errorCode;
197
+ return (typeof code === "string" &&
198
+ /^P\d{4}$/.test(code) &&
199
+ typeof candidate.clientVersion === "string");
200
+ }
201
+ /**
202
+ * Returns the Prisma / driver error code carried by an error, if any.
203
+ */
204
+ export function getDatabaseErrorCode(error) {
205
+ if (isDatabaseErrorLike(error)) {
206
+ if (error.databaseCode !== undefined)
207
+ return String(error.databaseCode);
208
+ return getDatabaseErrorCode(error.cause);
209
+ }
210
+ if (isPrismaError(error)) {
211
+ return error.code ?? error.errorCode;
212
+ }
213
+ if (error && typeof error === "object") {
214
+ const code = error.code;
215
+ if (typeof code === "string" && code.length > 0)
216
+ return code;
217
+ }
218
+ return undefined;
219
+ }
220
+ /**
221
+ * Classifies a database error.
222
+ */
223
+ export function getDatabaseErrorKind(error) {
224
+ const code = getDatabaseErrorCode(error);
225
+ if (code) {
226
+ const mapping = PRISMA_CODE_MAP[code];
227
+ if (mapping)
228
+ return mapping.kind;
229
+ if (code === "40001" || code === "40P01")
230
+ return "serialization";
231
+ }
232
+ if (isDatabaseErrorLike(error)) {
233
+ if (error.code === ErrorCode.DATABASE_CONNECTION)
234
+ return "connection";
235
+ if (error.code === ErrorCode.DATABASE_TIMEOUT)
236
+ return "timeout";
237
+ if (error.statusCode === 404)
238
+ return "not-found";
239
+ if (error.statusCode === 409)
240
+ return "conflict";
241
+ }
242
+ return "unknown";
243
+ }
244
+ /**
245
+ * Determines whether a transaction failure is safe to retry.
246
+ *
247
+ * Retrying re-runs the whole callback, so callbacks passed to
248
+ * `withTransactionRetry` must be idempotent outside the transaction.
249
+ */
250
+ export function isRetryableTransactionError(error) {
251
+ const code = getDatabaseErrorCode(error);
252
+ if (code && RETRYABLE_DATABASE_CODES.has(code))
253
+ return true;
254
+ if (isPrismaError(error) && error.retryable === true)
255
+ return true;
256
+ return getDatabaseErrorKind(error) === "serialization";
257
+ }
258
+ /**
259
+ * Determines whether an error represents a unique-constraint conflict.
260
+ */
261
+ export function isConflictError(error) {
262
+ const kind = getDatabaseErrorKind(error);
263
+ return kind === "conflict" || kind === "constraint";
264
+ }
265
+ /**
266
+ * Determines whether an error represents a missing record.
267
+ */
268
+ export function isNotFoundError(error) {
269
+ return getDatabaseErrorKind(error) === "not-found";
270
+ }
271
+ /**
272
+ * Returns a copy of a `DatabaseError` with extra metadata, preserving
273
+ * `operation`, `driver` and `databaseCode` (which `BaseError.withMetadata`
274
+ * drops).
275
+ */
276
+ export function withDatabaseErrorMetadata(error, metadata) {
277
+ return new DatabaseError(error.message, {
278
+ code: error.code,
279
+ category: error.category,
280
+ severity: error.severity,
281
+ statusCode: error.statusCode,
282
+ expose: error.expose,
283
+ isOperational: error.isOperational,
284
+ operation: error.operation,
285
+ driver: error.driver,
286
+ databaseCode: error.databaseCode,
287
+ metadata: { ...error.metadata, ...metadata },
288
+ cause: error.cause,
289
+ });
290
+ }
291
+ /**
292
+ * Converts any thrown value into a `DatabaseError`.
293
+ *
294
+ * Existing `DatabaseError`s are returned as-is unless `operation` or
295
+ * `metadata` need to be attached, in which case a copy carrying the extra
296
+ * information (and the original as `cause`) is returned. Prisma errors are
297
+ * mapped by code; connection failures get a fixed message so that host
298
+ * names from Prisma's messages are never exposed.
299
+ */
300
+ export function normalizeDatabaseError(error, options = {}) {
301
+ const fallbackMessage = options.fallbackMessage ?? "A database operation failed.";
302
+ if (isDatabaseErrorLike(error)) {
303
+ // Subclasses (abort, unhealthy, unsupported dialect, ...) carry their
304
+ // identity in the class, and instances from another copy of
305
+ // @zudojs/errors must not be re-wrapped; only a plain DatabaseError from
306
+ // this copy is rebuilt with the extra information.
307
+ if (Object.getPrototypeOf(error) !== DatabaseError.prototype)
308
+ return error;
309
+ const needsOperation = options.operation !== undefined &&
310
+ error.operation === DatabaseOperation.UNKNOWN;
311
+ if (!needsOperation && !options.metadata)
312
+ return error;
313
+ return new DatabaseError(error.message, {
314
+ code: error.code,
315
+ category: error.category,
316
+ severity: error.severity,
317
+ statusCode: error.statusCode,
318
+ expose: error.expose,
319
+ isOperational: error.isOperational,
320
+ operation: needsOperation ? options.operation : error.operation,
321
+ driver: error.driver,
322
+ databaseCode: error.databaseCode,
323
+ metadata: { ...error.metadata, ...options.metadata },
324
+ cause: error.cause ?? error,
325
+ });
326
+ }
327
+ if (isPrismaError(error)) {
328
+ const databaseCode = (error.code ?? error.errorCode);
329
+ const mapping = PRISMA_CODE_MAP[databaseCode];
330
+ const meta = sanitizeMeta(error.meta);
331
+ if (mapping) {
332
+ return new DatabaseError(mapping.message, {
333
+ code: mapping.code,
334
+ statusCode: mapping.statusCode,
335
+ expose: mapping.expose,
336
+ operation: options.operation ?? inferOperation(mapping.kind),
337
+ driver: "prisma",
338
+ databaseCode,
339
+ metadata: { ...meta, ...options.metadata, kind: mapping.kind },
340
+ cause: error,
341
+ });
342
+ }
343
+ const isConnection = databaseCode.startsWith("P1");
344
+ return new DatabaseError(isConnection ? CONNECTION_MESSAGE : (error.message ?? fallbackMessage), {
345
+ code: isConnection ? ErrorCode.DATABASE_CONNECTION : ErrorCode.DATABASE,
346
+ statusCode: isConnection ? 503 : 500,
347
+ expose: false,
348
+ operation: options.operation ??
349
+ (isConnection ? DatabaseOperation.CONNECT : DatabaseOperation.UNKNOWN),
350
+ driver: "prisma",
351
+ databaseCode,
352
+ metadata: {
353
+ ...meta,
354
+ ...options.metadata,
355
+ kind: isConnection ? "connection" : "unknown",
356
+ },
357
+ cause: error,
358
+ });
359
+ }
360
+ const message = error instanceof Error && error.message.length > 0
361
+ ? error.message
362
+ : fallbackMessage;
363
+ return new DatabaseError(message, {
364
+ operation: options.operation ?? DatabaseOperation.UNKNOWN,
365
+ metadata: options.metadata,
366
+ cause: error,
367
+ });
368
+ }
369
+ function inferOperation(kind) {
370
+ switch (kind) {
371
+ case "connection":
372
+ return DatabaseOperation.CONNECT;
373
+ case "serialization":
374
+ return DatabaseOperation.TRANSACTION;
375
+ default:
376
+ return DatabaseOperation.QUERY;
377
+ }
378
+ }
379
+ /**
380
+ * Keeps only JSON-safe, non-sensitive fields from Prisma's `meta`.
381
+ */
382
+ function sanitizeMeta(meta) {
383
+ const result = {};
384
+ if (!meta)
385
+ return result;
386
+ for (const key of ["modelName", "target", "field_name", "constraint", "cause"]) {
387
+ const value = meta[key];
388
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
389
+ result[key] = value;
390
+ }
391
+ else if (Array.isArray(value) && value.every((v) => typeof v === "string")) {
392
+ result[key] = Object.freeze([...value]);
393
+ }
394
+ }
395
+ return result;
396
+ }
397
+ /**
398
+ * Converts any failure into a plain, serialisable {@link DatabaseErrorInfo}
399
+ * (the shape exported for logging and API responses). The error is
400
+ * normalised first, so Prisma codes appear as `code`.
401
+ */
402
+ export function toDatabaseErrorInfo(error, options = {}) {
403
+ const normalized = normalizeDatabaseError(error, options);
404
+ const metadata = normalized.metadata;
405
+ const model = metadata["model"] ?? metadata["modelName"];
406
+ const field = metadata["field"] ?? metadata["field_name"];
407
+ const constraint = metadata["constraint"] ?? metadata["target"];
408
+ return {
409
+ code: normalized.databaseCode !== undefined
410
+ ? String(normalized.databaseCode)
411
+ : String(normalized.code),
412
+ message: normalized.message,
413
+ operation: normalized.operation,
414
+ ...(typeof model === "string" ? { model } : {}),
415
+ ...(typeof field === "string" ? { field } : {}),
416
+ ...(typeof constraint === "string"
417
+ ? { constraint }
418
+ : Array.isArray(constraint)
419
+ ? { constraint: constraint.join(",") }
420
+ : {}),
421
+ cause: normalized.cause,
422
+ metadata,
423
+ };
424
+ }
425
+ //# sourceMappingURL=databaseClient.errors.js.map
@@ -3,5 +3,6 @@
3
3
  *
4
4
  * Prisma-backed database client and connection infrastructure.
5
5
  */
6
- export { DatabaseClient, createDatabaseClient, type DatabaseClientOptions, type DatabaseTransactionContext, } 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 RawQueryOptions, } from "./databaseClient.core.js";
7
+ export { normalizeDatabaseError, withDatabaseErrorMetadata, isPrismaError, isRetryableTransactionError, isConflictError, isNotFoundError, getDatabaseErrorCode, getDatabaseErrorKind, isDatabaseErrorLike, toDatabaseErrorInfo, RETRYABLE_DATABASE_CODES, type DatabaseErrorKind, type NormalizeDatabaseErrorOptions, type PrismaErrorLike, } from "./databaseClient.errors.js";
7
8
  //# sourceMappingURL=index.d.ts.map
@@ -3,5 +3,6 @@
3
3
  *
4
4
  * Prisma-backed database client and connection infrastructure.
5
5
  */
6
- export { DatabaseClient, createDatabaseClient, } from "./databaseClient.core.js";
6
+ export { DatabaseClient, DatabaseAbortError, createDatabaseClient, buildPrismaTransactionOptions, createAbortError, raceAbort, throwIfAborted, SUPPORTED_ISOLATION_LEVELS, } from "./databaseClient.core.js";
7
+ export { normalizeDatabaseError, withDatabaseErrorMetadata, isPrismaError, isRetryableTransactionError, isConflictError, isNotFoundError, getDatabaseErrorCode, getDatabaseErrorKind, isDatabaseErrorLike, toDatabaseErrorInfo, RETRYABLE_DATABASE_CODES, } from "./databaseClient.errors.js";
7
8
  //# sourceMappingURL=index.js.map
@@ -0,0 +1,163 @@
1
+ import { DatabaseClient, type DatabaseClientOptions } from "../databaseClient/databaseClient.core.js";
2
+ import { type DatabaseHealth as DetailedDatabaseHealth } from "../health/health.check.js";
3
+ import type { DatabaseHealth, DatabaseStatus } from "../databaseType/databaseType.type.js";
4
+ /**
5
+ * Connection lifecycle events.
6
+ */
7
+ export type DatabaseConnectionEvent = "connecting" | "connected" | "disconnecting" | "disconnected" | "error" | "reconnecting";
8
+ /**
9
+ * Listener invoked when the connection state changes.
10
+ */
11
+ export type DatabaseConnectionListener = (event: DatabaseConnectionEvent, details: DatabaseConnectionEventDetails) => void;
12
+ /**
13
+ * Details associated with a connection event.
14
+ */
15
+ export interface DatabaseConnectionEventDetails {
16
+ readonly status: DatabaseStatus;
17
+ readonly timestamp: Date;
18
+ readonly error?: unknown;
19
+ readonly attempt?: number;
20
+ }
21
+ /**
22
+ * Reconnect policy applied after scheduled health checks fail.
23
+ */
24
+ export interface DatabaseReconnectOptions {
25
+ /**
26
+ * Consecutive failed health checks before a reconnect is attempted.
27
+ * Defaults to 1.
28
+ */
29
+ readonly failureThreshold?: number;
30
+ /**
31
+ * Maximum reconnect attempts per outage. Defaults to 5.
32
+ */
33
+ readonly maxAttempts?: number;
34
+ /**
35
+ * Base delay between reconnect attempts; doubles each attempt up to
36
+ * `maxDelayMs`. Defaults to 500 ms.
37
+ */
38
+ readonly baseDelayMs?: number;
39
+ /**
40
+ * Upper bound for the backoff delay. Defaults to 30 000 ms.
41
+ */
42
+ readonly maxDelayMs?: number;
43
+ }
44
+ /**
45
+ * Options for the connection manager.
46
+ */
47
+ export interface DatabaseConnectionManagerOptions extends DatabaseClientOptions {
48
+ /**
49
+ * Existing client to manage. When supplied the other client options are
50
+ * ignored, so one Prisma instance is never wrapped by two lifecycle
51
+ * state machines.
52
+ */
53
+ readonly client?: DatabaseClient;
54
+ readonly autoConnect?: boolean;
55
+ /**
56
+ * Interval for scheduled health checks. Disabled when omitted.
57
+ */
58
+ readonly healthCheckIntervalMs?: number;
59
+ /**
60
+ * Timeout for each scheduled health check. Defaults to the smaller of
61
+ * the interval and 5 000 ms.
62
+ */
63
+ readonly healthCheckTimeoutMs?: number;
64
+ /**
65
+ * Reconnect policy. Pass `false` to disable automatic reconnects.
66
+ */
67
+ readonly reconnect?: DatabaseReconnectOptions | false;
68
+ }
69
+ /**
70
+ * Manages the database connection lifecycle.
71
+ *
72
+ * The manager intentionally does not connect during construction.
73
+ * Application bootstrap should call `connect()` explicitly unless
74
+ * `autoConnect` is enabled.
75
+ */
76
+ export declare class DatabaseConnectionManager {
77
+ private readonly client;
78
+ private readonly listeners;
79
+ private readonly autoConnect;
80
+ private readonly healthCheckIntervalMs?;
81
+ private readonly healthCheckTimeoutMs;
82
+ private readonly reconnect;
83
+ private healthCheckTimer?;
84
+ private healthCheckInFlight?;
85
+ private consecutiveFailures;
86
+ private reconnectPromise?;
87
+ private lastHealth?;
88
+ private destroyed;
89
+ constructor(options?: DatabaseConnectionManagerOptions);
90
+ /**
91
+ * Initializes the connection manager.
92
+ */
93
+ initialize(): Promise<void>;
94
+ /**
95
+ * Opens the database connection. Concurrent calls share the client's
96
+ * in-flight attempt.
97
+ */
98
+ connect(): Promise<void>;
99
+ /**
100
+ * Closes the database connection.
101
+ */
102
+ disconnect(): Promise<void>;
103
+ /**
104
+ * Ensures that the connection is ready.
105
+ */
106
+ ensureConnected(): Promise<void>;
107
+ /**
108
+ * Returns the current connection status.
109
+ */
110
+ getStatus(): DatabaseStatus;
111
+ /**
112
+ * Performs a database health check (with the configured timeout).
113
+ */
114
+ healthCheck(): Promise<DatabaseHealth>;
115
+ /**
116
+ * Returns the result of the most recent scheduled health check.
117
+ */
118
+ getLastHealth(): DetailedDatabaseHealth | undefined;
119
+ /**
120
+ * Returns the underlying database client.
121
+ */
122
+ getClient(): DatabaseClient;
123
+ /**
124
+ * Subscribes to connection lifecycle events.
125
+ */
126
+ on(listener: DatabaseConnectionListener): () => void;
127
+ /**
128
+ * Removes a connection listener.
129
+ */
130
+ off(listener: DatabaseConnectionListener): boolean;
131
+ /**
132
+ * Removes all connection listeners.
133
+ */
134
+ removeAllListeners(): void;
135
+ /**
136
+ * Starts periodic database health checks. A tick is skipped while a
137
+ * previous check is still in flight, so a hung database never
138
+ * accumulates pending probes.
139
+ */
140
+ startHealthChecks(): void;
141
+ /**
142
+ * Stops periodic database health checks.
143
+ */
144
+ stopHealthChecks(): void;
145
+ /**
146
+ * Runs one scheduled health check immediately (also used by the timer).
147
+ * Resolves once the check, and any reconnect it triggers, has settled.
148
+ */
149
+ runScheduledHealthCheck(): Promise<void>;
150
+ /**
151
+ * Releases connection manager resources.
152
+ */
153
+ destroy(): Promise<void>;
154
+ private performScheduledHealthCheck;
155
+ private reconnectWithBackoff;
156
+ private performReconnect;
157
+ private emit;
158
+ }
159
+ /**
160
+ * Creates a database connection manager.
161
+ */
162
+ export declare function createConnectionManager(options?: DatabaseConnectionManagerOptions): DatabaseConnectionManager;
163
+ //# sourceMappingURL=databaseConnection.manager.d.ts.map