@zudojs/database 0.1.0 → 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 (150) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +223 -17
  3. package/dist/cache/cache.memory.d.ts +167 -0
  4. package/dist/cache/cache.memory.js +186 -40
  5. package/dist/cache/index.d.ts +2 -2
  6. package/dist/cache/index.js +2 -2
  7. package/dist/database/database.core.d.ts +91 -0
  8. package/dist/database/database.core.js +21 -4
  9. package/dist/databaseClient/databaseClient.core.d.ts +165 -14
  10. package/dist/databaseClient/databaseClient.core.js +304 -70
  11. package/dist/databaseClient/databaseClient.errors.d.ts +112 -0
  12. package/dist/databaseClient/databaseClient.errors.js +425 -0
  13. package/dist/databaseClient/index.d.ts +2 -1
  14. package/dist/databaseClient/index.js +2 -1
  15. package/dist/databaseConnection/databaseConnection.manager.d.ts +163 -0
  16. package/dist/databaseConnection/databaseConnection.manager.js +138 -76
  17. package/dist/databaseConnection/index.d.ts +1 -1
  18. package/dist/databaseType/databaseType.type.d.ts +224 -0
  19. package/dist/databaseType/databaseType.type.js +0 -35
  20. package/dist/health/health.check.d.ts +74 -0
  21. package/dist/health/health.check.js +78 -47
  22. package/dist/health/index.d.ts +1 -1
  23. package/dist/health/index.js +1 -1
  24. package/dist/index.d.ts +14 -14
  25. package/dist/index.js +11 -11
  26. package/dist/locks/index.d.ts +2 -2
  27. package/dist/locks/index.js +2 -2
  28. package/dist/locks/locks.core.d.ts +128 -0
  29. package/dist/locks/locks.core.js +144 -85
  30. package/dist/migration/index.d.ts +3 -1
  31. package/dist/migration/index.js +2 -0
  32. package/dist/migration/migration.dialect.d.ts +65 -0
  33. package/dist/migration/migration.dialect.js +63 -0
  34. package/dist/migration/migration.helpers.d.ts +65 -0
  35. package/dist/migration/migration.helpers.js +60 -18
  36. package/dist/migration/migration.runner.d.ts +75 -0
  37. package/dist/migration/migration.runner.js +195 -107
  38. package/dist/migration/migration.types.d.ts +85 -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 +78 -9
  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 +92 -14
  49. package/dist/queryBuilder/queryBuilder.factory.d.ts +6 -0
  50. package/dist/queryBuilder/queryBuilder.factory.js +0 -25
  51. package/dist/queryBuilder/queryBuilder.filter.d.ts +170 -0
  52. package/dist/queryBuilder/queryBuilder.filter.js +191 -30
  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/relations/index.d.ts +1 -1
  57. package/dist/relations/index.js +1 -1
  58. package/dist/relations/relations.definition.d.ts +184 -0
  59. package/dist/relations/relations.definition.js +186 -19
  60. package/dist/repository/index.d.ts +2 -1
  61. package/dist/repository/index.js +1 -0
  62. package/dist/repository/repository.base.d.ts +270 -0
  63. package/dist/repository/repository.base.js +350 -84
  64. package/dist/repository/repository.errors.d.ts +60 -0
  65. package/dist/repository/repository.errors.js +235 -0
  66. package/dist/seed/index.d.ts +2 -2
  67. package/dist/seed/index.js +1 -1
  68. package/dist/seed/seed.runner.d.ts +160 -0
  69. package/dist/seed/seed.runner.js +194 -225
  70. package/dist/transaction/index.d.ts +1 -1
  71. package/dist/transaction/index.js +1 -1
  72. package/dist/transaction/transaction.core.d.ts +117 -0
  73. package/dist/transaction/transaction.core.js +83 -56
  74. package/dist/unitOfWork/unitOfWork.core.d.ts +44 -0
  75. package/dist/unitOfWork/unitOfWork.core.js +9 -20
  76. package/package.json +40 -24
  77. package/dist/cache/cache.memory.d.ts.map +0 -1
  78. package/dist/cache/cache.memory.js.map +0 -1
  79. package/dist/cache/index.d.ts.map +0 -1
  80. package/dist/cache/index.js.map +0 -1
  81. package/dist/database/database.core.d.ts.map +0 -1
  82. package/dist/database/database.core.js.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/databaseType.type.d.ts.map +0 -1
  96. package/dist/databaseType/databaseType.type.js.map +0 -1
  97. package/dist/databaseType/index.d.ts.map +0 -1
  98. package/dist/databaseType/index.js.map +0 -1
  99. package/dist/health/health.check.d.ts.map +0 -1
  100. package/dist/health/health.check.js.map +0 -1
  101. package/dist/health/index.d.ts.map +0 -1
  102. package/dist/health/index.js.map +0 -1
  103. package/dist/index.d.ts.map +0 -1
  104. package/dist/index.js.map +0 -1
  105. package/dist/locks/index.d.ts.map +0 -1
  106. package/dist/locks/index.js.map +0 -1
  107. package/dist/locks/locks.core.d.ts.map +0 -1
  108. package/dist/locks/locks.core.js.map +0 -1
  109. package/dist/migration/index.d.ts.map +0 -1
  110. package/dist/migration/index.js.map +0 -1
  111. package/dist/migration/migration.helpers.d.ts.map +0 -1
  112. package/dist/migration/migration.helpers.js.map +0 -1
  113. package/dist/migration/migration.runner.d.ts.map +0 -1
  114. package/dist/migration/migration.runner.js.map +0 -1
  115. package/dist/migration/migration.types.d.ts.map +0 -1
  116. package/dist/migration/migration.types.js.map +0 -1
  117. package/dist/pagination/index.d.ts.map +0 -1
  118. package/dist/pagination/index.js.map +0 -1
  119. package/dist/pagination/pagination.core.d.ts.map +0 -1
  120. package/dist/pagination/pagination.core.js.map +0 -1
  121. package/dist/queryBuilder/index.d.ts.map +0 -1
  122. package/dist/queryBuilder/index.js.map +0 -1
  123. package/dist/queryBuilder/queryBuilder.core.d.ts.map +0 -1
  124. package/dist/queryBuilder/queryBuilder.core.js.map +0 -1
  125. package/dist/queryBuilder/queryBuilder.factory.d.ts.map +0 -1
  126. package/dist/queryBuilder/queryBuilder.factory.js.map +0 -1
  127. package/dist/queryBuilder/queryBuilder.filter.d.ts.map +0 -1
  128. package/dist/queryBuilder/queryBuilder.filter.js.map +0 -1
  129. package/dist/queryBuilder/queryBuilder.type.d.ts.map +0 -1
  130. package/dist/queryBuilder/queryBuilder.type.js.map +0 -1
  131. package/dist/relations/index.d.ts.map +0 -1
  132. package/dist/relations/index.js.map +0 -1
  133. package/dist/relations/relations.definition.d.ts.map +0 -1
  134. package/dist/relations/relations.definition.js.map +0 -1
  135. package/dist/repository/index.d.ts.map +0 -1
  136. package/dist/repository/index.js.map +0 -1
  137. package/dist/repository/repository.base.d.ts.map +0 -1
  138. package/dist/repository/repository.base.js.map +0 -1
  139. package/dist/seed/index.d.ts.map +0 -1
  140. package/dist/seed/index.js.map +0 -1
  141. package/dist/seed/seed.runner.d.ts.map +0 -1
  142. package/dist/seed/seed.runner.js.map +0 -1
  143. package/dist/transaction/index.d.ts.map +0 -1
  144. package/dist/transaction/index.js.map +0 -1
  145. package/dist/transaction/transaction.core.d.ts.map +0 -1
  146. package/dist/transaction/transaction.core.js.map +0 -1
  147. package/dist/unitOfWork/index.d.ts.map +0 -1
  148. package/dist/unitOfWork/index.js.map +0 -1
  149. package/dist/unitOfWork/unitOfWork.core.d.ts.map +0 -1
  150. package/dist/unitOfWork/unitOfWork.core.js.map +0 -1
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Zudojs Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,38 +1,244 @@
1
1
  # @zudojs/database
2
2
 
3
- Database abstraction layer with clients, repositories, transactions, and query building for Zudojs applications.
3
+ PostgreSQL data-access layer for Zudojs applications, built on Prisma 7: a
4
+ lifecycle-aware client, generic repositories with soft delete, a query builder
5
+ that translates to Prisma `where` clauses, managed transactions, keyset
6
+ pagination with signed cursors, migration and seed runners guarded by advisory
7
+ locks, health checks with reconnect, and a bounded in-memory cache.
4
8
 
5
9
  ## Installation
6
10
 
7
11
  ```bash
8
- npm install @zudojs/database
12
+ npm install @zudojs/database @prisma/client @prisma/adapter-pg
9
13
  ```
10
14
 
15
+ `@prisma/client` (`>=7 <8`) is a peer dependency. Prisma 7 connects through a
16
+ driver adapter, so you also need an adapter package for your database
17
+ (`@prisma/adapter-pg` for PostgreSQL).
18
+
11
19
  ## Quick Start
12
20
 
13
21
  ```typescript
14
- import { createDatabaseClient } from "@zudojs/database";
22
+ import { PrismaClient } from "@prisma/client";
23
+ import { PrismaPg } from "@prisma/adapter-pg";
24
+ import {
25
+ BaseRepository,
26
+ createDatabaseClient,
27
+ withTransaction,
28
+ } from "@zudojs/database";
29
+
30
+ interface User {
31
+ id: string;
32
+ email: string;
33
+ name: string;
34
+ deletedAt: Date | null;
35
+ }
36
+
37
+ // 1. Build the Prisma client with a driver adapter and hand it to the wrapper.
38
+ const prisma = new PrismaClient({
39
+ adapter: new PrismaPg({ connectionString: process.env.DATABASE_URL }),
40
+ });
41
+ const client = createDatabaseClient({ prisma });
42
+ await client.connect();
43
+
44
+ // 2. Repositories wrap a Prisma model delegate.
45
+ class UserRepository extends BaseRepository<User> {
46
+ constructor(delegate: ConstructorParameters<typeof BaseRepository<User>>[0]) {
47
+ super(delegate, { modelName: "User", softDelete: true });
48
+ }
49
+
50
+ findByEmail(email: string) {
51
+ return this.findOne({ email });
52
+ }
53
+ }
54
+
55
+ const users = new UserRepository(prisma.user);
56
+ const alice = await users.create({ email: "alice@example.com", name: "Alice" });
57
+ await users.softDelete(alice.id); // sets deletedAt; reads now skip the row
15
58
 
16
- const client = await createDatabaseClient({
17
- connection: { url: "postgresql://localhost/mydb" },
59
+ // 3. Transactions: rebind the repository to the transaction client.
60
+ await withTransaction(client, async (tx) => {
61
+ const txUsers = users.withTransaction(tx);
62
+ await txUsers.restore(alice.id);
63
+ await txUsers.update(alice.id, { name: "Alice Doe" });
18
64
  });
19
65
 
20
- const users = await client.repository("User").findMany();
66
+ await client.disconnect();
21
67
  ```
22
68
 
69
+ `createDatabaseClient` accepts either a pre-built `prisma` instance or an
70
+ `adapter` (in which case it constructs the `PrismaClient` for you). It throws a
71
+ `DatabaseError` if neither is supplied.
72
+
73
+ ## Querying
74
+
75
+ ```typescript
76
+ import { createQueryBuilder, toPrismaWhere, and, equals, isAfter } from "@zudojs/database";
77
+
78
+ const query = createQueryBuilder<"email" | "createdAt" | "role">()
79
+ .where("role", "admin")
80
+ .whereContains("email", "@example.com")
81
+ .orderByDesc("createdAt")
82
+ .paginate({ page: 1, limit: 20 });
83
+
84
+ // Repositories consume the builder directly ...
85
+ const admins = await users.findByQuery(query);
86
+
87
+ // ... or translate the filter yourself.
88
+ const where = toPrismaWhere(
89
+ and(equals("role", "admin"), isAfter("createdAt", new Date("2026-01-01"))),
90
+ );
91
+ ```
92
+
93
+ ## Pagination
94
+
95
+ ```typescript
96
+ // Offset pagination
97
+ const page = await users.findPaginated(undefined, {
98
+ pagination: { page: 2, limit: 25 },
99
+ sort: [{ field: "createdAt", direction: "desc" }],
100
+ });
101
+ page.meta.totalPages;
102
+
103
+ // Keyset pagination with HMAC-signed cursors (set `cursorSecret` on the repository)
104
+ const first = await users.paginateCursor(undefined, {
105
+ limit: 25,
106
+ sort: [{ field: "createdAt", direction: "desc" }],
107
+ });
108
+ const next = await users.paginateCursor(undefined, {
109
+ cursor: first.meta.nextCursor,
110
+ limit: 25,
111
+ sort: [{ field: "createdAt", direction: "desc" }],
112
+ });
113
+ ```
114
+
115
+ ## Migrations and seeds
116
+
117
+ ```typescript
118
+ import { createMigrationRunner, createSeedRunner } from "@zudojs/database";
119
+
120
+ const migrations = createMigrationRunner(
121
+ client,
122
+ [
123
+ {
124
+ version: 1,
125
+ name: "create-users",
126
+ up: async (tx) => {
127
+ await tx.$executeRawUnsafe(
128
+ 'CREATE TABLE "users" ("id" TEXT PRIMARY KEY, "email" TEXT UNIQUE NOT NULL)',
129
+ );
130
+ },
131
+ down: async (tx) => {
132
+ await tx.$executeRawUnsafe('DROP TABLE "users"');
133
+ },
134
+ },
135
+ ],
136
+ { transaction: { timeoutMs: 60_000 } },
137
+ );
138
+
139
+ await migrations.migrate(); // applies pending migrations under an advisory lock
140
+ await migrations.rollback(); // reverts the most recent one
141
+
142
+ const seeds = createSeedRunner(client, [
143
+ {
144
+ name: "roles",
145
+ order: 0,
146
+ run: async (tx) => {
147
+ await tx.$executeRawUnsafe('INSERT INTO "roles" ("name") VALUES ($1)', "admin");
148
+ },
149
+ },
150
+ ]);
151
+ await seeds.run();
152
+ ```
153
+
154
+ Each migration or seed runs in its own transaction by default
155
+ (`perItemTransaction: true`); pass `transaction: { timeoutMs, maxWaitMs, isolationLevel }`
156
+ to size it for long-running steps.
157
+
158
+ ## Errors
159
+
160
+ Every failure surfaces as a `DatabaseError` from `@zudojs/errors`. Prisma codes
161
+ are mapped to `databaseCode`, an `ErrorCode` and an HTTP status (`P2002` /
162
+ `P2003` → 409, `P2025` → 404, `P2034` → retryable, `P1xxx` → 503 with a fixed
163
+ message that never includes the host name).
164
+
165
+ ```typescript
166
+ import {
167
+ isConflictError,
168
+ isNotFoundError,
169
+ isRetryableTransactionError,
170
+ toDatabaseErrorInfo,
171
+ } from "@zudojs/database";
172
+
173
+ try {
174
+ await users.create({ email: "alice@example.com", name: "Alice" });
175
+ } catch (error) {
176
+ if (isConflictError(error)) {
177
+ // 409: unique or foreign-key violation
178
+ } else if (isNotFoundError(error)) {
179
+ // 404
180
+ } else if (!isRetryableTransactionError(error)) {
181
+ console.error(toDatabaseErrorInfo(error)); // { code: "P2002", message, operation, ... }
182
+ }
183
+ }
184
+ ```
185
+
186
+ ## Locks and health
187
+
188
+ ```typescript
189
+ import { assertDatabaseHealth, checkDatabaseHealth, createLockManager } from "@zudojs/database";
190
+
191
+ const locks = createLockManager(client);
192
+
193
+ // Advisory lock (FNV-1a 64 key, transaction-scoped). `timeoutMs` becomes
194
+ // `SET LOCAL lock_timeout`; the transaction timeout is raised to cover it.
195
+ await locks.withAdvisoryLock("reports:nightly", async (tx) => {
196
+ await tx.$executeRawUnsafe("REFRESH MATERIALIZED VIEW nightly_report");
197
+ }, { timeoutMs: 10_000 });
198
+
199
+ // Row lock: throws 404 when the row is missing, 409 when skipLocked skips it.
200
+ await locks.withRowLock("orders", orderId, async (tx) => {
201
+ await tx.$executeRawUnsafe('UPDATE "orders" SET "status" = $1 WHERE "id" = $2', "paid", orderId);
202
+ }, { mode: "for-no-key-update", skipLocked: true });
203
+
204
+ const health = await checkDatabaseHealth(client, { timeoutMs: 2_000 });
205
+ health.status; // "healthy" | "degraded" | "unhealthy"
206
+ await assertDatabaseHealth(client); // throws DatabaseUnhealthyError with the real cause
207
+ ```
208
+
209
+ ## Subpath exports
210
+
211
+ | Import | Contents |
212
+ | --- | --- |
213
+ | `@zudojs/database` | Everything |
214
+ | `@zudojs/database/client` | `DatabaseClient`, `createDatabaseClient`, error helpers |
215
+ | `@zudojs/database/repositories` | `BaseRepository`, `mapRepositoryError` |
216
+ | `@zudojs/database/transactions` | `TransactionManager`, `withTransaction`, `withTransactionRetry` |
217
+
23
218
  ## Features
24
219
 
25
- - Database-agnostic query builder
26
- - Repository pattern with CRUD operations
27
- - Transaction management with savepoints
28
- - Connection pooling and health checks
29
- - Migration runner
30
- - Seed runner for test data
31
- - Unit of Work pattern
220
+ - Prisma 7 client wrapper with connection lifecycle, in-flight connect de-duplication, raw queries, and typed error normalisation (Prisma `P*` codes mapped to conflict / not-found / timeout / connection outcomes)
221
+ - `BaseRepository` with CRUD, `createMany` / `deleteMany`, soft delete (`softDelete`, `restore`, `findDeleted`, `withDeleted`), transaction rebinding (`withTransaction`), and `findByQuery`
222
+ - Query builder and 40 filter helpers that translate to Prisma `where` / `orderBy` / `select` / `include`
223
+ - Managed transactions with context, error enrichment, and `withTransactionRetry` (serialization failures retried by default)
224
+ - Offset pagination and keyset pagination with HMAC-signed, shape-validated cursors
225
+ - Migration and seed runners using PostgreSQL advisory locks, per-item transactions, and BIGINT versioning
226
+ - Advisory and row locks with `lock_timeout`, `SKIP LOCKED` / `NOWAIT`, and namespaced keys
227
+ - Health and readiness probes with timeouts; connection manager with overlap-guarded scheduled checks and reconnect back-off
228
+ - Bounded in-memory LRU cache with TTL, periodic pruning, and loader coalescing
229
+ - Relation definitions with a registry and `toPrismaInclude` (depth and cycle guarded)
230
+
231
+ ## Limitations
232
+
233
+ - PostgreSQL only. Migration, seed, and lock helpers emit PostgreSQL SQL; other dialects throw `UnsupportedDialectError`.
234
+ - No savepoints or nested transactions. Prisma interactive transactions are used as-is.
235
+ - Connection pooling is handled by the driver adapter, not by this package.
236
+ - `timeoutMs` and `signal` on repository operations are client-side only; the database query is not cancelled server-side.
237
+ - The cache is not transaction-aware. Do not populate it from inside a transaction that may roll back.
32
238
 
33
239
  ## Use Cases
34
240
 
35
- - Data access layer for applications
36
- - Multi-database support (PostgreSQL, MySQL, SQLite)
37
- - Transaction coordination across repositories
38
- - Database migrations and seeding
241
+ - Data access layer for Zudojs services
242
+ - Transaction coordination across repositories (rebind with `withTransaction`)
243
+ - Database migrations and seeding with single-runner guarantees
244
+ - Health endpoints and readiness gating
@@ -0,0 +1,167 @@
1
+ /**
2
+ * Cache entry stored by the database cache.
3
+ */
4
+ export interface CacheEntry<TValue> {
5
+ readonly value: TValue;
6
+ readonly createdAt: number;
7
+ readonly expiresAt?: number;
8
+ }
9
+ /**
10
+ * Options for a cache operation.
11
+ */
12
+ export interface CacheOptions {
13
+ readonly ttlMs?: number;
14
+ }
15
+ /**
16
+ * Options for constructing a {@link MemoryDatabaseCache}.
17
+ */
18
+ export interface MemoryCacheOptions extends CacheOptions {
19
+ /**
20
+ * Maximum number of entries. When exceeded the least recently used entry
21
+ * is evicted. Unbounded when omitted.
22
+ */
23
+ readonly maxEntries?: number;
24
+ /**
25
+ * Interval at which expired entries are pruned in the background. The
26
+ * timer is `unref`'d so it never keeps the process alive. Disabled when
27
+ * omitted; expired entries are still removed lazily on access.
28
+ */
29
+ readonly pruneIntervalMs?: number;
30
+ }
31
+ /**
32
+ * Statistics exposed by the cache.
33
+ */
34
+ export interface CacheStats {
35
+ readonly size: number;
36
+ readonly hits: number;
37
+ readonly misses: number;
38
+ readonly hitRate: number;
39
+ readonly evictions: number;
40
+ }
41
+ /**
42
+ * Generic cache contract.
43
+ */
44
+ export interface DatabaseCache<TValue = unknown> {
45
+ get(key: string): TValue | undefined;
46
+ set(key: string, value: TValue, options?: CacheOptions): void;
47
+ has(key: string): boolean;
48
+ delete(key: string): boolean;
49
+ clear(): void;
50
+ /**
51
+ * Returns all currently valid keys. Optional; required by
52
+ * {@link invalidateByPrefix}.
53
+ */
54
+ keys?(): readonly string[];
55
+ }
56
+ /**
57
+ * In-memory LRU cache for database read results.
58
+ *
59
+ * This cache is process-local and is not transaction-aware: never populate
60
+ * it from inside a transaction that may still roll back, because other
61
+ * callers would observe the uncommitted value.
62
+ */
63
+ export declare class MemoryDatabaseCache<TValue = unknown> implements DatabaseCache<TValue> {
64
+ private readonly entries;
65
+ private hits;
66
+ private misses;
67
+ private evictions;
68
+ private readonly defaultTtlMs?;
69
+ private readonly maxEntries?;
70
+ private pruneTimer?;
71
+ constructor(options?: MemoryCacheOptions);
72
+ /**
73
+ * Gets a cached value and marks it as recently used.
74
+ */
75
+ get(key: string): TValue | undefined;
76
+ /**
77
+ * Sets a cached value, evicting the least recently used entry when the
78
+ * cache is full.
79
+ */
80
+ set(key: string, value: TValue, options?: CacheOptions): void;
81
+ /**
82
+ * Checks whether a valid cached value exists (does not touch recency).
83
+ */
84
+ has(key: string): boolean;
85
+ /**
86
+ * Deletes a cache entry.
87
+ */
88
+ delete(key: string): boolean;
89
+ /**
90
+ * Clears the entire cache.
91
+ */
92
+ clear(): void;
93
+ /**
94
+ * Removes expired entries.
95
+ */
96
+ prune(): number;
97
+ /**
98
+ * Stops the background prune timer.
99
+ */
100
+ dispose(): void;
101
+ /**
102
+ * Returns the number of valid entries.
103
+ */
104
+ get size(): number;
105
+ /**
106
+ * Returns cache statistics.
107
+ */
108
+ getStats(): CacheStats;
109
+ /**
110
+ * Resets hit/miss/eviction counters.
111
+ */
112
+ resetStats(): void;
113
+ /**
114
+ * Returns all currently valid cache keys (least recently used first).
115
+ */
116
+ keys(): readonly string[];
117
+ private evictOverflow;
118
+ private startPruning;
119
+ }
120
+ /**
121
+ * Creates an in-memory database cache.
122
+ */
123
+ export declare function createDatabaseCache<TValue = unknown>(options?: MemoryCacheOptions): MemoryDatabaseCache<TValue>;
124
+ /**
125
+ * Separator used between cache key parts.
126
+ */
127
+ export declare const CACHE_KEY_SEPARATOR = ":";
128
+ /**
129
+ * Builds a stable cache key from a namespace and parts.
130
+ *
131
+ * Each part is serialised deterministically (nested keys sorted at every
132
+ * level). Plain string parts are escaped so that a separator inside a part
133
+ * cannot collide with the part boundary: `("ns", "a:b", "c")` and
134
+ * `("ns", "a", "b:c")` produce different keys; serialised objects are
135
+ * self-delimiting and are left as-is. The namespace itself is used verbatim
136
+ * so it can be used as a prefix with {@link invalidateByPrefix}.
137
+ */
138
+ export declare function createCacheKey(namespace: string, ...parts: readonly unknown[]): string;
139
+ /**
140
+ * Escapes separators and backslashes inside a key part.
141
+ */
142
+ export declare function escapeCachePart(part: string): string;
143
+ /**
144
+ * Serializes a cache key component deterministically.
145
+ *
146
+ * Objects are serialised with keys sorted at every nesting level;
147
+ * `bigint`, `Date`, `undefined`, `Map` and `Set` are handled explicitly.
148
+ * Functions and symbols cannot be part of a cache key and throw.
149
+ */
150
+ export declare function serializeCachePart(value: unknown): string;
151
+ /**
152
+ * Wraps a cache around an asynchronous loader.
153
+ *
154
+ * Concurrent misses for the same key on the same cache share one loader
155
+ * call (stampede protection). Values are stored only when the loader
156
+ * resolves; `undefined` results are never cached.
157
+ */
158
+ export declare function getOrSet<TValue>(cache: DatabaseCache<TValue>, key: string, loader: () => Promise<TValue>, options?: CacheOptions): Promise<TValue>;
159
+ /**
160
+ * Invalidates all entries whose keys start with a prefix.
161
+ *
162
+ * Matching is separator-aware: `"user"` matches `"user"` and `"user:…"`
163
+ * but not `"users:…"`. Pass a prefix ending in the separator to match a
164
+ * namespace only.
165
+ */
166
+ export declare function invalidateByPrefix(cache: DatabaseCache, prefix: string): number;
167
+ //# sourceMappingURL=cache.memory.d.ts.map