@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,20 +1,26 @@
|
|
|
1
1
|
import { DatabaseError } from "@zudojs/errors";
|
|
2
2
|
/**
|
|
3
|
-
* In-memory cache
|
|
3
|
+
* In-memory LRU cache for database read results.
|
|
4
4
|
*
|
|
5
|
-
* This cache is
|
|
6
|
-
*
|
|
5
|
+
* This cache is process-local and is not transaction-aware: never populate
|
|
6
|
+
* it from inside a transaction that may still roll back, because other
|
|
7
|
+
* callers would observe the uncommitted value.
|
|
7
8
|
*/
|
|
8
9
|
export class MemoryDatabaseCache {
|
|
9
10
|
entries = new Map();
|
|
10
11
|
hits = 0;
|
|
11
12
|
misses = 0;
|
|
13
|
+
evictions = 0;
|
|
12
14
|
defaultTtlMs;
|
|
15
|
+
maxEntries;
|
|
16
|
+
pruneTimer;
|
|
13
17
|
constructor(options = {}) {
|
|
14
18
|
this.defaultTtlMs = normalizeTtl(options.ttlMs);
|
|
19
|
+
this.maxEntries = normalizeMaxEntries(options.maxEntries);
|
|
20
|
+
this.startPruning(options.pruneIntervalMs);
|
|
15
21
|
}
|
|
16
22
|
/**
|
|
17
|
-
* Gets a cached value.
|
|
23
|
+
* Gets a cached value and marks it as recently used.
|
|
18
24
|
*/
|
|
19
25
|
get(key) {
|
|
20
26
|
validateKey(key);
|
|
@@ -28,31 +34,36 @@ export class MemoryDatabaseCache {
|
|
|
28
34
|
this.misses += 1;
|
|
29
35
|
return undefined;
|
|
30
36
|
}
|
|
37
|
+
// Re-insert to move the key to the most-recently-used position.
|
|
38
|
+
this.entries.delete(key);
|
|
39
|
+
this.entries.set(key, entry);
|
|
31
40
|
this.hits += 1;
|
|
32
41
|
return entry.value;
|
|
33
42
|
}
|
|
34
43
|
/**
|
|
35
|
-
* Sets a cached value
|
|
44
|
+
* Sets a cached value, evicting the least recently used entry when the
|
|
45
|
+
* cache is full.
|
|
36
46
|
*/
|
|
37
47
|
set(key, value, options = {}) {
|
|
38
48
|
validateKey(key);
|
|
39
49
|
const ttlMs = normalizeTtl(options.ttlMs ?? this.defaultTtlMs);
|
|
40
50
|
const createdAt = Date.now();
|
|
51
|
+
this.entries.delete(key);
|
|
41
52
|
this.entries.set(key, {
|
|
42
53
|
value,
|
|
43
54
|
createdAt,
|
|
44
55
|
expiresAt: ttlMs === undefined ? undefined : createdAt + ttlMs,
|
|
45
56
|
});
|
|
57
|
+
this.evictOverflow();
|
|
46
58
|
}
|
|
47
59
|
/**
|
|
48
|
-
* Checks whether a valid cached value exists.
|
|
60
|
+
* Checks whether a valid cached value exists (does not touch recency).
|
|
49
61
|
*/
|
|
50
62
|
has(key) {
|
|
51
63
|
validateKey(key);
|
|
52
64
|
const entry = this.entries.get(key);
|
|
53
|
-
if (!entry)
|
|
65
|
+
if (!entry)
|
|
54
66
|
return false;
|
|
55
|
-
}
|
|
56
67
|
if (isExpired(entry)) {
|
|
57
68
|
this.entries.delete(key);
|
|
58
69
|
return false;
|
|
@@ -85,6 +96,15 @@ export class MemoryDatabaseCache {
|
|
|
85
96
|
}
|
|
86
97
|
return removed;
|
|
87
98
|
}
|
|
99
|
+
/**
|
|
100
|
+
* Stops the background prune timer.
|
|
101
|
+
*/
|
|
102
|
+
dispose() {
|
|
103
|
+
if (this.pruneTimer) {
|
|
104
|
+
clearInterval(this.pruneTimer);
|
|
105
|
+
this.pruneTimer = undefined;
|
|
106
|
+
}
|
|
107
|
+
}
|
|
88
108
|
/**
|
|
89
109
|
* Returns the number of valid entries.
|
|
90
110
|
*/
|
|
@@ -103,22 +123,46 @@ export class MemoryDatabaseCache {
|
|
|
103
123
|
hits: this.hits,
|
|
104
124
|
misses: this.misses,
|
|
105
125
|
hitRate: total === 0 ? 0 : this.hits / total,
|
|
126
|
+
evictions: this.evictions,
|
|
106
127
|
};
|
|
107
128
|
}
|
|
108
129
|
/**
|
|
109
|
-
* Resets hit/miss counters.
|
|
130
|
+
* Resets hit/miss/eviction counters.
|
|
110
131
|
*/
|
|
111
132
|
resetStats() {
|
|
112
133
|
this.hits = 0;
|
|
113
134
|
this.misses = 0;
|
|
135
|
+
this.evictions = 0;
|
|
114
136
|
}
|
|
115
137
|
/**
|
|
116
|
-
* Returns all currently valid cache keys.
|
|
138
|
+
* Returns all currently valid cache keys (least recently used first).
|
|
117
139
|
*/
|
|
118
140
|
keys() {
|
|
119
141
|
this.prune();
|
|
120
142
|
return Object.freeze([...this.entries.keys()]);
|
|
121
143
|
}
|
|
144
|
+
evictOverflow() {
|
|
145
|
+
if (this.maxEntries === undefined)
|
|
146
|
+
return;
|
|
147
|
+
while (this.entries.size > this.maxEntries) {
|
|
148
|
+
const oldest = this.entries.keys().next();
|
|
149
|
+
if (oldest.done)
|
|
150
|
+
break;
|
|
151
|
+
this.entries.delete(oldest.value);
|
|
152
|
+
this.evictions += 1;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
startPruning(intervalMs) {
|
|
156
|
+
if (intervalMs === undefined)
|
|
157
|
+
return;
|
|
158
|
+
if (!Number.isFinite(intervalMs) || intervalMs <= 0) {
|
|
159
|
+
throw new TypeError("Cache prune interval must be a positive finite number.");
|
|
160
|
+
}
|
|
161
|
+
this.pruneTimer = setInterval(() => {
|
|
162
|
+
this.prune();
|
|
163
|
+
}, Math.floor(intervalMs));
|
|
164
|
+
this.pruneTimer.unref?.();
|
|
165
|
+
}
|
|
122
166
|
}
|
|
123
167
|
/**
|
|
124
168
|
* Creates an in-memory database cache.
|
|
@@ -126,34 +170,51 @@ export class MemoryDatabaseCache {
|
|
|
126
170
|
export function createDatabaseCache(options = {}) {
|
|
127
171
|
return new MemoryDatabaseCache(options);
|
|
128
172
|
}
|
|
173
|
+
/**
|
|
174
|
+
* Separator used between cache key parts.
|
|
175
|
+
*/
|
|
176
|
+
export const CACHE_KEY_SEPARATOR = ":";
|
|
129
177
|
/**
|
|
130
178
|
* Builds a stable cache key from a namespace and parts.
|
|
179
|
+
*
|
|
180
|
+
* Each part is serialised deterministically (nested keys sorted at every
|
|
181
|
+
* level). Plain string parts are escaped so that a separator inside a part
|
|
182
|
+
* cannot collide with the part boundary: `("ns", "a:b", "c")` and
|
|
183
|
+
* `("ns", "a", "b:c")` produce different keys; serialised objects are
|
|
184
|
+
* self-delimiting and are left as-is. The namespace itself is used verbatim
|
|
185
|
+
* so it can be used as a prefix with {@link invalidateByPrefix}.
|
|
131
186
|
*/
|
|
132
187
|
export function createCacheKey(namespace, ...parts) {
|
|
133
188
|
validateKey(namespace);
|
|
134
|
-
return [
|
|
189
|
+
return [
|
|
190
|
+
namespace,
|
|
191
|
+
...parts.map((part) => typeof part === "string"
|
|
192
|
+
? escapeCachePart(part)
|
|
193
|
+
: serializeCachePart(part)),
|
|
194
|
+
].join(CACHE_KEY_SEPARATOR);
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Escapes separators and backslashes inside a key part.
|
|
198
|
+
*/
|
|
199
|
+
export function escapeCachePart(part) {
|
|
200
|
+
return part.replace(/\\/g, "\\\\").replace(/:/g, "\\:");
|
|
135
201
|
}
|
|
136
202
|
/**
|
|
137
203
|
* Serializes a cache key component deterministically.
|
|
204
|
+
*
|
|
205
|
+
* Objects are serialised with keys sorted at every nesting level;
|
|
206
|
+
* `bigint`, `Date`, `undefined`, `Map` and `Set` are handled explicitly.
|
|
207
|
+
* Functions and symbols cannot be part of a cache key and throw.
|
|
138
208
|
*/
|
|
139
209
|
export function serializeCachePart(value) {
|
|
140
|
-
if (value === null) {
|
|
141
|
-
return "null";
|
|
142
|
-
}
|
|
143
|
-
if (value === undefined) {
|
|
144
|
-
return "undefined";
|
|
145
|
-
}
|
|
146
210
|
if (typeof value === "string" ||
|
|
147
211
|
typeof value === "number" ||
|
|
148
212
|
typeof value === "boolean" ||
|
|
149
213
|
typeof value === "bigint") {
|
|
150
214
|
return String(value);
|
|
151
215
|
}
|
|
152
|
-
if (value instanceof Date) {
|
|
153
|
-
return value.toISOString();
|
|
154
|
-
}
|
|
155
216
|
try {
|
|
156
|
-
return
|
|
217
|
+
return stableSerialize(value, new Set());
|
|
157
218
|
}
|
|
158
219
|
catch (error) {
|
|
159
220
|
throw new DatabaseError("Unable to serialize database cache key.", {
|
|
@@ -161,8 +222,70 @@ export function serializeCachePart(value) {
|
|
|
161
222
|
});
|
|
162
223
|
}
|
|
163
224
|
}
|
|
225
|
+
function stableSerialize(value, seen) {
|
|
226
|
+
if (value === null)
|
|
227
|
+
return "null";
|
|
228
|
+
if (value === undefined)
|
|
229
|
+
return "undefined";
|
|
230
|
+
switch (typeof value) {
|
|
231
|
+
case "string":
|
|
232
|
+
return JSON.stringify(value);
|
|
233
|
+
case "number":
|
|
234
|
+
return Number.isFinite(value) ? String(value) : JSON.stringify(String(value));
|
|
235
|
+
case "boolean":
|
|
236
|
+
return String(value);
|
|
237
|
+
case "bigint":
|
|
238
|
+
return `${value.toString()}n`;
|
|
239
|
+
case "function":
|
|
240
|
+
case "symbol":
|
|
241
|
+
throw new TypeError(`Cannot serialize a ${typeof value} as a cache key part.`);
|
|
242
|
+
default:
|
|
243
|
+
break;
|
|
244
|
+
}
|
|
245
|
+
if (value instanceof Date) {
|
|
246
|
+
return Number.isNaN(value.getTime()) ? '"Invalid Date"' : JSON.stringify(value.toISOString());
|
|
247
|
+
}
|
|
248
|
+
if (typeof value === "object") {
|
|
249
|
+
if (seen.has(value)) {
|
|
250
|
+
throw new TypeError("Cannot serialize a circular structure as a cache key part.");
|
|
251
|
+
}
|
|
252
|
+
seen.add(value);
|
|
253
|
+
try {
|
|
254
|
+
if (Array.isArray(value)) {
|
|
255
|
+
return `[${value.map((item) => stableSerialize(item, seen)).join(",")}]`;
|
|
256
|
+
}
|
|
257
|
+
if (value instanceof Set) {
|
|
258
|
+
return `Set[${[...value].map((item) => stableSerialize(item, seen)).sort().join(",")}]`;
|
|
259
|
+
}
|
|
260
|
+
if (value instanceof Map) {
|
|
261
|
+
const entries = [...value.entries()]
|
|
262
|
+
.map(([k, v]) => `${stableSerialize(k, seen)}=>${stableSerialize(v, seen)}`)
|
|
263
|
+
.sort();
|
|
264
|
+
return `Map{${entries.join(",")}}`;
|
|
265
|
+
}
|
|
266
|
+
if (typeof value.toJSON === "function") {
|
|
267
|
+
return stableSerialize(value.toJSON(), seen);
|
|
268
|
+
}
|
|
269
|
+
const record = value;
|
|
270
|
+
const keys = Object.keys(record).sort();
|
|
271
|
+
const fields = keys
|
|
272
|
+
.filter((key) => record[key] !== undefined)
|
|
273
|
+
.map((key) => `${JSON.stringify(key)}:${stableSerialize(record[key], seen)}`);
|
|
274
|
+
return `{${fields.join(",")}}`;
|
|
275
|
+
}
|
|
276
|
+
finally {
|
|
277
|
+
seen.delete(value);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
return JSON.stringify(String(value));
|
|
281
|
+
}
|
|
282
|
+
const inflightLoaders = new WeakMap();
|
|
164
283
|
/**
|
|
165
284
|
* Wraps a cache around an asynchronous loader.
|
|
285
|
+
*
|
|
286
|
+
* Concurrent misses for the same key on the same cache share one loader
|
|
287
|
+
* call (stampede protection). Values are stored only when the loader
|
|
288
|
+
* resolves; `undefined` results are never cached.
|
|
166
289
|
*/
|
|
167
290
|
export async function getOrSet(cache, key, loader, options) {
|
|
168
291
|
validateKey(key);
|
|
@@ -170,43 +293,61 @@ export async function getOrSet(cache, key, loader, options) {
|
|
|
170
293
|
throw new TypeError("A cache loader function is required.");
|
|
171
294
|
}
|
|
172
295
|
const cached = cache.get(key);
|
|
173
|
-
if (cached !== undefined)
|
|
296
|
+
if (cached !== undefined)
|
|
174
297
|
return cached;
|
|
298
|
+
let inflight = inflightLoaders.get(cache);
|
|
299
|
+
if (!inflight) {
|
|
300
|
+
inflight = new Map();
|
|
301
|
+
inflightLoaders.set(cache, inflight);
|
|
175
302
|
}
|
|
176
|
-
const
|
|
177
|
-
|
|
178
|
-
|
|
303
|
+
const pending = inflight.get(key);
|
|
304
|
+
if (pending)
|
|
305
|
+
return pending;
|
|
306
|
+
const promise = (async () => {
|
|
307
|
+
try {
|
|
308
|
+
const value = await loader();
|
|
309
|
+
if (value !== undefined)
|
|
310
|
+
cache.set(key, value, options);
|
|
311
|
+
return value;
|
|
312
|
+
}
|
|
313
|
+
finally {
|
|
314
|
+
inflight.delete(key);
|
|
315
|
+
}
|
|
316
|
+
})();
|
|
317
|
+
inflight.set(key, promise);
|
|
318
|
+
return promise;
|
|
179
319
|
}
|
|
180
320
|
/**
|
|
181
321
|
* Invalidates all entries whose keys start with a prefix.
|
|
182
322
|
*
|
|
183
|
-
*
|
|
323
|
+
* Matching is separator-aware: `"user"` matches `"user"` and `"user:…"`
|
|
324
|
+
* but not `"users:…"`. Pass a prefix ending in the separator to match a
|
|
325
|
+
* namespace only.
|
|
184
326
|
*/
|
|
185
327
|
export function invalidateByPrefix(cache, prefix) {
|
|
186
328
|
validateKey(prefix);
|
|
329
|
+
if (typeof cache.keys !== "function") {
|
|
330
|
+
throw new TypeError("The cache does not expose keys(); cannot invalidate by prefix.");
|
|
331
|
+
}
|
|
332
|
+
const normalized = prefix.endsWith(CACHE_KEY_SEPARATOR)
|
|
333
|
+
? prefix.slice(0, -CACHE_KEY_SEPARATOR.length)
|
|
334
|
+
: prefix;
|
|
335
|
+
const withSeparator = normalized + CACHE_KEY_SEPARATOR;
|
|
187
336
|
let removed = 0;
|
|
188
337
|
for (const key of cache.keys()) {
|
|
189
|
-
if (key.startsWith(
|
|
190
|
-
if (cache.delete(key))
|
|
338
|
+
if (key === normalized || key.startsWith(withSeparator)) {
|
|
339
|
+
if (cache.delete(key))
|
|
191
340
|
removed += 1;
|
|
192
|
-
}
|
|
193
341
|
}
|
|
194
342
|
}
|
|
195
343
|
return removed;
|
|
196
344
|
}
|
|
197
|
-
/**
|
|
198
|
-
* Checks whether a cache entry has expired.
|
|
199
|
-
*/
|
|
200
345
|
function isExpired(entry) {
|
|
201
346
|
return entry.expiresAt !== undefined && entry.expiresAt <= Date.now();
|
|
202
347
|
}
|
|
203
|
-
/**
|
|
204
|
-
* Normalizes a TTL value.
|
|
205
|
-
*/
|
|
206
348
|
function normalizeTtl(ttlMs) {
|
|
207
|
-
if (ttlMs === undefined)
|
|
349
|
+
if (ttlMs === undefined)
|
|
208
350
|
return undefined;
|
|
209
|
-
}
|
|
210
351
|
if (!Number.isFinite(ttlMs)) {
|
|
211
352
|
throw new TypeError("Cache TTL must be a finite number.");
|
|
212
353
|
}
|
|
@@ -215,9 +356,14 @@ function normalizeTtl(ttlMs) {
|
|
|
215
356
|
}
|
|
216
357
|
return Math.floor(ttlMs);
|
|
217
358
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
359
|
+
function normalizeMaxEntries(maxEntries) {
|
|
360
|
+
if (maxEntries === undefined)
|
|
361
|
+
return undefined;
|
|
362
|
+
if (!Number.isInteger(maxEntries) || maxEntries <= 0) {
|
|
363
|
+
throw new TypeError("Cache maxEntries must be a positive integer.");
|
|
364
|
+
}
|
|
365
|
+
return maxEntries;
|
|
366
|
+
}
|
|
221
367
|
function validateKey(key) {
|
|
222
368
|
if (typeof key !== "string" || key.trim().length === 0) {
|
|
223
369
|
throw new TypeError("A non-empty cache key is required.");
|
package/dist/cache/index.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @zudojs/database — Database Cache
|
|
3
3
|
*
|
|
4
|
-
* In-memory cache for database read results.
|
|
4
|
+
* In-memory LRU cache for database read results.
|
|
5
5
|
*/
|
|
6
|
-
export { MemoryDatabaseCache, createDatabaseCache, createCacheKey, serializeCachePart, getOrSet, invalidateByPrefix, type CacheEntry, type CacheOptions, type CacheStats, type DatabaseCache, } from "./cache.memory.js";
|
|
6
|
+
export { MemoryDatabaseCache, createDatabaseCache, createCacheKey, escapeCachePart, serializeCachePart, getOrSet, invalidateByPrefix, CACHE_KEY_SEPARATOR, type CacheEntry, type CacheOptions, type MemoryCacheOptions, type CacheStats, type DatabaseCache, } from "./cache.memory.js";
|
|
7
7
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/cache/index.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @zudojs/database — Database Cache
|
|
3
3
|
*
|
|
4
|
-
* In-memory cache for database read results.
|
|
4
|
+
* In-memory LRU cache for database read results.
|
|
5
5
|
*/
|
|
6
|
-
export { MemoryDatabaseCache, createDatabaseCache, createCacheKey, serializeCachePart, getOrSet, invalidateByPrefix, } from "./cache.memory.js";
|
|
6
|
+
export { MemoryDatabaseCache, createDatabaseCache, createCacheKey, escapeCachePart, serializeCachePart, getOrSet, invalidateByPrefix, CACHE_KEY_SEPARATOR, } from "./cache.memory.js";
|
|
7
7
|
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { DatabaseClient, type DatabaseClientOptions, type DatabaseTransactionContext, type PrismaClientLike } from "../databaseClient/databaseClient.core.js";
|
|
2
|
+
import type { DatabaseHealth, DatabaseStatus, TransactionCallback, TransactionOptions } from "../databaseType/databaseType.type.js";
|
|
3
|
+
/**
|
|
4
|
+
* Database facade used by the application layer.
|
|
5
|
+
*
|
|
6
|
+
* This module provides a single database lifecycle entry point while
|
|
7
|
+
* keeping the underlying Prisma client implementation inside the
|
|
8
|
+
* database package.
|
|
9
|
+
*/
|
|
10
|
+
export declare class Database {
|
|
11
|
+
private readonly client;
|
|
12
|
+
/**
|
|
13
|
+
* @param options Client options, or an existing {@link DatabaseClient}
|
|
14
|
+
* to wrap so a single client is shared by the facade and other managers.
|
|
15
|
+
*/
|
|
16
|
+
constructor(options?: DatabaseClientOptions | DatabaseClient);
|
|
17
|
+
/**
|
|
18
|
+
* Initializes the database connection.
|
|
19
|
+
*/
|
|
20
|
+
connect(): Promise<void>;
|
|
21
|
+
/**
|
|
22
|
+
* Closes the database connection.
|
|
23
|
+
*/
|
|
24
|
+
disconnect(): Promise<void>;
|
|
25
|
+
/**
|
|
26
|
+
* Ensures the database is connected.
|
|
27
|
+
*/
|
|
28
|
+
ensureConnected(): Promise<void>;
|
|
29
|
+
/**
|
|
30
|
+
* Checks database connectivity.
|
|
31
|
+
*/
|
|
32
|
+
ping(): Promise<void>;
|
|
33
|
+
/**
|
|
34
|
+
* Returns the current database status.
|
|
35
|
+
*/
|
|
36
|
+
getStatus(): DatabaseStatus;
|
|
37
|
+
/**
|
|
38
|
+
* Returns database health information.
|
|
39
|
+
*/
|
|
40
|
+
healthCheck(): Promise<DatabaseHealth>;
|
|
41
|
+
/**
|
|
42
|
+
* Executes work inside a database transaction.
|
|
43
|
+
*/
|
|
44
|
+
transaction<TResult>(callback: TransactionCallback<DatabaseTransactionContext, TResult>, options?: TransactionOptions): Promise<TResult>;
|
|
45
|
+
/**
|
|
46
|
+
* Returns the underlying database client.
|
|
47
|
+
*
|
|
48
|
+
* This should primarily be used by repository and infrastructure
|
|
49
|
+
* implementations that require direct Prisma access.
|
|
50
|
+
*/
|
|
51
|
+
getClient(): DatabaseClient;
|
|
52
|
+
/**
|
|
53
|
+
* Returns the underlying Prisma client.
|
|
54
|
+
*/
|
|
55
|
+
getPrisma(): PrismaClientLike;
|
|
56
|
+
/**
|
|
57
|
+
* Releases all database resources.
|
|
58
|
+
*/
|
|
59
|
+
destroy(): Promise<void>;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Creates a database facade.
|
|
63
|
+
*/
|
|
64
|
+
export declare function createDatabase(options?: DatabaseClientOptions | DatabaseClient): Database;
|
|
65
|
+
/**
|
|
66
|
+
* Returns the shared application database instance.
|
|
67
|
+
*
|
|
68
|
+
* The connection is not established automatically. Call
|
|
69
|
+
* `connect()` during application bootstrap.
|
|
70
|
+
*
|
|
71
|
+
* @throws {TypeError} when options are supplied after the shared instance
|
|
72
|
+
* has already been created; they would otherwise be silently ignored.
|
|
73
|
+
* Call {@link resetDatabase} first to reconfigure.
|
|
74
|
+
*/
|
|
75
|
+
export declare function getDatabase(options?: DatabaseClientOptions | DatabaseClient): Database;
|
|
76
|
+
/**
|
|
77
|
+
* Connects the shared application database.
|
|
78
|
+
*/
|
|
79
|
+
export declare function connectDatabase(options?: DatabaseClientOptions | DatabaseClient): Promise<Database>;
|
|
80
|
+
/**
|
|
81
|
+
* Disconnects the shared application database.
|
|
82
|
+
*/
|
|
83
|
+
export declare function disconnectDatabase(): Promise<void>;
|
|
84
|
+
/**
|
|
85
|
+
* Resets the shared database instance.
|
|
86
|
+
*
|
|
87
|
+
* Primarily useful for application shutdown, tests, and isolated
|
|
88
|
+
* runtime environments.
|
|
89
|
+
*/
|
|
90
|
+
export declare function resetDatabase(): Promise<void>;
|
|
91
|
+
//# sourceMappingURL=database.core.d.ts.map
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { DatabaseClient, createDatabaseClient, } from "../databaseClient/databaseClient.core.js";
|
|
2
|
+
/**
|
|
3
|
+
* Database facade used by the application layer.
|
|
4
|
+
*
|
|
5
|
+
* This module provides a single database lifecycle entry point while
|
|
6
|
+
* keeping the underlying Prisma client implementation inside the
|
|
7
|
+
* database package.
|
|
8
|
+
*/
|
|
9
|
+
export class Database {
|
|
10
|
+
client;
|
|
11
|
+
/**
|
|
12
|
+
* @param options Client options, or an existing {@link DatabaseClient}
|
|
13
|
+
* to wrap so a single client is shared by the facade and other managers.
|
|
14
|
+
*/
|
|
15
|
+
constructor(options = {}) {
|
|
16
|
+
this.client =
|
|
17
|
+
options instanceof DatabaseClient ? options : createDatabaseClient(options);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Initializes the database connection.
|
|
21
|
+
*/
|
|
22
|
+
async connect() {
|
|
23
|
+
await this.client.connect();
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Closes the database connection.
|
|
27
|
+
*/
|
|
28
|
+
async disconnect() {
|
|
29
|
+
await this.client.disconnect();
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Ensures the database is connected.
|
|
33
|
+
*/
|
|
34
|
+
async ensureConnected() {
|
|
35
|
+
await this.client.ensureConnected();
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Checks database connectivity.
|
|
39
|
+
*/
|
|
40
|
+
async ping() {
|
|
41
|
+
await this.client.ping();
|
|
42
|
+
}
|
|
43
|
+
/**
|
|
44
|
+
* Returns the current database status.
|
|
45
|
+
*/
|
|
46
|
+
getStatus() {
|
|
47
|
+
return this.client.getStatus();
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Returns database health information.
|
|
51
|
+
*/
|
|
52
|
+
async healthCheck() {
|
|
53
|
+
return this.client.healthCheck();
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Executes work inside a database transaction.
|
|
57
|
+
*/
|
|
58
|
+
async transaction(callback, options) {
|
|
59
|
+
return this.client.transaction(callback, options);
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* Returns the underlying database client.
|
|
63
|
+
*
|
|
64
|
+
* This should primarily be used by repository and infrastructure
|
|
65
|
+
* implementations that require direct Prisma access.
|
|
66
|
+
*/
|
|
67
|
+
getClient() {
|
|
68
|
+
return this.client;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Returns the underlying Prisma client.
|
|
72
|
+
*/
|
|
73
|
+
getPrisma() {
|
|
74
|
+
return this.client.getPrisma();
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Releases all database resources.
|
|
78
|
+
*/
|
|
79
|
+
async destroy() {
|
|
80
|
+
await this.client.destroy();
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
/**
|
|
84
|
+
* Creates a database facade.
|
|
85
|
+
*/
|
|
86
|
+
export function createDatabase(options = {}) {
|
|
87
|
+
return new Database(options);
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* Default database instance.
|
|
91
|
+
*
|
|
92
|
+
* The instance is created lazily by consumers through the exported
|
|
93
|
+
* factory rather than connecting during module import.
|
|
94
|
+
*/
|
|
95
|
+
let defaultDatabase;
|
|
96
|
+
/**
|
|
97
|
+
* Returns the shared application database instance.
|
|
98
|
+
*
|
|
99
|
+
* The connection is not established automatically. Call
|
|
100
|
+
* `connect()` during application bootstrap.
|
|
101
|
+
*
|
|
102
|
+
* @throws {TypeError} when options are supplied after the shared instance
|
|
103
|
+
* has already been created; they would otherwise be silently ignored.
|
|
104
|
+
* Call {@link resetDatabase} first to reconfigure.
|
|
105
|
+
*/
|
|
106
|
+
export function getDatabase(options = {}) {
|
|
107
|
+
if (!defaultDatabase) {
|
|
108
|
+
defaultDatabase = createDatabase(options);
|
|
109
|
+
return defaultDatabase;
|
|
110
|
+
}
|
|
111
|
+
const hasOptions = options instanceof DatabaseClient || Object.keys(options).length > 0;
|
|
112
|
+
if (hasOptions && !(options instanceof DatabaseClient && defaultDatabase.getClient() === options)) {
|
|
113
|
+
throw new TypeError("The shared database instance already exists; options passed to getDatabase() would be ignored. Call resetDatabase() before reconfiguring.");
|
|
114
|
+
}
|
|
115
|
+
return defaultDatabase;
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Connects the shared application database.
|
|
119
|
+
*/
|
|
120
|
+
export async function connectDatabase(options = {}) {
|
|
121
|
+
const database = getDatabase(options);
|
|
122
|
+
await database.connect();
|
|
123
|
+
return database;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Disconnects the shared application database.
|
|
127
|
+
*/
|
|
128
|
+
export async function disconnectDatabase() {
|
|
129
|
+
if (!defaultDatabase) {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
await defaultDatabase.disconnect();
|
|
133
|
+
}
|
|
134
|
+
/**
|
|
135
|
+
* Resets the shared database instance.
|
|
136
|
+
*
|
|
137
|
+
* Primarily useful for application shutdown, tests, and isolated
|
|
138
|
+
* runtime environments.
|
|
139
|
+
*/
|
|
140
|
+
export async function resetDatabase() {
|
|
141
|
+
const database = defaultDatabase;
|
|
142
|
+
if (!database) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
// Clear the singleton first so a failing destroy() never leaves a stale
|
|
146
|
+
// instance behind.
|
|
147
|
+
defaultDatabase = undefined;
|
|
148
|
+
await database.destroy();
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=database.core.js.map
|