@lensjs/core 2.2.1 → 2.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (88) hide show
  1. package/dist/abstracts/store.cjs +2 -0
  2. package/dist/abstracts/store.d.cts +1 -0
  3. package/dist/abstracts/store.d.ts +1 -0
  4. package/dist/abstracts/store.js +2 -0
  5. package/dist/core/lens.cjs +115 -23
  6. package/dist/core/lens.d.cts +3 -2
  7. package/dist/core/lens.d.ts +3 -2
  8. package/dist/core/lens.js +115 -23
  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 +163 -28
  12. package/dist/index.d.cts +5 -2
  13. package/dist/index.d.ts +5 -2
  14. package/dist/index.js +162 -28
  15. package/dist/mixins/queued_store.cjs +113 -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 +78 -0
  19. package/dist/stores/better_sqlite.cjs +2 -0
  20. package/dist/stores/better_sqlite.js +2 -0
  21. package/dist/stores/index.cjs +94 -2
  22. package/dist/stores/index.d.cts +2 -0
  23. package/dist/stores/index.d.ts +2 -0
  24. package/dist/stores/index.js +92 -1
  25. package/dist/stores/queued_sqlite.cjs +278 -0
  26. package/dist/stores/queued_sqlite.d.cts +43 -0
  27. package/dist/stores/queued_sqlite.d.ts +43 -0
  28. package/dist/stores/queued_sqlite.js +245 -0
  29. package/dist/types/index.d.cts +14 -2
  30. package/dist/types/index.d.ts +14 -2
  31. package/dist/ui/assets/{CacheActionBadge-WGVr5yhe.js → CacheActionBadge-BB4uokI1.js} +1 -1
  32. package/dist/ui/assets/CacheEntriesTable-B8cUXhos.js +1 -0
  33. package/dist/ui/assets/CacheEntryContainer-WkdnGvnu.js +2 -0
  34. package/dist/ui/assets/CacheEntryDetails-BeZnoIpm.js +1 -0
  35. package/dist/ui/assets/CacheEntryDetailsContainer-DI0mEvpu.js +2 -0
  36. package/dist/ui/assets/ExceptionContainer-YNcR0F5U.js +2 -0
  37. package/dist/ui/assets/{ExceptionDetails-DX14YGmx.js → ExceptionDetails-BKHzv6hf.js} +1 -1
  38. package/dist/ui/assets/ExceptionDetailsContainer-CJHILjb3.js +2 -0
  39. package/dist/ui/assets/{ExceptionTable-Chiah5kT.js → ExceptionTable-DzBmQLLa.js} +1 -1
  40. package/dist/ui/assets/JsonViewer-D-KPN089.js +1 -0
  41. package/dist/ui/assets/{LoadMore-CosGy1fp.js → LoadMore-CLPR6Zd4.js} +1 -1
  42. package/dist/ui/assets/QueriesContainer-B_PmBkHR.js +2 -0
  43. package/dist/ui/assets/{QueryDetailsContainer-Dimuidq4.js → QueryDetailsContainer-Cqj3E6Dr.js} +16 -26
  44. package/dist/ui/assets/{QueryTable-BLpmWt7g.js → QueryTable-DmWdZSnJ.js} +1 -1
  45. package/dist/ui/assets/{RequestDetails-DeJofxSW.js → RequestDetails-CF338Kcv.js} +1 -1
  46. package/dist/ui/assets/{RequestDetailsContainer-BtDTKq1O.js → RequestDetailsContainer-aW4GLool.js} +2 -2
  47. package/dist/ui/assets/{RequestsContainer-CxIavrPj.js → RequestsContainer-DdLSvAbl.js} +2 -2
  48. package/dist/ui/assets/{RequetsTable-CUevs9X4.js → RequetsTable-Bdp_PhGU.js} +1 -1
  49. package/dist/ui/assets/StatusCode-C605nHvd.js +1 -0
  50. package/dist/ui/assets/TabbedDataViewer-ofhEq_Wj.js +2 -0
  51. package/dist/ui/assets/{Table-z_QSS7J8.js → Table-kak5sL5X.js} +1 -1
  52. package/dist/ui/assets/{columns-Bxz-DZCj.js → columns-BEyDhUNq.js} +1 -1
  53. package/dist/ui/assets/{columns-D0UC1pqC.js → columns-Bu5psHyp.js} +1 -1
  54. package/dist/ui/assets/{columns-DJaOm2Yg.js → columns-BvIUTkjN.js} +1 -1
  55. package/dist/ui/assets/copy-DzXuP4eO.js +11 -0
  56. package/dist/ui/assets/index-CsnKQ5Mh.css +1 -0
  57. package/dist/ui/assets/{index-CQJv-KV7.js → index-TW_-MgRG.js} +25 -25
  58. package/dist/ui/assets/{useCacheEntries-DWzLAzvE.js → useCacheEntries-Pvte_aNc.js} +1 -1
  59. package/dist/ui/assets/{useExceptions-BF0fHNek.js → useExceptions-P3cnURvN.js} +1 -1
  60. package/dist/ui/assets/{useLensApi-BEMg_OuP.js → useLensApi-BFdsfrzR.js} +1 -1
  61. package/dist/ui/assets/{useLoadMore-ClIkZ0Gw.js → useLoadMore-JCWak1Dg.js} +1 -1
  62. package/dist/ui/assets/{useQueries-C0vKfgzw.js → useQueries-CNquFtm0.js} +1 -1
  63. package/dist/ui/index.html +2 -2
  64. package/dist/utils/compose.cjs +32 -0
  65. package/dist/utils/compose.d.cts +11 -0
  66. package/dist/utils/compose.d.ts +11 -0
  67. package/dist/utils/compose.js +7 -0
  68. package/dist/utils/index.cjs +9 -0
  69. package/dist/utils/index.d.cts +3 -1
  70. package/dist/utils/index.d.ts +3 -1
  71. package/dist/utils/index.js +8 -0
  72. package/dist/watchers/index.cjs +45 -5
  73. package/dist/watchers/index.js +45 -5
  74. package/dist/watchers/request_watcher.cjs +45 -5
  75. package/dist/watchers/request_watcher.d.cts +12 -1
  76. package/dist/watchers/request_watcher.d.ts +12 -1
  77. package/dist/watchers/request_watcher.js +45 -5
  78. package/package.json +2 -1
  79. package/dist/ui/assets/CacheEntriesTable-txREHfFk.js +0 -1
  80. package/dist/ui/assets/CacheEntryContainer-BoeiVJN6.js +0 -2
  81. package/dist/ui/assets/CacheEntryDetails-DovbpPae.js +0 -1
  82. package/dist/ui/assets/CacheEntryDetailsContainer-DcmH8iqv.js +0 -2
  83. package/dist/ui/assets/ExceptionContainer-C0H0kKkb.js +0 -2
  84. package/dist/ui/assets/ExceptionDetailsContainer-CHAXdzwk.js +0 -2
  85. package/dist/ui/assets/QueriesContainer-CporgPyk.js +0 -2
  86. package/dist/ui/assets/StatusCode-D_bCVpUf.js +0 -1
  87. package/dist/ui/assets/TabbedDataViewer-CbZANxvH.js +0 -1
  88. package/dist/ui/assets/index-B2QCOgug.css +0 -1
package/dist/index.cjs CHANGED
@@ -38,6 +38,7 @@ __export(index_exports, {
38
38
  LensStore: () => Store,
39
39
  LensWatcher: () => Watcher,
40
40
  QueryWatcher: () => QueryWatcher,
41
+ QueuedSqliteStore: () => QueuedSqliteStore,
41
42
  RequestWatcher: () => RequestWatcher,
42
43
  WatcherTypeEnum: () => WatcherTypeEnum,
43
44
  createEmittery: () => createEmittery,
@@ -229,6 +230,8 @@ var path2 = __toESM(require("path"), 1);
229
230
 
230
231
  // src/abstracts/store.ts
231
232
  var Store = class {
233
+ constructor(...args) {
234
+ }
232
235
  getAllExceptions(_paginationParams) {
233
236
  return this.defaultMinimalPaginate();
234
237
  }
@@ -380,9 +383,98 @@ var BetterSqliteStore = class extends Store {
380
383
  }
381
384
  };
382
385
 
386
+ // src/mixins/queued_store.ts
387
+ var import_denque = __toESM(require("denque"), 1);
388
+ function QueuedStore(Base) {
389
+ return class Queued extends Base {
390
+ queue;
391
+ processingInterval = null;
392
+ BATCH_SIZE;
393
+ PROCESS_INTERVAL_MS;
394
+ WARN_THRESHOLD;
395
+ PREALLOCATE;
396
+ constructor(...args) {
397
+ super(...args);
398
+ const config = args[0] || {};
399
+ this.BATCH_SIZE = config.batchSize ?? 100;
400
+ this.PROCESS_INTERVAL_MS = config.processIntervalMs ?? 100;
401
+ this.WARN_THRESHOLD = config.warnThreshold ?? 1e5;
402
+ this.PREALLOCATE = config.preallocate ?? true;
403
+ this.queue = this.PREALLOCATE ? new import_denque.default([], { capacity: this.WARN_THRESHOLD * 2 }) : new import_denque.default();
404
+ }
405
+ async initialize() {
406
+ if (super["initialize"]) {
407
+ await super["initialize"].call(this);
408
+ }
409
+ this.startProcessingQueue();
410
+ process.on("SIGINT", () => this.shutdown());
411
+ process.on("SIGTERM", () => this.shutdown());
412
+ }
413
+ async truncate() {
414
+ this.queue.clear();
415
+ if (super["truncate"]) {
416
+ await super["truncate"].call(this);
417
+ }
418
+ }
419
+ async save(entry) {
420
+ this.queue.push(entry);
421
+ if (this.queue.length > this.WARN_THRESHOLD) {
422
+ console.warn(`\u26A0\uFE0F LensJs Queue size very large: ${this.queue.length}`);
423
+ }
424
+ }
425
+ startProcessingQueue() {
426
+ if (this.processingInterval) clearInterval(this.processingInterval);
427
+ this.processingInterval = setInterval(
428
+ () => this.processQueue(),
429
+ this.PROCESS_INTERVAL_MS
430
+ );
431
+ }
432
+ async processQueue() {
433
+ if (this.queue.isEmpty()) return;
434
+ const batchSize = Math.min(
435
+ this.BATCH_SIZE,
436
+ Math.max(10, Math.floor(this.queue.length / 10))
437
+ );
438
+ const entriesToProcess = this.queue.remove(0, batchSize);
439
+ if (!entriesToProcess.length) return;
440
+ for (const entry of entriesToProcess) {
441
+ super["save"]?.call(this, entry).catch((error) => {
442
+ console.error("Error saving queued entry:", error);
443
+ });
444
+ }
445
+ }
446
+ async stopProcessingQueue() {
447
+ if (this.processingInterval) {
448
+ clearInterval(this.processingInterval);
449
+ this.processingInterval = null;
450
+ }
451
+ if (!this.queue.isEmpty()) {
452
+ await this.processQueue();
453
+ }
454
+ }
455
+ async shutdown() {
456
+ await this.stopProcessingQueue();
457
+ process.exit(0);
458
+ }
459
+ };
460
+ }
461
+
462
+ // src/utils/compose.ts
463
+ function compose(superclass, ...mixins) {
464
+ return mixins.reduce((c, mixin) => mixin(c), superclass);
465
+ }
466
+
467
+ // src/stores/queued_sqlite.ts
468
+ var QueuedSqliteStore = class extends compose(
469
+ BetterSqliteStore,
470
+ QueuedStore
471
+ ) {
472
+ };
473
+
383
474
  // src/utils/index.ts
384
475
  var utils_exports = {};
385
476
  __export(utils_exports, {
477
+ compose: () => compose,
386
478
  formatSqlQuery: () => formatSqlQuery,
387
479
  generateRandomUuid: () => generateRandomUuid,
388
480
  getMeta: () => getMeta,
@@ -523,6 +615,7 @@ var Lens = class {
523
615
  static watchers = /* @__PURE__ */ new Map();
524
616
  static store;
525
617
  static adapter;
618
+ static config;
526
619
  static watch(watcher) {
527
620
  this.watchers.set(watcher.name, watcher);
528
621
  return this;
@@ -532,22 +625,23 @@ var Lens = class {
532
625
  return this;
533
626
  }
534
627
  static async start(config = {
535
- basePath: "lens",
628
+ path: "lens",
536
629
  appName: "Lens",
537
630
  enabled: true
538
631
  }) {
539
632
  if (!config.enabled) {
540
633
  return;
541
634
  }
542
- await this.bindContainerDeps(config);
635
+ this.config = config;
636
+ await this.bindContainerDeps();
543
637
  let adapter = this.getAdapter();
544
638
  adapter.setWatchers(Array.from(this.watchers.values())).setup();
545
639
  const { apiRoutes } = this.getRoutes({
546
- basePath: config.basePath
640
+ path: config.path
547
641
  });
548
642
  adapter.registerRoutes(apiRoutes);
549
643
  const uiPath = this.getUiPath();
550
- adapter.serveUI(uiPath, config.basePath, getUiConfig());
644
+ adapter.serveUI(uiPath, config.path, getUiConfig());
551
645
  }
552
646
  static setStore(store) {
553
647
  this.store = store;
@@ -570,7 +664,7 @@ var Lens = class {
570
664
  const { __dirname: __dirname2 } = getMeta(import_meta.url);
571
665
  return path2.resolve(this.normalizeDirName(__dirname2), "ui");
572
666
  }
573
- static getRoutes({ basePath }) {
667
+ static getRoutes({ path: path4 }) {
574
668
  const apiRoutes = [
575
669
  {
576
670
  method: "GET",
@@ -579,71 +673,71 @@ var Lens = class {
579
673
  },
580
674
  {
581
675
  method: "GET",
582
- path: `/${basePath}/api/requests`,
676
+ path: `/${path4}/api/requests`,
583
677
  handler: async (data) => await ApiController.getRequests(data)
584
678
  },
585
679
  {
586
680
  method: "GET",
587
- path: `/${basePath}/api/requests/:id`,
681
+ path: `/${path4}/api/requests/:id`,
588
682
  handler: async (data) => await ApiController.getRequest(data)
589
683
  },
590
684
  {
591
685
  method: "GET",
592
- path: `/${basePath}/api/queries`,
686
+ path: `/${path4}/api/queries`,
593
687
  handler: async (data) => await ApiController.getQueries(data)
594
688
  },
595
689
  {
596
690
  method: "GET",
597
- path: `/${basePath}/api/queries/:id`,
691
+ path: `/${path4}/api/queries/:id`,
598
692
  handler: async (data) => await ApiController.getQuery(data)
599
693
  },
600
694
  {
601
695
  method: "GET",
602
- path: `/${basePath}/api/cache`,
696
+ path: `/${path4}/api/cache`,
603
697
  handler: async (data) => await ApiController.getCacheEntries(data)
604
698
  },
605
699
  {
606
700
  method: "GET",
607
- path: `/${basePath}/api/cache/:id`,
701
+ path: `/${path4}/api/cache/:id`,
608
702
  handler: async (data) => await ApiController.getCacheEntry(data)
609
703
  },
610
704
  {
611
705
  method: "GET",
612
- path: `/${basePath}/api/exceptions`,
706
+ path: `/${path4}/api/exceptions`,
613
707
  handler: async (data) => await ApiController.getExceptions(data)
614
708
  },
615
709
  {
616
710
  method: "GET",
617
- path: `/${basePath}/api/exceptions/:id`,
711
+ path: `/${path4}/api/exceptions/:id`,
618
712
  handler: async (data) => await ApiController.getException(data)
619
713
  },
620
714
  {
621
715
  method: "DELETE",
622
- path: `/${basePath}/api/truncate`,
716
+ path: `/${path4}/api/truncate`,
623
717
  handler: async () => await ApiController.truncate()
624
718
  }
625
719
  ];
626
720
  return { apiRoutes };
627
721
  }
628
- static async bindContainerDeps(config) {
722
+ static async bindContainerDeps() {
629
723
  const dbStore = await this.getStore();
630
724
  Container.singleton("store", () => dbStore);
631
725
  Container.singleton("uiConfig", () => {
632
726
  return {
633
- appName: config.appName,
634
- path: `/${config.basePath}`,
727
+ appName: this.config.appName,
728
+ path: `/${this.config.path}`,
635
729
  api: {
636
- requests: `/${config.basePath}/api/requests`,
637
- queries: `/${config.basePath}/api/queries`,
638
- cache: `/${config.basePath}/api/cache`,
639
- exceptions: `/${config.basePath}/api/exceptions`,
640
- truncate: `/${config.basePath}/api/truncate`
730
+ requests: `/${this.config.path}/api/requests`,
731
+ queries: `/${this.config.path}/api/queries`,
732
+ cache: `/${this.config.path}/api/cache`,
733
+ exceptions: `/${this.config.path}/api/exceptions`,
734
+ truncate: `/${this.config.path}/api/truncate`
641
735
  }
642
736
  };
643
737
  });
644
738
  }
645
739
  static async getDefaultStore() {
646
- const store = new BetterSqliteStore();
740
+ const store = new QueuedSqliteStore(this.config.storeQueueConfig);
647
741
  await store.initialize();
648
742
  return store;
649
743
  }
@@ -669,10 +763,19 @@ var QueryWatcher = class extends Watcher {
669
763
  };
670
764
 
671
765
  // src/watchers/request_watcher.ts
672
- var RequestWatcher = class extends Watcher {
766
+ var RequestWatcher = class _RequestWatcher extends Watcher {
673
767
  name = "request" /* REQUEST */;
674
- async log(data) {
675
- await getStore().save({
768
+ static DEFAULT_HIDDEN_HEADERS = ["Authorization", "Basic"];
769
+ static DEFAULT_HIDDEN_BODY_PARAMS = [
770
+ "password",
771
+ "passwordConfirmation",
772
+ "secret",
773
+ "password_confirmation"
774
+ ];
775
+ async log(data, hidden = {}) {
776
+ const headersToHide = (hidden.headers?.length ? hidden.headers : _RequestWatcher.DEFAULT_HIDDEN_HEADERS).map((header) => header.toLowerCase());
777
+ const bodyParamsToHide = hidden.bodyParams?.length ? hidden.bodyParams : _RequestWatcher.DEFAULT_HIDDEN_BODY_PARAMS;
778
+ const payload = {
676
779
  id: data.request.id,
677
780
  type: this.name,
678
781
  minimal_data: {
@@ -686,9 +789,40 @@ var RequestWatcher = class extends Watcher {
686
789
  data: {
687
790
  ...data.request,
688
791
  user: data.user,
689
- response: data.response
792
+ response: data.response || {}
690
793
  }
691
- });
794
+ };
795
+ payload.data.headers = this.hideSensitive(
796
+ this.normalizeHeaders(payload.data.headers),
797
+ headersToHide
798
+ );
799
+ payload.data.response.headers = this.hideSensitive(
800
+ this.normalizeHeaders(payload.data.response?.headers),
801
+ headersToHide
802
+ );
803
+ payload.data.body = this.hideSensitive(payload.data.body, bodyParamsToHide, false);
804
+ await getStore().save(payload);
805
+ }
806
+ /**
807
+ * Normalize headers: converts keys to lowercase.
808
+ */
809
+ normalizeHeaders(headers) {
810
+ if (!headers || typeof headers !== "object") return {};
811
+ return Object.fromEntries(
812
+ Object.entries(headers).map(([k, v]) => [k.toLowerCase(), v])
813
+ );
814
+ }
815
+ hideSensitive(obj, keysToHide = [], isObjHeaders = true) {
816
+ if (!obj || typeof obj !== "object") {
817
+ return isObjHeaders ? {} : obj;
818
+ }
819
+ const clone = { ...obj };
820
+ for (const key of keysToHide) {
821
+ if (key in clone) {
822
+ clone[key] = "*******";
823
+ }
824
+ }
825
+ return clone;
692
826
  }
693
827
  };
694
828
 
@@ -919,6 +1053,7 @@ var handleUncaughExceptions = (logger) => {
919
1053
  LensStore,
920
1054
  LensWatcher,
921
1055
  QueryWatcher,
1056
+ QueuedSqliteStore,
922
1057
  RequestWatcher,
923
1058
  WatcherTypeEnum,
924
1059
  createEmittery,
package/dist/index.d.cts CHANGED
@@ -1,19 +1,22 @@
1
1
  export { default as Lens } from './core/lens.cjs';
2
2
  export { default as BetterSqliteStore } from './stores/better_sqlite.cjs';
3
+ export { default as QueuedSqliteStore } from './stores/queued_sqlite.cjs';
3
4
  export { default as QueryWatcher } from './watchers/query_watcher.cjs';
4
5
  export { default as RequestWatcher } from './watchers/request_watcher.cjs';
5
6
  export { default as CacheWatcher } from './watchers/cache_watcher.cjs';
6
7
  export { default as ExceptionWatcher } from './watchers/exception_watcher.cjs';
7
- export { ApiResponse, CacheAction, CacheEntry, Entry, ExceptionEntry, HttpMethod, LensConfig, LensEntry, PaginationParams, Paginator, QueryEntry, QueryType, RequestEntry, RouteDefinition, RouteDefinitionHandler, RouteHttpMethod, SqlQueryType, UserEntry, WatcherTypeEnum } from './types/index.cjs';
8
+ export { ApiResponse, CacheAction, CacheEntry, Constructor, Entry, ExceptionEntry, HttpMethod, LensConfig, LensEntry, PaginationParams, Paginator, QueryEntry, QueryType, QueuedStoreConfig, RequestEntry, RouteDefinition, RouteDefinitionHandler, RouteHttpMethod, SqlQueryType, UserEntry, WatcherTypeEnum } from './types/index.cjs';
8
9
  export { default as LensAdapter } from './abstracts/adapter.cjs';
9
10
  export { default as LensStore } from './abstracts/store.cjs';
10
11
  export { default as LensWatcher } from './core/watcher.cjs';
11
12
  export { getStore as getLensStore } from './context/context.cjs';
12
- export { i as lensUtils } from './index-CMvlRWcQ.cjs';
13
+ export { i as lensUtils } from './index-QmOJr0K-.cjs';
13
14
  export { e as lensExceptionUtils } from './exception-C69UCHPk.cjs';
14
15
  export { createEmittery, lensEmitter } from './utils/event_emitter.cjs';
15
16
  export { handleUncaughExceptions, lensContext } from './utils/async_context.cjs';
16
17
  import 'libsql';
18
+ import 'denque';
17
19
  import 'sql-formatter';
20
+ import './utils/compose.cjs';
18
21
  import 'events';
19
22
  import 'async_hooks';
package/dist/index.d.ts CHANGED
@@ -1,19 +1,22 @@
1
1
  export { default as Lens } from './core/lens.js';
2
2
  export { default as BetterSqliteStore } from './stores/better_sqlite.js';
3
+ export { default as QueuedSqliteStore } from './stores/queued_sqlite.js';
3
4
  export { default as QueryWatcher } from './watchers/query_watcher.js';
4
5
  export { default as RequestWatcher } from './watchers/request_watcher.js';
5
6
  export { default as CacheWatcher } from './watchers/cache_watcher.js';
6
7
  export { default as ExceptionWatcher } from './watchers/exception_watcher.js';
7
- export { ApiResponse, CacheAction, CacheEntry, Entry, ExceptionEntry, HttpMethod, LensConfig, LensEntry, PaginationParams, Paginator, QueryEntry, QueryType, RequestEntry, RouteDefinition, RouteDefinitionHandler, RouteHttpMethod, SqlQueryType, UserEntry, WatcherTypeEnum } from './types/index.js';
8
+ export { ApiResponse, CacheAction, CacheEntry, Constructor, Entry, ExceptionEntry, HttpMethod, LensConfig, LensEntry, PaginationParams, Paginator, QueryEntry, QueryType, QueuedStoreConfig, RequestEntry, RouteDefinition, RouteDefinitionHandler, RouteHttpMethod, SqlQueryType, UserEntry, WatcherTypeEnum } from './types/index.js';
8
9
  export { default as LensAdapter } from './abstracts/adapter.js';
9
10
  export { default as LensStore } from './abstracts/store.js';
10
11
  export { default as LensWatcher } from './core/watcher.js';
11
12
  export { getStore as getLensStore } from './context/context.js';
12
- export { i as lensUtils } from './index-CMvlRWcQ.js';
13
+ export { i as lensUtils } from './index-CZsa0Zcm.js';
13
14
  export { e as lensExceptionUtils } from './exception-3AZsPtAg.js';
14
15
  export { createEmittery, lensEmitter } from './utils/event_emitter.js';
15
16
  export { handleUncaughExceptions, lensContext } from './utils/async_context.js';
16
17
  import 'libsql';
18
+ import 'denque';
17
19
  import 'sql-formatter';
20
+ import './utils/compose.js';
18
21
  import 'events';
19
22
  import 'async_hooks';
package/dist/index.js CHANGED
@@ -183,6 +183,8 @@ import * as path2 from "path";
183
183
 
184
184
  // src/abstracts/store.ts
185
185
  var Store = class {
186
+ constructor(...args) {
187
+ }
186
188
  getAllExceptions(_paginationParams) {
187
189
  return this.defaultMinimalPaginate();
188
190
  }
@@ -334,9 +336,98 @@ var BetterSqliteStore = class extends Store {
334
336
  }
335
337
  };
336
338
 
339
+ // src/mixins/queued_store.ts
340
+ import Denque from "denque";
341
+ function QueuedStore(Base) {
342
+ return class Queued extends Base {
343
+ queue;
344
+ processingInterval = null;
345
+ BATCH_SIZE;
346
+ PROCESS_INTERVAL_MS;
347
+ WARN_THRESHOLD;
348
+ PREALLOCATE;
349
+ constructor(...args) {
350
+ super(...args);
351
+ const config = args[0] || {};
352
+ this.BATCH_SIZE = config.batchSize ?? 100;
353
+ this.PROCESS_INTERVAL_MS = config.processIntervalMs ?? 100;
354
+ this.WARN_THRESHOLD = config.warnThreshold ?? 1e5;
355
+ this.PREALLOCATE = config.preallocate ?? true;
356
+ this.queue = this.PREALLOCATE ? new Denque([], { capacity: this.WARN_THRESHOLD * 2 }) : new Denque();
357
+ }
358
+ async initialize() {
359
+ if (super["initialize"]) {
360
+ await super["initialize"].call(this);
361
+ }
362
+ this.startProcessingQueue();
363
+ process.on("SIGINT", () => this.shutdown());
364
+ process.on("SIGTERM", () => this.shutdown());
365
+ }
366
+ async truncate() {
367
+ this.queue.clear();
368
+ if (super["truncate"]) {
369
+ await super["truncate"].call(this);
370
+ }
371
+ }
372
+ async save(entry) {
373
+ this.queue.push(entry);
374
+ if (this.queue.length > this.WARN_THRESHOLD) {
375
+ console.warn(`\u26A0\uFE0F LensJs Queue size very large: ${this.queue.length}`);
376
+ }
377
+ }
378
+ startProcessingQueue() {
379
+ if (this.processingInterval) clearInterval(this.processingInterval);
380
+ this.processingInterval = setInterval(
381
+ () => this.processQueue(),
382
+ this.PROCESS_INTERVAL_MS
383
+ );
384
+ }
385
+ async processQueue() {
386
+ if (this.queue.isEmpty()) return;
387
+ const batchSize = Math.min(
388
+ this.BATCH_SIZE,
389
+ Math.max(10, Math.floor(this.queue.length / 10))
390
+ );
391
+ const entriesToProcess = this.queue.remove(0, batchSize);
392
+ if (!entriesToProcess.length) return;
393
+ for (const entry of entriesToProcess) {
394
+ super["save"]?.call(this, entry).catch((error) => {
395
+ console.error("Error saving queued entry:", error);
396
+ });
397
+ }
398
+ }
399
+ async stopProcessingQueue() {
400
+ if (this.processingInterval) {
401
+ clearInterval(this.processingInterval);
402
+ this.processingInterval = null;
403
+ }
404
+ if (!this.queue.isEmpty()) {
405
+ await this.processQueue();
406
+ }
407
+ }
408
+ async shutdown() {
409
+ await this.stopProcessingQueue();
410
+ process.exit(0);
411
+ }
412
+ };
413
+ }
414
+
415
+ // src/utils/compose.ts
416
+ function compose(superclass, ...mixins) {
417
+ return mixins.reduce((c, mixin) => mixin(c), superclass);
418
+ }
419
+
420
+ // src/stores/queued_sqlite.ts
421
+ var QueuedSqliteStore = class extends compose(
422
+ BetterSqliteStore,
423
+ QueuedStore
424
+ ) {
425
+ };
426
+
337
427
  // src/utils/index.ts
338
428
  var utils_exports = {};
339
429
  __export(utils_exports, {
430
+ compose: () => compose,
340
431
  formatSqlQuery: () => formatSqlQuery,
341
432
  generateRandomUuid: () => generateRandomUuid,
342
433
  getMeta: () => getMeta,
@@ -476,6 +567,7 @@ var Lens = class {
476
567
  static watchers = /* @__PURE__ */ new Map();
477
568
  static store;
478
569
  static adapter;
570
+ static config;
479
571
  static watch(watcher) {
480
572
  this.watchers.set(watcher.name, watcher);
481
573
  return this;
@@ -485,22 +577,23 @@ var Lens = class {
485
577
  return this;
486
578
  }
487
579
  static async start(config = {
488
- basePath: "lens",
580
+ path: "lens",
489
581
  appName: "Lens",
490
582
  enabled: true
491
583
  }) {
492
584
  if (!config.enabled) {
493
585
  return;
494
586
  }
495
- await this.bindContainerDeps(config);
587
+ this.config = config;
588
+ await this.bindContainerDeps();
496
589
  let adapter = this.getAdapter();
497
590
  adapter.setWatchers(Array.from(this.watchers.values())).setup();
498
591
  const { apiRoutes } = this.getRoutes({
499
- basePath: config.basePath
592
+ path: config.path
500
593
  });
501
594
  adapter.registerRoutes(apiRoutes);
502
595
  const uiPath = this.getUiPath();
503
- adapter.serveUI(uiPath, config.basePath, getUiConfig());
596
+ adapter.serveUI(uiPath, config.path, getUiConfig());
504
597
  }
505
598
  static setStore(store) {
506
599
  this.store = store;
@@ -523,7 +616,7 @@ var Lens = class {
523
616
  const { __dirname: __dirname2 } = getMeta(import.meta.url);
524
617
  return path2.resolve(this.normalizeDirName(__dirname2), "ui");
525
618
  }
526
- static getRoutes({ basePath }) {
619
+ static getRoutes({ path: path4 }) {
527
620
  const apiRoutes = [
528
621
  {
529
622
  method: "GET",
@@ -532,71 +625,71 @@ var Lens = class {
532
625
  },
533
626
  {
534
627
  method: "GET",
535
- path: `/${basePath}/api/requests`,
628
+ path: `/${path4}/api/requests`,
536
629
  handler: async (data) => await ApiController.getRequests(data)
537
630
  },
538
631
  {
539
632
  method: "GET",
540
- path: `/${basePath}/api/requests/:id`,
633
+ path: `/${path4}/api/requests/:id`,
541
634
  handler: async (data) => await ApiController.getRequest(data)
542
635
  },
543
636
  {
544
637
  method: "GET",
545
- path: `/${basePath}/api/queries`,
638
+ path: `/${path4}/api/queries`,
546
639
  handler: async (data) => await ApiController.getQueries(data)
547
640
  },
548
641
  {
549
642
  method: "GET",
550
- path: `/${basePath}/api/queries/:id`,
643
+ path: `/${path4}/api/queries/:id`,
551
644
  handler: async (data) => await ApiController.getQuery(data)
552
645
  },
553
646
  {
554
647
  method: "GET",
555
- path: `/${basePath}/api/cache`,
648
+ path: `/${path4}/api/cache`,
556
649
  handler: async (data) => await ApiController.getCacheEntries(data)
557
650
  },
558
651
  {
559
652
  method: "GET",
560
- path: `/${basePath}/api/cache/:id`,
653
+ path: `/${path4}/api/cache/:id`,
561
654
  handler: async (data) => await ApiController.getCacheEntry(data)
562
655
  },
563
656
  {
564
657
  method: "GET",
565
- path: `/${basePath}/api/exceptions`,
658
+ path: `/${path4}/api/exceptions`,
566
659
  handler: async (data) => await ApiController.getExceptions(data)
567
660
  },
568
661
  {
569
662
  method: "GET",
570
- path: `/${basePath}/api/exceptions/:id`,
663
+ path: `/${path4}/api/exceptions/:id`,
571
664
  handler: async (data) => await ApiController.getException(data)
572
665
  },
573
666
  {
574
667
  method: "DELETE",
575
- path: `/${basePath}/api/truncate`,
668
+ path: `/${path4}/api/truncate`,
576
669
  handler: async () => await ApiController.truncate()
577
670
  }
578
671
  ];
579
672
  return { apiRoutes };
580
673
  }
581
- static async bindContainerDeps(config) {
674
+ static async bindContainerDeps() {
582
675
  const dbStore = await this.getStore();
583
676
  Container.singleton("store", () => dbStore);
584
677
  Container.singleton("uiConfig", () => {
585
678
  return {
586
- appName: config.appName,
587
- path: `/${config.basePath}`,
679
+ appName: this.config.appName,
680
+ path: `/${this.config.path}`,
588
681
  api: {
589
- requests: `/${config.basePath}/api/requests`,
590
- queries: `/${config.basePath}/api/queries`,
591
- cache: `/${config.basePath}/api/cache`,
592
- exceptions: `/${config.basePath}/api/exceptions`,
593
- truncate: `/${config.basePath}/api/truncate`
682
+ requests: `/${this.config.path}/api/requests`,
683
+ queries: `/${this.config.path}/api/queries`,
684
+ cache: `/${this.config.path}/api/cache`,
685
+ exceptions: `/${this.config.path}/api/exceptions`,
686
+ truncate: `/${this.config.path}/api/truncate`
594
687
  }
595
688
  };
596
689
  });
597
690
  }
598
691
  static async getDefaultStore() {
599
- const store = new BetterSqliteStore();
692
+ const store = new QueuedSqliteStore(this.config.storeQueueConfig);
600
693
  await store.initialize();
601
694
  return store;
602
695
  }
@@ -622,10 +715,19 @@ var QueryWatcher = class extends Watcher {
622
715
  };
623
716
 
624
717
  // src/watchers/request_watcher.ts
625
- var RequestWatcher = class extends Watcher {
718
+ var RequestWatcher = class _RequestWatcher extends Watcher {
626
719
  name = "request" /* REQUEST */;
627
- async log(data) {
628
- await getStore().save({
720
+ static DEFAULT_HIDDEN_HEADERS = ["Authorization", "Basic"];
721
+ static DEFAULT_HIDDEN_BODY_PARAMS = [
722
+ "password",
723
+ "passwordConfirmation",
724
+ "secret",
725
+ "password_confirmation"
726
+ ];
727
+ async log(data, hidden = {}) {
728
+ const headersToHide = (hidden.headers?.length ? hidden.headers : _RequestWatcher.DEFAULT_HIDDEN_HEADERS).map((header) => header.toLowerCase());
729
+ const bodyParamsToHide = hidden.bodyParams?.length ? hidden.bodyParams : _RequestWatcher.DEFAULT_HIDDEN_BODY_PARAMS;
730
+ const payload = {
629
731
  id: data.request.id,
630
732
  type: this.name,
631
733
  minimal_data: {
@@ -639,9 +741,40 @@ var RequestWatcher = class extends Watcher {
639
741
  data: {
640
742
  ...data.request,
641
743
  user: data.user,
642
- response: data.response
744
+ response: data.response || {}
643
745
  }
644
- });
746
+ };
747
+ payload.data.headers = this.hideSensitive(
748
+ this.normalizeHeaders(payload.data.headers),
749
+ headersToHide
750
+ );
751
+ payload.data.response.headers = this.hideSensitive(
752
+ this.normalizeHeaders(payload.data.response?.headers),
753
+ headersToHide
754
+ );
755
+ payload.data.body = this.hideSensitive(payload.data.body, bodyParamsToHide, false);
756
+ await getStore().save(payload);
757
+ }
758
+ /**
759
+ * Normalize headers: converts keys to lowercase.
760
+ */
761
+ normalizeHeaders(headers) {
762
+ if (!headers || typeof headers !== "object") return {};
763
+ return Object.fromEntries(
764
+ Object.entries(headers).map(([k, v]) => [k.toLowerCase(), v])
765
+ );
766
+ }
767
+ hideSensitive(obj, keysToHide = [], isObjHeaders = true) {
768
+ if (!obj || typeof obj !== "object") {
769
+ return isObjHeaders ? {} : obj;
770
+ }
771
+ const clone = { ...obj };
772
+ for (const key of keysToHide) {
773
+ if (key in clone) {
774
+ clone[key] = "*******";
775
+ }
776
+ }
777
+ return clone;
645
778
  }
646
779
  };
647
780
 
@@ -871,6 +1004,7 @@ export {
871
1004
  Store as LensStore,
872
1005
  Watcher as LensWatcher,
873
1006
  QueryWatcher,
1007
+ QueuedSqliteStore,
874
1008
  RequestWatcher,
875
1009
  WatcherTypeEnum,
876
1010
  createEmittery,