@nebulaaa/lru 0.0.0-stage → 1.1.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.
package/dist/lru.cjs ADDED
@@ -0,0 +1,213 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * LRU (Least Recently Used) 缓存
5
+ *
6
+ * 最近最少使用淘汰策略的缓存实现
7
+ */
8
+ class LRU {
9
+ constructor(capacity) {
10
+ // Number.isInteger 一并拦截 NaN / Infinity / 小数:NaN 时 size > NaN 恒为 false,缓存永不淘汰
11
+ if (!Number.isInteger(capacity) || capacity <= 0) {
12
+ throw new Error('LRU capacity must be an integer greater than 0');
13
+ }
14
+ this._capacity = capacity;
15
+ this._cache = new Map();
16
+ this._head = null;
17
+ this._tail = null;
18
+ }
19
+ /**
20
+ * 获取缓存值,并提升为最近使用
21
+ * 注意:未命中返回 null;若业务可能存入 null 值,返回值无法与未命中区分,请用 Has 判断
22
+ */
23
+ Get(key) {
24
+ const node = this._cache.get(key);
25
+ if (!node) {
26
+ return null;
27
+ }
28
+ // 移动到头部(标记为最近使用)
29
+ this.moveToHead(node);
30
+ return node.Value;
31
+ }
32
+ /**
33
+ * 读取缓存值,但不提升新鲜度(不改变淘汰顺序)
34
+ * 注意:未命中返回 null,与存入的 null 值无法区分,请用 Has 判断
35
+ */
36
+ Peek(key) {
37
+ const node = this._cache.get(key);
38
+ return node ? node.Value : null;
39
+ }
40
+ /**
41
+ * 读取缓存值,未命中时用 creator 计算并写入缓存(写入视为最近使用)
42
+ * 注意:creator 抛出的异常会向上传播,不会写入缓存
43
+ */
44
+ GetOrSet(key, creator) {
45
+ const node = this._cache.get(key);
46
+ if (node) {
47
+ this.moveToHead(node);
48
+ return node.Value;
49
+ }
50
+ const value = creator(key);
51
+ this.Set(key, value);
52
+ return value;
53
+ }
54
+ /**
55
+ * 设置缓存值
56
+ */
57
+ Set(key, value) {
58
+ const existing = this._cache.get(key);
59
+ if (existing) {
60
+ // 更新现有节点
61
+ existing.Value = value;
62
+ this.moveToHead(existing);
63
+ return;
64
+ }
65
+ // 创建新节点
66
+ const node = {
67
+ Key: key,
68
+ Value: value,
69
+ Prev: null,
70
+ Next: null,
71
+ };
72
+ // 添加到缓存
73
+ this._cache.set(key, node);
74
+ this.addToHead(node);
75
+ // 检查容量,淘汰最久未使用的节点
76
+ if (this._cache.size > this._capacity) {
77
+ this.removeTail();
78
+ }
79
+ }
80
+ /**
81
+ * 检查是否包含键
82
+ */
83
+ Has(key) {
84
+ return this._cache.has(key);
85
+ }
86
+ /**
87
+ * 删除缓存值
88
+ */
89
+ Delete(key) {
90
+ const node = this._cache.get(key);
91
+ if (!node) {
92
+ return false;
93
+ }
94
+ this._cache.delete(key);
95
+ this.removeNode(node);
96
+ return true;
97
+ }
98
+ /**
99
+ * 清空缓存
100
+ */
101
+ Clear() {
102
+ this._cache.clear();
103
+ this._head = null;
104
+ this._tail = null;
105
+ }
106
+ /**
107
+ * 获取缓存大小
108
+ */
109
+ get Size() {
110
+ return this._cache.size;
111
+ }
112
+ /**
113
+ * 获取缓存容量
114
+ */
115
+ get Capacity() {
116
+ return this._capacity;
117
+ }
118
+ /**
119
+ * 添加节点到头部
120
+ */
121
+ addToHead(node) {
122
+ node.Prev = null;
123
+ node.Next = this._head;
124
+ if (this._head) {
125
+ this._head.Prev = node;
126
+ }
127
+ this._head = node;
128
+ if (!this._tail) {
129
+ this._tail = node;
130
+ }
131
+ }
132
+ /**
133
+ * 移动节点到头部
134
+ */
135
+ moveToHead(node) {
136
+ // 先移除节点
137
+ this.removeNode(node);
138
+ // 添加到头部
139
+ this.addToHead(node);
140
+ }
141
+ /**
142
+ * 移除节点
143
+ */
144
+ removeNode(node) {
145
+ if (node.Prev) {
146
+ node.Prev.Next = node.Next;
147
+ }
148
+ else {
149
+ this._head = node.Next;
150
+ }
151
+ if (node.Next) {
152
+ node.Next.Prev = node.Prev;
153
+ }
154
+ else {
155
+ this._tail = node.Prev;
156
+ }
157
+ }
158
+ /**
159
+ * 移除尾部节点(最久未使用)
160
+ */
161
+ removeTail() {
162
+ if (!this._tail) {
163
+ return;
164
+ }
165
+ this._cache.delete(this._tail.Key);
166
+ this.removeNode(this._tail);
167
+ }
168
+ /**
169
+ * 获取所有键(按最近使用到最久使用排序,与 values() / forEach() 同序)
170
+ */
171
+ Keys() {
172
+ const result = [];
173
+ let node = this._head;
174
+ while (node) {
175
+ result.push(node.Key);
176
+ node = node.Next;
177
+ }
178
+ return result;
179
+ }
180
+ /**
181
+ * 获取所有值
182
+ */
183
+ Values() {
184
+ const result = [];
185
+ let node = this._head;
186
+ while (node) {
187
+ result.push(node.Value);
188
+ node = node.Next;
189
+ }
190
+ return result;
191
+ }
192
+ /**
193
+ * 遍历缓存(按最近使用到最久使用)
194
+ * 注意:内部为快照遍历,回调中增删改/清空缓存是安全的——已被删除或淘汰的条目不再回调,
195
+ * 遍历期间新增的条目不参与本次遍历
196
+ */
197
+ Each(callback) {
198
+ const snapshot = [];
199
+ let node = this._head;
200
+ while (node) {
201
+ snapshot.push(node);
202
+ node = node.Next;
203
+ }
204
+ for (const item of snapshot) {
205
+ // 节点身份校验:Set 更新已有键不会更换节点,仍会回调;Delete 后重设的同键新节点则跳过
206
+ if (this._cache.get(item.Key) === item) {
207
+ callback(item.Value, item.Key);
208
+ }
209
+ }
210
+ }
211
+ }
212
+
213
+ exports.LRU = LRU;
package/dist/lru.d.ts ADDED
@@ -0,0 +1,98 @@
1
+ /**
2
+ * LRU 缓存节点
3
+ */
4
+ interface LRUNode<K, V> {
5
+ Key: K;
6
+ Value: V;
7
+ Prev: LRUNode<K, V> | null;
8
+ Next: LRUNode<K, V> | null;
9
+ }
10
+
11
+ /**
12
+ * LRU (Least Recently Used) 缓存
13
+ *
14
+ * 最近最少使用淘汰策略的缓存实现
15
+ */
16
+ declare class LRU<K, V> {
17
+ /** 容量 */
18
+ private _capacity;
19
+ /** 存储 Map */
20
+ private _cache;
21
+ /** 头节点(最近使用) */
22
+ private _head;
23
+ /** 尾节点(最久未使用) */
24
+ private _tail;
25
+ constructor(capacity: number);
26
+ /**
27
+ * 获取缓存值,并提升为最近使用
28
+ * 注意:未命中返回 null;若业务可能存入 null 值,返回值无法与未命中区分,请用 Has 判断
29
+ */
30
+ Get(key: K): V | null;
31
+ /**
32
+ * 读取缓存值,但不提升新鲜度(不改变淘汰顺序)
33
+ * 注意:未命中返回 null,与存入的 null 值无法区分,请用 Has 判断
34
+ */
35
+ Peek(key: K): V | null;
36
+ /**
37
+ * 读取缓存值,未命中时用 creator 计算并写入缓存(写入视为最近使用)
38
+ * 注意:creator 抛出的异常会向上传播,不会写入缓存
39
+ */
40
+ GetOrSet(key: K, creator: (key: K) => V): V;
41
+ /**
42
+ * 设置缓存值
43
+ */
44
+ Set(key: K, value: V): void;
45
+ /**
46
+ * 检查是否包含键
47
+ */
48
+ Has(key: K): boolean;
49
+ /**
50
+ * 删除缓存值
51
+ */
52
+ Delete(key: K): boolean;
53
+ /**
54
+ * 清空缓存
55
+ */
56
+ Clear(): void;
57
+ /**
58
+ * 获取缓存大小
59
+ */
60
+ get Size(): number;
61
+ /**
62
+ * 获取缓存容量
63
+ */
64
+ get Capacity(): number;
65
+ /**
66
+ * 添加节点到头部
67
+ */
68
+ private addToHead;
69
+ /**
70
+ * 移动节点到头部
71
+ */
72
+ private moveToHead;
73
+ /**
74
+ * 移除节点
75
+ */
76
+ private removeNode;
77
+ /**
78
+ * 移除尾部节点(最久未使用)
79
+ */
80
+ private removeTail;
81
+ /**
82
+ * 获取所有键(按最近使用到最久使用排序,与 values() / forEach() 同序)
83
+ */
84
+ Keys(): K[];
85
+ /**
86
+ * 获取所有值
87
+ */
88
+ Values(): V[];
89
+ /**
90
+ * 遍历缓存(按最近使用到最久使用)
91
+ * 注意:内部为快照遍历,回调中增删改/清空缓存是安全的——已被删除或淘汰的条目不再回调,
92
+ * 遍历期间新增的条目不参与本次遍历
93
+ */
94
+ Each(callback: (value: V, key: K) => void): void;
95
+ }
96
+
97
+ export { LRU };
98
+ export type { LRUNode };
@@ -0,0 +1 @@
1
+ "use strict";exports.LRU=class{constructor(e){if(!Number.isInteger(e)||e<=0)throw new Error("LRU capacity must be an integer greater than 0");this._capacity=e,this._cache=new Map,this._head=null,this._tail=null}Get(e){const t=this._cache.get(e);return t?(this.moveToHead(t),t.Value):null}Peek(e){const t=this._cache.get(e);return t?t.Value:null}GetOrSet(e,t){const h=this._cache.get(e);if(h)return this.moveToHead(h),h.Value;const s=t(e);return this.Set(e,s),s}Set(e,t){const h=this._cache.get(e);if(h)return h.Value=t,void this.moveToHead(h);const s={Key:e,Value:t,Prev:null,Next:null};this._cache.set(e,s),this.addToHead(s),this._cache.size>this._capacity&&this.removeTail()}Has(e){return this._cache.has(e)}Delete(e){const t=this._cache.get(e);return!!t&&(this._cache.delete(e),this.removeNode(t),!0)}Clear(){this._cache.clear(),this._head=null,this._tail=null}get Size(){return this._cache.size}get Capacity(){return this._capacity}addToHead(e){e.Prev=null,e.Next=this._head,this._head&&(this._head.Prev=e),this._head=e,this._tail||(this._tail=e)}moveToHead(e){this.removeNode(e),this.addToHead(e)}removeNode(e){e.Prev?e.Prev.Next=e.Next:this._head=e.Next,e.Next?e.Next.Prev=e.Prev:this._tail=e.Prev}removeTail(){this._tail&&(this._cache.delete(this._tail.Key),this.removeNode(this._tail))}Keys(){const e=[];let t=this._head;for(;t;)e.push(t.Key),t=t.Next;return e}Values(){const e=[];let t=this._head;for(;t;)e.push(t.Value),t=t.Next;return e}Each(e){const t=[];let h=this._head;for(;h;)t.push(h),h=h.Next;for(const h of t)this._cache.get(h.Key)===h&&e(h.Value,h.Key)}};
@@ -0,0 +1 @@
1
+ class e{constructor(e){if(!Number.isInteger(e)||e<=0)throw new Error("LRU capacity must be an integer greater than 0");this._capacity=e,this._cache=new Map,this._head=null,this._tail=null}Get(e){const t=this._cache.get(e);return t?(this.moveToHead(t),t.Value):null}Peek(e){const t=this._cache.get(e);return t?t.Value:null}GetOrSet(e,t){const h=this._cache.get(e);if(h)return this.moveToHead(h),h.Value;const a=t(e);return this.Set(e,a),a}Set(e,t){const h=this._cache.get(e);if(h)return h.Value=t,void this.moveToHead(h);const a={Key:e,Value:t,Prev:null,Next:null};this._cache.set(e,a),this.addToHead(a),this._cache.size>this._capacity&&this.removeTail()}Has(e){return this._cache.has(e)}Delete(e){const t=this._cache.get(e);return!!t&&(this._cache.delete(e),this.removeNode(t),!0)}Clear(){this._cache.clear(),this._head=null,this._tail=null}get Size(){return this._cache.size}get Capacity(){return this._capacity}addToHead(e){e.Prev=null,e.Next=this._head,this._head&&(this._head.Prev=e),this._head=e,this._tail||(this._tail=e)}moveToHead(e){this.removeNode(e),this.addToHead(e)}removeNode(e){e.Prev?e.Prev.Next=e.Next:this._head=e.Next,e.Next?e.Next.Prev=e.Prev:this._tail=e.Prev}removeTail(){this._tail&&(this._cache.delete(this._tail.Key),this.removeNode(this._tail))}Keys(){const e=[];let t=this._head;for(;t;)e.push(t.Key),t=t.Next;return e}Values(){const e=[];let t=this._head;for(;t;)e.push(t.Value),t=t.Next;return e}Each(e){const t=[];let h=this._head;for(;h;)t.push(h),h=h.Next;for(const h of t)this._cache.get(h.Key)===h&&e(h.Value,h.Key)}}export{e as LRU};
package/dist/lru.mjs ADDED
@@ -0,0 +1,211 @@
1
+ /**
2
+ * LRU (Least Recently Used) 缓存
3
+ *
4
+ * 最近最少使用淘汰策略的缓存实现
5
+ */
6
+ class LRU {
7
+ constructor(capacity) {
8
+ // Number.isInteger 一并拦截 NaN / Infinity / 小数:NaN 时 size > NaN 恒为 false,缓存永不淘汰
9
+ if (!Number.isInteger(capacity) || capacity <= 0) {
10
+ throw new Error('LRU capacity must be an integer greater than 0');
11
+ }
12
+ this._capacity = capacity;
13
+ this._cache = new Map();
14
+ this._head = null;
15
+ this._tail = null;
16
+ }
17
+ /**
18
+ * 获取缓存值,并提升为最近使用
19
+ * 注意:未命中返回 null;若业务可能存入 null 值,返回值无法与未命中区分,请用 Has 判断
20
+ */
21
+ Get(key) {
22
+ const node = this._cache.get(key);
23
+ if (!node) {
24
+ return null;
25
+ }
26
+ // 移动到头部(标记为最近使用)
27
+ this.moveToHead(node);
28
+ return node.Value;
29
+ }
30
+ /**
31
+ * 读取缓存值,但不提升新鲜度(不改变淘汰顺序)
32
+ * 注意:未命中返回 null,与存入的 null 值无法区分,请用 Has 判断
33
+ */
34
+ Peek(key) {
35
+ const node = this._cache.get(key);
36
+ return node ? node.Value : null;
37
+ }
38
+ /**
39
+ * 读取缓存值,未命中时用 creator 计算并写入缓存(写入视为最近使用)
40
+ * 注意:creator 抛出的异常会向上传播,不会写入缓存
41
+ */
42
+ GetOrSet(key, creator) {
43
+ const node = this._cache.get(key);
44
+ if (node) {
45
+ this.moveToHead(node);
46
+ return node.Value;
47
+ }
48
+ const value = creator(key);
49
+ this.Set(key, value);
50
+ return value;
51
+ }
52
+ /**
53
+ * 设置缓存值
54
+ */
55
+ Set(key, value) {
56
+ const existing = this._cache.get(key);
57
+ if (existing) {
58
+ // 更新现有节点
59
+ existing.Value = value;
60
+ this.moveToHead(existing);
61
+ return;
62
+ }
63
+ // 创建新节点
64
+ const node = {
65
+ Key: key,
66
+ Value: value,
67
+ Prev: null,
68
+ Next: null,
69
+ };
70
+ // 添加到缓存
71
+ this._cache.set(key, node);
72
+ this.addToHead(node);
73
+ // 检查容量,淘汰最久未使用的节点
74
+ if (this._cache.size > this._capacity) {
75
+ this.removeTail();
76
+ }
77
+ }
78
+ /**
79
+ * 检查是否包含键
80
+ */
81
+ Has(key) {
82
+ return this._cache.has(key);
83
+ }
84
+ /**
85
+ * 删除缓存值
86
+ */
87
+ Delete(key) {
88
+ const node = this._cache.get(key);
89
+ if (!node) {
90
+ return false;
91
+ }
92
+ this._cache.delete(key);
93
+ this.removeNode(node);
94
+ return true;
95
+ }
96
+ /**
97
+ * 清空缓存
98
+ */
99
+ Clear() {
100
+ this._cache.clear();
101
+ this._head = null;
102
+ this._tail = null;
103
+ }
104
+ /**
105
+ * 获取缓存大小
106
+ */
107
+ get Size() {
108
+ return this._cache.size;
109
+ }
110
+ /**
111
+ * 获取缓存容量
112
+ */
113
+ get Capacity() {
114
+ return this._capacity;
115
+ }
116
+ /**
117
+ * 添加节点到头部
118
+ */
119
+ addToHead(node) {
120
+ node.Prev = null;
121
+ node.Next = this._head;
122
+ if (this._head) {
123
+ this._head.Prev = node;
124
+ }
125
+ this._head = node;
126
+ if (!this._tail) {
127
+ this._tail = node;
128
+ }
129
+ }
130
+ /**
131
+ * 移动节点到头部
132
+ */
133
+ moveToHead(node) {
134
+ // 先移除节点
135
+ this.removeNode(node);
136
+ // 添加到头部
137
+ this.addToHead(node);
138
+ }
139
+ /**
140
+ * 移除节点
141
+ */
142
+ removeNode(node) {
143
+ if (node.Prev) {
144
+ node.Prev.Next = node.Next;
145
+ }
146
+ else {
147
+ this._head = node.Next;
148
+ }
149
+ if (node.Next) {
150
+ node.Next.Prev = node.Prev;
151
+ }
152
+ else {
153
+ this._tail = node.Prev;
154
+ }
155
+ }
156
+ /**
157
+ * 移除尾部节点(最久未使用)
158
+ */
159
+ removeTail() {
160
+ if (!this._tail) {
161
+ return;
162
+ }
163
+ this._cache.delete(this._tail.Key);
164
+ this.removeNode(this._tail);
165
+ }
166
+ /**
167
+ * 获取所有键(按最近使用到最久使用排序,与 values() / forEach() 同序)
168
+ */
169
+ Keys() {
170
+ const result = [];
171
+ let node = this._head;
172
+ while (node) {
173
+ result.push(node.Key);
174
+ node = node.Next;
175
+ }
176
+ return result;
177
+ }
178
+ /**
179
+ * 获取所有值
180
+ */
181
+ Values() {
182
+ const result = [];
183
+ let node = this._head;
184
+ while (node) {
185
+ result.push(node.Value);
186
+ node = node.Next;
187
+ }
188
+ return result;
189
+ }
190
+ /**
191
+ * 遍历缓存(按最近使用到最久使用)
192
+ * 注意:内部为快照遍历,回调中增删改/清空缓存是安全的——已被删除或淘汰的条目不再回调,
193
+ * 遍历期间新增的条目不参与本次遍历
194
+ */
195
+ Each(callback) {
196
+ const snapshot = [];
197
+ let node = this._head;
198
+ while (node) {
199
+ snapshot.push(node);
200
+ node = node.Next;
201
+ }
202
+ for (const item of snapshot) {
203
+ // 节点身份校验:Set 更新已有键不会更换节点,仍会回调;Delete 后重设的同键新节点则跳过
204
+ if (this._cache.get(item.Key) === item) {
205
+ callback(item.Value, item.Key);
206
+ }
207
+ }
208
+ }
209
+ }
210
+
211
+ export { LRU };
@@ -0,0 +1,34 @@
1
+ {
2
+ "name": "@nebulaaa/lru",
3
+ "version": "1.1.0",
4
+ "description": "LRU",
5
+ "author": "doooreyn",
6
+ "license": "MIT",
7
+ "keywords": [
8
+ "lru",
9
+ "nebulaaa"
10
+ ],
11
+ "engines": {
12
+ "node": ">=16.0.0",
13
+ "npm": ">=10.0.0"
14
+ },
15
+ "main": "./lru.cjs",
16
+ "module": "./lru.mjs",
17
+ "types": "./lru.d.ts",
18
+ "exports": {
19
+ ".": {
20
+ "types": "./lru.d.ts",
21
+ "require": "./lru.cjs",
22
+ "import": "./lru.mjs",
23
+ "default": "./lru.cjs"
24
+ },
25
+ "./min": {
26
+ "require": "./lru.min.cjs",
27
+ "import": "./lru.min.mjs"
28
+ }
29
+ },
30
+ "publishConfig": {
31
+ "registry": "https://registry.npmjs.org/",
32
+ "access": "public"
33
+ }
34
+ }
package/package.json CHANGED
@@ -1,6 +1,66 @@
1
- {
2
- "name": "@nebulaaa/lru",
3
- "version": "0.0.0-stage",
4
- "stub": true,
5
- "description": "Temporary package placeholder for staged publishing"
6
- }
1
+ {
2
+ "name": "@nebulaaa/lru",
3
+ "version": "1.1.0",
4
+ "description": "LRU",
5
+ "main": "./dist/lru.cjs",
6
+ "module": "./dist/lru.mjs",
7
+ "types": "./dist/lru.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "require": "./dist/lru.cjs",
11
+ "import": "./dist/lru.mjs",
12
+ "types": "./dist/lru.d.ts",
13
+ "default": "./dist/lru.cjs"
14
+ },
15
+ "./min": {
16
+ "require": "./dist/lru.min.cjs",
17
+ "import": "./dist/lru.min.mjs"
18
+ }
19
+ },
20
+ "files": [
21
+ "dist"
22
+ ],
23
+ "scripts": {
24
+ "clean": "node -e \"require('fs').rmSync('dist',{recursive:true,force:true})\"",
25
+ "build": "npm run clean && rollup -c rollup.config.mjs && node ../scripts/write-dist-package.mjs lru",
26
+ "test": "jest"
27
+ },
28
+ "keywords": [
29
+ "lru",
30
+ "nebulaaa"
31
+ ],
32
+ "author": "doooreyn",
33
+ "license": "MIT",
34
+ "devEngines": {
35
+ "packageManager": {
36
+ "name": "npm",
37
+ "version": ">=10.0.0",
38
+ "onFail": "warn"
39
+ }
40
+ },
41
+ "engines": {
42
+ "node": ">=16.0.0",
43
+ "npm": ">=10.0.0"
44
+ },
45
+ "publishConfig": {
46
+ "registry": "https://registry.npmjs.org/",
47
+ "access": "public"
48
+ },
49
+ "allowScripts": {
50
+ "unrs-resolver@1.12.2": true,
51
+ "@parcel/watcher@2.6.0": true
52
+ },
53
+ "devDependencies": {
54
+ "@rollup/plugin-terser": "^1.0.2",
55
+ "@rollup/plugin-typescript": "^12.1.2",
56
+ "@types/jest": "^30.0.0",
57
+ "@types/node": "^22.10.2",
58
+ "jest": "^30.5.2",
59
+ "rollup": "^4.28.1",
60
+ "rollup-plugin-dts": "^6.1.1",
61
+ "ts-jest": "^29.1.2",
62
+ "ts-node": "^10.9.2",
63
+ "typescript": "5.7.3"
64
+ },
65
+ "dependencies": {}
66
+ }
package/README.md DELETED
@@ -1,3 +0,0 @@
1
- # Temporary Holding Version
2
-
3
- This version is a temporary placeholder for this package. An operational version to replace this has been submitted for review and is awaiting a staged release.