mm_cachebase 1.7.0 → 1.8.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/README.md CHANGED
@@ -273,15 +273,17 @@ await cache.has(key)
273
273
 
274
274
  #### 增加浮点数(兼容方法)
275
275
  ```javascript
276
- await cache.addFloat(key, num)
276
+ await cache.addFloat(key, num, maxAge)
277
277
  ```
278
278
  - `num`: 要增加的浮点数
279
+ - `maxAge`: 过期时间(秒),默认0表示永不过期
279
280
  - 返回: 计算后的结果
280
281
 
281
282
  #### 追加字符串(兼容方法)
282
283
  ```javascript
283
- await cache.addStr(key, str)
284
+ await cache.addStr(key, str, maxAge)
284
285
  ```
286
+ - `maxAge`: 过期时间(秒),默认0表示永不过期
285
287
  - 返回: 追加后的完整字符串
286
288
 
287
289
  #### 清空匹配缓存(兼容方法)
@@ -12,6 +12,7 @@
12
12
  | clear | ❌ | ✅ | ✅ | Redis无此方法,CacheBase保持兼容 |
13
13
  | keys | ✅ | ✅ | ✅ | 完全兼容 |
14
14
  | ttl | ✅ | ❌ | ✅ | Redis原生支持,CacheBase模拟实现 |
15
+ | persist | ✅ | ❌ | ✅ | Redis原生支持,CacheBase模拟实现 |
15
16
  | add | ❌ | ✅ | ✅ | Redis无此方法,CacheBase保持兼容 |
16
17
  | addInt | ❌ | ✅ | ✅ | Redis无此方法,CacheBase保持兼容 |
17
18
  | addStr | ❌ | ✅ | ✅ | Redis无此方法,CacheBase保持兼容 |
package/index.js CHANGED
@@ -8,7 +8,7 @@ class CacheBase extends EventEmitter {
8
8
  */
9
9
  constructor(scope = 'sys', config = {}) {
10
10
  super();
11
-
11
+
12
12
  // 持久化相关属性
13
13
  this.scope = scope;
14
14
  // 配置选项
@@ -24,25 +24,24 @@ class CacheBase extends EventEmitter {
24
24
  // 键列表,用于存储所有的键名
25
25
  this.list = [];
26
26
  this.saveTimer = null;
27
- this.persist = config.persist || false;
28
27
  this.dir = this.config.dir;
29
28
  this.setConfig(config);
30
29
  this.init();
31
30
  }
32
-
31
+
33
32
  /**
34
33
  * 初始化方法
35
34
  * @private
36
35
  */
37
36
  init() {
38
37
  // 如果启用了持久化,尝试加载数据
39
- if (this.persist) {
38
+ if (this.config.persist) {
40
39
  this._loadData().catch(error => {
41
40
  $.log.error('[cachebase] 数据加载失败:', error);
42
41
  });
43
42
  }
44
43
  }
45
-
44
+
46
45
  /**
47
46
  * 启动定时保存功能
48
47
  * @returns {Promise<boolean>} 是否成功连接
@@ -50,17 +49,17 @@ class CacheBase extends EventEmitter {
50
49
  async connect() {
51
50
  try {
52
51
  this.emit('connect_before');
53
-
52
+
54
53
  // 只有在启用持久化时才启动定时保存
55
- if (this.persist) {
54
+ if (this.config.persist) {
56
55
  // 先立即保存一次
57
56
  await this._saveData();
58
-
57
+
59
58
  // 清除已存在的定时器(如果有)
60
59
  if (this.saveTimer) {
61
60
  clearInterval(this.saveTimer);
62
61
  }
63
-
62
+
64
63
  // 设置新的定时器
65
64
  const interval = this.config.saveInterval || 60000;
66
65
  this.saveTimer = setInterval(() => {
@@ -68,10 +67,10 @@ class CacheBase extends EventEmitter {
68
67
  $.log.error('[cachebase] 定时保存失败:', error);
69
68
  });
70
69
  }, interval);
71
-
70
+
72
71
  $.log.info(`[cachebase] 定时保存已启动,间隔 ${interval}ms`);
73
72
  }
74
-
73
+
75
74
  this.emit('connect_after');
76
75
  return true;
77
76
  } catch (error) {
@@ -80,7 +79,7 @@ class CacheBase extends EventEmitter {
80
79
  return false;
81
80
  }
82
81
  }
83
-
82
+
84
83
  /**
85
84
  * 手动保存数据到文件
86
85
  * @returns {Promise<boolean>} 是否保存成功
@@ -97,24 +96,24 @@ class CacheBase extends EventEmitter {
97
96
  return false;
98
97
  }
99
98
  }
100
-
99
+
101
100
  /**
102
101
  * 保存数据到文件
103
102
  * @private
104
103
  * @returns {Promise<void>}
105
104
  */
106
105
  async _saveData() {
107
- if (!this.persist) return;
108
-
106
+ if (!this.config.persist) return;
107
+
109
108
  try {
110
109
  // 确保目录存在
111
110
  this.dir.addDir();
112
-
111
+
113
112
  // 过滤过期数据
114
113
  const now = Date.now();
115
114
  const validData = {};
116
115
  const validList = [];
117
-
116
+
118
117
  for (const key of this.list) {
119
118
  if (this.data.hasOwnProperty(key)) {
120
119
  const item = this.data[key];
@@ -125,14 +124,14 @@ class CacheBase extends EventEmitter {
125
124
  }
126
125
  }
127
126
  }
128
-
127
+
129
128
  // 准备保存的数据
130
129
  const saveData = {
131
130
  data: validData,
132
131
  list: validList,
133
132
  timestamp: now
134
133
  };
135
-
134
+
136
135
  // 保存到文件
137
136
  const file = `./${this.scope}.cache.json`.fullname(this.dir);
138
137
  file.saveJson(saveData);
@@ -142,42 +141,42 @@ class CacheBase extends EventEmitter {
142
141
  throw error;
143
142
  }
144
143
  }
145
-
144
+
146
145
  /**
147
146
  * 从文件加载数据
148
147
  * @private
149
148
  * @returns {Promise<void>}
150
149
  */
151
150
  async _loadData() {
152
- if (!this.persist) return;
153
-
151
+ if (!this.config.persist) return;
152
+
154
153
  try {
155
154
  // 确保目录存在
156
155
  this.dir.addDir();
157
-
156
+
158
157
  // 只加载当前scope的缓存文件
159
158
  const file = `./${this.scope}.cache.json`.fullname(this.dir);
160
-
159
+
161
160
  if (!file.hasFile()) {
162
161
  $.log.debug(`[cachebase] 未找到当前scope的缓存文件: ${file}`);
163
162
  return;
164
163
  }
165
-
164
+
166
165
  $.log.debug(`[cachebase] 尝试从 ${file} 加载数据`);
167
-
166
+
168
167
  try {
169
168
  try {
170
169
  const savedData = file.loadJson();
171
170
  $.log.debug(`[cachebase] 解析数据成功,包含data: ${savedData.data ? '是' : '否'}, list: ${savedData.list ? '是' : '否'}`);
172
-
171
+
173
172
  if (savedData.data && savedData.list && Array.isArray(savedData.list)) {
174
173
  $.log.debug(`[cachebase] 数据格式正确,list长度: ${savedData.list.length}`);
175
-
174
+
176
175
  // 验证数据并过滤过期项
177
176
  const now = Date.now();
178
177
  const validData = {};
179
178
  const validList = [];
180
-
179
+
181
180
  for (const key of savedData.list) {
182
181
  if (savedData.data.hasOwnProperty(key)) {
183
182
  const item = savedData.data[key];
@@ -191,12 +190,12 @@ class CacheBase extends EventEmitter {
191
190
  }
192
191
  }
193
192
  }
194
-
193
+
195
194
  // 直接使用加载的数据替换当前数据
196
195
  this.data = validData;
197
196
  this.list = validList;
198
197
  this.dict = this.data; // 确保dict指向最新的data
199
-
198
+
200
199
  $.log.info(`[cachebase] 从 ${this.scope}.cache.json 加载数据成功,共 ${validList.length} 个键`);
201
200
  if (validList.length > 0) {
202
201
  $.log.debug(`[cachebase] 加载的键: ${validList.join(', ')}`);
@@ -586,6 +585,47 @@ class CacheBase extends EventEmitter {
586
585
  }
587
586
  }
588
587
 
588
+ /**
589
+ * 移除键的过期时间,使其永不过期
590
+ * @param {string} key - 键名
591
+ * @returns {Promise<number>} - 返回1表示成功移除过期时间,0表示键不存在
592
+ */
593
+ async persist(key) {
594
+ try {
595
+ this.emit('persist_before', { key });
596
+
597
+ // 检查键是否存在
598
+ if (!this.data.hasOwnProperty(key)) {
599
+ this.emit('persist_after', { key, result: 0 });
600
+ return 0;
601
+ }
602
+
603
+ const item = this.data[key];
604
+
605
+ // 检查是否已过期
606
+ if (item.expire > 0 && Date.now() > item.expire) {
607
+ // 键已过期,删除并返回0
608
+ delete this.data[key];
609
+ const index = this.list.indexOf(key);
610
+ if (index !== -1) {
611
+ this.list.splice(index, 1);
612
+ }
613
+ this.emit('persist_after', { key, result: 0 });
614
+ return 0;
615
+ }
616
+
617
+ // 设置为永不过期
618
+ item.expire = 0;
619
+
620
+ this.emit('persist_after', { key, result: 1 });
621
+ return 1;
622
+ } catch (error) {
623
+ this.emit('persist_error', { key, error });
624
+ $.log.error('[cachebase] persist操作失败:', error);
625
+ throw error;
626
+ }
627
+ }
628
+
589
629
  /**
590
630
  * 获取键的剩余生存时间
591
631
  * @param {string} key - 键名
@@ -1040,52 +1080,202 @@ class CacheBase extends EventEmitter {
1040
1080
  }
1041
1081
  }
1042
1082
 
1043
- /**
1044
- * 清空数据库
1045
- * @returns {Promise<string>} - 返回'OK'
1046
- */
1047
- async flushdb() {
1083
+ // 保持兼容性的方法
1084
+ async has(key) {
1085
+ try {
1086
+ const exists = await this.exists(key);
1087
+ return exists > 0;
1088
+ } catch (error) {
1089
+ $.log.error('[cachebase] has操作失败:', error);
1090
+ throw error;
1091
+ }
1092
+ }
1093
+
1094
+ async clear(pattern) {
1048
1095
  try {
1049
- this.emit('flushdb_before');
1096
+ this.emit('clear_before');
1050
1097
 
1051
1098
  // 清空数据
1052
1099
  this.data = {};
1053
1100
  this.list = [];
1054
1101
 
1055
- this.emit('flushdb_after', { result: 'OK' });
1102
+ this.emit('clear_after', { result: 'OK' });
1056
1103
  return 'OK';
1057
1104
  } catch (error) {
1058
- this.emit('flushdb_error', { error });
1059
- $.log.error('[cachebase] flushdb操作失败:', error);
1105
+ this.emit('clear_error', { error });
1106
+ $.log.error('[cachebase] clear操作失败:', error);
1060
1107
  throw error;
1061
1108
  }
1062
1109
  }
1063
1110
 
1064
- // 保持兼容性的方法
1065
- async has(key) {
1111
+ /**
1112
+ * 添加缓存(仅当键不存在时)- 兼容方法
1113
+ * @param {string} key - 键名
1114
+ * @param {number} value - 要增加的值
1115
+ * @param {number} maxAge - 过期时间(秒),0表示永不过期
1116
+ * @returns {Promise<number>} - 计算后的结果
1117
+ */
1118
+ async add(key, value, maxAge = 0) {
1066
1119
  try {
1067
- const exists = await this.exists(key);
1068
- return exists > 0;
1120
+ this.emit('add_before', { key, value, maxAge });
1121
+
1122
+ // 原生实现递增逻辑
1123
+ let currentValue = 0;
1124
+ if (this.data.hasOwnProperty(key)) {
1125
+ const item = this.data[key];
1126
+ if (item.expire === 0 || Date.now() < item.expire) {
1127
+ currentValue = Number(item.value) || 0;
1128
+ }
1129
+ }
1130
+ const result = currentValue + Number(value);
1131
+
1132
+ // 设置值和过期时间
1133
+ this.data[key] = {
1134
+ value: result,
1135
+ expire: maxAge > 0 ? Date.now() + maxAge * 1000 : 0
1136
+ };
1137
+
1138
+ // 更新列表
1139
+ if (!this.list.includes(key)) {
1140
+ this.list.push(key);
1141
+ }
1142
+
1143
+ this.emit('add_after', { key, value, maxAge, result });
1144
+ return result;
1069
1145
  } catch (error) {
1070
- $.log.error('[cachebase] has操作失败:', error);
1146
+ this.emit('add_error', { key, value, maxAge, error });
1147
+ $.log.error('[cachebase] add操作失败:', error);
1071
1148
  throw error;
1072
1149
  }
1073
1150
  }
1074
1151
 
1075
- async clear(pattern) {
1076
- return this.flushdb();
1077
- }
1152
+ /**
1153
+ * 增加浮点数(兼容方法)
1154
+ * @param {string} key - 键名
1155
+ * @param {number} num - 要增加的浮点数
1156
+ * @param {number} maxAge - 过期时间(秒),0表示永不过期
1157
+ * @returns {Promise<number>} - 计算后的结果
1158
+ */
1159
+ async addFloat(key, num, maxAge = 0) {
1160
+ try {
1161
+ this.emit('addFloat_before', { key, num, maxAge });
1162
+
1163
+ // 原生实现浮点数递增逻辑
1164
+ let currentValue = 0;
1165
+ if (this.data.hasOwnProperty(key)) {
1166
+ const item = this.data[key];
1167
+ if (item.expire === 0 || Date.now() < item.expire) {
1168
+ currentValue = Number(item.value) || 0;
1169
+ }
1170
+ }
1171
+ const result = currentValue + Number(num);
1172
+
1173
+ // 设置值和过期时间
1174
+ this.data[key] = {
1175
+ value: result,
1176
+ expire: maxAge > 0 ? Date.now() + maxAge * 1000 : 0
1177
+ };
1078
1178
 
1079
- async add(key, value) {
1080
- return this.incrby(key, value);
1179
+ // 更新列表
1180
+ if (!this.list.includes(key)) {
1181
+ this.list.push(key);
1182
+ }
1183
+
1184
+ this.emit('addFloat_after', { key, num, maxAge, result });
1185
+ return result;
1186
+ } catch (error) {
1187
+ this.emit('addFloat_error', { key, num, maxAge, error });
1188
+ $.log.error('[cachebase] addFloat操作失败:', error);
1189
+ throw error;
1190
+ }
1081
1191
  }
1082
1192
 
1083
- async addFloat(key, value) {
1084
- return this.incrbyfloat(key, value);
1193
+ /**
1194
+ * 增加整数(兼容方法)
1195
+ * @param {string} key - 键名
1196
+ * @param {number} value - 要增加的整数值
1197
+ * @param {number} maxAge - 过期时间(秒),0表示永不过期
1198
+ * @returns {Promise<number>} - 计算后的整数结果
1199
+ */
1200
+ async addInt(key, value, maxAge = 0) {
1201
+ try {
1202
+ this.emit('addInt_before', { key, value, maxAge });
1203
+
1204
+ // 原生实现整数递增逻辑
1205
+ let currentValue = 0;
1206
+ if (this.data.hasOwnProperty(key)) {
1207
+ const item = this.data[key];
1208
+ if (item.expire === 0 || Date.now() < item.expire) {
1209
+ currentValue = parseInt(item.value, 10) || 0;
1210
+ }
1211
+ }
1212
+ const result = currentValue + parseInt(value, 10);
1213
+
1214
+ // 设置值和过期时间
1215
+ this.data[key] = {
1216
+ value: result,
1217
+ expire: maxAge > 0 ? Date.now() + maxAge * 1000 : 0
1218
+ };
1219
+
1220
+ // 更新列表
1221
+ if (!this.list.includes(key)) {
1222
+ this.list.push(key);
1223
+ }
1224
+
1225
+ this.emit('addInt_after', { key, value, maxAge, result });
1226
+ return result;
1227
+ } catch (error) {
1228
+ this.emit('addInt_error', { key, value, maxAge, error });
1229
+ $.log.error('[cachebase] addInt操作失败:', error);
1230
+ throw error;
1231
+ }
1085
1232
  }
1233
+ /**
1234
+ * 追加字符串(兼容方法)
1235
+ * @param {string} key - 键名
1236
+ * @param {string} value - 要追加的字符串
1237
+ * @param {number} maxAge - 过期时间(秒),0表示永不过期
1238
+ * @returns {Promise<number>} - 追加后的字符串长度
1239
+ */
1240
+ async addStr(key, value, maxAge = 0) {
1241
+ try {
1242
+ this.emit('addStr_before', { key, value, maxAge });
1243
+
1244
+ // 原生实现字符串追加逻辑
1245
+ let currentValue = '';
1246
+ let originalExpire = 0;
1086
1247
 
1087
- async addStr(key, value) {
1088
- return this.append(key, value);
1248
+ if (this.data.hasOwnProperty(key)) {
1249
+ const item = this.data[key];
1250
+ if (item.expire === 0 || Date.now() < item.expire) {
1251
+ currentValue = String(item.value) || '';
1252
+ originalExpire = item.expire;
1253
+ }
1254
+ }
1255
+
1256
+ const newValue = currentValue + String(value);
1257
+ const result = newValue.length;
1258
+
1259
+ // 设置值和过期时间(如果提供了新的过期时间,则使用新的,否则保持原来的)
1260
+ const expireTime = maxAge > 0 ? Date.now() + maxAge * 1000 : originalExpire;
1261
+
1262
+ this.data[key] = {
1263
+ value: newValue,
1264
+ expire: expireTime
1265
+ };
1266
+
1267
+ // 更新列表
1268
+ if (!this.list.includes(key)) {
1269
+ this.list.push(key);
1270
+ }
1271
+
1272
+ this.emit('addStr_after', { key, value, maxAge, result });
1273
+ return result;
1274
+ } catch (error) {
1275
+ this.emit('addStr_error', { key, value, maxAge, error });
1276
+ $.log.error('[cachebase] addStr操作失败:', error);
1277
+ throw error;
1278
+ }
1089
1279
  }
1090
1280
 
1091
1281
  /**
@@ -1095,28 +1285,28 @@ class CacheBase extends EventEmitter {
1095
1285
  async dispose() {
1096
1286
  try {
1097
1287
  this.emit('dispose_before');
1098
-
1288
+
1099
1289
  // 清除定时器
1100
1290
  if (this.saveTimer) {
1101
1291
  clearInterval(this.saveTimer);
1102
1292
  this.saveTimer = null;
1103
1293
  $.log.info('[cachebase] 定时保存已停止');
1104
1294
  }
1105
-
1295
+
1106
1296
  // 如果启用了持久化,在销毁前保存一次
1107
- if (this.persist) {
1297
+ if (this.config.persist) {
1108
1298
  await this._saveData().catch(error => {
1109
1299
  $.log.error('[cachebase] 销毁前保存数据失败:', error);
1110
1300
  });
1111
1301
  }
1112
-
1302
+
1113
1303
  // 清空数据
1114
1304
  this.data = {};
1115
1305
  this.list = [];
1116
1306
  this.dict = this.data; // 确保dict也被清空
1117
-
1307
+
1118
1308
  this.emit('dispose_after');
1119
-
1309
+
1120
1310
  // 移除所有监听器
1121
1311
  this.removeAllListeners();
1122
1312
  } catch (error) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "mm_cachebase",
3
- "version": "1.7.0",
3
+ "version": "1.8.0",
4
4
  "description": "一个轻量级的本地缓存类库,提供与Redis兼容的API接口,支持过期时间设置、哈希表操作、数值操作、字符串操作等功能,以及完整的事件通知系统和多文件持久化功能。",
5
5
  "main": "index.js",
6
6
  "scripts": {
package/test.js CHANGED
@@ -107,7 +107,7 @@ async function testBasicMethods(runner) {
107
107
  });
108
108
 
109
109
  await runner.runTest('keys方法', async () => {
110
- await cache.flushdb();
110
+ await cache.clear();
111
111
  await cache.set('key4', 'value4');
112
112
  await cache.set('key5', 'value5');
113
113
  const keys = await cache.keys();
@@ -139,7 +139,7 @@ async function testBasicMethods(runner) {
139
139
  });
140
140
 
141
141
  await runner.runTest('flushdb/clear方法', async () => {
142
- let result = await cache.flushdb();
142
+ let result = await cache.clear();
143
143
  if (result !== 'OK') {
144
144
  throw new Error(`flushdb失败,预期: OK, 实际: ${result}`);
145
145
  }