@lensjs/core 2.2.2 → 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.
Files changed (89) hide show
  1. package/dist/abstracts/store.cjs +3 -0
  2. package/dist/abstracts/store.d.cts +3 -1
  3. package/dist/abstracts/store.d.ts +3 -1
  4. package/dist/abstracts/store.js +3 -0
  5. package/dist/core/lens.cjs +151 -24
  6. package/dist/core/lens.d.cts +3 -2
  7. package/dist/core/lens.d.ts +3 -2
  8. package/dist/core/lens.js +151 -24
  9. package/dist/{index-CMvlRWcQ.d.cts → index-CZsa0Zcm.d.ts} +3 -1
  10. package/dist/{index-CMvlRWcQ.d.ts → index-QmOJr0K-.d.cts} +3 -1
  11. package/dist/index.cjs +199 -29
  12. package/dist/index.d.cts +5 -2
  13. package/dist/index.d.ts +5 -2
  14. package/dist/index.js +198 -29
  15. package/dist/mixins/queued_store.cjs +114 -0
  16. package/dist/mixins/queued_store.d.cts +38 -0
  17. package/dist/mixins/queued_store.d.ts +38 -0
  18. package/dist/mixins/queued_store.js +79 -0
  19. package/dist/stores/better_sqlite.cjs +36 -0
  20. package/dist/stores/better_sqlite.d.cts +3 -0
  21. package/dist/stores/better_sqlite.d.ts +3 -0
  22. package/dist/stores/better_sqlite.js +36 -0
  23. package/dist/stores/index.cjs +129 -2
  24. package/dist/stores/index.d.cts +2 -0
  25. package/dist/stores/index.d.ts +2 -0
  26. package/dist/stores/index.js +127 -1
  27. package/dist/stores/queued_sqlite.cjs +313 -0
  28. package/dist/stores/queued_sqlite.d.cts +43 -0
  29. package/dist/stores/queued_sqlite.d.ts +43 -0
  30. package/dist/stores/queued_sqlite.js +280 -0
  31. package/dist/types/index.d.cts +16 -2
  32. package/dist/types/index.d.ts +16 -2
  33. package/dist/ui/assets/{CacheActionBadge-CL10Xlw7.js → CacheActionBadge-BB4uokI1.js} +1 -1
  34. package/dist/ui/assets/CacheEntriesTable-B8cUXhos.js +1 -0
  35. package/dist/ui/assets/CacheEntryContainer-WkdnGvnu.js +2 -0
  36. package/dist/ui/assets/CacheEntryDetails-BeZnoIpm.js +1 -0
  37. package/dist/ui/assets/CacheEntryDetailsContainer-DI0mEvpu.js +2 -0
  38. package/dist/ui/assets/ExceptionContainer-YNcR0F5U.js +2 -0
  39. package/dist/ui/assets/{ExceptionDetails-Bzj8OZ70.js → ExceptionDetails-BKHzv6hf.js} +1 -1
  40. package/dist/ui/assets/ExceptionDetailsContainer-CJHILjb3.js +2 -0
  41. package/dist/ui/assets/{ExceptionTable-BkFSFGbn.js → ExceptionTable-DzBmQLLa.js} +1 -1
  42. package/dist/ui/assets/JsonViewer-D-KPN089.js +1 -0
  43. package/dist/ui/assets/{LoadMore-Du7yL-Hr.js → LoadMore-CLPR6Zd4.js} +1 -1
  44. package/dist/ui/assets/QueriesContainer-B_PmBkHR.js +2 -0
  45. package/dist/ui/assets/{QueryDetailsContainer-BtbvRyBf.js → QueryDetailsContainer-Cqj3E6Dr.js} +16 -26
  46. package/dist/ui/assets/{QueryTable-tJVEncUM.js → QueryTable-DmWdZSnJ.js} +1 -1
  47. package/dist/ui/assets/{RequestDetails-C6tqSqg9.js → RequestDetails-CF338Kcv.js} +1 -1
  48. package/dist/ui/assets/{RequestDetailsContainer---KvdZKp.js → RequestDetailsContainer-aW4GLool.js} +2 -2
  49. package/dist/ui/assets/{RequestsContainer-Bogurt1b.js → RequestsContainer-DdLSvAbl.js} +2 -2
  50. package/dist/ui/assets/{RequetsTable-BMrYHd0d.js → RequetsTable-Bdp_PhGU.js} +1 -1
  51. package/dist/ui/assets/{StatusCode-DpZO0dUJ.js → StatusCode-C605nHvd.js} +1 -1
  52. package/dist/ui/assets/TabbedDataViewer-ofhEq_Wj.js +2 -0
  53. package/dist/ui/assets/{Table-BtkmKVTF.js → Table-kak5sL5X.js} +1 -1
  54. package/dist/ui/assets/{columns-D_NhXbk6.js → columns-BEyDhUNq.js} +1 -1
  55. package/dist/ui/assets/{columns-DlGaMv5C.js → columns-Bu5psHyp.js} +1 -1
  56. package/dist/ui/assets/{columns-DF7BR0z_.js → columns-BvIUTkjN.js} +1 -1
  57. package/dist/ui/assets/copy-DzXuP4eO.js +11 -0
  58. package/dist/ui/assets/index-CsnKQ5Mh.css +1 -0
  59. package/dist/ui/assets/{index-EQXljT95.js → index-TW_-MgRG.js} +25 -25
  60. package/dist/ui/assets/{useCacheEntries-jC9XYsV_.js → useCacheEntries-Pvte_aNc.js} +1 -1
  61. package/dist/ui/assets/{useExceptions-CwwK33mG.js → useExceptions-P3cnURvN.js} +1 -1
  62. package/dist/ui/assets/{useLensApi-CPvDlyGv.js → useLensApi-BFdsfrzR.js} +1 -1
  63. package/dist/ui/assets/{useLoadMore-C2bqGSaL.js → useLoadMore-JCWak1Dg.js} +1 -1
  64. package/dist/ui/assets/{useQueries-CzbIajH6.js → useQueries-CNquFtm0.js} +1 -1
  65. package/dist/ui/index.html +2 -2
  66. package/dist/utils/compose.cjs +32 -0
  67. package/dist/utils/compose.d.cts +11 -0
  68. package/dist/utils/compose.d.ts +11 -0
  69. package/dist/utils/compose.js +7 -0
  70. package/dist/utils/index.cjs +10 -1
  71. package/dist/utils/index.d.cts +3 -1
  72. package/dist/utils/index.d.ts +3 -1
  73. package/dist/utils/index.js +9 -1
  74. package/dist/watchers/index.cjs +45 -5
  75. package/dist/watchers/index.js +45 -5
  76. package/dist/watchers/request_watcher.cjs +45 -5
  77. package/dist/watchers/request_watcher.d.cts +12 -1
  78. package/dist/watchers/request_watcher.d.ts +12 -1
  79. package/dist/watchers/request_watcher.js +45 -5
  80. package/package.json +2 -1
  81. package/dist/ui/assets/CacheEntriesTable-DuLoeu0e.js +0 -1
  82. package/dist/ui/assets/CacheEntryContainer-DqHm-jQl.js +0 -2
  83. package/dist/ui/assets/CacheEntryDetails-k-74LsSb.js +0 -1
  84. package/dist/ui/assets/CacheEntryDetailsContainer-DTI7gtUq.js +0 -2
  85. package/dist/ui/assets/ExceptionContainer-0dCs6QMQ.js +0 -2
  86. package/dist/ui/assets/ExceptionDetailsContainer-Bim0gTpE.js +0 -2
  87. package/dist/ui/assets/QueriesContainer-5xlqsYl0.js +0 -2
  88. package/dist/ui/assets/TabbedDataViewer-C60W9bqz.js +0 -1
  89. package/dist/ui/assets/index-CMJVCuvo.css +0 -1
@@ -36,6 +36,9 @@ module.exports = __toCommonJS(better_sqlite_exports);
36
36
 
37
37
  // src/abstracts/store.ts
38
38
  var Store = class {
39
+ storeConfig;
40
+ constructor(...args) {
41
+ }
39
42
  getAllExceptions(_paginationParams) {
40
43
  return this.defaultMinimalPaginate();
41
44
  }
@@ -66,6 +69,8 @@ var import_crypto = require("crypto");
66
69
  var import_libsql = __toESM(require("libsql"), 1);
67
70
  var import_date = require("@lensjs/date");
68
71
  var TABLE_NAME = "lens_entries";
72
+ var BYTES_IN_GB = 1024 * 1024 * 1024;
73
+ var PRUNE_BATCH_SIZE = 1e3;
69
74
  var BetterSqliteStore = class extends Store {
70
75
  connection;
71
76
  async initialize() {
@@ -87,6 +92,7 @@ var BetterSqliteStore = class extends Store {
87
92
  lens_entry_id: entry.requestId || null,
88
93
  minimalData: this.stringifyData(entry.minimal_data ?? {})
89
94
  });
95
+ this.maybePruneDatabase();
90
96
  }
91
97
  async getAllQueries(pagination) {
92
98
  return await this.paginate("query" /* QUERY */, pagination);
@@ -162,6 +168,36 @@ var BetterSqliteStore = class extends Store {
162
168
  this.connection.exec(createIndex);
163
169
  this.connection.exec(lensEntryIdIndex);
164
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
+ }
165
201
  mapRow(row, includeFullData = true) {
166
202
  let data = includeFullData ? JSON.parse(row.data) : {};
167
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,8 @@
1
1
  // src/abstracts/store.ts
2
2
  var Store = class {
3
+ storeConfig;
4
+ constructor(...args) {
5
+ }
3
6
  getAllExceptions(_paginationParams) {
4
7
  return this.defaultMinimalPaginate();
5
8
  }
@@ -30,6 +33,8 @@ import { randomUUID } from "crypto";
30
33
  import Database from "libsql";
31
34
  import { nowISO } from "@lensjs/date";
32
35
  var TABLE_NAME = "lens_entries";
36
+ var BYTES_IN_GB = 1024 * 1024 * 1024;
37
+ var PRUNE_BATCH_SIZE = 1e3;
33
38
  var BetterSqliteStore = class extends Store {
34
39
  connection;
35
40
  async initialize() {
@@ -51,6 +56,7 @@ var BetterSqliteStore = class extends Store {
51
56
  lens_entry_id: entry.requestId || null,
52
57
  minimalData: this.stringifyData(entry.minimal_data ?? {})
53
58
  });
59
+ this.maybePruneDatabase();
54
60
  }
55
61
  async getAllQueries(pagination) {
56
62
  return await this.paginate("query" /* QUERY */, pagination);
@@ -126,6 +132,36 @@ var BetterSqliteStore = class extends Store {
126
132
  this.connection.exec(createIndex);
127
133
  this.connection.exec(lensEntryIdIndex);
128
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
+ }
129
165
  mapRow(row, includeFullData = true) {
130
166
  let data = includeFullData ? JSON.parse(row.data) : {};
131
167
  if (!includeFullData) {
@@ -30,12 +30,16 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
30
30
  // src/stores/index.ts
31
31
  var stores_exports = {};
32
32
  __export(stores_exports, {
33
- BetterSqliteStore: () => BetterSqliteStore
33
+ BetterSqliteStore: () => BetterSqliteStore,
34
+ QueuedSqliteStore: () => QueuedSqliteStore
34
35
  });
35
36
  module.exports = __toCommonJS(stores_exports);
36
37
 
37
38
  // src/abstracts/store.ts
38
39
  var Store = class {
40
+ storeConfig;
41
+ constructor(...args) {
42
+ }
39
43
  getAllExceptions(_paginationParams) {
40
44
  return this.defaultMinimalPaginate();
41
45
  }
@@ -66,6 +70,8 @@ var import_crypto = require("crypto");
66
70
  var import_libsql = __toESM(require("libsql"), 1);
67
71
  var import_date = require("@lensjs/date");
68
72
  var TABLE_NAME = "lens_entries";
73
+ var BYTES_IN_GB = 1024 * 1024 * 1024;
74
+ var PRUNE_BATCH_SIZE = 1e3;
69
75
  var BetterSqliteStore = class extends Store {
70
76
  connection;
71
77
  async initialize() {
@@ -87,6 +93,7 @@ var BetterSqliteStore = class extends Store {
87
93
  lens_entry_id: entry.requestId || null,
88
94
  minimalData: this.stringifyData(entry.minimal_data ?? {})
89
95
  });
96
+ this.maybePruneDatabase();
90
97
  }
91
98
  async getAllQueries(pagination) {
92
99
  return await this.paginate("query" /* QUERY */, pagination);
@@ -162,6 +169,36 @@ var BetterSqliteStore = class extends Store {
162
169
  this.connection.exec(createIndex);
163
170
  this.connection.exec(lensEntryIdIndex);
164
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
+ }
165
202
  mapRow(row, includeFullData = true) {
166
203
  let data = includeFullData ? JSON.parse(row.data) : {};
167
204
  if (!includeFullData) {
@@ -186,7 +223,97 @@ var BetterSqliteStore = class extends Store {
186
223
  return `SELECT id, minimal_data, type, created_at, lens_entry_id ${includeFullData ? ",data" : ""}`;
187
224
  }
188
225
  };
226
+
227
+ // src/mixins/queued_store.ts
228
+ var import_denque = __toESM(require("denque"), 1);
229
+ function QueuedStore(Base) {
230
+ return class Queued extends Base {
231
+ queue;
232
+ processingInterval = null;
233
+ BATCH_SIZE;
234
+ PROCESS_INTERVAL_MS;
235
+ WARN_THRESHOLD;
236
+ PREALLOCATE;
237
+ constructor(...args) {
238
+ super(...args);
239
+ const config = args[0] || {};
240
+ this.storeConfig = config;
241
+ this.BATCH_SIZE = config.batchSize ?? 100;
242
+ this.PROCESS_INTERVAL_MS = config.processIntervalMs ?? 100;
243
+ this.WARN_THRESHOLD = config.warnThreshold ?? 1e5;
244
+ this.PREALLOCATE = config.preallocate ?? true;
245
+ this.queue = this.PREALLOCATE ? new import_denque.default([], { capacity: this.WARN_THRESHOLD * 2 }) : new import_denque.default();
246
+ }
247
+ async initialize() {
248
+ if (super["initialize"]) {
249
+ await super["initialize"].call(this);
250
+ }
251
+ this.startProcessingQueue();
252
+ process.on("SIGINT", () => this.shutdown());
253
+ process.on("SIGTERM", () => this.shutdown());
254
+ }
255
+ async truncate() {
256
+ this.queue.clear();
257
+ if (super["truncate"]) {
258
+ await super["truncate"].call(this);
259
+ }
260
+ }
261
+ async save(entry) {
262
+ this.queue.push(entry);
263
+ if (this.queue.length > this.WARN_THRESHOLD) {
264
+ console.warn(`\u26A0\uFE0F LensJs Queue size very large: ${this.queue.length}`);
265
+ }
266
+ }
267
+ startProcessingQueue() {
268
+ if (this.processingInterval) clearInterval(this.processingInterval);
269
+ this.processingInterval = setInterval(
270
+ () => this.processQueue(),
271
+ this.PROCESS_INTERVAL_MS
272
+ );
273
+ }
274
+ async processQueue() {
275
+ if (this.queue.isEmpty()) return;
276
+ const batchSize = Math.min(
277
+ this.BATCH_SIZE,
278
+ Math.max(10, Math.floor(this.queue.length / 10))
279
+ );
280
+ const entriesToProcess = this.queue.remove(0, batchSize);
281
+ if (!entriesToProcess.length) return;
282
+ for (const entry of entriesToProcess) {
283
+ super["save"]?.call(this, entry).catch((error) => {
284
+ console.error("Error saving queued entry:", error);
285
+ });
286
+ }
287
+ }
288
+ async stopProcessingQueue() {
289
+ if (this.processingInterval) {
290
+ clearInterval(this.processingInterval);
291
+ this.processingInterval = null;
292
+ }
293
+ if (!this.queue.isEmpty()) {
294
+ await this.processQueue();
295
+ }
296
+ }
297
+ async shutdown() {
298
+ await this.stopProcessingQueue();
299
+ process.exit(0);
300
+ }
301
+ };
302
+ }
303
+
304
+ // src/utils/compose.ts
305
+ function compose(superclass, ...mixins) {
306
+ return mixins.reduce((c, mixin) => mixin(c), superclass);
307
+ }
308
+
309
+ // src/stores/queued_sqlite.ts
310
+ var QueuedSqliteStore = class extends compose(
311
+ BetterSqliteStore,
312
+ QueuedStore
313
+ ) {
314
+ };
189
315
  // Annotate the CommonJS export names for ESM import in node:
190
316
  0 && (module.exports = {
191
- BetterSqliteStore
317
+ BetterSqliteStore,
318
+ QueuedSqliteStore
192
319
  });
@@ -1,5 +1,7 @@
1
1
  export { default as BetterSqliteStore } from './better_sqlite.cjs';
2
+ export { default as QueuedSqliteStore } from './queued_sqlite.cjs';
2
3
  import '../abstracts/store.cjs';
3
4
  import '../types/index.cjs';
4
5
  import 'sql-formatter';
5
6
  import 'libsql';
7
+ import 'denque';
@@ -1,5 +1,7 @@
1
1
  export { default as BetterSqliteStore } from './better_sqlite.js';
2
+ export { default as QueuedSqliteStore } from './queued_sqlite.js';
2
3
  import '../abstracts/store.js';
3
4
  import '../types/index.js';
4
5
  import 'sql-formatter';
5
6
  import 'libsql';
7
+ import 'denque';
@@ -1,5 +1,8 @@
1
1
  // src/abstracts/store.ts
2
2
  var Store = class {
3
+ storeConfig;
4
+ constructor(...args) {
5
+ }
3
6
  getAllExceptions(_paginationParams) {
4
7
  return this.defaultMinimalPaginate();
5
8
  }
@@ -30,6 +33,8 @@ import { randomUUID } from "crypto";
30
33
  import Database from "libsql";
31
34
  import { nowISO } from "@lensjs/date";
32
35
  var TABLE_NAME = "lens_entries";
36
+ var BYTES_IN_GB = 1024 * 1024 * 1024;
37
+ var PRUNE_BATCH_SIZE = 1e3;
33
38
  var BetterSqliteStore = class extends Store {
34
39
  connection;
35
40
  async initialize() {
@@ -51,6 +56,7 @@ var BetterSqliteStore = class extends Store {
51
56
  lens_entry_id: entry.requestId || null,
52
57
  minimalData: this.stringifyData(entry.minimal_data ?? {})
53
58
  });
59
+ this.maybePruneDatabase();
54
60
  }
55
61
  async getAllQueries(pagination) {
56
62
  return await this.paginate("query" /* QUERY */, pagination);
@@ -126,6 +132,36 @@ var BetterSqliteStore = class extends Store {
126
132
  this.connection.exec(createIndex);
127
133
  this.connection.exec(lensEntryIdIndex);
128
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
+ }
129
165
  mapRow(row, includeFullData = true) {
130
166
  let data = includeFullData ? JSON.parse(row.data) : {};
131
167
  if (!includeFullData) {
@@ -150,6 +186,96 @@ var BetterSqliteStore = class extends Store {
150
186
  return `SELECT id, minimal_data, type, created_at, lens_entry_id ${includeFullData ? ",data" : ""}`;
151
187
  }
152
188
  };
189
+
190
+ // src/mixins/queued_store.ts
191
+ import Denque from "denque";
192
+ function QueuedStore(Base) {
193
+ return class Queued extends Base {
194
+ queue;
195
+ processingInterval = null;
196
+ BATCH_SIZE;
197
+ PROCESS_INTERVAL_MS;
198
+ WARN_THRESHOLD;
199
+ PREALLOCATE;
200
+ constructor(...args) {
201
+ super(...args);
202
+ const config = args[0] || {};
203
+ this.storeConfig = config;
204
+ this.BATCH_SIZE = config.batchSize ?? 100;
205
+ this.PROCESS_INTERVAL_MS = config.processIntervalMs ?? 100;
206
+ this.WARN_THRESHOLD = config.warnThreshold ?? 1e5;
207
+ this.PREALLOCATE = config.preallocate ?? true;
208
+ this.queue = this.PREALLOCATE ? new Denque([], { capacity: this.WARN_THRESHOLD * 2 }) : new Denque();
209
+ }
210
+ async initialize() {
211
+ if (super["initialize"]) {
212
+ await super["initialize"].call(this);
213
+ }
214
+ this.startProcessingQueue();
215
+ process.on("SIGINT", () => this.shutdown());
216
+ process.on("SIGTERM", () => this.shutdown());
217
+ }
218
+ async truncate() {
219
+ this.queue.clear();
220
+ if (super["truncate"]) {
221
+ await super["truncate"].call(this);
222
+ }
223
+ }
224
+ async save(entry) {
225
+ this.queue.push(entry);
226
+ if (this.queue.length > this.WARN_THRESHOLD) {
227
+ console.warn(`\u26A0\uFE0F LensJs Queue size very large: ${this.queue.length}`);
228
+ }
229
+ }
230
+ startProcessingQueue() {
231
+ if (this.processingInterval) clearInterval(this.processingInterval);
232
+ this.processingInterval = setInterval(
233
+ () => this.processQueue(),
234
+ this.PROCESS_INTERVAL_MS
235
+ );
236
+ }
237
+ async processQueue() {
238
+ if (this.queue.isEmpty()) return;
239
+ const batchSize = Math.min(
240
+ this.BATCH_SIZE,
241
+ Math.max(10, Math.floor(this.queue.length / 10))
242
+ );
243
+ const entriesToProcess = this.queue.remove(0, batchSize);
244
+ if (!entriesToProcess.length) return;
245
+ for (const entry of entriesToProcess) {
246
+ super["save"]?.call(this, entry).catch((error) => {
247
+ console.error("Error saving queued entry:", error);
248
+ });
249
+ }
250
+ }
251
+ async stopProcessingQueue() {
252
+ if (this.processingInterval) {
253
+ clearInterval(this.processingInterval);
254
+ this.processingInterval = null;
255
+ }
256
+ if (!this.queue.isEmpty()) {
257
+ await this.processQueue();
258
+ }
259
+ }
260
+ async shutdown() {
261
+ await this.stopProcessingQueue();
262
+ process.exit(0);
263
+ }
264
+ };
265
+ }
266
+
267
+ // src/utils/compose.ts
268
+ function compose(superclass, ...mixins) {
269
+ return mixins.reduce((c, mixin) => mixin(c), superclass);
270
+ }
271
+
272
+ // src/stores/queued_sqlite.ts
273
+ var QueuedSqliteStore = class extends compose(
274
+ BetterSqliteStore,
275
+ QueuedStore
276
+ ) {
277
+ };
153
278
  export {
154
- BetterSqliteStore
279
+ BetterSqliteStore,
280
+ QueuedSqliteStore
155
281
  };