@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.
- package/dist/abstracts/store.cjs +3 -0
- package/dist/abstracts/store.d.cts +3 -1
- package/dist/abstracts/store.d.ts +3 -1
- package/dist/abstracts/store.js +3 -0
- package/dist/core/lens.cjs +151 -24
- package/dist/core/lens.d.cts +3 -2
- package/dist/core/lens.d.ts +3 -2
- package/dist/core/lens.js +151 -24
- package/dist/{index-CMvlRWcQ.d.cts → index-CZsa0Zcm.d.ts} +3 -1
- package/dist/{index-CMvlRWcQ.d.ts → index-QmOJr0K-.d.cts} +3 -1
- package/dist/index.cjs +199 -29
- package/dist/index.d.cts +5 -2
- package/dist/index.d.ts +5 -2
- package/dist/index.js +198 -29
- package/dist/mixins/queued_store.cjs +114 -0
- package/dist/mixins/queued_store.d.cts +38 -0
- package/dist/mixins/queued_store.d.ts +38 -0
- package/dist/mixins/queued_store.js +79 -0
- package/dist/stores/better_sqlite.cjs +36 -0
- package/dist/stores/better_sqlite.d.cts +3 -0
- package/dist/stores/better_sqlite.d.ts +3 -0
- package/dist/stores/better_sqlite.js +36 -0
- package/dist/stores/index.cjs +129 -2
- package/dist/stores/index.d.cts +2 -0
- package/dist/stores/index.d.ts +2 -0
- package/dist/stores/index.js +127 -1
- package/dist/stores/queued_sqlite.cjs +313 -0
- package/dist/stores/queued_sqlite.d.cts +43 -0
- package/dist/stores/queued_sqlite.d.ts +43 -0
- package/dist/stores/queued_sqlite.js +280 -0
- package/dist/types/index.d.cts +16 -2
- package/dist/types/index.d.ts +16 -2
- package/dist/ui/assets/{CacheActionBadge-CL10Xlw7.js → CacheActionBadge-BB4uokI1.js} +1 -1
- package/dist/ui/assets/CacheEntriesTable-B8cUXhos.js +1 -0
- package/dist/ui/assets/CacheEntryContainer-WkdnGvnu.js +2 -0
- package/dist/ui/assets/CacheEntryDetails-BeZnoIpm.js +1 -0
- package/dist/ui/assets/CacheEntryDetailsContainer-DI0mEvpu.js +2 -0
- package/dist/ui/assets/ExceptionContainer-YNcR0F5U.js +2 -0
- package/dist/ui/assets/{ExceptionDetails-Bzj8OZ70.js → ExceptionDetails-BKHzv6hf.js} +1 -1
- package/dist/ui/assets/ExceptionDetailsContainer-CJHILjb3.js +2 -0
- package/dist/ui/assets/{ExceptionTable-BkFSFGbn.js → ExceptionTable-DzBmQLLa.js} +1 -1
- package/dist/ui/assets/JsonViewer-D-KPN089.js +1 -0
- package/dist/ui/assets/{LoadMore-Du7yL-Hr.js → LoadMore-CLPR6Zd4.js} +1 -1
- package/dist/ui/assets/QueriesContainer-B_PmBkHR.js +2 -0
- package/dist/ui/assets/{QueryDetailsContainer-BtbvRyBf.js → QueryDetailsContainer-Cqj3E6Dr.js} +16 -26
- package/dist/ui/assets/{QueryTable-tJVEncUM.js → QueryTable-DmWdZSnJ.js} +1 -1
- package/dist/ui/assets/{RequestDetails-C6tqSqg9.js → RequestDetails-CF338Kcv.js} +1 -1
- package/dist/ui/assets/{RequestDetailsContainer---KvdZKp.js → RequestDetailsContainer-aW4GLool.js} +2 -2
- package/dist/ui/assets/{RequestsContainer-Bogurt1b.js → RequestsContainer-DdLSvAbl.js} +2 -2
- package/dist/ui/assets/{RequetsTable-BMrYHd0d.js → RequetsTable-Bdp_PhGU.js} +1 -1
- package/dist/ui/assets/{StatusCode-DpZO0dUJ.js → StatusCode-C605nHvd.js} +1 -1
- package/dist/ui/assets/TabbedDataViewer-ofhEq_Wj.js +2 -0
- package/dist/ui/assets/{Table-BtkmKVTF.js → Table-kak5sL5X.js} +1 -1
- package/dist/ui/assets/{columns-D_NhXbk6.js → columns-BEyDhUNq.js} +1 -1
- package/dist/ui/assets/{columns-DlGaMv5C.js → columns-Bu5psHyp.js} +1 -1
- package/dist/ui/assets/{columns-DF7BR0z_.js → columns-BvIUTkjN.js} +1 -1
- package/dist/ui/assets/copy-DzXuP4eO.js +11 -0
- package/dist/ui/assets/index-CsnKQ5Mh.css +1 -0
- package/dist/ui/assets/{index-EQXljT95.js → index-TW_-MgRG.js} +25 -25
- package/dist/ui/assets/{useCacheEntries-jC9XYsV_.js → useCacheEntries-Pvte_aNc.js} +1 -1
- package/dist/ui/assets/{useExceptions-CwwK33mG.js → useExceptions-P3cnURvN.js} +1 -1
- package/dist/ui/assets/{useLensApi-CPvDlyGv.js → useLensApi-BFdsfrzR.js} +1 -1
- package/dist/ui/assets/{useLoadMore-C2bqGSaL.js → useLoadMore-JCWak1Dg.js} +1 -1
- package/dist/ui/assets/{useQueries-CzbIajH6.js → useQueries-CNquFtm0.js} +1 -1
- package/dist/ui/index.html +2 -2
- package/dist/utils/compose.cjs +32 -0
- package/dist/utils/compose.d.cts +11 -0
- package/dist/utils/compose.d.ts +11 -0
- package/dist/utils/compose.js +7 -0
- package/dist/utils/index.cjs +10 -1
- package/dist/utils/index.d.cts +3 -1
- package/dist/utils/index.d.ts +3 -1
- package/dist/utils/index.js +9 -1
- package/dist/watchers/index.cjs +45 -5
- package/dist/watchers/index.js +45 -5
- package/dist/watchers/request_watcher.cjs +45 -5
- package/dist/watchers/request_watcher.d.cts +12 -1
- package/dist/watchers/request_watcher.d.ts +12 -1
- package/dist/watchers/request_watcher.js +45 -5
- package/package.json +2 -1
- package/dist/ui/assets/CacheEntriesTable-DuLoeu0e.js +0 -1
- package/dist/ui/assets/CacheEntryContainer-DqHm-jQl.js +0 -2
- package/dist/ui/assets/CacheEntryDetails-k-74LsSb.js +0 -1
- package/dist/ui/assets/CacheEntryDetailsContainer-DTI7gtUq.js +0 -2
- package/dist/ui/assets/ExceptionContainer-0dCs6QMQ.js +0 -2
- package/dist/ui/assets/ExceptionDetailsContainer-Bim0gTpE.js +0 -2
- package/dist/ui/assets/QueriesContainer-5xlqsYl0.js +0 -2
- package/dist/ui/assets/TabbedDataViewer-C60W9bqz.js +0 -1
- package/dist/ui/assets/index-CMJVCuvo.css +0 -1
package/dist/index.js
CHANGED
|
@@ -183,6 +183,9 @@ import * as path2 from "path";
|
|
|
183
183
|
|
|
184
184
|
// src/abstracts/store.ts
|
|
185
185
|
var Store = class {
|
|
186
|
+
storeConfig;
|
|
187
|
+
constructor(...args) {
|
|
188
|
+
}
|
|
186
189
|
getAllExceptions(_paginationParams) {
|
|
187
190
|
return this.defaultMinimalPaginate();
|
|
188
191
|
}
|
|
@@ -213,6 +216,8 @@ import { randomUUID } from "crypto";
|
|
|
213
216
|
import Database from "libsql";
|
|
214
217
|
import { nowISO } from "@lensjs/date";
|
|
215
218
|
var TABLE_NAME = "lens_entries";
|
|
219
|
+
var BYTES_IN_GB = 1024 * 1024 * 1024;
|
|
220
|
+
var PRUNE_BATCH_SIZE = 1e3;
|
|
216
221
|
var BetterSqliteStore = class extends Store {
|
|
217
222
|
connection;
|
|
218
223
|
async initialize() {
|
|
@@ -234,6 +239,7 @@ var BetterSqliteStore = class extends Store {
|
|
|
234
239
|
lens_entry_id: entry.requestId || null,
|
|
235
240
|
minimalData: this.stringifyData(entry.minimal_data ?? {})
|
|
236
241
|
});
|
|
242
|
+
this.maybePruneDatabase();
|
|
237
243
|
}
|
|
238
244
|
async getAllQueries(pagination) {
|
|
239
245
|
return await this.paginate("query" /* QUERY */, pagination);
|
|
@@ -309,6 +315,36 @@ var BetterSqliteStore = class extends Store {
|
|
|
309
315
|
this.connection.exec(createIndex);
|
|
310
316
|
this.connection.exec(lensEntryIdIndex);
|
|
311
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
|
+
}
|
|
312
348
|
mapRow(row, includeFullData = true) {
|
|
313
349
|
let data = includeFullData ? JSON.parse(row.data) : {};
|
|
314
350
|
if (!includeFullData) {
|
|
@@ -334,9 +370,99 @@ var BetterSqliteStore = class extends Store {
|
|
|
334
370
|
}
|
|
335
371
|
};
|
|
336
372
|
|
|
373
|
+
// src/mixins/queued_store.ts
|
|
374
|
+
import Denque from "denque";
|
|
375
|
+
function QueuedStore(Base) {
|
|
376
|
+
return class Queued extends Base {
|
|
377
|
+
queue;
|
|
378
|
+
processingInterval = null;
|
|
379
|
+
BATCH_SIZE;
|
|
380
|
+
PROCESS_INTERVAL_MS;
|
|
381
|
+
WARN_THRESHOLD;
|
|
382
|
+
PREALLOCATE;
|
|
383
|
+
constructor(...args) {
|
|
384
|
+
super(...args);
|
|
385
|
+
const config = args[0] || {};
|
|
386
|
+
this.storeConfig = config;
|
|
387
|
+
this.BATCH_SIZE = config.batchSize ?? 100;
|
|
388
|
+
this.PROCESS_INTERVAL_MS = config.processIntervalMs ?? 100;
|
|
389
|
+
this.WARN_THRESHOLD = config.warnThreshold ?? 1e5;
|
|
390
|
+
this.PREALLOCATE = config.preallocate ?? true;
|
|
391
|
+
this.queue = this.PREALLOCATE ? new Denque([], { capacity: this.WARN_THRESHOLD * 2 }) : new Denque();
|
|
392
|
+
}
|
|
393
|
+
async initialize() {
|
|
394
|
+
if (super["initialize"]) {
|
|
395
|
+
await super["initialize"].call(this);
|
|
396
|
+
}
|
|
397
|
+
this.startProcessingQueue();
|
|
398
|
+
process.on("SIGINT", () => this.shutdown());
|
|
399
|
+
process.on("SIGTERM", () => this.shutdown());
|
|
400
|
+
}
|
|
401
|
+
async truncate() {
|
|
402
|
+
this.queue.clear();
|
|
403
|
+
if (super["truncate"]) {
|
|
404
|
+
await super["truncate"].call(this);
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
async save(entry) {
|
|
408
|
+
this.queue.push(entry);
|
|
409
|
+
if (this.queue.length > this.WARN_THRESHOLD) {
|
|
410
|
+
console.warn(`\u26A0\uFE0F LensJs Queue size very large: ${this.queue.length}`);
|
|
411
|
+
}
|
|
412
|
+
}
|
|
413
|
+
startProcessingQueue() {
|
|
414
|
+
if (this.processingInterval) clearInterval(this.processingInterval);
|
|
415
|
+
this.processingInterval = setInterval(
|
|
416
|
+
() => this.processQueue(),
|
|
417
|
+
this.PROCESS_INTERVAL_MS
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
async processQueue() {
|
|
421
|
+
if (this.queue.isEmpty()) return;
|
|
422
|
+
const batchSize = Math.min(
|
|
423
|
+
this.BATCH_SIZE,
|
|
424
|
+
Math.max(10, Math.floor(this.queue.length / 10))
|
|
425
|
+
);
|
|
426
|
+
const entriesToProcess = this.queue.remove(0, batchSize);
|
|
427
|
+
if (!entriesToProcess.length) return;
|
|
428
|
+
for (const entry of entriesToProcess) {
|
|
429
|
+
super["save"]?.call(this, entry).catch((error) => {
|
|
430
|
+
console.error("Error saving queued entry:", error);
|
|
431
|
+
});
|
|
432
|
+
}
|
|
433
|
+
}
|
|
434
|
+
async stopProcessingQueue() {
|
|
435
|
+
if (this.processingInterval) {
|
|
436
|
+
clearInterval(this.processingInterval);
|
|
437
|
+
this.processingInterval = null;
|
|
438
|
+
}
|
|
439
|
+
if (!this.queue.isEmpty()) {
|
|
440
|
+
await this.processQueue();
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
async shutdown() {
|
|
444
|
+
await this.stopProcessingQueue();
|
|
445
|
+
process.exit(0);
|
|
446
|
+
}
|
|
447
|
+
};
|
|
448
|
+
}
|
|
449
|
+
|
|
450
|
+
// src/utils/compose.ts
|
|
451
|
+
function compose(superclass, ...mixins) {
|
|
452
|
+
return mixins.reduce((c, mixin) => mixin(c), superclass);
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
// src/stores/queued_sqlite.ts
|
|
456
|
+
var QueuedSqliteStore = class extends compose(
|
|
457
|
+
BetterSqliteStore,
|
|
458
|
+
QueuedStore
|
|
459
|
+
) {
|
|
460
|
+
};
|
|
461
|
+
|
|
337
462
|
// src/utils/index.ts
|
|
338
463
|
var utils_exports = {};
|
|
339
464
|
__export(utils_exports, {
|
|
465
|
+
compose: () => compose,
|
|
340
466
|
formatSqlQuery: () => formatSqlQuery,
|
|
341
467
|
generateRandomUuid: () => generateRandomUuid,
|
|
342
468
|
getMeta: () => getMeta,
|
|
@@ -411,7 +537,7 @@ var formatSqlQuery = (query, language) => {
|
|
|
411
537
|
});
|
|
412
538
|
};
|
|
413
539
|
function getMeta(metaUrl) {
|
|
414
|
-
const isESM = typeof __dirname === "undefined";
|
|
540
|
+
const isESM = typeof __dirname === "undefined" || typeof __filename === "undefined";
|
|
415
541
|
if (isESM) {
|
|
416
542
|
if (!metaUrl) {
|
|
417
543
|
throw new Error("In ESM, you must pass import.meta.url to getMeta()");
|
|
@@ -476,6 +602,7 @@ var Lens = class {
|
|
|
476
602
|
static watchers = /* @__PURE__ */ new Map();
|
|
477
603
|
static store;
|
|
478
604
|
static adapter;
|
|
605
|
+
static config;
|
|
479
606
|
static watch(watcher) {
|
|
480
607
|
this.watchers.set(watcher.name, watcher);
|
|
481
608
|
return this;
|
|
@@ -485,22 +612,23 @@ var Lens = class {
|
|
|
485
612
|
return this;
|
|
486
613
|
}
|
|
487
614
|
static async start(config = {
|
|
488
|
-
|
|
615
|
+
path: "lens",
|
|
489
616
|
appName: "Lens",
|
|
490
617
|
enabled: true
|
|
491
618
|
}) {
|
|
492
619
|
if (!config.enabled) {
|
|
493
620
|
return;
|
|
494
621
|
}
|
|
495
|
-
|
|
622
|
+
this.config = config;
|
|
623
|
+
await this.bindContainerDeps();
|
|
496
624
|
let adapter = this.getAdapter();
|
|
497
625
|
adapter.setWatchers(Array.from(this.watchers.values())).setup();
|
|
498
626
|
const { apiRoutes } = this.getRoutes({
|
|
499
|
-
|
|
627
|
+
path: config.path
|
|
500
628
|
});
|
|
501
629
|
adapter.registerRoutes(apiRoutes);
|
|
502
630
|
const uiPath = this.getUiPath();
|
|
503
|
-
adapter.serveUI(uiPath, config.
|
|
631
|
+
adapter.serveUI(uiPath, config.path, getUiConfig());
|
|
504
632
|
}
|
|
505
633
|
static setStore(store) {
|
|
506
634
|
this.store = store;
|
|
@@ -523,7 +651,7 @@ var Lens = class {
|
|
|
523
651
|
const { __dirname: __dirname2 } = getMeta(import.meta.url);
|
|
524
652
|
return path2.resolve(this.normalizeDirName(__dirname2), "ui");
|
|
525
653
|
}
|
|
526
|
-
static getRoutes({
|
|
654
|
+
static getRoutes({ path: path4 }) {
|
|
527
655
|
const apiRoutes = [
|
|
528
656
|
{
|
|
529
657
|
method: "GET",
|
|
@@ -532,71 +660,71 @@ var Lens = class {
|
|
|
532
660
|
},
|
|
533
661
|
{
|
|
534
662
|
method: "GET",
|
|
535
|
-
path: `/${
|
|
663
|
+
path: `/${path4}/api/requests`,
|
|
536
664
|
handler: async (data) => await ApiController.getRequests(data)
|
|
537
665
|
},
|
|
538
666
|
{
|
|
539
667
|
method: "GET",
|
|
540
|
-
path: `/${
|
|
668
|
+
path: `/${path4}/api/requests/:id`,
|
|
541
669
|
handler: async (data) => await ApiController.getRequest(data)
|
|
542
670
|
},
|
|
543
671
|
{
|
|
544
672
|
method: "GET",
|
|
545
|
-
path: `/${
|
|
673
|
+
path: `/${path4}/api/queries`,
|
|
546
674
|
handler: async (data) => await ApiController.getQueries(data)
|
|
547
675
|
},
|
|
548
676
|
{
|
|
549
677
|
method: "GET",
|
|
550
|
-
path: `/${
|
|
678
|
+
path: `/${path4}/api/queries/:id`,
|
|
551
679
|
handler: async (data) => await ApiController.getQuery(data)
|
|
552
680
|
},
|
|
553
681
|
{
|
|
554
682
|
method: "GET",
|
|
555
|
-
path: `/${
|
|
683
|
+
path: `/${path4}/api/cache`,
|
|
556
684
|
handler: async (data) => await ApiController.getCacheEntries(data)
|
|
557
685
|
},
|
|
558
686
|
{
|
|
559
687
|
method: "GET",
|
|
560
|
-
path: `/${
|
|
688
|
+
path: `/${path4}/api/cache/:id`,
|
|
561
689
|
handler: async (data) => await ApiController.getCacheEntry(data)
|
|
562
690
|
},
|
|
563
691
|
{
|
|
564
692
|
method: "GET",
|
|
565
|
-
path: `/${
|
|
693
|
+
path: `/${path4}/api/exceptions`,
|
|
566
694
|
handler: async (data) => await ApiController.getExceptions(data)
|
|
567
695
|
},
|
|
568
696
|
{
|
|
569
697
|
method: "GET",
|
|
570
|
-
path: `/${
|
|
698
|
+
path: `/${path4}/api/exceptions/:id`,
|
|
571
699
|
handler: async (data) => await ApiController.getException(data)
|
|
572
700
|
},
|
|
573
701
|
{
|
|
574
702
|
method: "DELETE",
|
|
575
|
-
path: `/${
|
|
703
|
+
path: `/${path4}/api/truncate`,
|
|
576
704
|
handler: async () => await ApiController.truncate()
|
|
577
705
|
}
|
|
578
706
|
];
|
|
579
707
|
return { apiRoutes };
|
|
580
708
|
}
|
|
581
|
-
static async bindContainerDeps(
|
|
709
|
+
static async bindContainerDeps() {
|
|
582
710
|
const dbStore = await this.getStore();
|
|
583
711
|
Container.singleton("store", () => dbStore);
|
|
584
712
|
Container.singleton("uiConfig", () => {
|
|
585
713
|
return {
|
|
586
|
-
appName: config.appName,
|
|
587
|
-
path: `/${config.
|
|
714
|
+
appName: this.config.appName,
|
|
715
|
+
path: `/${this.config.path}`,
|
|
588
716
|
api: {
|
|
589
|
-
requests: `/${config.
|
|
590
|
-
queries: `/${config.
|
|
591
|
-
cache: `/${config.
|
|
592
|
-
exceptions: `/${config.
|
|
593
|
-
truncate: `/${config.
|
|
717
|
+
requests: `/${this.config.path}/api/requests`,
|
|
718
|
+
queries: `/${this.config.path}/api/queries`,
|
|
719
|
+
cache: `/${this.config.path}/api/cache`,
|
|
720
|
+
exceptions: `/${this.config.path}/api/exceptions`,
|
|
721
|
+
truncate: `/${this.config.path}/api/truncate`
|
|
594
722
|
}
|
|
595
723
|
};
|
|
596
724
|
});
|
|
597
725
|
}
|
|
598
726
|
static async getDefaultStore() {
|
|
599
|
-
const store = new
|
|
727
|
+
const store = new QueuedSqliteStore(this.config.storeQueueConfig);
|
|
600
728
|
await store.initialize();
|
|
601
729
|
return store;
|
|
602
730
|
}
|
|
@@ -622,10 +750,19 @@ var QueryWatcher = class extends Watcher {
|
|
|
622
750
|
};
|
|
623
751
|
|
|
624
752
|
// src/watchers/request_watcher.ts
|
|
625
|
-
var RequestWatcher = class extends Watcher {
|
|
753
|
+
var RequestWatcher = class _RequestWatcher extends Watcher {
|
|
626
754
|
name = "request" /* REQUEST */;
|
|
627
|
-
|
|
628
|
-
|
|
755
|
+
static DEFAULT_HIDDEN_HEADERS = ["Authorization", "Basic"];
|
|
756
|
+
static DEFAULT_HIDDEN_BODY_PARAMS = [
|
|
757
|
+
"password",
|
|
758
|
+
"passwordConfirmation",
|
|
759
|
+
"secret",
|
|
760
|
+
"password_confirmation"
|
|
761
|
+
];
|
|
762
|
+
async log(data, hidden = {}) {
|
|
763
|
+
const headersToHide = (hidden.headers?.length ? hidden.headers : _RequestWatcher.DEFAULT_HIDDEN_HEADERS).map((header) => header.toLowerCase());
|
|
764
|
+
const bodyParamsToHide = hidden.bodyParams?.length ? hidden.bodyParams : _RequestWatcher.DEFAULT_HIDDEN_BODY_PARAMS;
|
|
765
|
+
const payload = {
|
|
629
766
|
id: data.request.id,
|
|
630
767
|
type: this.name,
|
|
631
768
|
minimal_data: {
|
|
@@ -639,9 +776,40 @@ var RequestWatcher = class extends Watcher {
|
|
|
639
776
|
data: {
|
|
640
777
|
...data.request,
|
|
641
778
|
user: data.user,
|
|
642
|
-
response: data.response
|
|
779
|
+
response: data.response || {}
|
|
643
780
|
}
|
|
644
|
-
}
|
|
781
|
+
};
|
|
782
|
+
payload.data.headers = this.hideSensitive(
|
|
783
|
+
this.normalizeHeaders(payload.data.headers),
|
|
784
|
+
headersToHide
|
|
785
|
+
);
|
|
786
|
+
payload.data.response.headers = this.hideSensitive(
|
|
787
|
+
this.normalizeHeaders(payload.data.response?.headers),
|
|
788
|
+
headersToHide
|
|
789
|
+
);
|
|
790
|
+
payload.data.body = this.hideSensitive(payload.data.body, bodyParamsToHide, false);
|
|
791
|
+
await getStore().save(payload);
|
|
792
|
+
}
|
|
793
|
+
/**
|
|
794
|
+
* Normalize headers: converts keys to lowercase.
|
|
795
|
+
*/
|
|
796
|
+
normalizeHeaders(headers) {
|
|
797
|
+
if (!headers || typeof headers !== "object") return {};
|
|
798
|
+
return Object.fromEntries(
|
|
799
|
+
Object.entries(headers).map(([k, v]) => [k.toLowerCase(), v])
|
|
800
|
+
);
|
|
801
|
+
}
|
|
802
|
+
hideSensitive(obj, keysToHide = [], isObjHeaders = true) {
|
|
803
|
+
if (!obj || typeof obj !== "object") {
|
|
804
|
+
return isObjHeaders ? {} : obj;
|
|
805
|
+
}
|
|
806
|
+
const clone = { ...obj };
|
|
807
|
+
for (const key of keysToHide) {
|
|
808
|
+
if (key in clone) {
|
|
809
|
+
clone[key] = "*******";
|
|
810
|
+
}
|
|
811
|
+
}
|
|
812
|
+
return clone;
|
|
645
813
|
}
|
|
646
814
|
};
|
|
647
815
|
|
|
@@ -871,6 +1039,7 @@ export {
|
|
|
871
1039
|
Store as LensStore,
|
|
872
1040
|
Watcher as LensWatcher,
|
|
873
1041
|
QueryWatcher,
|
|
1042
|
+
QueuedSqliteStore,
|
|
874
1043
|
RequestWatcher,
|
|
875
1044
|
WatcherTypeEnum,
|
|
876
1045
|
createEmittery,
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __create = Object.create;
|
|
3
|
+
var __defProp = Object.defineProperty;
|
|
4
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
5
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
6
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
7
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
8
|
+
var __export = (target, all) => {
|
|
9
|
+
for (var name in all)
|
|
10
|
+
__defProp(target, name, { get: all[name], enumerable: true });
|
|
11
|
+
};
|
|
12
|
+
var __copyProps = (to, from, except, desc) => {
|
|
13
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
14
|
+
for (let key of __getOwnPropNames(from))
|
|
15
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
16
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
17
|
+
}
|
|
18
|
+
return to;
|
|
19
|
+
};
|
|
20
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
21
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
22
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
23
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
24
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
25
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
26
|
+
mod
|
|
27
|
+
));
|
|
28
|
+
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
29
|
+
|
|
30
|
+
// src/mixins/queued_store.ts
|
|
31
|
+
var queued_store_exports = {};
|
|
32
|
+
__export(queued_store_exports, {
|
|
33
|
+
QueuedStore: () => QueuedStore
|
|
34
|
+
});
|
|
35
|
+
module.exports = __toCommonJS(queued_store_exports);
|
|
36
|
+
var import_denque = __toESM(require("denque"), 1);
|
|
37
|
+
function QueuedStore(Base) {
|
|
38
|
+
return class Queued extends Base {
|
|
39
|
+
queue;
|
|
40
|
+
processingInterval = null;
|
|
41
|
+
BATCH_SIZE;
|
|
42
|
+
PROCESS_INTERVAL_MS;
|
|
43
|
+
WARN_THRESHOLD;
|
|
44
|
+
PREALLOCATE;
|
|
45
|
+
constructor(...args) {
|
|
46
|
+
super(...args);
|
|
47
|
+
const config = args[0] || {};
|
|
48
|
+
this.storeConfig = config;
|
|
49
|
+
this.BATCH_SIZE = config.batchSize ?? 100;
|
|
50
|
+
this.PROCESS_INTERVAL_MS = config.processIntervalMs ?? 100;
|
|
51
|
+
this.WARN_THRESHOLD = config.warnThreshold ?? 1e5;
|
|
52
|
+
this.PREALLOCATE = config.preallocate ?? true;
|
|
53
|
+
this.queue = this.PREALLOCATE ? new import_denque.default([], { capacity: this.WARN_THRESHOLD * 2 }) : new import_denque.default();
|
|
54
|
+
}
|
|
55
|
+
async initialize() {
|
|
56
|
+
if (super["initialize"]) {
|
|
57
|
+
await super["initialize"].call(this);
|
|
58
|
+
}
|
|
59
|
+
this.startProcessingQueue();
|
|
60
|
+
process.on("SIGINT", () => this.shutdown());
|
|
61
|
+
process.on("SIGTERM", () => this.shutdown());
|
|
62
|
+
}
|
|
63
|
+
async truncate() {
|
|
64
|
+
this.queue.clear();
|
|
65
|
+
if (super["truncate"]) {
|
|
66
|
+
await super["truncate"].call(this);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
async save(entry) {
|
|
70
|
+
this.queue.push(entry);
|
|
71
|
+
if (this.queue.length > this.WARN_THRESHOLD) {
|
|
72
|
+
console.warn(`\u26A0\uFE0F LensJs Queue size very large: ${this.queue.length}`);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
startProcessingQueue() {
|
|
76
|
+
if (this.processingInterval) clearInterval(this.processingInterval);
|
|
77
|
+
this.processingInterval = setInterval(
|
|
78
|
+
() => this.processQueue(),
|
|
79
|
+
this.PROCESS_INTERVAL_MS
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
async processQueue() {
|
|
83
|
+
if (this.queue.isEmpty()) return;
|
|
84
|
+
const batchSize = Math.min(
|
|
85
|
+
this.BATCH_SIZE,
|
|
86
|
+
Math.max(10, Math.floor(this.queue.length / 10))
|
|
87
|
+
);
|
|
88
|
+
const entriesToProcess = this.queue.remove(0, batchSize);
|
|
89
|
+
if (!entriesToProcess.length) return;
|
|
90
|
+
for (const entry of entriesToProcess) {
|
|
91
|
+
super["save"]?.call(this, entry).catch((error) => {
|
|
92
|
+
console.error("Error saving queued entry:", error);
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
async stopProcessingQueue() {
|
|
97
|
+
if (this.processingInterval) {
|
|
98
|
+
clearInterval(this.processingInterval);
|
|
99
|
+
this.processingInterval = null;
|
|
100
|
+
}
|
|
101
|
+
if (!this.queue.isEmpty()) {
|
|
102
|
+
await this.processQueue();
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
async shutdown() {
|
|
106
|
+
await this.stopProcessingQueue();
|
|
107
|
+
process.exit(0);
|
|
108
|
+
}
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
// Annotate the CommonJS export names for ESM import in node:
|
|
112
|
+
0 && (module.exports = {
|
|
113
|
+
QueuedStore
|
|
114
|
+
});
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import denque__default from 'denque';
|
|
2
|
+
import { Constructor, WatcherTypeEnum } from '../types/index.cjs';
|
|
3
|
+
import 'sql-formatter';
|
|
4
|
+
|
|
5
|
+
declare function QueuedStore<TBase extends Constructor>(Base: TBase): {
|
|
6
|
+
new (...args: any[]): {
|
|
7
|
+
[x: string]: any;
|
|
8
|
+
queue: denque__default<{
|
|
9
|
+
id?: string;
|
|
10
|
+
data: Record<string, any>;
|
|
11
|
+
minimal_data?: Record<string, any>;
|
|
12
|
+
type: WatcherTypeEnum;
|
|
13
|
+
timestamp?: string;
|
|
14
|
+
requestId?: string;
|
|
15
|
+
}>;
|
|
16
|
+
processingInterval: NodeJS.Timeout | null;
|
|
17
|
+
readonly BATCH_SIZE: number;
|
|
18
|
+
readonly PROCESS_INTERVAL_MS: number;
|
|
19
|
+
readonly WARN_THRESHOLD: number;
|
|
20
|
+
readonly PREALLOCATE: boolean;
|
|
21
|
+
initialize(): Promise<void>;
|
|
22
|
+
truncate(): Promise<void>;
|
|
23
|
+
save(entry: {
|
|
24
|
+
id?: string;
|
|
25
|
+
data: Record<string, any>;
|
|
26
|
+
minimal_data?: Record<string, any>;
|
|
27
|
+
type: WatcherTypeEnum;
|
|
28
|
+
timestamp?: string;
|
|
29
|
+
requestId?: string;
|
|
30
|
+
}): Promise<void>;
|
|
31
|
+
startProcessingQueue(): void;
|
|
32
|
+
processQueue(): Promise<void>;
|
|
33
|
+
stopProcessingQueue(): Promise<void>;
|
|
34
|
+
shutdown(): Promise<void>;
|
|
35
|
+
};
|
|
36
|
+
} & TBase;
|
|
37
|
+
|
|
38
|
+
export { QueuedStore };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import denque__default from 'denque';
|
|
2
|
+
import { Constructor, WatcherTypeEnum } from '../types/index.js';
|
|
3
|
+
import 'sql-formatter';
|
|
4
|
+
|
|
5
|
+
declare function QueuedStore<TBase extends Constructor>(Base: TBase): {
|
|
6
|
+
new (...args: any[]): {
|
|
7
|
+
[x: string]: any;
|
|
8
|
+
queue: denque__default<{
|
|
9
|
+
id?: string;
|
|
10
|
+
data: Record<string, any>;
|
|
11
|
+
minimal_data?: Record<string, any>;
|
|
12
|
+
type: WatcherTypeEnum;
|
|
13
|
+
timestamp?: string;
|
|
14
|
+
requestId?: string;
|
|
15
|
+
}>;
|
|
16
|
+
processingInterval: NodeJS.Timeout | null;
|
|
17
|
+
readonly BATCH_SIZE: number;
|
|
18
|
+
readonly PROCESS_INTERVAL_MS: number;
|
|
19
|
+
readonly WARN_THRESHOLD: number;
|
|
20
|
+
readonly PREALLOCATE: boolean;
|
|
21
|
+
initialize(): Promise<void>;
|
|
22
|
+
truncate(): Promise<void>;
|
|
23
|
+
save(entry: {
|
|
24
|
+
id?: string;
|
|
25
|
+
data: Record<string, any>;
|
|
26
|
+
minimal_data?: Record<string, any>;
|
|
27
|
+
type: WatcherTypeEnum;
|
|
28
|
+
timestamp?: string;
|
|
29
|
+
requestId?: string;
|
|
30
|
+
}): Promise<void>;
|
|
31
|
+
startProcessingQueue(): void;
|
|
32
|
+
processQueue(): Promise<void>;
|
|
33
|
+
stopProcessingQueue(): Promise<void>;
|
|
34
|
+
shutdown(): Promise<void>;
|
|
35
|
+
};
|
|
36
|
+
} & TBase;
|
|
37
|
+
|
|
38
|
+
export { QueuedStore };
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
// src/mixins/queued_store.ts
|
|
2
|
+
import Denque from "denque";
|
|
3
|
+
function QueuedStore(Base) {
|
|
4
|
+
return class Queued extends Base {
|
|
5
|
+
queue;
|
|
6
|
+
processingInterval = null;
|
|
7
|
+
BATCH_SIZE;
|
|
8
|
+
PROCESS_INTERVAL_MS;
|
|
9
|
+
WARN_THRESHOLD;
|
|
10
|
+
PREALLOCATE;
|
|
11
|
+
constructor(...args) {
|
|
12
|
+
super(...args);
|
|
13
|
+
const config = args[0] || {};
|
|
14
|
+
this.storeConfig = config;
|
|
15
|
+
this.BATCH_SIZE = config.batchSize ?? 100;
|
|
16
|
+
this.PROCESS_INTERVAL_MS = config.processIntervalMs ?? 100;
|
|
17
|
+
this.WARN_THRESHOLD = config.warnThreshold ?? 1e5;
|
|
18
|
+
this.PREALLOCATE = config.preallocate ?? true;
|
|
19
|
+
this.queue = this.PREALLOCATE ? new Denque([], { capacity: this.WARN_THRESHOLD * 2 }) : new Denque();
|
|
20
|
+
}
|
|
21
|
+
async initialize() {
|
|
22
|
+
if (super["initialize"]) {
|
|
23
|
+
await super["initialize"].call(this);
|
|
24
|
+
}
|
|
25
|
+
this.startProcessingQueue();
|
|
26
|
+
process.on("SIGINT", () => this.shutdown());
|
|
27
|
+
process.on("SIGTERM", () => this.shutdown());
|
|
28
|
+
}
|
|
29
|
+
async truncate() {
|
|
30
|
+
this.queue.clear();
|
|
31
|
+
if (super["truncate"]) {
|
|
32
|
+
await super["truncate"].call(this);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
async save(entry) {
|
|
36
|
+
this.queue.push(entry);
|
|
37
|
+
if (this.queue.length > this.WARN_THRESHOLD) {
|
|
38
|
+
console.warn(`\u26A0\uFE0F LensJs Queue size very large: ${this.queue.length}`);
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
startProcessingQueue() {
|
|
42
|
+
if (this.processingInterval) clearInterval(this.processingInterval);
|
|
43
|
+
this.processingInterval = setInterval(
|
|
44
|
+
() => this.processQueue(),
|
|
45
|
+
this.PROCESS_INTERVAL_MS
|
|
46
|
+
);
|
|
47
|
+
}
|
|
48
|
+
async processQueue() {
|
|
49
|
+
if (this.queue.isEmpty()) return;
|
|
50
|
+
const batchSize = Math.min(
|
|
51
|
+
this.BATCH_SIZE,
|
|
52
|
+
Math.max(10, Math.floor(this.queue.length / 10))
|
|
53
|
+
);
|
|
54
|
+
const entriesToProcess = this.queue.remove(0, batchSize);
|
|
55
|
+
if (!entriesToProcess.length) return;
|
|
56
|
+
for (const entry of entriesToProcess) {
|
|
57
|
+
super["save"]?.call(this, entry).catch((error) => {
|
|
58
|
+
console.error("Error saving queued entry:", error);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
async stopProcessingQueue() {
|
|
63
|
+
if (this.processingInterval) {
|
|
64
|
+
clearInterval(this.processingInterval);
|
|
65
|
+
this.processingInterval = null;
|
|
66
|
+
}
|
|
67
|
+
if (!this.queue.isEmpty()) {
|
|
68
|
+
await this.processQueue();
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
async shutdown() {
|
|
72
|
+
await this.stopProcessingQueue();
|
|
73
|
+
process.exit(0);
|
|
74
|
+
}
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
export {
|
|
78
|
+
QueuedStore
|
|
79
|
+
};
|