@oxy.so/core 1.1.1 → 1.1.2

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.
@@ -118,10 +118,7 @@ export declare function createCache<T>(ttl?: number): TTLCache<T>;
118
118
  * @param cache Cache instance to register
119
119
  */
120
120
  export declare function registerCacheForCleanup(cache: TTLCache<any>): void;
121
- /**
122
- * Unregister a cache from automatic cleanup
123
- * @param cache Cache instance to unregister
124
- */
121
+ /** Unregister a cache without changing its entries or TTL behavior. */
125
122
  export declare function unregisterCacheFromCleanup(cache: TTLCache<any>): void;
126
123
  /**
127
124
  * Stop all cleanup intervals (useful for testing)
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@oxy.so/core",
3
- "version": "1.1.1",
3
+ "version": "1.1.2",
4
4
  "description": "OxyHQ SDK Foundation — API client, authentication, cryptographic identity, and shared utilities",
5
5
  "main": "dist/cjs/index.js",
6
6
  "module": "dist/esm/index.js",
@@ -0,0 +1,65 @@
1
+ import { TTLCache, registerCacheForCleanup, unregisterCacheFromCleanup, stopAllCleanupIntervals } from '../cache';
2
+
3
+ afterEach(() => { stopAllCleanupIntervals(); jest.useRealTimers(); });
4
+
5
+ test('empty registration is inert and first data starts one shared cleanup timer', () => {
6
+ jest.useFakeTimers();
7
+ const a = new TTLCache<string>(10);
8
+ const b = new TTLCache<string>(10);
9
+ registerCacheForCleanup(a);
10
+ registerCacheForCleanup(b);
11
+ expect(jest.getTimerCount()).toBe(0);
12
+ a.set('a', 'value');
13
+ b.set('b', 'value');
14
+ expect(jest.getTimerCount()).toBe(1);
15
+ jest.advanceTimersByTime(11);
16
+ expect(a.get('a')).toBeNull();
17
+ expect(b.has('b')).toBe(false);
18
+ jest.advanceTimersByTime(60_000);
19
+ expect(jest.getTimerCount()).toBe(0);
20
+ });
21
+ test('periodic cleanup expires unread entries and clear restarts lazily without losing registration', () => {
22
+ jest.useFakeTimers();
23
+ const a = new TTLCache<string>(10);
24
+ registerCacheForCleanup(a);
25
+ a.set('expired', 'value');
26
+ jest.advanceTimersByTime(60_000);
27
+ expect(a.size()).toBe(0);
28
+ expect(jest.getTimerCount()).toBe(0);
29
+ a.set('again', 'value');
30
+ expect(jest.getTimerCount()).toBe(1);
31
+ a.clear();
32
+ expect(jest.getTimerCount()).toBe(0);
33
+ a.set('after-clear', 'value');
34
+ expect(jest.getTimerCount()).toBe(1);
35
+ });
36
+ test('unregister and stop preserve entries but prevent writes restarting automatic cleanup', () => {
37
+ jest.useFakeTimers();
38
+ const a = new TTLCache<string>(100_000);
39
+ const b = new TTLCache<string>(100_000);
40
+ a.set('a', 'value');
41
+ registerCacheForCleanup(a);
42
+ registerCacheForCleanup(b);
43
+ b.set('b', 'value');
44
+ unregisterCacheFromCleanup(a);
45
+ expect(jest.getTimerCount()).toBe(1);
46
+ b.delete('b');
47
+ expect(jest.getTimerCount()).toBe(0);
48
+ a.set('unregistered', 'value');
49
+ expect(jest.getTimerCount()).toBe(0);
50
+ registerCacheForCleanup(a);
51
+ expect(jest.getTimerCount()).toBe(1);
52
+ stopAllCleanupIntervals();
53
+ a.set('stopped', 'value');
54
+ expect(jest.getTimerCount()).toBe(0);
55
+ expect(a.get('a')).toBe('value');
56
+ });
57
+ test('server barrel imports and client construction start no background timers', () => {
58
+ jest.useFakeTimers();
59
+ jest.isolateModules(() => {
60
+ const server = require('../../server');
61
+ expect(typeof server.createEcosystemTraffic).toBe('function');
62
+ expect(jest.getTimerCount()).toBe(0);
63
+ require('../cache').stopAllCleanupIntervals();
64
+ });
65
+ });
@@ -104,6 +104,7 @@ export class TTLCache<T> {
104
104
  const effectiveTTL = ttl === undefined ? this.defaultTTL : ttl;
105
105
  const expiresAt = now + effectiveTTL;
106
106
  this.cache.set(key, { data, timestamp: now, expiresAt });
107
+ if (!cleanupInterval && activeCaches.has(this)) updateCleanupInterval();
107
108
  }
108
109
 
109
110
  /**
@@ -112,7 +113,9 @@ export class TTLCache<T> {
112
113
  * @returns true if entry was deleted, false if not found
113
114
  */
114
115
  delete(key: string): boolean {
115
- return this.cache.delete(key);
116
+ const deleted = this.cache.delete(key);
117
+ if (activeCaches.has(this)) updateCleanupInterval();
118
+ return deleted;
116
119
  }
117
120
 
118
121
  /**
@@ -122,6 +125,7 @@ export class TTLCache<T> {
122
125
  this.cache.clear();
123
126
  this.hits = 0;
124
127
  this.misses = 0;
128
+ if (activeCaches.has(this)) updateCleanupInterval();
125
129
  }
126
130
 
127
131
  /**
@@ -232,31 +236,29 @@ const activeCaches = new Set<TTLCache<any>>();
232
236
  */
233
237
  export function registerCacheForCleanup(cache: TTLCache<any>): void {
234
238
  activeCaches.add(cache);
239
+ updateCleanupInterval();
240
+ }
235
241
 
236
- // Start cleanup interval if not already running
237
- if (!cleanupInterval) {
238
- cleanupInterval = setInterval(() => {
239
- for (const cache of activeCaches) {
240
- cache.cleanup();
241
- }
242
- }, 60000); // Every minute
243
- // Never block process/worker exit on a housekeeping timer.
244
- cleanupInterval.unref?.();
242
+ /** Empty registered caches need no timer; importing a client must stay inert. */
243
+ function updateCleanupInterval(): void {
244
+ const hasEntries = [...activeCaches].some(cache => cache.size() > 0);
245
+ if (!hasEntries) {
246
+ if (cleanupInterval) clearInterval(cleanupInterval);
247
+ cleanupInterval = null;
248
+ return;
245
249
  }
250
+ if (cleanupInterval) return;
251
+ cleanupInterval = setInterval(() => {
252
+ for (const cache of activeCaches) cache.cleanup();
253
+ updateCleanupInterval();
254
+ }, 60_000);
255
+ cleanupInterval.unref?.();
246
256
  }
247
257
 
248
- /**
249
- * Unregister a cache from automatic cleanup
250
- * @param cache Cache instance to unregister
251
- */
258
+ /** Unregister a cache without changing its entries or TTL behavior. */
252
259
  export function unregisterCacheFromCleanup(cache: TTLCache<any>): void {
253
260
  activeCaches.delete(cache);
254
-
255
- // Stop cleanup interval if no caches are registered
256
- if (activeCaches.size === 0 && cleanupInterval) {
257
- clearInterval(cleanupInterval);
258
- cleanupInterval = null;
259
- }
261
+ updateCleanupInterval();
260
262
  }
261
263
 
262
264
  /**