@getcodesentinel/codesentinel 1.17.2 → 1.17.3

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/index.js CHANGED
@@ -703,6 +703,14 @@ var DEFAULT_OPTIONS = {
703
703
  maxEntryBytes: 4 * 1024 * 1024,
704
704
  sweepIntervalWrites: 25
705
705
  };
706
+ var DEFAULT_BUCKET = "default";
707
+ var normalizeBucket = (value) => {
708
+ const trimmed = value.trim().toLowerCase();
709
+ if (trimmed.length === 0) {
710
+ return DEFAULT_BUCKET;
711
+ }
712
+ return trimmed.replace(/[^a-z0-9_-]/g, "_");
713
+ };
706
714
  var FileCacheStore = class {
707
715
  constructor(directoryPath, options = DEFAULT_OPTIONS) {
708
716
  this.directoryPath = directoryPath;
@@ -712,13 +720,22 @@ var FileCacheStore = class {
712
720
  inFlightWrites = /* @__PURE__ */ new Map();
713
721
  writesSinceSweep = 0;
714
722
  sweepPromise = Promise.resolve();
723
+ bucketForKey(key) {
724
+ const configured = this.options.bucketForKey?.(key);
725
+ if (configured === void 0) {
726
+ return DEFAULT_BUCKET;
727
+ }
728
+ return normalizeBucket(configured);
729
+ }
715
730
  toEntryPath(key) {
731
+ const bucket = this.bucketForKey(key);
716
732
  const digest = createHash("sha256").update(key).digest("hex");
717
- return join3(this.directoryPath, `${digest}.json`);
733
+ return join3(this.directoryPath, bucket, `${digest}.json`);
718
734
  }
719
735
  async writeEntry(key, entry) {
720
736
  const filePath = this.toEntryPath(key);
721
- await mkdir(this.directoryPath, { recursive: true });
737
+ const bucketPath = join3(this.directoryPath, this.bucketForKey(key));
738
+ await mkdir(bucketPath, { recursive: true });
722
739
  const tempPath = `${filePath}.tmp`;
723
740
  const payload = {
724
741
  key,
@@ -745,34 +762,74 @@ var FileCacheStore = class {
745
762
  await this.sweepPromise;
746
763
  }
747
764
  async evictToSizeLimit() {
765
+ const nowMs = Date.now();
748
766
  let entries;
749
767
  try {
750
- const dirEntries = await readdir(this.directoryPath, { withFileTypes: true });
768
+ const bucketEntries = await readdir(this.directoryPath, { withFileTypes: true });
769
+ const bucketNames = bucketEntries.filter((entry) => entry.isDirectory()).map((entry) => entry.name);
751
770
  entries = (await Promise.all(
752
- dirEntries.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map(async (entry) => {
753
- const path = join3(this.directoryPath, entry.name);
754
- const info = await stat(path);
755
- return { path, size: info.size, mtimeMs: info.mtimeMs };
771
+ bucketNames.map(async (bucket) => {
772
+ const bucketPath = join3(this.directoryPath, bucket);
773
+ const files = await readdir(bucketPath, { withFileTypes: true });
774
+ return await Promise.all(
775
+ files.filter((entry) => entry.isFile() && entry.name.endsWith(".json")).map(async (entry) => {
776
+ const path = join3(bucketPath, entry.name);
777
+ const info = await stat(path);
778
+ return {
779
+ path,
780
+ size: info.size,
781
+ mtimeMs: info.mtimeMs,
782
+ bucket
783
+ };
784
+ })
785
+ );
756
786
  })
757
- )).filter((entry) => Number.isFinite(entry.size) && entry.size > 0);
787
+ )).flat().filter((entry) => Number.isFinite(entry.size) && entry.size > 0);
758
788
  } catch {
759
789
  return;
760
790
  }
791
+ if (this.options.maxAgeMsByBucket !== void 0) {
792
+ const retained = [];
793
+ let deletedAny2 = false;
794
+ for (const entry of entries) {
795
+ const maxAgeMs = this.options.maxAgeMsByBucket[entry.bucket];
796
+ const expired = typeof maxAgeMs === "number" && Number.isFinite(maxAgeMs) && nowMs - entry.mtimeMs > maxAgeMs;
797
+ if (!expired) {
798
+ retained.push(entry);
799
+ continue;
800
+ }
801
+ try {
802
+ await unlink(entry.path);
803
+ deletedAny2 = true;
804
+ } catch {
805
+ retained.push(entry);
806
+ }
807
+ }
808
+ entries = retained;
809
+ if (deletedAny2) {
810
+ this.byKey.clear();
811
+ }
812
+ }
761
813
  let totalBytes = entries.reduce((sum, entry) => sum + entry.size, 0);
762
814
  if (totalBytes <= this.options.maxBytes) {
763
815
  return;
764
816
  }
765
817
  entries.sort((a, b) => a.mtimeMs - b.mtimeMs);
818
+ let deletedAny = false;
766
819
  for (const entry of entries) {
767
820
  if (totalBytes <= this.options.maxBytes) {
768
821
  break;
769
822
  }
770
823
  try {
771
824
  await unlink(entry.path);
825
+ deletedAny = true;
772
826
  totalBytes -= entry.size;
773
827
  } catch {
774
828
  }
775
829
  }
830
+ if (deletedAny) {
831
+ this.byKey.clear();
832
+ }
776
833
  }
777
834
  async get(key) {
778
835
  if (this.byKey.has(key)) {
@@ -808,7 +865,7 @@ var FileCacheStore = class {
808
865
  };
809
866
  var SIX_HOURS_MS = 6 * 60 * 60 * 1e3;
810
867
  var ONE_DAY_MS = 24 * 60 * 60 * 1e3;
811
- var DEFAULT_MAX_BYTES = 100 * 1024 * 1024;
868
+ var DEFAULT_MAX_BYTES = 20 * 1024 * 1024;
812
869
  var DEFAULT_MAX_ENTRY_BYTES = 4 * 1024 * 1024;
813
870
  var DEFAULT_SWEEP_INTERVAL_WRITES = 25;
814
871
  var cacheStoreSingleton;
@@ -835,6 +892,8 @@ var getNpmMetadataCacheStore = () => {
835
892
  return cacheStoreSingleton;
836
893
  }
837
894
  const path = join4(resolveCodesentinelCacheDir(process.env), "npm-metadata-v2");
895
+ const packumentTtlMs = getPackumentCacheTtlMs();
896
+ const downloadsTtlMs = getWeeklyDownloadsCacheTtlMs();
838
897
  cacheStoreSingleton = new FileCacheStore(path, {
839
898
  maxBytes: parsePositiveIntegerFromEnv(
840
899
  process.env["CODESENTINEL_CACHE_MAX_BYTES"],
@@ -847,7 +906,20 @@ var getNpmMetadataCacheStore = () => {
847
906
  sweepIntervalWrites: parsePositiveIntegerFromEnv(
848
907
  process.env["CODESENTINEL_CACHE_SWEEP_INTERVAL_WRITES"],
849
908
  DEFAULT_SWEEP_INTERVAL_WRITES
850
- )
909
+ ),
910
+ bucketForKey: (key) => {
911
+ if (key.startsWith("npm:downloads:last-week:")) {
912
+ return "downloads";
913
+ }
914
+ if (key.startsWith("npm:packument:")) {
915
+ return "packument";
916
+ }
917
+ return "default";
918
+ },
919
+ maxAgeMsByBucket: {
920
+ downloads: downloadsTtlMs,
921
+ packument: packumentTtlMs
922
+ }
851
923
  });
852
924
  return cacheStoreSingleton;
853
925
  };