@ogxjs/core 0.1.2 → 0.2.0-alpha.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/cache/hash.d.ts +66 -0
- package/dist/cache/hash.d.ts.map +1 -0
- package/dist/cache/hash.js +161 -0
- package/dist/cache/index.d.ts +10 -0
- package/dist/cache/index.d.ts.map +1 -0
- package/dist/cache/index.js +12 -0
- package/dist/cache/lru.d.ts +122 -0
- package/dist/cache/lru.d.ts.map +1 -0
- package/dist/cache/lru.js +269 -0
- package/dist/cache/snapshot.d.ts +116 -0
- package/dist/cache/snapshot.d.ts.map +1 -0
- package/dist/cache/snapshot.js +204 -0
- package/dist/cache.d.ts +2 -2
- package/dist/cache.js +2 -2
- package/dist/css.d.ts +19 -6
- package/dist/css.d.ts.map +1 -1
- package/dist/index.d.ts +17 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +40 -9
- package/dist/ogx.js +2 -2
- package/dist/perf/index.d.ts +8 -0
- package/dist/perf/index.d.ts.map +1 -0
- package/dist/perf/index.js +7 -0
- package/dist/perf/timing.d.ts +160 -0
- package/dist/perf/timing.d.ts.map +1 -0
- package/dist/perf/timing.js +305 -0
- package/dist/presets/blog.js +1 -1
- package/dist/presets/docs.d.ts +2 -0
- package/dist/presets/docs.d.ts.map +1 -1
- package/dist/presets/docs.js +26 -23
- package/dist/presets/minimal.d.ts +2 -0
- package/dist/presets/minimal.d.ts.map +1 -1
- package/dist/presets/minimal.js +8 -16
- package/dist/presets/social.d.ts +2 -0
- package/dist/presets/social.d.ts.map +1 -1
- package/dist/presets/social.js +28 -18
- package/dist/render-png.d.ts.map +1 -1
- package/dist/render-png.js +9 -1
- package/dist/render-svg.d.ts.map +1 -1
- package/dist/render-svg.js +11 -1
- package/dist/tailwind/class-cache.d.ts +141 -0
- package/dist/tailwind/class-cache.d.ts.map +1 -0
- package/dist/tailwind/class-cache.js +212 -0
- package/dist/tailwind/index.d.ts +14 -1
- package/dist/tailwind/index.d.ts.map +1 -1
- package/dist/tailwind/index.js +15 -1
- package/dist/tailwind/lookup-tables.d.ts +30 -0
- package/dist/tailwind/lookup-tables.d.ts.map +1 -0
- package/dist/tailwind/lookup-tables.js +427 -0
- package/dist/tailwind/parser-v2.d.ts +54 -0
- package/dist/tailwind/parser-v2.d.ts.map +1 -0
- package/dist/tailwind/parser-v2.js +250 -0
- package/dist/tailwind/parser.d.ts +1 -0
- package/dist/tailwind/parser.d.ts.map +1 -1
- package/dist/tailwind/parser.js +1 -0
- package/dist/tailwind/prefix-handlers.d.ts +68 -0
- package/dist/tailwind/prefix-handlers.d.ts.map +1 -0
- package/dist/tailwind/prefix-handlers.js +931 -0
- package/package.json +17 -2
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ogxjs/core - Fast Hash Functions
|
|
3
|
+
* High-performance sync hashing for cache keys
|
|
4
|
+
*
|
|
5
|
+
* @description
|
|
6
|
+
* SHA-256 is secure but slow (~200μs per hash).
|
|
7
|
+
* For cache keys where collision resistance is less critical,
|
|
8
|
+
* we use faster algorithms:
|
|
9
|
+
* - FNV-1a: ~10μs per hash (20x faster)
|
|
10
|
+
* - xxHash-inspired: ~5μs per hash (40x faster)
|
|
11
|
+
*
|
|
12
|
+
* @version 0.2.0 "Turbo"
|
|
13
|
+
*/
|
|
14
|
+
/**
|
|
15
|
+
* FNV-1a hash algorithm (32-bit)
|
|
16
|
+
*
|
|
17
|
+
* Fast, simple, good distribution for short strings.
|
|
18
|
+
* Collision probability: ~1 in 4 billion for random inputs.
|
|
19
|
+
*
|
|
20
|
+
* @param str - String to hash
|
|
21
|
+
* @returns 32-bit hash as unsigned integer
|
|
22
|
+
*
|
|
23
|
+
* @example
|
|
24
|
+
* ```ts
|
|
25
|
+
* fnv1a("hello") // → 1335831723
|
|
26
|
+
* fnv1a("hello world") // → 3582672807
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export declare function fnv1a(str: string): number;
|
|
30
|
+
/**
|
|
31
|
+
* FNV-1a hash with hex string output
|
|
32
|
+
*
|
|
33
|
+
* @param str - String to hash
|
|
34
|
+
* @returns Hash as 8-character hex string
|
|
35
|
+
*/
|
|
36
|
+
export declare function fnv1aHex(str: string): string;
|
|
37
|
+
/**
|
|
38
|
+
* xxHash-inspired fast hash (32-bit)
|
|
39
|
+
*
|
|
40
|
+
* Even faster than FNV-1a for longer strings.
|
|
41
|
+
* Processes 4 bytes at a time when possible.
|
|
42
|
+
*
|
|
43
|
+
* @param str - String to hash
|
|
44
|
+
* @param seed - Optional seed value (default: 0)
|
|
45
|
+
* @returns 32-bit hash as unsigned integer
|
|
46
|
+
*/
|
|
47
|
+
export declare function fastHash(str: string, seed?: number): number;
|
|
48
|
+
/**
|
|
49
|
+
* Fast hash with hex string output
|
|
50
|
+
*/
|
|
51
|
+
export declare function fastHashHex(str: string, seed?: number): string;
|
|
52
|
+
/**
|
|
53
|
+
* Hash a configuration object
|
|
54
|
+
*
|
|
55
|
+
* Serializes the object to JSON and hashes it.
|
|
56
|
+
* Excludes functions and debug flags.
|
|
57
|
+
*
|
|
58
|
+
* @param obj - Object to hash
|
|
59
|
+
* @returns Hash string
|
|
60
|
+
*/
|
|
61
|
+
export declare function hashObject(obj: unknown): string;
|
|
62
|
+
/**
|
|
63
|
+
* Create a composite hash from multiple values
|
|
64
|
+
*/
|
|
65
|
+
export declare function hashComposite(...values: unknown[]): string;
|
|
66
|
+
//# sourceMappingURL=hash.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../src/cache/hash.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAIH;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAUzC;AAED;;;;;GAKG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAE5C;AAID;;;;;;;;;GASG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,SAAI,GAAG,MAAM,CA8DtD;AAED;;GAEG;AACH,wBAAgB,WAAW,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,SAAI,GAAG,MAAM,CAEzD;AA2BD;;;;;;;;GAQG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,GAAG,MAAM,CAQ/C;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,GAAG,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAM1D"}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ogxjs/core - Fast Hash Functions
|
|
3
|
+
* High-performance sync hashing for cache keys
|
|
4
|
+
*
|
|
5
|
+
* @description
|
|
6
|
+
* SHA-256 is secure but slow (~200μs per hash).
|
|
7
|
+
* For cache keys where collision resistance is less critical,
|
|
8
|
+
* we use faster algorithms:
|
|
9
|
+
* - FNV-1a: ~10μs per hash (20x faster)
|
|
10
|
+
* - xxHash-inspired: ~5μs per hash (40x faster)
|
|
11
|
+
*
|
|
12
|
+
* @version 0.2.0 "Turbo"
|
|
13
|
+
*/
|
|
14
|
+
// FNV-1A HASH
|
|
15
|
+
/**
|
|
16
|
+
* FNV-1a hash algorithm (32-bit)
|
|
17
|
+
*
|
|
18
|
+
* Fast, simple, good distribution for short strings.
|
|
19
|
+
* Collision probability: ~1 in 4 billion for random inputs.
|
|
20
|
+
*
|
|
21
|
+
* @param str - String to hash
|
|
22
|
+
* @returns 32-bit hash as unsigned integer
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```ts
|
|
26
|
+
* fnv1a("hello") // → 1335831723
|
|
27
|
+
* fnv1a("hello world") // → 3582672807
|
|
28
|
+
* ```
|
|
29
|
+
*/
|
|
30
|
+
export function fnv1a(str) {
|
|
31
|
+
let hash = 2166136261; // FNV offset basis
|
|
32
|
+
for (let i = 0; i < str.length; i++) {
|
|
33
|
+
hash ^= str.charCodeAt(i);
|
|
34
|
+
// FNV prime multiplication (using Math.imul for 32-bit overflow)
|
|
35
|
+
hash = Math.imul(hash, 16777619);
|
|
36
|
+
}
|
|
37
|
+
return hash >>> 0; // Ensure unsigned
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* FNV-1a hash with hex string output
|
|
41
|
+
*
|
|
42
|
+
* @param str - String to hash
|
|
43
|
+
* @returns Hash as 8-character hex string
|
|
44
|
+
*/
|
|
45
|
+
export function fnv1aHex(str) {
|
|
46
|
+
return fnv1a(str).toString(16).padStart(8, "0");
|
|
47
|
+
}
|
|
48
|
+
// XXHASH-INSPIRED HASH
|
|
49
|
+
/**
|
|
50
|
+
* xxHash-inspired fast hash (32-bit)
|
|
51
|
+
*
|
|
52
|
+
* Even faster than FNV-1a for longer strings.
|
|
53
|
+
* Processes 4 bytes at a time when possible.
|
|
54
|
+
*
|
|
55
|
+
* @param str - String to hash
|
|
56
|
+
* @param seed - Optional seed value (default: 0)
|
|
57
|
+
* @returns 32-bit hash as unsigned integer
|
|
58
|
+
*/
|
|
59
|
+
export function fastHash(str, seed = 0) {
|
|
60
|
+
const PRIME1 = 2654435761;
|
|
61
|
+
const PRIME2 = 2246822519;
|
|
62
|
+
const PRIME3 = 3266489917;
|
|
63
|
+
const PRIME4 = 668265263;
|
|
64
|
+
const PRIME5 = 374761393;
|
|
65
|
+
let h32;
|
|
66
|
+
const len = str.length;
|
|
67
|
+
if (len >= 16) {
|
|
68
|
+
// Process in 16-byte blocks
|
|
69
|
+
let v1 = (seed + PRIME1 + PRIME2) | 0;
|
|
70
|
+
let v2 = (seed + PRIME2) | 0;
|
|
71
|
+
let v3 = seed | 0;
|
|
72
|
+
let v4 = (seed - PRIME1) | 0;
|
|
73
|
+
let i = 0;
|
|
74
|
+
const limit = len - 16;
|
|
75
|
+
while (i <= limit) {
|
|
76
|
+
v1 = Math.imul(rotl(v1 + Math.imul(read32(str, i), PRIME2), 13), PRIME1);
|
|
77
|
+
i += 4;
|
|
78
|
+
v2 = Math.imul(rotl(v2 + Math.imul(read32(str, i), PRIME2), 13), PRIME1);
|
|
79
|
+
i += 4;
|
|
80
|
+
v3 = Math.imul(rotl(v3 + Math.imul(read32(str, i), PRIME2), 13), PRIME1);
|
|
81
|
+
i += 4;
|
|
82
|
+
v4 = Math.imul(rotl(v4 + Math.imul(read32(str, i), PRIME2), 13), PRIME1);
|
|
83
|
+
i += 4;
|
|
84
|
+
}
|
|
85
|
+
h32 = rotl(v1, 1) + rotl(v2, 7) + rotl(v3, 12) + rotl(v4, 18);
|
|
86
|
+
}
|
|
87
|
+
else {
|
|
88
|
+
h32 = seed + PRIME5;
|
|
89
|
+
}
|
|
90
|
+
h32 += len;
|
|
91
|
+
// Process remaining bytes
|
|
92
|
+
let i = len >= 16 ? len - (len % 16) : 0;
|
|
93
|
+
while (i <= len - 4) {
|
|
94
|
+
h32 = Math.imul(rotl(h32 + Math.imul(read32(str, i), PRIME3), 17), PRIME4);
|
|
95
|
+
i += 4;
|
|
96
|
+
}
|
|
97
|
+
while (i < len) {
|
|
98
|
+
h32 = Math.imul(rotl(h32 + Math.imul(str.charCodeAt(i), PRIME5), 11), PRIME1);
|
|
99
|
+
i++;
|
|
100
|
+
}
|
|
101
|
+
// Final mix
|
|
102
|
+
h32 ^= h32 >>> 15;
|
|
103
|
+
h32 = Math.imul(h32, PRIME2);
|
|
104
|
+
h32 ^= h32 >>> 13;
|
|
105
|
+
h32 = Math.imul(h32, PRIME3);
|
|
106
|
+
h32 ^= h32 >>> 16;
|
|
107
|
+
return h32 >>> 0;
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Fast hash with hex string output
|
|
111
|
+
*/
|
|
112
|
+
export function fastHashHex(str, seed = 0) {
|
|
113
|
+
return fastHash(str, seed).toString(16).padStart(8, "0");
|
|
114
|
+
}
|
|
115
|
+
// HELPERS
|
|
116
|
+
/**
|
|
117
|
+
* Rotate left (32-bit)
|
|
118
|
+
*/
|
|
119
|
+
function rotl(x, r) {
|
|
120
|
+
return (x << r) | (x >>> (32 - r));
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* Read 4 characters as a 32-bit value
|
|
124
|
+
* Safely handles bounds by returning 0 for out-of-bounds indices
|
|
125
|
+
*/
|
|
126
|
+
function read32(str, i) {
|
|
127
|
+
const len = str.length;
|
|
128
|
+
return ((i < len ? str.charCodeAt(i) : 0) |
|
|
129
|
+
((i + 1 < len ? str.charCodeAt(i + 1) : 0) << 8) |
|
|
130
|
+
((i + 2 < len ? str.charCodeAt(i + 2) : 0) << 16) |
|
|
131
|
+
((i + 3 < len ? str.charCodeAt(i + 3) : 0) << 24));
|
|
132
|
+
}
|
|
133
|
+
// OBJECT HASHING
|
|
134
|
+
/**
|
|
135
|
+
* Hash a configuration object
|
|
136
|
+
*
|
|
137
|
+
* Serializes the object to JSON and hashes it.
|
|
138
|
+
* Excludes functions and debug flags.
|
|
139
|
+
*
|
|
140
|
+
* @param obj - Object to hash
|
|
141
|
+
* @returns Hash string
|
|
142
|
+
*/
|
|
143
|
+
export function hashObject(obj) {
|
|
144
|
+
const str = JSON.stringify(obj, (key, value) => {
|
|
145
|
+
if (typeof value === "function")
|
|
146
|
+
return undefined;
|
|
147
|
+
if (key === "debug")
|
|
148
|
+
return undefined;
|
|
149
|
+
return value;
|
|
150
|
+
});
|
|
151
|
+
return fastHashHex(str);
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* Create a composite hash from multiple values
|
|
155
|
+
*/
|
|
156
|
+
export function hashComposite(...values) {
|
|
157
|
+
const combined = values
|
|
158
|
+
.map((v) => (typeof v === "string" ? v : JSON.stringify(v)))
|
|
159
|
+
.join("|");
|
|
160
|
+
return fastHashHex(combined);
|
|
161
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ogxjs/core - Cache Module
|
|
3
|
+
* High-performance caching system
|
|
4
|
+
*
|
|
5
|
+
* @version 0.2.0 "Turbo"
|
|
6
|
+
*/
|
|
7
|
+
export { fastHash, fastHashHex, fnv1a, fnv1aHex, hashComposite, hashObject, } from "./hash";
|
|
8
|
+
export { createLargeCache, createMediumCache, createSmallCache, LRUCache, type LRUCacheOptions, type LRUCacheStats, } from "./lru";
|
|
9
|
+
export { configureSnapshotCache, getSnapshotCache, type SnapshotCacheOptions, type SnapshotCacheStats, SnapshotCacheV2, snapshotCache, } from "./snapshot";
|
|
10
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/cache/index.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAGH,OAAO,EACN,QAAQ,EACR,WAAW,EACX,KAAK,EACL,QAAQ,EACR,aAAa,EACb,UAAU,GACV,MAAM,QAAQ,CAAC;AAGhB,OAAO,EACN,gBAAgB,EAChB,iBAAiB,EACjB,gBAAgB,EAChB,QAAQ,EACR,KAAK,eAAe,EACpB,KAAK,aAAa,GAClB,MAAM,OAAO,CAAC;AAGf,OAAO,EACN,sBAAsB,EACtB,gBAAgB,EAChB,KAAK,oBAAoB,EACzB,KAAK,kBAAkB,EACvB,eAAe,EACf,aAAa,GACb,MAAM,YAAY,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ogxjs/core - Cache Module
|
|
3
|
+
* High-performance caching system
|
|
4
|
+
*
|
|
5
|
+
* @version 0.2.0 "Turbo"
|
|
6
|
+
*/
|
|
7
|
+
// Hash functions
|
|
8
|
+
export { fastHash, fastHashHex, fnv1a, fnv1aHex, hashComposite, hashObject, } from "./hash";
|
|
9
|
+
// LRU Cache
|
|
10
|
+
export { createLargeCache, createMediumCache, createSmallCache, LRUCache, } from "./lru";
|
|
11
|
+
// Snapshot Cache
|
|
12
|
+
export { configureSnapshotCache, getSnapshotCache, SnapshotCacheV2, snapshotCache, } from "./snapshot";
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ogxjs/core - LRU Cache Implementation
|
|
3
|
+
* Least Recently Used cache with O(1) operations
|
|
4
|
+
*
|
|
5
|
+
* @description
|
|
6
|
+
* High-performance LRU cache using Map + doubly-linked list.
|
|
7
|
+
* - O(1) get, set, delete
|
|
8
|
+
* - Automatic eviction when max size reached
|
|
9
|
+
* - Optional TTL (time-to-live) support
|
|
10
|
+
* - Memory-bounded
|
|
11
|
+
*
|
|
12
|
+
* @version 0.2.0 "Turbo"
|
|
13
|
+
*/
|
|
14
|
+
export interface LRUCacheOptions<K = unknown, V = unknown> {
|
|
15
|
+
/** Maximum number of items in cache */
|
|
16
|
+
maxSize?: number;
|
|
17
|
+
/** Time-to-live in milliseconds (0 = no expiration) */
|
|
18
|
+
ttl?: number;
|
|
19
|
+
/** Callback when an item is evicted */
|
|
20
|
+
onEvict?: (key: K, value: V) => void;
|
|
21
|
+
}
|
|
22
|
+
export interface LRUCacheStats {
|
|
23
|
+
size: number;
|
|
24
|
+
maxSize: number;
|
|
25
|
+
hits: number;
|
|
26
|
+
misses: number;
|
|
27
|
+
hitRate: number;
|
|
28
|
+
evictions: number;
|
|
29
|
+
}
|
|
30
|
+
/**
|
|
31
|
+
* LRU Cache with O(1) operations
|
|
32
|
+
*
|
|
33
|
+
* @example
|
|
34
|
+
* ```ts
|
|
35
|
+
* const cache = new LRUCache<string, Buffer>({ maxSize: 100, ttl: 60000 });
|
|
36
|
+
*
|
|
37
|
+
* cache.set("image-1", buffer);
|
|
38
|
+
* const result = cache.get("image-1"); // → buffer
|
|
39
|
+
*
|
|
40
|
+
* console.log(cache.stats); // { size: 1, hits: 1, ... }
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export declare class LRUCache<K, V> {
|
|
44
|
+
private cache;
|
|
45
|
+
private head;
|
|
46
|
+
private tail;
|
|
47
|
+
private readonly maxSize;
|
|
48
|
+
private readonly ttl;
|
|
49
|
+
private readonly onEvict?;
|
|
50
|
+
private _hits;
|
|
51
|
+
private _misses;
|
|
52
|
+
private _evictions;
|
|
53
|
+
constructor(options?: LRUCacheOptions<K, V>);
|
|
54
|
+
/**
|
|
55
|
+
* Get a value from the cache
|
|
56
|
+
* Moves the item to the front (most recently used)
|
|
57
|
+
*/
|
|
58
|
+
get(key: K): V | undefined;
|
|
59
|
+
/**
|
|
60
|
+
* Set a value in the cache
|
|
61
|
+
* Evicts least recently used item if at capacity
|
|
62
|
+
*/
|
|
63
|
+
set(key: K, value: V): void;
|
|
64
|
+
/**
|
|
65
|
+
* Check if a key exists (without updating LRU order)
|
|
66
|
+
*/
|
|
67
|
+
has(key: K): boolean;
|
|
68
|
+
/**
|
|
69
|
+
* Delete a key from the cache
|
|
70
|
+
*/
|
|
71
|
+
delete(key: K): boolean;
|
|
72
|
+
/**
|
|
73
|
+
* Clear the entire cache
|
|
74
|
+
*/
|
|
75
|
+
clear(): void;
|
|
76
|
+
/**
|
|
77
|
+
* Get cache size
|
|
78
|
+
*/
|
|
79
|
+
get size(): number;
|
|
80
|
+
/**
|
|
81
|
+
* Get cache statistics
|
|
82
|
+
*/
|
|
83
|
+
get stats(): LRUCacheStats;
|
|
84
|
+
/**
|
|
85
|
+
* Get all keys (in LRU order, most recent first)
|
|
86
|
+
*/
|
|
87
|
+
keys(): K[];
|
|
88
|
+
/**
|
|
89
|
+
* Iterate over entries (in LRU order)
|
|
90
|
+
*/
|
|
91
|
+
entries(): Generator<[K, V]>;
|
|
92
|
+
/**
|
|
93
|
+
* Prune expired entries (if TTL is set)
|
|
94
|
+
* Call periodically for long-running processes
|
|
95
|
+
*/
|
|
96
|
+
prune(): number;
|
|
97
|
+
/**
|
|
98
|
+
* Move a node to the front (most recently used)
|
|
99
|
+
*/
|
|
100
|
+
private moveToFront;
|
|
101
|
+
/**
|
|
102
|
+
* Remove a node from the linked list (but not from Map)
|
|
103
|
+
*/
|
|
104
|
+
private removeNode;
|
|
105
|
+
/**
|
|
106
|
+
* Evict the least recently used item
|
|
107
|
+
*/
|
|
108
|
+
private evictLRU;
|
|
109
|
+
}
|
|
110
|
+
/**
|
|
111
|
+
* Create a small LRU cache (100 items)
|
|
112
|
+
*/
|
|
113
|
+
export declare function createSmallCache<K, V>(ttl?: number): LRUCache<K, V>;
|
|
114
|
+
/**
|
|
115
|
+
* Create a medium LRU cache (1000 items)
|
|
116
|
+
*/
|
|
117
|
+
export declare function createMediumCache<K, V>(ttl?: number): LRUCache<K, V>;
|
|
118
|
+
/**
|
|
119
|
+
* Create a large LRU cache (10000 items)
|
|
120
|
+
*/
|
|
121
|
+
export declare function createLargeCache<K, V>(ttl?: number): LRUCache<K, V>;
|
|
122
|
+
//# sourceMappingURL=lru.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"lru.d.ts","sourceRoot":"","sources":["../../src/cache/lru.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAYH,MAAM,WAAW,eAAe,CAAC,CAAC,GAAG,OAAO,EAAE,CAAC,GAAG,OAAO;IACxD,uCAAuC;IACvC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,uDAAuD;IACvD,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,uCAAuC;IACvC,OAAO,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,KAAK,IAAI,CAAC;CACrC;AAED,MAAM,WAAW,aAAa;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CAClB;AAID;;;;;;;;;;;;GAYG;AACH,qBAAa,QAAQ,CAAC,CAAC,EAAE,CAAC;IACzB,OAAO,CAAC,KAAK,CAAiC;IAC9C,OAAO,CAAC,IAAI,CAAgC;IAC5C,OAAO,CAAC,IAAI,CAAgC;IAC5C,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAS;IACjC,OAAO,CAAC,QAAQ,CAAC,GAAG,CAAS;IAC7B,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAC,CAA6B;IAGtD,OAAO,CAAC,KAAK,CAAK;IAClB,OAAO,CAAC,OAAO,CAAK;IACpB,OAAO,CAAC,UAAU,CAAK;gBAEX,OAAO,GAAE,eAAe,CAAC,CAAC,EAAE,CAAC,CAAM;IAM/C;;;OAGG;IACH,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,GAAG,SAAS;IAsB1B;;;OAGG;IACH,GAAG,CAAC,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,IAAI;IAsC3B;;OAEG;IACH,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAapB;;OAEG;IACH,MAAM,CAAC,GAAG,EAAE,CAAC,GAAG,OAAO;IAUvB;;OAEG;IACH,KAAK,IAAI,IAAI;IASb;;OAEG;IACH,IAAI,IAAI,IAAI,MAAM,CAEjB;IAED;;OAEG;IACH,IAAI,KAAK,IAAI,aAAa,CAUzB;IAED;;OAEG;IACH,IAAI,IAAI,CAAC,EAAE;IAUX;;OAEG;IACF,OAAO,IAAI,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAQ7B;;;OAGG;IACH,KAAK,IAAI,MAAM;IAoBf;;OAEG;IACH,OAAO,CAAC,WAAW;IAoBnB;;OAEG;IACH,OAAO,CAAC,UAAU;IAclB;;OAEG;IACH,OAAO,CAAC,QAAQ;CAehB;AAID;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,SAAI,GAAG,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAE9D;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,SAAI,GAAG,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAE/D;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,CAAC,EAAE,CAAC,EAAE,GAAG,SAAI,GAAG,QAAQ,CAAC,CAAC,EAAE,CAAC,CAAC,CAE9D"}
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @ogxjs/core - LRU Cache Implementation
|
|
3
|
+
* Least Recently Used cache with O(1) operations
|
|
4
|
+
*
|
|
5
|
+
* @description
|
|
6
|
+
* High-performance LRU cache using Map + doubly-linked list.
|
|
7
|
+
* - O(1) get, set, delete
|
|
8
|
+
* - Automatic eviction when max size reached
|
|
9
|
+
* - Optional TTL (time-to-live) support
|
|
10
|
+
* - Memory-bounded
|
|
11
|
+
*
|
|
12
|
+
* @version 0.2.0 "Turbo"
|
|
13
|
+
*/
|
|
14
|
+
// LRU CACHE CLASS
|
|
15
|
+
/**
|
|
16
|
+
* LRU Cache with O(1) operations
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* const cache = new LRUCache<string, Buffer>({ maxSize: 100, ttl: 60000 });
|
|
21
|
+
*
|
|
22
|
+
* cache.set("image-1", buffer);
|
|
23
|
+
* const result = cache.get("image-1"); // → buffer
|
|
24
|
+
*
|
|
25
|
+
* console.log(cache.stats); // { size: 1, hits: 1, ... }
|
|
26
|
+
* ```
|
|
27
|
+
*/
|
|
28
|
+
export class LRUCache {
|
|
29
|
+
cache = new Map();
|
|
30
|
+
head = null;
|
|
31
|
+
tail = null;
|
|
32
|
+
maxSize;
|
|
33
|
+
ttl;
|
|
34
|
+
onEvict;
|
|
35
|
+
// Stats
|
|
36
|
+
_hits = 0;
|
|
37
|
+
_misses = 0;
|
|
38
|
+
_evictions = 0;
|
|
39
|
+
constructor(options = {}) {
|
|
40
|
+
this.maxSize = options.maxSize ?? 1000;
|
|
41
|
+
this.ttl = options.ttl ?? 0;
|
|
42
|
+
this.onEvict = options.onEvict;
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Get a value from the cache
|
|
46
|
+
* Moves the item to the front (most recently used)
|
|
47
|
+
*/
|
|
48
|
+
get(key) {
|
|
49
|
+
const node = this.cache.get(key);
|
|
50
|
+
if (!node) {
|
|
51
|
+
this._misses++;
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
// Check TTL
|
|
55
|
+
if (this.ttl > 0 && Date.now() - node.timestamp > this.ttl) {
|
|
56
|
+
this.delete(key);
|
|
57
|
+
this._misses++;
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
// Move to front
|
|
61
|
+
this.moveToFront(node);
|
|
62
|
+
this._hits++;
|
|
63
|
+
return node.value;
|
|
64
|
+
}
|
|
65
|
+
/**
|
|
66
|
+
* Set a value in the cache
|
|
67
|
+
* Evicts least recently used item if at capacity
|
|
68
|
+
*/
|
|
69
|
+
set(key, value) {
|
|
70
|
+
const existing = this.cache.get(key);
|
|
71
|
+
if (existing) {
|
|
72
|
+
// Update existing
|
|
73
|
+
existing.value = value;
|
|
74
|
+
existing.timestamp = Date.now();
|
|
75
|
+
this.moveToFront(existing);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
// Evict if at capacity
|
|
79
|
+
if (this.cache.size >= this.maxSize) {
|
|
80
|
+
this.evictLRU();
|
|
81
|
+
}
|
|
82
|
+
// Create new node
|
|
83
|
+
const node = {
|
|
84
|
+
key,
|
|
85
|
+
value,
|
|
86
|
+
timestamp: Date.now(),
|
|
87
|
+
prev: null,
|
|
88
|
+
next: this.head,
|
|
89
|
+
};
|
|
90
|
+
// Update links
|
|
91
|
+
if (this.head) {
|
|
92
|
+
this.head.prev = node;
|
|
93
|
+
}
|
|
94
|
+
this.head = node;
|
|
95
|
+
if (!this.tail) {
|
|
96
|
+
this.tail = node;
|
|
97
|
+
}
|
|
98
|
+
this.cache.set(key, node);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Check if a key exists (without updating LRU order)
|
|
102
|
+
*/
|
|
103
|
+
has(key) {
|
|
104
|
+
const node = this.cache.get(key);
|
|
105
|
+
if (!node)
|
|
106
|
+
return false;
|
|
107
|
+
// Check TTL
|
|
108
|
+
if (this.ttl > 0 && Date.now() - node.timestamp > this.ttl) {
|
|
109
|
+
this.delete(key);
|
|
110
|
+
return false;
|
|
111
|
+
}
|
|
112
|
+
return true;
|
|
113
|
+
}
|
|
114
|
+
/**
|
|
115
|
+
* Delete a key from the cache
|
|
116
|
+
*/
|
|
117
|
+
delete(key) {
|
|
118
|
+
const node = this.cache.get(key);
|
|
119
|
+
if (!node)
|
|
120
|
+
return false;
|
|
121
|
+
this.removeNode(node);
|
|
122
|
+
this.cache.delete(key);
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* Clear the entire cache
|
|
127
|
+
*/
|
|
128
|
+
clear() {
|
|
129
|
+
this.cache.clear();
|
|
130
|
+
this.head = null;
|
|
131
|
+
this.tail = null;
|
|
132
|
+
this._hits = 0;
|
|
133
|
+
this._misses = 0;
|
|
134
|
+
this._evictions = 0;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* Get cache size
|
|
138
|
+
*/
|
|
139
|
+
get size() {
|
|
140
|
+
return this.cache.size;
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Get cache statistics
|
|
144
|
+
*/
|
|
145
|
+
get stats() {
|
|
146
|
+
const total = this._hits + this._misses;
|
|
147
|
+
return {
|
|
148
|
+
size: this.cache.size,
|
|
149
|
+
maxSize: this.maxSize,
|
|
150
|
+
hits: this._hits,
|
|
151
|
+
misses: this._misses,
|
|
152
|
+
hitRate: total > 0 ? this._hits / total : 0,
|
|
153
|
+
evictions: this._evictions,
|
|
154
|
+
};
|
|
155
|
+
}
|
|
156
|
+
/**
|
|
157
|
+
* Get all keys (in LRU order, most recent first)
|
|
158
|
+
*/
|
|
159
|
+
keys() {
|
|
160
|
+
const result = [];
|
|
161
|
+
let node = this.head;
|
|
162
|
+
while (node) {
|
|
163
|
+
result.push(node.key);
|
|
164
|
+
node = node.next;
|
|
165
|
+
}
|
|
166
|
+
return result;
|
|
167
|
+
}
|
|
168
|
+
/**
|
|
169
|
+
* Iterate over entries (in LRU order)
|
|
170
|
+
*/
|
|
171
|
+
*entries() {
|
|
172
|
+
let node = this.head;
|
|
173
|
+
while (node) {
|
|
174
|
+
yield [node.key, node.value];
|
|
175
|
+
node = node.next;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* Prune expired entries (if TTL is set)
|
|
180
|
+
* Call periodically for long-running processes
|
|
181
|
+
*/
|
|
182
|
+
prune() {
|
|
183
|
+
if (this.ttl === 0)
|
|
184
|
+
return 0;
|
|
185
|
+
const now = Date.now();
|
|
186
|
+
let pruned = 0;
|
|
187
|
+
for (const [key, node] of this.cache) {
|
|
188
|
+
if (now - node.timestamp > this.ttl) {
|
|
189
|
+
this.delete(key);
|
|
190
|
+
pruned++;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return pruned;
|
|
194
|
+
}
|
|
195
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
196
|
+
// PRIVATE METHODS
|
|
197
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
198
|
+
/**
|
|
199
|
+
* Move a node to the front (most recently used)
|
|
200
|
+
*/
|
|
201
|
+
moveToFront(node) {
|
|
202
|
+
if (node === this.head)
|
|
203
|
+
return;
|
|
204
|
+
// Remove from current position
|
|
205
|
+
this.removeNode(node);
|
|
206
|
+
// Add to front
|
|
207
|
+
node.prev = null;
|
|
208
|
+
node.next = this.head;
|
|
209
|
+
if (this.head) {
|
|
210
|
+
this.head.prev = node;
|
|
211
|
+
}
|
|
212
|
+
this.head = node;
|
|
213
|
+
if (!this.tail) {
|
|
214
|
+
this.tail = node;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
/**
|
|
218
|
+
* Remove a node from the linked list (but not from Map)
|
|
219
|
+
*/
|
|
220
|
+
removeNode(node) {
|
|
221
|
+
if (node.prev) {
|
|
222
|
+
node.prev.next = node.next;
|
|
223
|
+
}
|
|
224
|
+
else {
|
|
225
|
+
this.head = node.next;
|
|
226
|
+
}
|
|
227
|
+
if (node.next) {
|
|
228
|
+
node.next.prev = node.prev;
|
|
229
|
+
}
|
|
230
|
+
else {
|
|
231
|
+
this.tail = node.prev;
|
|
232
|
+
}
|
|
233
|
+
}
|
|
234
|
+
/**
|
|
235
|
+
* Evict the least recently used item
|
|
236
|
+
*/
|
|
237
|
+
evictLRU() {
|
|
238
|
+
if (!this.tail)
|
|
239
|
+
return;
|
|
240
|
+
const evicted = this.tail;
|
|
241
|
+
// Call eviction callback
|
|
242
|
+
if (this.onEvict) {
|
|
243
|
+
this.onEvict(evicted.key, evicted.value);
|
|
244
|
+
}
|
|
245
|
+
// Remove from list
|
|
246
|
+
this.removeNode(evicted);
|
|
247
|
+
this.cache.delete(evicted.key);
|
|
248
|
+
this._evictions++;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
// CONVENIENCE FACTORIES
|
|
252
|
+
/**
|
|
253
|
+
* Create a small LRU cache (100 items)
|
|
254
|
+
*/
|
|
255
|
+
export function createSmallCache(ttl = 0) {
|
|
256
|
+
return new LRUCache({ maxSize: 100, ttl });
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Create a medium LRU cache (1000 items)
|
|
260
|
+
*/
|
|
261
|
+
export function createMediumCache(ttl = 0) {
|
|
262
|
+
return new LRUCache({ maxSize: 1000, ttl });
|
|
263
|
+
}
|
|
264
|
+
/**
|
|
265
|
+
* Create a large LRU cache (10000 items)
|
|
266
|
+
*/
|
|
267
|
+
export function createLargeCache(ttl = 0) {
|
|
268
|
+
return new LRUCache({ maxSize: 10000, ttl });
|
|
269
|
+
}
|