@lensjs/core 2.3.0 → 2.3.1

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.
@@ -24,6 +24,7 @@ __export(store_exports, {
24
24
  });
25
25
  module.exports = __toCommonJS(store_exports);
26
26
  var Store = class {
27
+ storeConfig;
27
28
  constructor(...args) {
28
29
  }
29
30
  getAllExceptions(_paginationParams) {
@@ -1,8 +1,9 @@
1
- import { WatcherTypeEnum, PaginationParams, Paginator, LensEntry } from '../types/index.cjs';
1
+ import { QueuedStoreConfig, WatcherTypeEnum, PaginationParams, Paginator, LensEntry } from '../types/index.cjs';
2
2
  import 'sql-formatter';
3
3
 
4
4
  type MinimalPaginatePromise = Promise<Paginator<Omit<LensEntry, "data">[]>>;
5
5
  declare abstract class Store {
6
+ protected storeConfig?: QueuedStoreConfig;
6
7
  constructor(...args: any[]);
7
8
  abstract initialize(): Promise<void>;
8
9
  abstract save(entry: {
@@ -1,8 +1,9 @@
1
- import { WatcherTypeEnum, PaginationParams, Paginator, LensEntry } from '../types/index.js';
1
+ import { QueuedStoreConfig, WatcherTypeEnum, PaginationParams, Paginator, LensEntry } from '../types/index.js';
2
2
  import 'sql-formatter';
3
3
 
4
4
  type MinimalPaginatePromise = Promise<Paginator<Omit<LensEntry, "data">[]>>;
5
5
  declare abstract class Store {
6
+ protected storeConfig?: QueuedStoreConfig;
6
7
  constructor(...args: any[]);
7
8
  abstract initialize(): Promise<void>;
8
9
  abstract save(entry: {
@@ -1,5 +1,6 @@
1
1
  // src/abstracts/store.ts
2
2
  var Store = class {
3
+ storeConfig;
3
4
  constructor(...args) {
4
5
  }
5
6
  getAllExceptions(_paginationParams) {
@@ -204,6 +204,7 @@ var path2 = __toESM(require("path"), 1);
204
204
 
205
205
  // src/abstracts/store.ts
206
206
  var Store = class {
207
+ storeConfig;
207
208
  constructor(...args) {
208
209
  }
209
210
  getAllExceptions(_paginationParams) {
@@ -236,6 +237,8 @@ var import_crypto = require("crypto");
236
237
  var import_libsql = __toESM(require("libsql"), 1);
237
238
  var import_date = require("@lensjs/date");
238
239
  var TABLE_NAME = "lens_entries";
240
+ var BYTES_IN_GB = 1024 * 1024 * 1024;
241
+ var PRUNE_BATCH_SIZE = 1e3;
239
242
  var BetterSqliteStore = class extends Store {
240
243
  connection;
241
244
  async initialize() {
@@ -257,6 +260,7 @@ var BetterSqliteStore = class extends Store {
257
260
  lens_entry_id: entry.requestId || null,
258
261
  minimalData: this.stringifyData(entry.minimal_data ?? {})
259
262
  });
263
+ this.maybePruneDatabase();
260
264
  }
261
265
  async getAllQueries(pagination) {
262
266
  return await this.paginate("query" /* QUERY */, pagination);
@@ -332,6 +336,36 @@ var BetterSqliteStore = class extends Store {
332
336
  this.connection.exec(createIndex);
333
337
  this.connection.exec(lensEntryIdIndex);
334
338
  }
339
+ maybePruneDatabase() {
340
+ const maxGb = this.storeConfig?.dbMaxSizeGb;
341
+ const pruneGb = this.storeConfig?.dbPruneSizeGb;
342
+ if (!maxGb || !pruneGb) return;
343
+ const maxBytes = maxGb * BYTES_IN_GB;
344
+ const pruneBytes = pruneGb * BYTES_IN_GB;
345
+ if (maxBytes <= 0 || pruneBytes <= 0) return;
346
+ const targetBytes = Math.max(0, maxBytes - pruneBytes);
347
+ let usedBytes = this.getDatabaseUsedBytes();
348
+ if (usedBytes < maxBytes) return;
349
+ while (usedBytes > targetBytes) {
350
+ const deletedRows = this.deleteOldestEntries(PRUNE_BATCH_SIZE);
351
+ if (deletedRows === 0) break;
352
+ usedBytes = this.getDatabaseUsedBytes();
353
+ }
354
+ this.connection.exec("PRAGMA wal_checkpoint(TRUNCATE);");
355
+ }
356
+ getDatabaseUsedBytes() {
357
+ const pageSizeResult = this.connection.prepare("PRAGMA page_size;").get();
358
+ const pageCountResult = this.connection.prepare("PRAGMA page_count;").get();
359
+ const freelistCountResult = this.connection.prepare("PRAGMA freelist_count;").get();
360
+ const usedPages = pageCountResult.page_count - freelistCountResult.freelist_count;
361
+ return usedPages * pageSizeResult.page_size;
362
+ }
363
+ deleteOldestEntries(batchSize) {
364
+ const result = this.connection.prepare(
365
+ `DELETE FROM ${TABLE_NAME} WHERE id IN (SELECT id FROM ${TABLE_NAME} ORDER BY created_at ASC LIMIT ?)`
366
+ ).run(batchSize);
367
+ return Number(result.changes ?? 0);
368
+ }
335
369
  mapRow(row, includeFullData = true) {
336
370
  let data = includeFullData ? JSON.parse(row.data) : {};
337
371
  if (!includeFullData) {
@@ -370,6 +404,7 @@ function QueuedStore(Base) {
370
404
  constructor(...args) {
371
405
  super(...args);
372
406
  const config = args[0] || {};
407
+ this.storeConfig = config;
373
408
  this.BATCH_SIZE = config.batchSize ?? 100;
374
409
  this.PROCESS_INTERVAL_MS = config.processIntervalMs ?? 100;
375
410
  this.WARN_THRESHOLD = config.warnThreshold ?? 1e5;
@@ -452,7 +487,7 @@ var import_crypto2 = require("crypto");
452
487
  var import_url = require("url");
453
488
  var path = __toESM(require("path"), 1);
454
489
  function getMeta(metaUrl) {
455
- const isESM = typeof __dirname === "undefined";
490
+ const isESM = typeof __dirname === "undefined" || typeof __filename === "undefined";
456
491
  if (isESM) {
457
492
  if (!metaUrl) {
458
493
  throw new Error("In ESM, you must pass import.meta.url to getMeta()");
package/dist/core/lens.js CHANGED
@@ -168,6 +168,7 @@ import * as path2 from "path";
168
168
 
169
169
  // src/abstracts/store.ts
170
170
  var Store = class {
171
+ storeConfig;
171
172
  constructor(...args) {
172
173
  }
173
174
  getAllExceptions(_paginationParams) {
@@ -200,6 +201,8 @@ import { randomUUID } from "crypto";
200
201
  import Database from "libsql";
201
202
  import { nowISO } from "@lensjs/date";
202
203
  var TABLE_NAME = "lens_entries";
204
+ var BYTES_IN_GB = 1024 * 1024 * 1024;
205
+ var PRUNE_BATCH_SIZE = 1e3;
203
206
  var BetterSqliteStore = class extends Store {
204
207
  connection;
205
208
  async initialize() {
@@ -221,6 +224,7 @@ var BetterSqliteStore = class extends Store {
221
224
  lens_entry_id: entry.requestId || null,
222
225
  minimalData: this.stringifyData(entry.minimal_data ?? {})
223
226
  });
227
+ this.maybePruneDatabase();
224
228
  }
225
229
  async getAllQueries(pagination) {
226
230
  return await this.paginate("query" /* QUERY */, pagination);
@@ -296,6 +300,36 @@ var BetterSqliteStore = class extends Store {
296
300
  this.connection.exec(createIndex);
297
301
  this.connection.exec(lensEntryIdIndex);
298
302
  }
303
+ maybePruneDatabase() {
304
+ const maxGb = this.storeConfig?.dbMaxSizeGb;
305
+ const pruneGb = this.storeConfig?.dbPruneSizeGb;
306
+ if (!maxGb || !pruneGb) return;
307
+ const maxBytes = maxGb * BYTES_IN_GB;
308
+ const pruneBytes = pruneGb * BYTES_IN_GB;
309
+ if (maxBytes <= 0 || pruneBytes <= 0) return;
310
+ const targetBytes = Math.max(0, maxBytes - pruneBytes);
311
+ let usedBytes = this.getDatabaseUsedBytes();
312
+ if (usedBytes < maxBytes) return;
313
+ while (usedBytes > targetBytes) {
314
+ const deletedRows = this.deleteOldestEntries(PRUNE_BATCH_SIZE);
315
+ if (deletedRows === 0) break;
316
+ usedBytes = this.getDatabaseUsedBytes();
317
+ }
318
+ this.connection.exec("PRAGMA wal_checkpoint(TRUNCATE);");
319
+ }
320
+ getDatabaseUsedBytes() {
321
+ const pageSizeResult = this.connection.prepare("PRAGMA page_size;").get();
322
+ const pageCountResult = this.connection.prepare("PRAGMA page_count;").get();
323
+ const freelistCountResult = this.connection.prepare("PRAGMA freelist_count;").get();
324
+ const usedPages = pageCountResult.page_count - freelistCountResult.freelist_count;
325
+ return usedPages * pageSizeResult.page_size;
326
+ }
327
+ deleteOldestEntries(batchSize) {
328
+ const result = this.connection.prepare(
329
+ `DELETE FROM ${TABLE_NAME} WHERE id IN (SELECT id FROM ${TABLE_NAME} ORDER BY created_at ASC LIMIT ?)`
330
+ ).run(batchSize);
331
+ return Number(result.changes ?? 0);
332
+ }
299
333
  mapRow(row, includeFullData = true) {
300
334
  let data = includeFullData ? JSON.parse(row.data) : {};
301
335
  if (!includeFullData) {
@@ -334,6 +368,7 @@ function QueuedStore(Base) {
334
368
  constructor(...args) {
335
369
  super(...args);
336
370
  const config = args[0] || {};
371
+ this.storeConfig = config;
337
372
  this.BATCH_SIZE = config.batchSize ?? 100;
338
373
  this.PROCESS_INTERVAL_MS = config.processIntervalMs ?? 100;
339
374
  this.WARN_THRESHOLD = config.warnThreshold ?? 1e5;
@@ -416,7 +451,7 @@ import { randomUUID as randomUUID2 } from "crypto";
416
451
  import { fileURLToPath } from "url";
417
452
  import * as path from "path";
418
453
  function getMeta(metaUrl) {
419
- const isESM = typeof __dirname === "undefined";
454
+ const isESM = typeof __dirname === "undefined" || typeof __filename === "undefined";
420
455
  if (isESM) {
421
456
  if (!metaUrl) {
422
457
  throw new Error("In ESM, you must pass import.meta.url to getMeta()");
package/dist/index.cjs CHANGED
@@ -230,6 +230,7 @@ var path2 = __toESM(require("path"), 1);
230
230
 
231
231
  // src/abstracts/store.ts
232
232
  var Store = class {
233
+ storeConfig;
233
234
  constructor(...args) {
234
235
  }
235
236
  getAllExceptions(_paginationParams) {
@@ -262,6 +263,8 @@ var import_crypto = require("crypto");
262
263
  var import_libsql = __toESM(require("libsql"), 1);
263
264
  var import_date = require("@lensjs/date");
264
265
  var TABLE_NAME = "lens_entries";
266
+ var BYTES_IN_GB = 1024 * 1024 * 1024;
267
+ var PRUNE_BATCH_SIZE = 1e3;
265
268
  var BetterSqliteStore = class extends Store {
266
269
  connection;
267
270
  async initialize() {
@@ -283,6 +286,7 @@ var BetterSqliteStore = class extends Store {
283
286
  lens_entry_id: entry.requestId || null,
284
287
  minimalData: this.stringifyData(entry.minimal_data ?? {})
285
288
  });
289
+ this.maybePruneDatabase();
286
290
  }
287
291
  async getAllQueries(pagination) {
288
292
  return await this.paginate("query" /* QUERY */, pagination);
@@ -358,6 +362,36 @@ var BetterSqliteStore = class extends Store {
358
362
  this.connection.exec(createIndex);
359
363
  this.connection.exec(lensEntryIdIndex);
360
364
  }
365
+ maybePruneDatabase() {
366
+ const maxGb = this.storeConfig?.dbMaxSizeGb;
367
+ const pruneGb = this.storeConfig?.dbPruneSizeGb;
368
+ if (!maxGb || !pruneGb) return;
369
+ const maxBytes = maxGb * BYTES_IN_GB;
370
+ const pruneBytes = pruneGb * BYTES_IN_GB;
371
+ if (maxBytes <= 0 || pruneBytes <= 0) return;
372
+ const targetBytes = Math.max(0, maxBytes - pruneBytes);
373
+ let usedBytes = this.getDatabaseUsedBytes();
374
+ if (usedBytes < maxBytes) return;
375
+ while (usedBytes > targetBytes) {
376
+ const deletedRows = this.deleteOldestEntries(PRUNE_BATCH_SIZE);
377
+ if (deletedRows === 0) break;
378
+ usedBytes = this.getDatabaseUsedBytes();
379
+ }
380
+ this.connection.exec("PRAGMA wal_checkpoint(TRUNCATE);");
381
+ }
382
+ getDatabaseUsedBytes() {
383
+ const pageSizeResult = this.connection.prepare("PRAGMA page_size;").get();
384
+ const pageCountResult = this.connection.prepare("PRAGMA page_count;").get();
385
+ const freelistCountResult = this.connection.prepare("PRAGMA freelist_count;").get();
386
+ const usedPages = pageCountResult.page_count - freelistCountResult.freelist_count;
387
+ return usedPages * pageSizeResult.page_size;
388
+ }
389
+ deleteOldestEntries(batchSize) {
390
+ const result = this.connection.prepare(
391
+ `DELETE FROM ${TABLE_NAME} WHERE id IN (SELECT id FROM ${TABLE_NAME} ORDER BY created_at ASC LIMIT ?)`
392
+ ).run(batchSize);
393
+ return Number(result.changes ?? 0);
394
+ }
361
395
  mapRow(row, includeFullData = true) {
362
396
  let data = includeFullData ? JSON.parse(row.data) : {};
363
397
  if (!includeFullData) {
@@ -396,6 +430,7 @@ function QueuedStore(Base) {
396
430
  constructor(...args) {
397
431
  super(...args);
398
432
  const config = args[0] || {};
433
+ this.storeConfig = config;
399
434
  this.BATCH_SIZE = config.batchSize ?? 100;
400
435
  this.PROCESS_INTERVAL_MS = config.processIntervalMs ?? 100;
401
436
  this.WARN_THRESHOLD = config.warnThreshold ?? 1e5;
@@ -549,7 +584,7 @@ var formatSqlQuery = (query, language) => {
549
584
  });
550
585
  };
551
586
  function getMeta(metaUrl) {
552
- const isESM = typeof __dirname === "undefined";
587
+ const isESM = typeof __dirname === "undefined" || typeof __filename === "undefined";
553
588
  if (isESM) {
554
589
  if (!metaUrl) {
555
590
  throw new Error("In ESM, you must pass import.meta.url to getMeta()");
package/dist/index.js CHANGED
@@ -183,6 +183,7 @@ import * as path2 from "path";
183
183
 
184
184
  // src/abstracts/store.ts
185
185
  var Store = class {
186
+ storeConfig;
186
187
  constructor(...args) {
187
188
  }
188
189
  getAllExceptions(_paginationParams) {
@@ -215,6 +216,8 @@ import { randomUUID } from "crypto";
215
216
  import Database from "libsql";
216
217
  import { nowISO } from "@lensjs/date";
217
218
  var TABLE_NAME = "lens_entries";
219
+ var BYTES_IN_GB = 1024 * 1024 * 1024;
220
+ var PRUNE_BATCH_SIZE = 1e3;
218
221
  var BetterSqliteStore = class extends Store {
219
222
  connection;
220
223
  async initialize() {
@@ -236,6 +239,7 @@ var BetterSqliteStore = class extends Store {
236
239
  lens_entry_id: entry.requestId || null,
237
240
  minimalData: this.stringifyData(entry.minimal_data ?? {})
238
241
  });
242
+ this.maybePruneDatabase();
239
243
  }
240
244
  async getAllQueries(pagination) {
241
245
  return await this.paginate("query" /* QUERY */, pagination);
@@ -311,6 +315,36 @@ var BetterSqliteStore = class extends Store {
311
315
  this.connection.exec(createIndex);
312
316
  this.connection.exec(lensEntryIdIndex);
313
317
  }
318
+ maybePruneDatabase() {
319
+ const maxGb = this.storeConfig?.dbMaxSizeGb;
320
+ const pruneGb = this.storeConfig?.dbPruneSizeGb;
321
+ if (!maxGb || !pruneGb) return;
322
+ const maxBytes = maxGb * BYTES_IN_GB;
323
+ const pruneBytes = pruneGb * BYTES_IN_GB;
324
+ if (maxBytes <= 0 || pruneBytes <= 0) return;
325
+ const targetBytes = Math.max(0, maxBytes - pruneBytes);
326
+ let usedBytes = this.getDatabaseUsedBytes();
327
+ if (usedBytes < maxBytes) return;
328
+ while (usedBytes > targetBytes) {
329
+ const deletedRows = this.deleteOldestEntries(PRUNE_BATCH_SIZE);
330
+ if (deletedRows === 0) break;
331
+ usedBytes = this.getDatabaseUsedBytes();
332
+ }
333
+ this.connection.exec("PRAGMA wal_checkpoint(TRUNCATE);");
334
+ }
335
+ getDatabaseUsedBytes() {
336
+ const pageSizeResult = this.connection.prepare("PRAGMA page_size;").get();
337
+ const pageCountResult = this.connection.prepare("PRAGMA page_count;").get();
338
+ const freelistCountResult = this.connection.prepare("PRAGMA freelist_count;").get();
339
+ const usedPages = pageCountResult.page_count - freelistCountResult.freelist_count;
340
+ return usedPages * pageSizeResult.page_size;
341
+ }
342
+ deleteOldestEntries(batchSize) {
343
+ const result = this.connection.prepare(
344
+ `DELETE FROM ${TABLE_NAME} WHERE id IN (SELECT id FROM ${TABLE_NAME} ORDER BY created_at ASC LIMIT ?)`
345
+ ).run(batchSize);
346
+ return Number(result.changes ?? 0);
347
+ }
314
348
  mapRow(row, includeFullData = true) {
315
349
  let data = includeFullData ? JSON.parse(row.data) : {};
316
350
  if (!includeFullData) {
@@ -349,6 +383,7 @@ function QueuedStore(Base) {
349
383
  constructor(...args) {
350
384
  super(...args);
351
385
  const config = args[0] || {};
386
+ this.storeConfig = config;
352
387
  this.BATCH_SIZE = config.batchSize ?? 100;
353
388
  this.PROCESS_INTERVAL_MS = config.processIntervalMs ?? 100;
354
389
  this.WARN_THRESHOLD = config.warnThreshold ?? 1e5;
@@ -502,7 +537,7 @@ var formatSqlQuery = (query, language) => {
502
537
  });
503
538
  };
504
539
  function getMeta(metaUrl) {
505
- const isESM = typeof __dirname === "undefined";
540
+ const isESM = typeof __dirname === "undefined" || typeof __filename === "undefined";
506
541
  if (isESM) {
507
542
  if (!metaUrl) {
508
543
  throw new Error("In ESM, you must pass import.meta.url to getMeta()");
@@ -45,6 +45,7 @@ function QueuedStore(Base) {
45
45
  constructor(...args) {
46
46
  super(...args);
47
47
  const config = args[0] || {};
48
+ this.storeConfig = config;
48
49
  this.BATCH_SIZE = config.batchSize ?? 100;
49
50
  this.PROCESS_INTERVAL_MS = config.processIntervalMs ?? 100;
50
51
  this.WARN_THRESHOLD = config.warnThreshold ?? 1e5;
@@ -11,6 +11,7 @@ function QueuedStore(Base) {
11
11
  constructor(...args) {
12
12
  super(...args);
13
13
  const config = args[0] || {};
14
+ this.storeConfig = config;
14
15
  this.BATCH_SIZE = config.batchSize ?? 100;
15
16
  this.PROCESS_INTERVAL_MS = config.processIntervalMs ?? 100;
16
17
  this.WARN_THRESHOLD = config.warnThreshold ?? 1e5;
@@ -36,6 +36,7 @@ module.exports = __toCommonJS(better_sqlite_exports);
36
36
 
37
37
  // src/abstracts/store.ts
38
38
  var Store = class {
39
+ storeConfig;
39
40
  constructor(...args) {
40
41
  }
41
42
  getAllExceptions(_paginationParams) {
@@ -68,6 +69,8 @@ var import_crypto = require("crypto");
68
69
  var import_libsql = __toESM(require("libsql"), 1);
69
70
  var import_date = require("@lensjs/date");
70
71
  var TABLE_NAME = "lens_entries";
72
+ var BYTES_IN_GB = 1024 * 1024 * 1024;
73
+ var PRUNE_BATCH_SIZE = 1e3;
71
74
  var BetterSqliteStore = class extends Store {
72
75
  connection;
73
76
  async initialize() {
@@ -89,6 +92,7 @@ var BetterSqliteStore = class extends Store {
89
92
  lens_entry_id: entry.requestId || null,
90
93
  minimalData: this.stringifyData(entry.minimal_data ?? {})
91
94
  });
95
+ this.maybePruneDatabase();
92
96
  }
93
97
  async getAllQueries(pagination) {
94
98
  return await this.paginate("query" /* QUERY */, pagination);
@@ -164,6 +168,36 @@ var BetterSqliteStore = class extends Store {
164
168
  this.connection.exec(createIndex);
165
169
  this.connection.exec(lensEntryIdIndex);
166
170
  }
171
+ maybePruneDatabase() {
172
+ const maxGb = this.storeConfig?.dbMaxSizeGb;
173
+ const pruneGb = this.storeConfig?.dbPruneSizeGb;
174
+ if (!maxGb || !pruneGb) return;
175
+ const maxBytes = maxGb * BYTES_IN_GB;
176
+ const pruneBytes = pruneGb * BYTES_IN_GB;
177
+ if (maxBytes <= 0 || pruneBytes <= 0) return;
178
+ const targetBytes = Math.max(0, maxBytes - pruneBytes);
179
+ let usedBytes = this.getDatabaseUsedBytes();
180
+ if (usedBytes < maxBytes) return;
181
+ while (usedBytes > targetBytes) {
182
+ const deletedRows = this.deleteOldestEntries(PRUNE_BATCH_SIZE);
183
+ if (deletedRows === 0) break;
184
+ usedBytes = this.getDatabaseUsedBytes();
185
+ }
186
+ this.connection.exec("PRAGMA wal_checkpoint(TRUNCATE);");
187
+ }
188
+ getDatabaseUsedBytes() {
189
+ const pageSizeResult = this.connection.prepare("PRAGMA page_size;").get();
190
+ const pageCountResult = this.connection.prepare("PRAGMA page_count;").get();
191
+ const freelistCountResult = this.connection.prepare("PRAGMA freelist_count;").get();
192
+ const usedPages = pageCountResult.page_count - freelistCountResult.freelist_count;
193
+ return usedPages * pageSizeResult.page_size;
194
+ }
195
+ deleteOldestEntries(batchSize) {
196
+ const result = this.connection.prepare(
197
+ `DELETE FROM ${TABLE_NAME} WHERE id IN (SELECT id FROM ${TABLE_NAME} ORDER BY created_at ASC LIMIT ?)`
198
+ ).run(batchSize);
199
+ return Number(result.changes ?? 0);
200
+ }
167
201
  mapRow(row, includeFullData = true) {
168
202
  let data = includeFullData ? JSON.parse(row.data) : {};
169
203
  if (!includeFullData) {
@@ -59,6 +59,9 @@ declare class BetterSqliteStore extends Store {
59
59
  count(type: WatcherTypeEnum): Promise<number>;
60
60
  find(type: WatcherTypeEnum, id: string): Promise<LensEntry | null>;
61
61
  private setupSchema;
62
+ private maybePruneDatabase;
63
+ private getDatabaseUsedBytes;
64
+ private deleteOldestEntries;
62
65
  protected mapRow(row: any, includeFullData?: boolean): LensEntry;
63
66
  protected mapRows(rows: any[], includeFullData?: boolean): LensEntry[];
64
67
  protected getSelectedColumns(includeFullData?: boolean): string;
@@ -59,6 +59,9 @@ declare class BetterSqliteStore extends Store {
59
59
  count(type: WatcherTypeEnum): Promise<number>;
60
60
  find(type: WatcherTypeEnum, id: string): Promise<LensEntry | null>;
61
61
  private setupSchema;
62
+ private maybePruneDatabase;
63
+ private getDatabaseUsedBytes;
64
+ private deleteOldestEntries;
62
65
  protected mapRow(row: any, includeFullData?: boolean): LensEntry;
63
66
  protected mapRows(rows: any[], includeFullData?: boolean): LensEntry[];
64
67
  protected getSelectedColumns(includeFullData?: boolean): string;
@@ -1,5 +1,6 @@
1
1
  // src/abstracts/store.ts
2
2
  var Store = class {
3
+ storeConfig;
3
4
  constructor(...args) {
4
5
  }
5
6
  getAllExceptions(_paginationParams) {
@@ -32,6 +33,8 @@ import { randomUUID } from "crypto";
32
33
  import Database from "libsql";
33
34
  import { nowISO } from "@lensjs/date";
34
35
  var TABLE_NAME = "lens_entries";
36
+ var BYTES_IN_GB = 1024 * 1024 * 1024;
37
+ var PRUNE_BATCH_SIZE = 1e3;
35
38
  var BetterSqliteStore = class extends Store {
36
39
  connection;
37
40
  async initialize() {
@@ -53,6 +56,7 @@ var BetterSqliteStore = class extends Store {
53
56
  lens_entry_id: entry.requestId || null,
54
57
  minimalData: this.stringifyData(entry.minimal_data ?? {})
55
58
  });
59
+ this.maybePruneDatabase();
56
60
  }
57
61
  async getAllQueries(pagination) {
58
62
  return await this.paginate("query" /* QUERY */, pagination);
@@ -128,6 +132,36 @@ var BetterSqliteStore = class extends Store {
128
132
  this.connection.exec(createIndex);
129
133
  this.connection.exec(lensEntryIdIndex);
130
134
  }
135
+ maybePruneDatabase() {
136
+ const maxGb = this.storeConfig?.dbMaxSizeGb;
137
+ const pruneGb = this.storeConfig?.dbPruneSizeGb;
138
+ if (!maxGb || !pruneGb) return;
139
+ const maxBytes = maxGb * BYTES_IN_GB;
140
+ const pruneBytes = pruneGb * BYTES_IN_GB;
141
+ if (maxBytes <= 0 || pruneBytes <= 0) return;
142
+ const targetBytes = Math.max(0, maxBytes - pruneBytes);
143
+ let usedBytes = this.getDatabaseUsedBytes();
144
+ if (usedBytes < maxBytes) return;
145
+ while (usedBytes > targetBytes) {
146
+ const deletedRows = this.deleteOldestEntries(PRUNE_BATCH_SIZE);
147
+ if (deletedRows === 0) break;
148
+ usedBytes = this.getDatabaseUsedBytes();
149
+ }
150
+ this.connection.exec("PRAGMA wal_checkpoint(TRUNCATE);");
151
+ }
152
+ getDatabaseUsedBytes() {
153
+ const pageSizeResult = this.connection.prepare("PRAGMA page_size;").get();
154
+ const pageCountResult = this.connection.prepare("PRAGMA page_count;").get();
155
+ const freelistCountResult = this.connection.prepare("PRAGMA freelist_count;").get();
156
+ const usedPages = pageCountResult.page_count - freelistCountResult.freelist_count;
157
+ return usedPages * pageSizeResult.page_size;
158
+ }
159
+ deleteOldestEntries(batchSize) {
160
+ const result = this.connection.prepare(
161
+ `DELETE FROM ${TABLE_NAME} WHERE id IN (SELECT id FROM ${TABLE_NAME} ORDER BY created_at ASC LIMIT ?)`
162
+ ).run(batchSize);
163
+ return Number(result.changes ?? 0);
164
+ }
131
165
  mapRow(row, includeFullData = true) {
132
166
  let data = includeFullData ? JSON.parse(row.data) : {};
133
167
  if (!includeFullData) {
@@ -37,6 +37,7 @@ module.exports = __toCommonJS(stores_exports);
37
37
 
38
38
  // src/abstracts/store.ts
39
39
  var Store = class {
40
+ storeConfig;
40
41
  constructor(...args) {
41
42
  }
42
43
  getAllExceptions(_paginationParams) {
@@ -69,6 +70,8 @@ var import_crypto = require("crypto");
69
70
  var import_libsql = __toESM(require("libsql"), 1);
70
71
  var import_date = require("@lensjs/date");
71
72
  var TABLE_NAME = "lens_entries";
73
+ var BYTES_IN_GB = 1024 * 1024 * 1024;
74
+ var PRUNE_BATCH_SIZE = 1e3;
72
75
  var BetterSqliteStore = class extends Store {
73
76
  connection;
74
77
  async initialize() {
@@ -90,6 +93,7 @@ var BetterSqliteStore = class extends Store {
90
93
  lens_entry_id: entry.requestId || null,
91
94
  minimalData: this.stringifyData(entry.minimal_data ?? {})
92
95
  });
96
+ this.maybePruneDatabase();
93
97
  }
94
98
  async getAllQueries(pagination) {
95
99
  return await this.paginate("query" /* QUERY */, pagination);
@@ -165,6 +169,36 @@ var BetterSqliteStore = class extends Store {
165
169
  this.connection.exec(createIndex);
166
170
  this.connection.exec(lensEntryIdIndex);
167
171
  }
172
+ maybePruneDatabase() {
173
+ const maxGb = this.storeConfig?.dbMaxSizeGb;
174
+ const pruneGb = this.storeConfig?.dbPruneSizeGb;
175
+ if (!maxGb || !pruneGb) return;
176
+ const maxBytes = maxGb * BYTES_IN_GB;
177
+ const pruneBytes = pruneGb * BYTES_IN_GB;
178
+ if (maxBytes <= 0 || pruneBytes <= 0) return;
179
+ const targetBytes = Math.max(0, maxBytes - pruneBytes);
180
+ let usedBytes = this.getDatabaseUsedBytes();
181
+ if (usedBytes < maxBytes) return;
182
+ while (usedBytes > targetBytes) {
183
+ const deletedRows = this.deleteOldestEntries(PRUNE_BATCH_SIZE);
184
+ if (deletedRows === 0) break;
185
+ usedBytes = this.getDatabaseUsedBytes();
186
+ }
187
+ this.connection.exec("PRAGMA wal_checkpoint(TRUNCATE);");
188
+ }
189
+ getDatabaseUsedBytes() {
190
+ const pageSizeResult = this.connection.prepare("PRAGMA page_size;").get();
191
+ const pageCountResult = this.connection.prepare("PRAGMA page_count;").get();
192
+ const freelistCountResult = this.connection.prepare("PRAGMA freelist_count;").get();
193
+ const usedPages = pageCountResult.page_count - freelistCountResult.freelist_count;
194
+ return usedPages * pageSizeResult.page_size;
195
+ }
196
+ deleteOldestEntries(batchSize) {
197
+ const result = this.connection.prepare(
198
+ `DELETE FROM ${TABLE_NAME} WHERE id IN (SELECT id FROM ${TABLE_NAME} ORDER BY created_at ASC LIMIT ?)`
199
+ ).run(batchSize);
200
+ return Number(result.changes ?? 0);
201
+ }
168
202
  mapRow(row, includeFullData = true) {
169
203
  let data = includeFullData ? JSON.parse(row.data) : {};
170
204
  if (!includeFullData) {
@@ -203,6 +237,7 @@ function QueuedStore(Base) {
203
237
  constructor(...args) {
204
238
  super(...args);
205
239
  const config = args[0] || {};
240
+ this.storeConfig = config;
206
241
  this.BATCH_SIZE = config.batchSize ?? 100;
207
242
  this.PROCESS_INTERVAL_MS = config.processIntervalMs ?? 100;
208
243
  this.WARN_THRESHOLD = config.warnThreshold ?? 1e5;
@@ -1,5 +1,6 @@
1
1
  // src/abstracts/store.ts
2
2
  var Store = class {
3
+ storeConfig;
3
4
  constructor(...args) {
4
5
  }
5
6
  getAllExceptions(_paginationParams) {
@@ -32,6 +33,8 @@ import { randomUUID } from "crypto";
32
33
  import Database from "libsql";
33
34
  import { nowISO } from "@lensjs/date";
34
35
  var TABLE_NAME = "lens_entries";
36
+ var BYTES_IN_GB = 1024 * 1024 * 1024;
37
+ var PRUNE_BATCH_SIZE = 1e3;
35
38
  var BetterSqliteStore = class extends Store {
36
39
  connection;
37
40
  async initialize() {
@@ -53,6 +56,7 @@ var BetterSqliteStore = class extends Store {
53
56
  lens_entry_id: entry.requestId || null,
54
57
  minimalData: this.stringifyData(entry.minimal_data ?? {})
55
58
  });
59
+ this.maybePruneDatabase();
56
60
  }
57
61
  async getAllQueries(pagination) {
58
62
  return await this.paginate("query" /* QUERY */, pagination);
@@ -128,6 +132,36 @@ var BetterSqliteStore = class extends Store {
128
132
  this.connection.exec(createIndex);
129
133
  this.connection.exec(lensEntryIdIndex);
130
134
  }
135
+ maybePruneDatabase() {
136
+ const maxGb = this.storeConfig?.dbMaxSizeGb;
137
+ const pruneGb = this.storeConfig?.dbPruneSizeGb;
138
+ if (!maxGb || !pruneGb) return;
139
+ const maxBytes = maxGb * BYTES_IN_GB;
140
+ const pruneBytes = pruneGb * BYTES_IN_GB;
141
+ if (maxBytes <= 0 || pruneBytes <= 0) return;
142
+ const targetBytes = Math.max(0, maxBytes - pruneBytes);
143
+ let usedBytes = this.getDatabaseUsedBytes();
144
+ if (usedBytes < maxBytes) return;
145
+ while (usedBytes > targetBytes) {
146
+ const deletedRows = this.deleteOldestEntries(PRUNE_BATCH_SIZE);
147
+ if (deletedRows === 0) break;
148
+ usedBytes = this.getDatabaseUsedBytes();
149
+ }
150
+ this.connection.exec("PRAGMA wal_checkpoint(TRUNCATE);");
151
+ }
152
+ getDatabaseUsedBytes() {
153
+ const pageSizeResult = this.connection.prepare("PRAGMA page_size;").get();
154
+ const pageCountResult = this.connection.prepare("PRAGMA page_count;").get();
155
+ const freelistCountResult = this.connection.prepare("PRAGMA freelist_count;").get();
156
+ const usedPages = pageCountResult.page_count - freelistCountResult.freelist_count;
157
+ return usedPages * pageSizeResult.page_size;
158
+ }
159
+ deleteOldestEntries(batchSize) {
160
+ const result = this.connection.prepare(
161
+ `DELETE FROM ${TABLE_NAME} WHERE id IN (SELECT id FROM ${TABLE_NAME} ORDER BY created_at ASC LIMIT ?)`
162
+ ).run(batchSize);
163
+ return Number(result.changes ?? 0);
164
+ }
131
165
  mapRow(row, includeFullData = true) {
132
166
  let data = includeFullData ? JSON.parse(row.data) : {};
133
167
  if (!includeFullData) {
@@ -166,6 +200,7 @@ function QueuedStore(Base) {
166
200
  constructor(...args) {
167
201
  super(...args);
168
202
  const config = args[0] || {};
203
+ this.storeConfig = config;
169
204
  this.BATCH_SIZE = config.batchSize ?? 100;
170
205
  this.PROCESS_INTERVAL_MS = config.processIntervalMs ?? 100;
171
206
  this.WARN_THRESHOLD = config.warnThreshold ?? 1e5;
@@ -47,6 +47,7 @@ function QueuedStore(Base) {
47
47
  constructor(...args) {
48
48
  super(...args);
49
49
  const config = args[0] || {};
50
+ this.storeConfig = config;
50
51
  this.BATCH_SIZE = config.batchSize ?? 100;
51
52
  this.PROCESS_INTERVAL_MS = config.processIntervalMs ?? 100;
52
53
  this.WARN_THRESHOLD = config.warnThreshold ?? 1e5;
@@ -117,6 +118,7 @@ function compose(superclass, ...mixins) {
117
118
 
118
119
  // src/abstracts/store.ts
119
120
  var Store = class {
121
+ storeConfig;
120
122
  constructor(...args) {
121
123
  }
122
124
  getAllExceptions(_paginationParams) {
@@ -149,6 +151,8 @@ var import_crypto = require("crypto");
149
151
  var import_libsql = __toESM(require("libsql"), 1);
150
152
  var import_date = require("@lensjs/date");
151
153
  var TABLE_NAME = "lens_entries";
154
+ var BYTES_IN_GB = 1024 * 1024 * 1024;
155
+ var PRUNE_BATCH_SIZE = 1e3;
152
156
  var BetterSqliteStore = class extends Store {
153
157
  connection;
154
158
  async initialize() {
@@ -170,6 +174,7 @@ var BetterSqliteStore = class extends Store {
170
174
  lens_entry_id: entry.requestId || null,
171
175
  minimalData: this.stringifyData(entry.minimal_data ?? {})
172
176
  });
177
+ this.maybePruneDatabase();
173
178
  }
174
179
  async getAllQueries(pagination) {
175
180
  return await this.paginate("query" /* QUERY */, pagination);
@@ -245,6 +250,36 @@ var BetterSqliteStore = class extends Store {
245
250
  this.connection.exec(createIndex);
246
251
  this.connection.exec(lensEntryIdIndex);
247
252
  }
253
+ maybePruneDatabase() {
254
+ const maxGb = this.storeConfig?.dbMaxSizeGb;
255
+ const pruneGb = this.storeConfig?.dbPruneSizeGb;
256
+ if (!maxGb || !pruneGb) return;
257
+ const maxBytes = maxGb * BYTES_IN_GB;
258
+ const pruneBytes = pruneGb * BYTES_IN_GB;
259
+ if (maxBytes <= 0 || pruneBytes <= 0) return;
260
+ const targetBytes = Math.max(0, maxBytes - pruneBytes);
261
+ let usedBytes = this.getDatabaseUsedBytes();
262
+ if (usedBytes < maxBytes) return;
263
+ while (usedBytes > targetBytes) {
264
+ const deletedRows = this.deleteOldestEntries(PRUNE_BATCH_SIZE);
265
+ if (deletedRows === 0) break;
266
+ usedBytes = this.getDatabaseUsedBytes();
267
+ }
268
+ this.connection.exec("PRAGMA wal_checkpoint(TRUNCATE);");
269
+ }
270
+ getDatabaseUsedBytes() {
271
+ const pageSizeResult = this.connection.prepare("PRAGMA page_size;").get();
272
+ const pageCountResult = this.connection.prepare("PRAGMA page_count;").get();
273
+ const freelistCountResult = this.connection.prepare("PRAGMA freelist_count;").get();
274
+ const usedPages = pageCountResult.page_count - freelistCountResult.freelist_count;
275
+ return usedPages * pageSizeResult.page_size;
276
+ }
277
+ deleteOldestEntries(batchSize) {
278
+ const result = this.connection.prepare(
279
+ `DELETE FROM ${TABLE_NAME} WHERE id IN (SELECT id FROM ${TABLE_NAME} ORDER BY created_at ASC LIMIT ?)`
280
+ ).run(batchSize);
281
+ return Number(result.changes ?? 0);
282
+ }
248
283
  mapRow(row, includeFullData = true) {
249
284
  let data = includeFullData ? JSON.parse(row.data) : {};
250
285
  if (!includeFullData) {
@@ -11,6 +11,7 @@ function QueuedStore(Base) {
11
11
  constructor(...args) {
12
12
  super(...args);
13
13
  const config = args[0] || {};
14
+ this.storeConfig = config;
14
15
  this.BATCH_SIZE = config.batchSize ?? 100;
15
16
  this.PROCESS_INTERVAL_MS = config.processIntervalMs ?? 100;
16
17
  this.WARN_THRESHOLD = config.warnThreshold ?? 1e5;
@@ -81,6 +82,7 @@ function compose(superclass, ...mixins) {
81
82
 
82
83
  // src/abstracts/store.ts
83
84
  var Store = class {
85
+ storeConfig;
84
86
  constructor(...args) {
85
87
  }
86
88
  getAllExceptions(_paginationParams) {
@@ -113,6 +115,8 @@ import { randomUUID } from "crypto";
113
115
  import Database from "libsql";
114
116
  import { nowISO } from "@lensjs/date";
115
117
  var TABLE_NAME = "lens_entries";
118
+ var BYTES_IN_GB = 1024 * 1024 * 1024;
119
+ var PRUNE_BATCH_SIZE = 1e3;
116
120
  var BetterSqliteStore = class extends Store {
117
121
  connection;
118
122
  async initialize() {
@@ -134,6 +138,7 @@ var BetterSqliteStore = class extends Store {
134
138
  lens_entry_id: entry.requestId || null,
135
139
  minimalData: this.stringifyData(entry.minimal_data ?? {})
136
140
  });
141
+ this.maybePruneDatabase();
137
142
  }
138
143
  async getAllQueries(pagination) {
139
144
  return await this.paginate("query" /* QUERY */, pagination);
@@ -209,6 +214,36 @@ var BetterSqliteStore = class extends Store {
209
214
  this.connection.exec(createIndex);
210
215
  this.connection.exec(lensEntryIdIndex);
211
216
  }
217
+ maybePruneDatabase() {
218
+ const maxGb = this.storeConfig?.dbMaxSizeGb;
219
+ const pruneGb = this.storeConfig?.dbPruneSizeGb;
220
+ if (!maxGb || !pruneGb) return;
221
+ const maxBytes = maxGb * BYTES_IN_GB;
222
+ const pruneBytes = pruneGb * BYTES_IN_GB;
223
+ if (maxBytes <= 0 || pruneBytes <= 0) return;
224
+ const targetBytes = Math.max(0, maxBytes - pruneBytes);
225
+ let usedBytes = this.getDatabaseUsedBytes();
226
+ if (usedBytes < maxBytes) return;
227
+ while (usedBytes > targetBytes) {
228
+ const deletedRows = this.deleteOldestEntries(PRUNE_BATCH_SIZE);
229
+ if (deletedRows === 0) break;
230
+ usedBytes = this.getDatabaseUsedBytes();
231
+ }
232
+ this.connection.exec("PRAGMA wal_checkpoint(TRUNCATE);");
233
+ }
234
+ getDatabaseUsedBytes() {
235
+ const pageSizeResult = this.connection.prepare("PRAGMA page_size;").get();
236
+ const pageCountResult = this.connection.prepare("PRAGMA page_count;").get();
237
+ const freelistCountResult = this.connection.prepare("PRAGMA freelist_count;").get();
238
+ const usedPages = pageCountResult.page_count - freelistCountResult.freelist_count;
239
+ return usedPages * pageSizeResult.page_size;
240
+ }
241
+ deleteOldestEntries(batchSize) {
242
+ const result = this.connection.prepare(
243
+ `DELETE FROM ${TABLE_NAME} WHERE id IN (SELECT id FROM ${TABLE_NAME} ORDER BY created_at ASC LIMIT ?)`
244
+ ).run(batchSize);
245
+ return Number(result.changes ?? 0);
246
+ }
212
247
  mapRow(row, includeFullData = true) {
213
248
  let data = includeFullData ? JSON.parse(row.data) : {};
214
249
  if (!includeFullData) {
@@ -145,6 +145,8 @@ interface QueuedStoreConfig {
145
145
  processIntervalMs?: number;
146
146
  warnThreshold?: number;
147
147
  preallocate?: boolean;
148
+ dbMaxSizeGb?: number;
149
+ dbPruneSizeGb?: number;
148
150
  }
149
151
 
150
152
  export { type ApiResponse, type CacheAction, type CacheEntry, type Constructor, type Entry, type ExceptionEntry, type HttpMethod, type LensConfig, type LensEntry, type PaginationParams, type Paginator, type QueryEntry, type QueryType, type QueuedStoreConfig, type RequestEntry, type RouteDefinition, type RouteDefinitionHandler, type RouteHttpMethod, type SqlQueryType, type UserEntry, WatcherTypeEnum };
@@ -145,6 +145,8 @@ interface QueuedStoreConfig {
145
145
  processIntervalMs?: number;
146
146
  warnThreshold?: number;
147
147
  preallocate?: boolean;
148
+ dbMaxSizeGb?: number;
149
+ dbPruneSizeGb?: number;
148
150
  }
149
151
 
150
152
  export { type ApiResponse, type CacheAction, type CacheEntry, type Constructor, type Entry, type ExceptionEntry, type HttpMethod, type LensConfig, type LensEntry, type PaginationParams, type Paginator, type QueryEntry, type QueryType, type QueuedStoreConfig, type RequestEntry, type RouteDefinition, type RouteDefinitionHandler, type RouteHttpMethod, type SqlQueryType, type UserEntry, WatcherTypeEnum };
@@ -113,7 +113,7 @@ var formatSqlQuery = (query, language) => {
113
113
  });
114
114
  };
115
115
  function getMeta(metaUrl) {
116
- const isESM = typeof __dirname === "undefined";
116
+ const isESM = typeof __dirname === "undefined" || typeof __filename === "undefined";
117
117
  if (isESM) {
118
118
  if (!metaUrl) {
119
119
  throw new Error("In ESM, you must pass import.meta.url to getMeta()");
@@ -69,7 +69,7 @@ var formatSqlQuery = (query, language) => {
69
69
  });
70
70
  };
71
71
  function getMeta(metaUrl) {
72
- const isESM = typeof __dirname === "undefined";
72
+ const isESM = typeof __dirname === "undefined" || typeof __filename === "undefined";
73
73
  if (isESM) {
74
74
  if (!metaUrl) {
75
75
  throw new Error("In ESM, you must pass import.meta.url to getMeta()");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lensjs/core",
3
- "version": "2.3.0",
3
+ "version": "2.3.1",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",