@bitclaw/sqlite 1.1.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 (41) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +251 -0
  3. package/dist/scripts/benchmark.d.ts +3 -0
  4. package/dist/scripts/benchmark.d.ts.map +1 -0
  5. package/dist/scripts/benchmark.js +286 -0
  6. package/dist/scripts/load-test-utils.d.ts +77 -0
  7. package/dist/scripts/load-test-utils.d.ts.map +1 -0
  8. package/dist/scripts/load-test-utils.js +235 -0
  9. package/dist/src/cache-lock.d.ts +25 -0
  10. package/dist/src/cache-lock.d.ts.map +1 -0
  11. package/dist/src/cache-lock.js +95 -0
  12. package/dist/src/connection.d.ts +26 -0
  13. package/dist/src/connection.d.ts.map +1 -0
  14. package/dist/src/connection.js +132 -0
  15. package/dist/src/json-cache.d.ts +89 -0
  16. package/dist/src/json-cache.d.ts.map +1 -0
  17. package/dist/src/json-cache.js +289 -0
  18. package/dist/src/pool.d.ts +98 -0
  19. package/dist/src/pool.d.ts.map +1 -0
  20. package/dist/src/pool.js +331 -0
  21. package/dist/src/prisma-immediate-tx.d.ts +23 -0
  22. package/dist/src/prisma-immediate-tx.d.ts.map +1 -0
  23. package/dist/src/prisma-immediate-tx.js +42 -0
  24. package/dist/src/query-logger.d.ts +21 -0
  25. package/dist/src/query-logger.d.ts.map +1 -0
  26. package/dist/src/query-logger.js +60 -0
  27. package/dist/src/retry.d.ts +14 -0
  28. package/dist/src/retry.d.ts.map +1 -0
  29. package/dist/src/retry.js +49 -0
  30. package/dist/src/ttl-cache.d.ts +57 -0
  31. package/dist/src/ttl-cache.d.ts.map +1 -0
  32. package/dist/src/ttl-cache.js +92 -0
  33. package/dist/src/worker.d.ts +38 -0
  34. package/dist/src/worker.d.ts.map +1 -0
  35. package/dist/src/worker.js +294 -0
  36. package/dist/src/write-mutex.d.ts +33 -0
  37. package/dist/src/write-mutex.d.ts.map +1 -0
  38. package/dist/src/write-mutex.js +60 -0
  39. package/package.json +48 -0
  40. package/scripts/benchmark.ts +373 -0
  41. package/scripts/load-test-utils.ts +370 -0
@@ -0,0 +1,92 @@
1
+ // packages/sqlite/src/ttl-cache.ts
2
+ // In-memory TTL cache for server-side deduplication across HTTP requests.
3
+ // Complements json-cache (persistent, file-based) with a lightweight
4
+ // in-memory cache that auto-expires and auto-prunes.
5
+ /**
6
+ * In-memory TTL cache backed by a Map.
7
+ *
8
+ * Designed for server-side deduplication of expensive lookups (auth sessions,
9
+ * membership checks, bootstrap data) across HTTP requests. Entries expire
10
+ * after `ttl` milliseconds and are automatically pruned when the map exceeds
11
+ * `maxSize`.
12
+ *
13
+ * Unlike WeakMap per-request caching (which deduplicates within a single
14
+ * request), TTLCache deduplicates across requests — e.g. when TanStack
15
+ * Router replays `beforeLoad` on client hydration, the server returns the
16
+ * cached result instantly (0 DB queries).
17
+ *
18
+ * @example
19
+ * ```typescript
20
+ * import { TTLCache } from '@bitclaw/sqlite/ttl-cache';
21
+ *
22
+ * type BootstrapData = { user: User; workspaces: Workspace[] };
23
+ *
24
+ * const bootstrapCache = new TTLCache<BootstrapData>({ ttl: 30_000 });
25
+ *
26
+ * // In your server function:
27
+ * const cached = bootstrapCache.get(sessionId);
28
+ * if (cached) return cached;
29
+ *
30
+ * const data = await expensiveQuery();
31
+ * bootstrapCache.set(sessionId, data);
32
+ * return data;
33
+ * ```
34
+ */
35
+ export class TTLCache {
36
+ cache = new Map();
37
+ ttl;
38
+ maxSize;
39
+ constructor(options = {}) {
40
+ this.ttl = options.ttl ?? 30_000;
41
+ this.maxSize = options.maxSize ?? 100;
42
+ }
43
+ /** Get a cached value if it exists and hasn't expired. */
44
+ get(key) {
45
+ const entry = this.cache.get(key);
46
+ if (!entry)
47
+ return undefined;
48
+ if (entry.expiresAt <= Date.now()) {
49
+ this.cache.delete(key);
50
+ return undefined;
51
+ }
52
+ return entry.value;
53
+ }
54
+ /** Check if a non-expired entry exists for the given key. */
55
+ has(key) {
56
+ return this.get(key) !== undefined;
57
+ }
58
+ /** Store a value with the configured TTL. Auto-prunes if maxSize exceeded. */
59
+ set(key, value) {
60
+ this.cache.set(key, {
61
+ value,
62
+ expiresAt: Date.now() + this.ttl
63
+ });
64
+ if (this.cache.size > this.maxSize) {
65
+ this.prune();
66
+ }
67
+ }
68
+ /** Remove a specific entry. */
69
+ delete(key) {
70
+ return this.cache.delete(key);
71
+ }
72
+ /** Remove all entries. */
73
+ clear() {
74
+ this.cache.clear();
75
+ }
76
+ /** Number of entries (including potentially expired ones). */
77
+ get size() {
78
+ return this.cache.size;
79
+ }
80
+ /** Remove all expired entries. */
81
+ prune() {
82
+ const now = Date.now();
83
+ let pruned = 0;
84
+ for (const [key, entry] of this.cache) {
85
+ if (entry.expiresAt <= now) {
86
+ this.cache.delete(key);
87
+ pruned++;
88
+ }
89
+ }
90
+ return pruned;
91
+ }
92
+ }
@@ -0,0 +1,38 @@
1
+ type DatabaseConfig = {
2
+ path: string;
3
+ options: {
4
+ verbose?: ((message?: unknown, ...additionalArgs: unknown[]) => void) | undefined;
5
+ fileMustExist: boolean;
6
+ };
7
+ };
8
+ type WorkerMessage = {
9
+ id: string;
10
+ sql: string;
11
+ params?: unknown[];
12
+ type?: string;
13
+ };
14
+ type WorkerResponse = {
15
+ id: string;
16
+ result?: unknown;
17
+ error?: {
18
+ message: string;
19
+ code?: string;
20
+ errno?: number;
21
+ };
22
+ workerId: string;
23
+ durationMs: number;
24
+ success: boolean;
25
+ };
26
+ export declare class SQLiteWorker {
27
+ private db;
28
+ private config;
29
+ private workerId;
30
+ constructor(config?: DatabaseConfig);
31
+ private initializeDatabase;
32
+ handleMessage(message: WorkerMessage): Promise<WorkerResponse>;
33
+ private executeQuery;
34
+ shutdown(): void;
35
+ getWorkerId(): string;
36
+ }
37
+ export {};
38
+ //# sourceMappingURL=worker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worker.d.ts","sourceRoot":"","sources":["../../src/worker.ts"],"names":[],"mappings":"AAoEA,KAAK,cAAc,GAAG;IACpB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE;QACP,OAAO,CAAC,EACJ,CAAC,CAAC,OAAO,CAAC,EAAE,OAAO,EAAE,GAAG,cAAc,EAAE,OAAO,EAAE,KAAK,IAAI,CAAC,GAC3D,SAAS,CAAC;QACd,aAAa,EAAE,OAAO,CAAC;KACxB,CAAC;CACH,CAAC;AAEF,KAAK,aAAa,GAAG;IACnB,EAAE,EAAE,MAAM,CAAC;IACX,GAAG,EAAE,MAAM,CAAC;IACZ,MAAM,CAAC,EAAE,OAAO,EAAE,CAAC;IACnB,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF,KAAK,cAAc,GAAG;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,KAAK,CAAC,EAAE;QACN,OAAO,EAAE,MAAM,CAAC;QAChB,IAAI,CAAC,EAAE,MAAM,CAAC;QACd,KAAK,CAAC,EAAE,MAAM,CAAC;KAChB,CAAC;IACF,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,OAAO,CAAC;CAClB,CAAC;AA6DF,qBAAa,YAAY;IACvB,OAAO,CAAC,EAAE,CAAyB;IACnC,OAAO,CAAC,MAAM,CAA6B;IAC3C,OAAO,CAAC,QAAQ,CAAS;gBAEb,MAAM,CAAC,EAAE,cAAc;YAKrB,kBAAkB;IAkD1B,aAAa,CAAC,OAAO,EAAE,aAAa,GAAG,OAAO,CAAC,cAAc,CAAC;IA6CpE,OAAO,CAAC,YAAY;IAoDpB,QAAQ,IAAI,IAAI;IAchB,WAAW,IAAI,MAAM;CAGtB"}
@@ -0,0 +1,294 @@
1
+ // packages/sqlite/src/worker.ts
2
+ // SQLite Worker Thread - Optimized for Hetzner VPS deployment
3
+ // Uses bun:sqlite for 3-6x faster reads compared to better-sqlite3
4
+ import { Database } from 'bun:sqlite';
5
+ import { parentPort, workerData } from 'node:worker_threads';
6
+ const isDevelopment = process.env.NODE_ENV === 'development';
7
+ const isTest = process.env.NODE_ENV === 'test';
8
+ // ---------------------------------------------------------------------------
9
+ // Prepared-statement LRU cache for optimal performance
10
+ // ---------------------------------------------------------------------------
11
+ class StatementCache {
12
+ cache = new Map();
13
+ maxSize = 256;
14
+ hits = 0;
15
+ misses = 0;
16
+ get(sql) {
17
+ const stmt = this.cache.get(sql);
18
+ if (stmt) {
19
+ this.hits += 1;
20
+ // Move to end (LRU behavior)
21
+ this.cache.delete(sql);
22
+ this.cache.set(sql, stmt);
23
+ return stmt;
24
+ }
25
+ return null;
26
+ }
27
+ set(sql, stmt) {
28
+ this.misses += 1;
29
+ // If at capacity, remove oldest
30
+ if (this.cache.size >= this.maxSize) {
31
+ const firstKey = this.cache.keys().next().value;
32
+ if (firstKey) {
33
+ this.cache.delete(firstKey);
34
+ }
35
+ }
36
+ this.cache.set(sql, stmt);
37
+ }
38
+ getStats() {
39
+ const total = this.hits + this.misses;
40
+ return {
41
+ hits: this.hits,
42
+ misses: this.misses,
43
+ size: this.cache.size,
44
+ hitRate: total > 0 ? (this.hits / total) * 100 : 0
45
+ };
46
+ }
47
+ clear() {
48
+ this.cache.clear();
49
+ this.hits = 0;
50
+ this.misses = 0;
51
+ }
52
+ }
53
+ const stmtCache = new StatementCache();
54
+ // Worker-specific database configuration
55
+ async function getDbConfig() {
56
+ const envPath = workerData?.databasePath ??
57
+ process.env.DATABASE_PATH;
58
+ if (envPath) {
59
+ return {
60
+ path: envPath,
61
+ options: {
62
+ verbose: undefined,
63
+ fileMustExist: false
64
+ }
65
+ };
66
+ }
67
+ if (isTest) {
68
+ return {
69
+ path: ':memory:',
70
+ options: {
71
+ verbose: undefined,
72
+ fileMustExist: false
73
+ }
74
+ };
75
+ }
76
+ if (isDevelopment) {
77
+ return {
78
+ path: './data/app.db',
79
+ options: {
80
+ verbose: undefined,
81
+ fileMustExist: false
82
+ }
83
+ };
84
+ }
85
+ // Production
86
+ return {
87
+ path: '/data/app.db',
88
+ options: { verbose: undefined, fileMustExist: false }
89
+ };
90
+ }
91
+ function getOrCreateStatement(db, sql) {
92
+ // Normalize SQL for better cache hits
93
+ const normalizedSql = sql.trim().replace(/\s+/g, ' ');
94
+ let stmt = stmtCache.get(normalizedSql);
95
+ if (stmt) {
96
+ return stmt;
97
+ }
98
+ // Cache miss → prepare + insert (bun:sqlite uses .query() instead of .prepare())
99
+ stmt = db.query(normalizedSql);
100
+ stmtCache.set(normalizedSql, stmt);
101
+ return stmt;
102
+ }
103
+ // SQLite Worker class
104
+ export class SQLiteWorker {
105
+ db = null;
106
+ config;
107
+ workerId;
108
+ constructor(config) {
109
+ this.config = config;
110
+ this.workerId = `worker-${Date.now()}-${Math.random().toString(36).substring(2, 7)}`;
111
+ }
112
+ async initializeDatabase() {
113
+ if (this.db)
114
+ return;
115
+ // Get config if not provided
116
+ if (!this.config) {
117
+ this.config = await getDbConfig();
118
+ }
119
+ try {
120
+ // bun:sqlite constructor options differ from better-sqlite3
121
+ this.db = new Database(this.config.path, {
122
+ create: !this.config.options.fileMustExist,
123
+ readonly: false
124
+ });
125
+ // ✅ OPTIMIZED: Enhanced multi-worker SQLite configuration
126
+ // bun:sqlite uses run() for PRAGMA statements instead of pragma()
127
+ if (this.config.path !== ':memory:') {
128
+ // WAL mode with optimized settings
129
+ this.db.run('PRAGMA journal_mode = WAL');
130
+ // ✅ CRITICAL: Reduced timeout for better worker coordination
131
+ this.db.run('PRAGMA busy_timeout = 5000');
132
+ // ✅ PERFORMANCE: More frequent checkpoints for multi-worker
133
+ this.db.run('PRAGMA wal_autocheckpoint = 100');
134
+ // ✅ CONCURRENCY: Enable shared cache for same-process workers
135
+ // Note: cache_shared is not available in bun:sqlite, skip it
136
+ }
137
+ // ✅ PERFORMANCE: Optimized PRAGMA settings for workers
138
+ this.db.run('PRAGMA foreign_keys = ON');
139
+ this.db.run('PRAGMA synchronous = NORMAL'); // Optimal for WAL mode
140
+ // ✅ MEMORY: 4MB per worker cache
141
+ this.db.run('PRAGMA cache_size = -4000');
142
+ this.db.run('PRAGMA temp_store = MEMORY');
143
+ // ✅ CONCURRENCY: Optimized mmap for multi-worker
144
+ this.db.run('PRAGMA mmap_size = 67108864'); // 64MB
145
+ // ✅ PERFORMANCE: Enable query planner optimizations
146
+ this.db.run('PRAGMA optimize');
147
+ }
148
+ catch (error) {
149
+ console.error(`[${this.workerId}] Failed to initialize database:`, error);
150
+ throw error;
151
+ }
152
+ }
153
+ async handleMessage(message) {
154
+ const start = performance.now();
155
+ let success = false;
156
+ let result;
157
+ let error;
158
+ try {
159
+ if (message.sql === '__SHUTDOWN__') {
160
+ this.shutdown();
161
+ return {
162
+ id: message.id,
163
+ result: { shutdown: true },
164
+ workerId: this.workerId,
165
+ durationMs: performance.now() - start,
166
+ success: true
167
+ };
168
+ }
169
+ // Lazy initialization
170
+ if (!this.db) {
171
+ await this.initializeDatabase();
172
+ }
173
+ result = this.executeQuery(message.sql, message.params || []);
174
+ success = true;
175
+ }
176
+ catch (err) {
177
+ // err (not error) to avoid shadowing the outer `error` return variable
178
+ const e = err instanceof Error ? err : new Error(String(err));
179
+ error = {
180
+ message: e.message,
181
+ code: e.code,
182
+ errno: e.errno
183
+ };
184
+ }
185
+ return {
186
+ id: message.id,
187
+ result,
188
+ error,
189
+ workerId: this.workerId,
190
+ durationMs: performance.now() - start,
191
+ success
192
+ };
193
+ }
194
+ executeQuery(sql, params) {
195
+ if (!this.db) {
196
+ throw new Error('Database not initialized');
197
+ }
198
+ const sqlUpper = sql.trim().toUpperCase();
199
+ try {
200
+ const stmt = getOrCreateStatement(this.db, sql);
201
+ let result;
202
+ if (sqlUpper.startsWith('SELECT')) {
203
+ if (sqlUpper.includes('LIMIT 1') || sqlUpper.includes('COUNT(*)')) {
204
+ // bun:sqlite uses .get() with params directly
205
+ result = stmt.get(...params);
206
+ }
207
+ else {
208
+ result = stmt.all(...params);
209
+ }
210
+ }
211
+ else if (sqlUpper.startsWith('INSERT') ||
212
+ sqlUpper.startsWith('UPDATE') ||
213
+ sqlUpper.startsWith('DELETE')) {
214
+ // bun:sqlite .run() returns { changes, lastInsertRowid }
215
+ const runResult = stmt.run(...params);
216
+ result = {
217
+ changes: runResult.changes,
218
+ lastInsertRowid: runResult.lastInsertRowid
219
+ };
220
+ }
221
+ else {
222
+ this.db.run(sql);
223
+ result = { success: true };
224
+ }
225
+ return result;
226
+ }
227
+ catch (error) {
228
+ const e = error instanceof Error ? error : new Error(String(error));
229
+ console.error(`[${this.workerId}] SQL FAILED:`, {
230
+ sql: `${sql.substring(0, 60)}...`,
231
+ error: e.message,
232
+ code: e.code,
233
+ errno: e.errno,
234
+ params: params?.length || 0
235
+ });
236
+ throw error;
237
+ }
238
+ }
239
+ shutdown() {
240
+ if (this.db) {
241
+ try {
242
+ // Clear prepared statement cache
243
+ stmtCache.clear();
244
+ this.db.close();
245
+ this.db = null;
246
+ }
247
+ catch (error) {
248
+ console.error(`[${this.workerId}] Error during shutdown:`, error);
249
+ }
250
+ }
251
+ }
252
+ getWorkerId() {
253
+ return this.workerId;
254
+ }
255
+ }
256
+ // Worker thread main execution
257
+ if (parentPort) {
258
+ const worker = new SQLiteWorker();
259
+ parentPort.on('message', async (message) => {
260
+ try {
261
+ if (!message.id) {
262
+ console.error(`[${worker.getWorkerId()}] CRITICAL: Message missing ID:`, message);
263
+ parentPort?.postMessage({
264
+ id: 'unknown',
265
+ error: { message: 'Message missing ID' },
266
+ workerId: worker.getWorkerId(),
267
+ durationMs: 0,
268
+ success: false
269
+ });
270
+ return;
271
+ }
272
+ const response = await worker.handleMessage(message);
273
+ if (!response.id) {
274
+ console.error(`[${worker.getWorkerId()}] CRITICAL: Response missing ID:`, response);
275
+ response.id = message.id;
276
+ }
277
+ parentPort?.postMessage(response);
278
+ }
279
+ catch (error) {
280
+ const e = error instanceof Error ? error : new Error(String(error));
281
+ parentPort?.postMessage({
282
+ id: message.id || 'unknown',
283
+ error: {
284
+ message: e.message,
285
+ code: e.code,
286
+ errno: e.errno
287
+ },
288
+ workerId: worker.getWorkerId(),
289
+ durationMs: 0,
290
+ success: false
291
+ });
292
+ }
293
+ });
294
+ }
@@ -0,0 +1,33 @@
1
+ /**
2
+ * A simple async mutex that serializes write access to a resource.
3
+ * Bun is single-threaded, so this works as a plain promise queue —
4
+ * no atomic operations needed.
5
+ */
6
+ export declare class WriteMutex {
7
+ private queue;
8
+ /**
9
+ * Acquire the mutex, execute the function, then release.
10
+ * Only one function runs at a time per mutex instance.
11
+ */
12
+ acquire<T>(fn: () => T | Promise<T>): Promise<T>;
13
+ }
14
+ /**
15
+ * A map of named mutexes for per-resource write serialization.
16
+ * Useful for per-workspace or per-database write locking.
17
+ */
18
+ export declare class WriteMutexMap {
19
+ private mutexes;
20
+ /**
21
+ * Acquire the mutex for a given key, execute the function, then release.
22
+ */
23
+ withLock<T>(key: string, fn: () => T | Promise<T>): Promise<T>;
24
+ /**
25
+ * Remove a mutex for a key (e.g., when evicting a connection).
26
+ */
27
+ delete(key: string): void;
28
+ /**
29
+ * Get the number of tracked mutexes.
30
+ */
31
+ get size(): number;
32
+ }
33
+ //# sourceMappingURL=write-mutex.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"write-mutex.d.ts","sourceRoot":"","sources":["../../src/write-mutex.ts"],"names":[],"mappings":"AAGA;;;;GAIG;AACH,qBAAa,UAAU;IACrB,OAAO,CAAC,KAAK,CAAoC;IAEjD;;;OAGG;IACG,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;CAkBvD;AAED;;;GAGG;AACH,qBAAa,aAAa;IACxB,OAAO,CAAC,OAAO,CAAiC;IAEhD;;OAEG;IACG,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC,GAAG,OAAO,CAAC,CAAC,CAAC;IASpE;;OAEG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,IAAI;IAIzB;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;CACF"}
@@ -0,0 +1,60 @@
1
+ // packages/sqlite/src/write-mutex.ts
2
+ // Promise-based per-resource write mutex for SQLite concurrency
3
+ /**
4
+ * A simple async mutex that serializes write access to a resource.
5
+ * Bun is single-threaded, so this works as a plain promise queue —
6
+ * no atomic operations needed.
7
+ */
8
+ export class WriteMutex {
9
+ queue = Promise.resolve();
10
+ /**
11
+ * Acquire the mutex, execute the function, then release.
12
+ * Only one function runs at a time per mutex instance.
13
+ */
14
+ async acquire(fn) {
15
+ let release;
16
+ const gate = new Promise(resolve => {
17
+ release = resolve;
18
+ });
19
+ // Chain onto the queue so we wait for prior operations
20
+ const prior = this.queue;
21
+ this.queue = gate;
22
+ await prior;
23
+ try {
24
+ return await fn();
25
+ }
26
+ finally {
27
+ release();
28
+ }
29
+ }
30
+ }
31
+ /**
32
+ * A map of named mutexes for per-resource write serialization.
33
+ * Useful for per-workspace or per-database write locking.
34
+ */
35
+ export class WriteMutexMap {
36
+ mutexes = new Map();
37
+ /**
38
+ * Acquire the mutex for a given key, execute the function, then release.
39
+ */
40
+ async withLock(key, fn) {
41
+ let mutex = this.mutexes.get(key);
42
+ if (!mutex) {
43
+ mutex = new WriteMutex();
44
+ this.mutexes.set(key, mutex);
45
+ }
46
+ return mutex.acquire(fn);
47
+ }
48
+ /**
49
+ * Remove a mutex for a key (e.g., when evicting a connection).
50
+ */
51
+ delete(key) {
52
+ this.mutexes.delete(key);
53
+ }
54
+ /**
55
+ * Get the number of tracked mutexes.
56
+ */
57
+ get size() {
58
+ return this.mutexes.size;
59
+ }
60
+ }
package/package.json ADDED
@@ -0,0 +1,48 @@
1
+ {
2
+ "name": "@bitclaw/sqlite",
3
+ "version": "1.1.0",
4
+ "description": "High-performance SQLite worker pool and utilities using bun:sqlite",
5
+ "files": ["dist", "scripts", "LICENSE", "README.md"],
6
+ "type": "module",
7
+ "exports": {
8
+ "./pool": { "types": "./dist/src/pool.d.ts", "default": "./dist/src/pool.js" },
9
+ "./worker": { "types": "./dist/src/worker.d.ts", "default": "./dist/src/worker.js" },
10
+ "./connection": { "types": "./dist/src/connection.d.ts", "default": "./dist/src/connection.js" },
11
+ "./json-cache": { "types": "./dist/src/json-cache.d.ts", "default": "./dist/src/json-cache.js" },
12
+ "./retry": { "types": "./dist/src/retry.d.ts", "default": "./dist/src/retry.js" },
13
+ "./write-mutex": { "types": "./dist/src/write-mutex.d.ts", "default": "./dist/src/write-mutex.js" },
14
+ "./prisma-immediate-tx": { "types": "./dist/src/prisma-immediate-tx.d.ts", "default": "./dist/src/prisma-immediate-tx.js" },
15
+ "./query-logger": { "types": "./dist/src/query-logger.d.ts", "default": "./dist/src/query-logger.js" },
16
+ "./ttl-cache": { "types": "./dist/src/ttl-cache.d.ts", "default": "./dist/src/ttl-cache.js" },
17
+ "./cache-lock": { "types": "./dist/src/cache-lock.d.ts", "default": "./dist/src/cache-lock.js" },
18
+ "./load-test-utils": { "types": "./dist/scripts/load-test-utils.d.ts", "default": "./dist/scripts/load-test-utils.js" }
19
+ },
20
+ "scripts": {
21
+ "build": "tsc -p tsconfig.build.json",
22
+ "test": "bun test",
23
+ "test:watch": "bun test --watch",
24
+ "typecheck": "tsc --noEmit",
25
+ "lint": "biome check",
26
+ "lint:fix": "biome check --write",
27
+ "format": "biome format --write",
28
+ "knip": "knip",
29
+ "benchmark": "bun scripts/benchmark.ts",
30
+ "benchmark:quick": "bun scripts/benchmark.ts --quick",
31
+ "prepublishOnly": "npm run build && npm run test",
32
+ "publish:dev": "npm run build && npm publish --tag dev --access public",
33
+ "publish:patch": "npm whoami && npm version patch && git push --follow-tags && npm publish --access public",
34
+ "publish:minor": "npm whoami && npm version minor && git push --follow-tags && npm publish --access public",
35
+ "publish:major": "npm whoami && npm version major && git push --follow-tags && npm publish --access public"
36
+ },
37
+ "keywords": ["sqlite", "bun", "worker-pool", "json-cache", "ttl-cache"],
38
+ "author": "bitclaw",
39
+ "license": "MIT",
40
+ "engines": { "bun": ">=1.3.0" },
41
+ "devDependencies": {
42
+ "@biomejs/biome": "^2.4.15",
43
+ "knip": "^6.12.1",
44
+ "@types/bun": "^1.3.9",
45
+ "@types/node": "^22.0.0",
46
+ "typescript": "^5.8.3"
47
+ }
48
+ }