node-karin 1.15.5 → 1.16.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/CHANGELOG.md +31 -0
- package/dist/adapter-BqlH3u3X.mjs +218 -0
- package/dist/app-DdMQbBEY.mjs +4109 -0
- package/dist/cache-CPcPeo6N.mjs +163 -0
- package/dist/chunk-NzVPYdc1.mjs +21 -0
- package/dist/cli/index.cjs +10900 -1
- package/dist/cli/index.d.ts +1 -1
- package/dist/cli/index.mjs +10770 -10224
- package/dist/file-ZGuqNDd-.mjs +15987 -0
- package/dist/file-dGy9of8-.mjs +268 -0
- package/dist/fsSync-Cf5MWILk.mjs +65 -0
- package/dist/index.d.ts +12235 -12738
- package/dist/index.mjs +2054 -25247
- package/dist/internal-DupfycKE.mjs +597 -0
- package/dist/kv-DZp4UIxg.mjs +192 -0
- package/dist/module/art-template.d.ts +2 -13
- package/dist/module/art-template.mjs +3 -1
- package/dist/module/axios.d.ts +3 -2
- package/dist/module/axios.mjs +5 -2
- package/dist/module/chalk.d.ts +3 -2
- package/dist/module/chalk.mjs +5 -2
- package/dist/module/chokidar.d.ts +3 -2
- package/dist/module/chokidar.mjs +5 -2
- package/dist/module/express.d.ts +2 -1
- package/dist/module/express.mjs +3 -1
- package/dist/module/lodash.d.ts +2 -1
- package/dist/module/lodash.mjs +3 -1
- package/dist/module/log4js.d.ts +3 -2
- package/dist/module/log4js.mjs +5 -2
- package/dist/module/moment.d.ts +2 -1
- package/dist/module/moment.mjs +3 -1
- package/dist/module/node-schedule.d.ts +3 -2
- package/dist/module/node-schedule.mjs +5 -2
- package/dist/module/redis.d.ts +3 -2
- package/dist/module/redis.mjs +5 -2
- package/dist/module/sqlite3.d.ts +3 -2
- package/dist/module/sqlite3.mjs +5 -2
- package/dist/module/ws.d.ts +3 -2
- package/dist/module/ws.mjs +5 -2
- package/dist/module/yaml.d.ts +3 -2
- package/dist/module/yaml.mjs +5 -2
- package/dist/queue-CnKedaZA.mjs +70 -0
- package/dist/redis-aLJ7wbJH.mjs +1556 -0
- package/dist/render-DPqueDZr.mjs +170 -0
- package/dist/root.d.ts +46 -46
- package/dist/root.mjs +136 -93
- package/dist/router-zPSN9-tY.mjs +124 -0
- package/dist/server-DT64D-m-.mjs +38 -0
- package/dist/snapka-BTlnZOyI.mjs +450 -0
- package/dist/sqlite-Dcj9jlW9.mjs +307 -0
- package/dist/start/app.d.ts +1 -1
- package/dist/start/app.mjs +14 -7
- package/dist/start/index.d.ts +1 -1
- package/dist/start/index.mjs +325 -656
- package/dist/template-Djk6y0uC.mjs +133 -0
- package/dist/terminalManager-Lxa8Sm06.mjs +783 -0
- package/dist/uptime-C121X_rq.mjs +210 -0
- package/dist/web/{CompressaPRO-GX.woff2.br → CompressaPRO-GX.woff2} +0 -0
- package/dist/web/assets/css/style-CBB8wM_W.css +14880 -0
- package/dist/web/assets/js/entry-Blf4Trpx.js +258540 -0
- package/dist/web/{googleapis.woff2.br → googleapis.woff2} +0 -0
- package/dist/web/index.html +2 -15
- package/dist/web/karin.png +0 -0
- package/dist/web/sha256.min.js +9 -0
- package/dist/ws-BLDoC2gV.mjs +80 -0
- package/dist/ws-CcoWd3Ar.mjs +106 -0
- package/package.json +7 -7
- package/dist/global.d.d.ts +0 -68
- package/dist/types-hAhbXJDZ.d.ts +0 -109
- package/dist/web/assets/css/components-ep7vm38G.css +0 -1
- package/dist/web/assets/css/index-Dadvd9mn.css.br +0 -0
- package/dist/web/assets/css/vendor-editor-CFbL2ovg.css.br +0 -0
- package/dist/web/assets/css/vendor-others-ZgkIHsf0.css +0 -1
- package/dist/web/assets/js/components-CU2xw4lY.js.br +0 -0
- package/dist/web/assets/js/entry-Dvb7eYLE.js.br +0 -0
- package/dist/web/assets/js/hooks-CRfhs4ON.js.br +0 -0
- package/dist/web/assets/js/page-404.tsx-DYMd_RI_.js +0 -1
- package/dist/web/assets/js/page-dashboard-CG60V_Z-.js.br +0 -0
- package/dist/web/assets/js/page-loading.tsx-wY8a9me3.js.br +0 -0
- package/dist/web/assets/js/page-login.tsx-B54ZOEZB.js.br +0 -0
- package/dist/web/assets/js/utils-C9nWTSuo.js +0 -2
- package/dist/web/assets/js/vendor-editor-BmqYP7lh.js.br +0 -0
- package/dist/web/assets/js/vendor-heroui-ClBCy2zk.js.br +0 -0
- package/dist/web/assets/js/vendor-others-6GiMrjd4.js.br +0 -0
- package/dist/web/assets/js/vendor-react-Dc9jdQiK.js.br +0 -0
- package/dist/web/assets/js/vendor-ui-utils-D0xkboLL.js.br +0 -0
- package/dist/web/assets/js/vendor-visual-saF8KLH_.js.br +0 -0
- package/dist/web/karin.png.br +0 -0
- package/dist/web/sha256.min.js.br +0 -0
|
@@ -0,0 +1,1556 @@
|
|
|
1
|
+
import { t as __exportAll } from "./chunk-NzVPYdc1.mjs";
|
|
2
|
+
import { redisSqlite3Path } from "./root.mjs";
|
|
3
|
+
import { Ir as diffSimpleArray, Mi as isWin, n as redis$1 } from "./file-ZGuqNDd-.mjs";
|
|
4
|
+
import { f as exec } from "./uptime-C121X_rq.mjs";
|
|
5
|
+
import { t as SQLiteWrapper } from "./sqlite-Dcj9jlW9.mjs";
|
|
6
|
+
import path from "node:path";
|
|
7
|
+
import lodash from "lodash";
|
|
8
|
+
import { EventEmitter } from "node:events";
|
|
9
|
+
import os from "node:os";
|
|
10
|
+
import moment from "moment";
|
|
11
|
+
import { createClient } from "redis";
|
|
12
|
+
|
|
13
|
+
//#region src/core/db/redis/mock/index.ts
|
|
14
|
+
/**
|
|
15
|
+
* @description 轻量化的 Redis 客户端 仅支持部分命令
|
|
16
|
+
* @class RedisClient
|
|
17
|
+
*/
|
|
18
|
+
var RedisClient = class extends EventEmitter {
|
|
19
|
+
id;
|
|
20
|
+
/** 键、类型、过期时间映射 */
|
|
21
|
+
store = {};
|
|
22
|
+
/** 键值对 */
|
|
23
|
+
#str = {};
|
|
24
|
+
/** 数字对 */
|
|
25
|
+
#num = {};
|
|
26
|
+
/** 哈希表 */
|
|
27
|
+
#hash = {};
|
|
28
|
+
/** 列表 */
|
|
29
|
+
#list = {};
|
|
30
|
+
/** 集合 */
|
|
31
|
+
#set = {};
|
|
32
|
+
/** 有序集合 */
|
|
33
|
+
#zset = {};
|
|
34
|
+
/** HyperLogLog */
|
|
35
|
+
#pf = {};
|
|
36
|
+
/** 位图 */
|
|
37
|
+
#bit = {};
|
|
38
|
+
/** sqlite3 */
|
|
39
|
+
#sqlite;
|
|
40
|
+
constructor(sqlite) {
|
|
41
|
+
super();
|
|
42
|
+
this.id = "mock";
|
|
43
|
+
this.store = {};
|
|
44
|
+
this.#str = {};
|
|
45
|
+
this.#num = {};
|
|
46
|
+
this.#hash = {};
|
|
47
|
+
this.#list = {};
|
|
48
|
+
this.#set = {};
|
|
49
|
+
this.#zset = {};
|
|
50
|
+
this.#pf = {};
|
|
51
|
+
this.#bit = {};
|
|
52
|
+
this.#sqlite = sqlite;
|
|
53
|
+
}
|
|
54
|
+
async init() {
|
|
55
|
+
await this.loadData();
|
|
56
|
+
/** 每30秒检查过期 */
|
|
57
|
+
setInterval(() => {
|
|
58
|
+
Object.keys(this.store).forEach((key) => this.checkExpire(key));
|
|
59
|
+
}, 3e4);
|
|
60
|
+
/** 每5分钟存一次sqlite */
|
|
61
|
+
setInterval(() => this.save(), 300 * 1e3);
|
|
62
|
+
return this;
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* @description 加载数据
|
|
66
|
+
*/
|
|
67
|
+
async loadData() {
|
|
68
|
+
const list = await this.#sqlite.getAllData();
|
|
69
|
+
const keyMap = {
|
|
70
|
+
["str"]: (key, value) => {
|
|
71
|
+
this.#str[key] = value;
|
|
72
|
+
},
|
|
73
|
+
["num"]: (key, value) => {
|
|
74
|
+
this.#num[key] = Number(value);
|
|
75
|
+
},
|
|
76
|
+
["hash"]: (key, value) => {
|
|
77
|
+
const hash = JSON.parse(value);
|
|
78
|
+
for (const field in hash) if (typeof hash[field] !== "string") hash[field] = Buffer.from(hash[field]);
|
|
79
|
+
this.#hash[key] = hash;
|
|
80
|
+
},
|
|
81
|
+
["list"]: (key, value) => {
|
|
82
|
+
this.#list[key] = JSON.parse(value);
|
|
83
|
+
},
|
|
84
|
+
["set"]: (key, value) => {
|
|
85
|
+
this.#set[key] = new Set(JSON.parse(value));
|
|
86
|
+
},
|
|
87
|
+
["zset"]: (key, value) => {
|
|
88
|
+
this.#zset[key] = JSON.parse(value);
|
|
89
|
+
},
|
|
90
|
+
["pf"]: (key, value) => {
|
|
91
|
+
this.#pf[key] = new Set(JSON.parse(value));
|
|
92
|
+
},
|
|
93
|
+
["bit"]: (key, value) => {
|
|
94
|
+
this.#bit[key] = Buffer.from(value);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
const isKey = (type) => {
|
|
98
|
+
return keyMap[type] !== void 0;
|
|
99
|
+
};
|
|
100
|
+
list.forEach((item) => {
|
|
101
|
+
const { key, type, expire, value } = item;
|
|
102
|
+
if (isKey(type)) {
|
|
103
|
+
this.store[key] = {
|
|
104
|
+
type,
|
|
105
|
+
expire
|
|
106
|
+
};
|
|
107
|
+
keyMap[type](key, value);
|
|
108
|
+
} else this.#sqlite.del(key);
|
|
109
|
+
});
|
|
110
|
+
logger.debug(`[Redis-mock] 加载数据完成: ${list.length}`);
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* @description 检查过期
|
|
114
|
+
* @param key 键
|
|
115
|
+
* @param isRemove 是否删除 默认删除
|
|
116
|
+
* @returns 是否过期或值
|
|
117
|
+
*/
|
|
118
|
+
checkExpire(key, isRemove = true) {
|
|
119
|
+
if (!this.store[key]) return false;
|
|
120
|
+
if (this.store[key].expire !== -1 && this.store[key].expire < moment().valueOf()) {
|
|
121
|
+
if (!isRemove) return true;
|
|
122
|
+
this.#del(key);
|
|
123
|
+
return true;
|
|
124
|
+
}
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
/**
|
|
128
|
+
* @description 直接删除键 不检查是否存在
|
|
129
|
+
* @param key 键
|
|
130
|
+
*/
|
|
131
|
+
#del(key) {
|
|
132
|
+
const { type } = this.store[key];
|
|
133
|
+
delete this.store[key];
|
|
134
|
+
switch (type) {
|
|
135
|
+
case "str":
|
|
136
|
+
delete this.#str[key];
|
|
137
|
+
break;
|
|
138
|
+
case "num":
|
|
139
|
+
delete this.#num[key];
|
|
140
|
+
break;
|
|
141
|
+
case "hash":
|
|
142
|
+
delete this.#hash[key];
|
|
143
|
+
break;
|
|
144
|
+
case "list":
|
|
145
|
+
delete this.#list[key];
|
|
146
|
+
break;
|
|
147
|
+
case "set":
|
|
148
|
+
delete this.#set[key];
|
|
149
|
+
break;
|
|
150
|
+
case "zset":
|
|
151
|
+
delete this.#zset[key];
|
|
152
|
+
break;
|
|
153
|
+
case "pf":
|
|
154
|
+
delete this.#pf[key];
|
|
155
|
+
break;
|
|
156
|
+
case "bit":
|
|
157
|
+
delete this.#bit[key];
|
|
158
|
+
break;
|
|
159
|
+
}
|
|
160
|
+
this.#sqlite.del(key);
|
|
161
|
+
logger.trace(`[Redis-mock] 删除键 ${key}`);
|
|
162
|
+
}
|
|
163
|
+
/**
|
|
164
|
+
* @description 获取键的类型
|
|
165
|
+
* @param key 键
|
|
166
|
+
* @returns 键的类型
|
|
167
|
+
*/
|
|
168
|
+
type(key) {
|
|
169
|
+
return this.store[key]?.type;
|
|
170
|
+
}
|
|
171
|
+
/**
|
|
172
|
+
* @description 存储键值对
|
|
173
|
+
* @param key 键
|
|
174
|
+
* @param value 值
|
|
175
|
+
* @param options 其他参数
|
|
176
|
+
*/
|
|
177
|
+
async set(key, value, options = {}) {
|
|
178
|
+
let expire = -1;
|
|
179
|
+
if (!Buffer.isBuffer(value) && typeof value !== "string") value = String(value);
|
|
180
|
+
else if (Buffer.isBuffer(value)) value = value.toString();
|
|
181
|
+
/** 参数3如果为空直接保存即可 */
|
|
182
|
+
if (lodash.isEmpty(options)) {
|
|
183
|
+
this.store[key] = {
|
|
184
|
+
type: "str",
|
|
185
|
+
expire
|
|
186
|
+
};
|
|
187
|
+
this.#str[key] = value;
|
|
188
|
+
this.#sqlite.set(key, value, "str", expire);
|
|
189
|
+
return "OK";
|
|
190
|
+
}
|
|
191
|
+
/** EX */
|
|
192
|
+
if (options?.EX !== void 0) {
|
|
193
|
+
const EX = Number(options.EX);
|
|
194
|
+
if (!isNaN(EX)) expire = moment().add(EX, "seconds").valueOf();
|
|
195
|
+
this.store[key] = {
|
|
196
|
+
type: "str",
|
|
197
|
+
expire
|
|
198
|
+
};
|
|
199
|
+
this.#str[key] = value;
|
|
200
|
+
} else if (options?.PX !== void 0) {
|
|
201
|
+
const PX = Number(options.PX);
|
|
202
|
+
if (!isNaN(PX)) expire = moment().add(PX, "milliseconds").valueOf();
|
|
203
|
+
this.store[key] = {
|
|
204
|
+
type: "str",
|
|
205
|
+
expire
|
|
206
|
+
};
|
|
207
|
+
this.#str[key] = value;
|
|
208
|
+
} else if (options?.EXAT !== void 0) {
|
|
209
|
+
const EXAT = Number(options.EXAT);
|
|
210
|
+
if (!isNaN(EXAT)) expire = EXAT;
|
|
211
|
+
this.store[key] = {
|
|
212
|
+
type: "str",
|
|
213
|
+
expire
|
|
214
|
+
};
|
|
215
|
+
this.#str[key] = value;
|
|
216
|
+
} else if (options?.PXAT !== void 0) {
|
|
217
|
+
const PXAT = Number(options.PXAT);
|
|
218
|
+
if (!isNaN(PXAT)) expire = PXAT;
|
|
219
|
+
this.store[key] = {
|
|
220
|
+
type: "str",
|
|
221
|
+
expire
|
|
222
|
+
};
|
|
223
|
+
this.#str[key] = value;
|
|
224
|
+
} else if (options?.KEEPTTL) if (this.#str[key] && this.store[key]) {
|
|
225
|
+
this.#str[key] = value;
|
|
226
|
+
expire = this.store[key].expire;
|
|
227
|
+
} else {
|
|
228
|
+
this.store[key] = {
|
|
229
|
+
type: "str",
|
|
230
|
+
expire: -1
|
|
231
|
+
};
|
|
232
|
+
this.#str[key] = value;
|
|
233
|
+
expire = -1;
|
|
234
|
+
}
|
|
235
|
+
else if (options?.NX) {
|
|
236
|
+
if (!this.store[key] || this.checkExpire(key, false)) {
|
|
237
|
+
this.store[key] = {
|
|
238
|
+
type: "str",
|
|
239
|
+
expire: -1
|
|
240
|
+
};
|
|
241
|
+
this.#str[key] = value;
|
|
242
|
+
this.#sqlite.set(key, value, "str", -1);
|
|
243
|
+
}
|
|
244
|
+
return "OK";
|
|
245
|
+
} else if (options?.XX) if (this.store[key]) {
|
|
246
|
+
const currentExpire = this.store[key].expire;
|
|
247
|
+
const oldType = this.store[key].type;
|
|
248
|
+
if (oldType !== "str") switch (oldType) {
|
|
249
|
+
case "num":
|
|
250
|
+
delete this.#num[key];
|
|
251
|
+
break;
|
|
252
|
+
case "hash":
|
|
253
|
+
delete this.#hash[key];
|
|
254
|
+
break;
|
|
255
|
+
case "list":
|
|
256
|
+
delete this.#list[key];
|
|
257
|
+
break;
|
|
258
|
+
case "set":
|
|
259
|
+
delete this.#set[key];
|
|
260
|
+
break;
|
|
261
|
+
case "zset":
|
|
262
|
+
delete this.#zset[key];
|
|
263
|
+
break;
|
|
264
|
+
case "pf":
|
|
265
|
+
delete this.#pf[key];
|
|
266
|
+
break;
|
|
267
|
+
case "bit":
|
|
268
|
+
delete this.#bit[key];
|
|
269
|
+
break;
|
|
270
|
+
}
|
|
271
|
+
this.store[key] = {
|
|
272
|
+
type: "str",
|
|
273
|
+
expire: currentExpire
|
|
274
|
+
};
|
|
275
|
+
this.#str[key] = value;
|
|
276
|
+
this.#sqlite.set(key, value, "str", currentExpire);
|
|
277
|
+
return "OK";
|
|
278
|
+
} else return null;
|
|
279
|
+
else if (options?.GET) {
|
|
280
|
+
this.store[key] = {
|
|
281
|
+
type: "str",
|
|
282
|
+
expire: -1
|
|
283
|
+
};
|
|
284
|
+
if (this.#str[key]) {
|
|
285
|
+
const oldValue = this.#str[key];
|
|
286
|
+
this.#str[key] = value;
|
|
287
|
+
this.#sqlite.set(key, value, "str", expire);
|
|
288
|
+
return oldValue;
|
|
289
|
+
}
|
|
290
|
+
this.#str[key] = value;
|
|
291
|
+
this.#sqlite.set(key, value, "str", expire);
|
|
292
|
+
return null;
|
|
293
|
+
} else {
|
|
294
|
+
this.store[key] = {
|
|
295
|
+
type: "str",
|
|
296
|
+
expire: -1
|
|
297
|
+
};
|
|
298
|
+
this.#str[key] = value;
|
|
299
|
+
}
|
|
300
|
+
this.#sqlite.set(key, value, "str", expire);
|
|
301
|
+
return "OK";
|
|
302
|
+
}
|
|
303
|
+
/**
|
|
304
|
+
* @description 获取键值
|
|
305
|
+
* @param key 键
|
|
306
|
+
*/
|
|
307
|
+
async get(key) {
|
|
308
|
+
if (!this.store[key]) return null;
|
|
309
|
+
/** 检查过期 */
|
|
310
|
+
if (this.checkExpire(key)) return null;
|
|
311
|
+
const { type } = this.store[key];
|
|
312
|
+
if (type === "num") return String(this.#num[key]);
|
|
313
|
+
else return this.#str[key].toString();
|
|
314
|
+
}
|
|
315
|
+
/**
|
|
316
|
+
* @description 设置键值对并指定过期时间(秒)
|
|
317
|
+
* @param key 键
|
|
318
|
+
* @param seconds 过期时间(秒)
|
|
319
|
+
* @param value 值
|
|
320
|
+
*/
|
|
321
|
+
async setEx(key, seconds, value) {
|
|
322
|
+
return await this.set(key, value, { EX: seconds });
|
|
323
|
+
}
|
|
324
|
+
/**
|
|
325
|
+
* @description 设置键值对并指定过期时间(毫秒)
|
|
326
|
+
* @param key 键
|
|
327
|
+
* @param milliseconds 过期时间(毫秒)
|
|
328
|
+
* @param value 值
|
|
329
|
+
*/
|
|
330
|
+
async pSetEx(key, milliseconds, value) {
|
|
331
|
+
return await this.set(key, value, { PX: milliseconds });
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* @description 仅当键不存在时设置键值对
|
|
335
|
+
* @param key 键
|
|
336
|
+
* @param value 值
|
|
337
|
+
* @returns 返回 1 表示键已设置,0 表示键已存在
|
|
338
|
+
*/
|
|
339
|
+
async setNX(key, value) {
|
|
340
|
+
if (this.store[key] && !this.checkExpire(key)) return 0;
|
|
341
|
+
await this.set(key, value, { NX: true });
|
|
342
|
+
return 1;
|
|
343
|
+
}
|
|
344
|
+
/**
|
|
345
|
+
* @description 获取键值并设置过期时间
|
|
346
|
+
* @param key 键
|
|
347
|
+
* @param options 过期时间选项(EX: 秒, PX: 毫秒, EXAT: 秒级时间戳, PXAT: 毫秒级时间戳)
|
|
348
|
+
*/
|
|
349
|
+
async getEx(key, options) {
|
|
350
|
+
const value = await this.get(key);
|
|
351
|
+
if (value === null) return null;
|
|
352
|
+
if (options?.PERSIST) {
|
|
353
|
+
if (this.store[key]) {
|
|
354
|
+
this.store[key].expire = -1;
|
|
355
|
+
const { type } = this.store[key];
|
|
356
|
+
const currentValue = this.getValueStringByKey(key);
|
|
357
|
+
this.#sqlite.set(key, currentValue, type, -1);
|
|
358
|
+
}
|
|
359
|
+
} else if (options?.EX !== void 0) await this.expire(key, options.EX);
|
|
360
|
+
else if (options?.PX !== void 0) {
|
|
361
|
+
const expire = moment().add(options.PX, "milliseconds").valueOf();
|
|
362
|
+
this.store[key].expire = expire;
|
|
363
|
+
const { type } = this.store[key];
|
|
364
|
+
const currentValue = this.getValueStringByKey(key);
|
|
365
|
+
this.#sqlite.set(key, currentValue, type, expire);
|
|
366
|
+
} else if (options?.EXAT !== void 0) {
|
|
367
|
+
this.store[key].expire = options.EXAT * 1e3;
|
|
368
|
+
const { type } = this.store[key];
|
|
369
|
+
const currentValue = this.getValueStringByKey(key);
|
|
370
|
+
this.#sqlite.set(key, currentValue, type, options.EXAT * 1e3);
|
|
371
|
+
} else if (options?.PXAT !== void 0) {
|
|
372
|
+
this.store[key].expire = options.PXAT;
|
|
373
|
+
const { type } = this.store[key];
|
|
374
|
+
const currentValue = this.getValueStringByKey(key);
|
|
375
|
+
this.#sqlite.set(key, currentValue, type, options.PXAT);
|
|
376
|
+
}
|
|
377
|
+
return value;
|
|
378
|
+
}
|
|
379
|
+
/**
|
|
380
|
+
* @description 获取键值并删除键
|
|
381
|
+
* @param key 键
|
|
382
|
+
*/
|
|
383
|
+
async getDel(key) {
|
|
384
|
+
const value = await this.get(key);
|
|
385
|
+
if (value !== null) await this.del(key);
|
|
386
|
+
return value;
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* @description 删除键
|
|
390
|
+
* @param key 键
|
|
391
|
+
*/
|
|
392
|
+
async del(key) {
|
|
393
|
+
if (!this.store[key]) return 0;
|
|
394
|
+
this.#del(key);
|
|
395
|
+
return 1;
|
|
396
|
+
}
|
|
397
|
+
/**
|
|
398
|
+
* @description 检查键是否存在
|
|
399
|
+
* @param key 键
|
|
400
|
+
*/
|
|
401
|
+
async exists(key) {
|
|
402
|
+
if (!this.store[key]) return 0;
|
|
403
|
+
if (this.checkExpire(key)) return 0;
|
|
404
|
+
return 1;
|
|
405
|
+
}
|
|
406
|
+
/**
|
|
407
|
+
* @description 设置键的过期时间
|
|
408
|
+
* @param key 键
|
|
409
|
+
* @param seconds 过期时间(秒)
|
|
410
|
+
*/
|
|
411
|
+
async expire(key, seconds) {
|
|
412
|
+
if (!this.store[key]) return 0;
|
|
413
|
+
const expire = moment().add(seconds, "seconds").valueOf();
|
|
414
|
+
this.store[key].expire = expire;
|
|
415
|
+
this.#sqlite.expire(key, expire);
|
|
416
|
+
return 1;
|
|
417
|
+
}
|
|
418
|
+
/**
|
|
419
|
+
* @description 设置键的过期时间戳(秒)
|
|
420
|
+
* @param key 键
|
|
421
|
+
* @param timestamp 过期时间戳(秒)
|
|
422
|
+
*/
|
|
423
|
+
async expireAt(key, timestamp) {
|
|
424
|
+
if (!this.store[key]) return 0;
|
|
425
|
+
this.store[key].expire = timestamp * 1e3;
|
|
426
|
+
this.#sqlite.expire(key, timestamp * 1e3);
|
|
427
|
+
return 1;
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* @description 设置键的过期时间(毫秒)
|
|
431
|
+
* @param key 键
|
|
432
|
+
* @param milliseconds 过期时间(毫秒)
|
|
433
|
+
*/
|
|
434
|
+
async pExpire(key, milliseconds) {
|
|
435
|
+
if (!this.store[key]) return 0;
|
|
436
|
+
const expire = moment().add(milliseconds, "milliseconds").valueOf();
|
|
437
|
+
this.store[key].expire = expire;
|
|
438
|
+
this.#sqlite.expire(key, expire);
|
|
439
|
+
return 1;
|
|
440
|
+
}
|
|
441
|
+
/**
|
|
442
|
+
* @description 设置键的过期时间戳(毫秒)
|
|
443
|
+
* @param key 键
|
|
444
|
+
* @param timestamp 过期时间戳(毫秒)
|
|
445
|
+
*/
|
|
446
|
+
async pExpireAt(key, timestamp) {
|
|
447
|
+
if (!this.store[key]) return 0;
|
|
448
|
+
this.store[key].expire = timestamp;
|
|
449
|
+
this.#sqlite.expire(key, timestamp);
|
|
450
|
+
return 1;
|
|
451
|
+
}
|
|
452
|
+
/**
|
|
453
|
+
* @description 移除键的过期时间
|
|
454
|
+
* @param key 键
|
|
455
|
+
*/
|
|
456
|
+
async persist(key) {
|
|
457
|
+
if (!this.store[key]) return 0;
|
|
458
|
+
if (this.store[key].expire === -1) return 0;
|
|
459
|
+
this.store[key].expire = -1;
|
|
460
|
+
const { type } = this.store[key];
|
|
461
|
+
const currentValue = this.getValueStringByKey(key);
|
|
462
|
+
this.#sqlite.set(key, currentValue, type, -1);
|
|
463
|
+
return 1;
|
|
464
|
+
}
|
|
465
|
+
/**
|
|
466
|
+
* @description 获取键的过期时间
|
|
467
|
+
* @param key 键
|
|
468
|
+
*/
|
|
469
|
+
async ttl(key) {
|
|
470
|
+
if (!this.store[key]) return -2;
|
|
471
|
+
if (this.store[key].expire === -1) return -1;
|
|
472
|
+
if (this.checkExpire(key)) return -2;
|
|
473
|
+
return moment(this.store[key].expire).diff(moment(), "seconds");
|
|
474
|
+
}
|
|
475
|
+
/**
|
|
476
|
+
* @description 获取键的过期时间(毫秒)
|
|
477
|
+
* @param key 键
|
|
478
|
+
*/
|
|
479
|
+
async pTTL(key) {
|
|
480
|
+
if (!this.store[key]) return -2;
|
|
481
|
+
if (this.store[key].expire === -1) return -1;
|
|
482
|
+
if (this.checkExpire(key)) return -2;
|
|
483
|
+
return moment(this.store[key].expire).diff(moment(), "milliseconds");
|
|
484
|
+
}
|
|
485
|
+
/**
|
|
486
|
+
* @description 获取字符串长度
|
|
487
|
+
* @param key 键
|
|
488
|
+
*/
|
|
489
|
+
async strLen(key) {
|
|
490
|
+
if (!this.store[key]) return 0;
|
|
491
|
+
if (this.checkExpire(key)) return 0;
|
|
492
|
+
const { type } = this.store[key];
|
|
493
|
+
if (type === "str") return this.#str[key].length;
|
|
494
|
+
return 0;
|
|
495
|
+
}
|
|
496
|
+
/**
|
|
497
|
+
* @description 重命名键
|
|
498
|
+
* @param key 原键名
|
|
499
|
+
* @param newKey 新键名
|
|
500
|
+
*/
|
|
501
|
+
async rename(key, newKey) {
|
|
502
|
+
if (!this.store[key]) throw new Error("no such key");
|
|
503
|
+
const { type, expire } = this.store[key];
|
|
504
|
+
const value = this.getValueStringByKey(key);
|
|
505
|
+
this.#del(key);
|
|
506
|
+
this.store[newKey] = {
|
|
507
|
+
type,
|
|
508
|
+
expire
|
|
509
|
+
};
|
|
510
|
+
switch (type) {
|
|
511
|
+
case "str":
|
|
512
|
+
this.#str[newKey] = value;
|
|
513
|
+
break;
|
|
514
|
+
case "num":
|
|
515
|
+
this.#num[newKey] = Number(value);
|
|
516
|
+
break;
|
|
517
|
+
case "hash":
|
|
518
|
+
this.#hash[newKey] = JSON.parse(value);
|
|
519
|
+
break;
|
|
520
|
+
case "list":
|
|
521
|
+
this.#list[newKey] = JSON.parse(value);
|
|
522
|
+
break;
|
|
523
|
+
case "set":
|
|
524
|
+
this.#set[newKey] = new Set(JSON.parse(value));
|
|
525
|
+
break;
|
|
526
|
+
case "zset":
|
|
527
|
+
this.#zset[newKey] = JSON.parse(value);
|
|
528
|
+
break;
|
|
529
|
+
case "pf":
|
|
530
|
+
this.#pf[newKey] = new Set(JSON.parse(value));
|
|
531
|
+
break;
|
|
532
|
+
case "bit":
|
|
533
|
+
this.#bit[newKey] = Buffer.from(value, "base64");
|
|
534
|
+
break;
|
|
535
|
+
}
|
|
536
|
+
this.#sqlite.set(newKey, value, type, expire);
|
|
537
|
+
return "OK";
|
|
538
|
+
}
|
|
539
|
+
/**
|
|
540
|
+
* @description 仅当新键不存在时重命名键
|
|
541
|
+
* @param key 原键名
|
|
542
|
+
* @param newKey 新键名
|
|
543
|
+
*/
|
|
544
|
+
async renameNX(key, newKey) {
|
|
545
|
+
if (!this.store[key]) return 0;
|
|
546
|
+
if (this.store[newKey] && !this.checkExpire(newKey)) return 0;
|
|
547
|
+
await this.rename(key, newKey);
|
|
548
|
+
return 1;
|
|
549
|
+
}
|
|
550
|
+
/**
|
|
551
|
+
* @description 返回数据库中键的数量
|
|
552
|
+
*/
|
|
553
|
+
async dbSize() {
|
|
554
|
+
Object.keys(this.store).forEach((key) => this.checkExpire(key));
|
|
555
|
+
return Object.keys(this.store).length;
|
|
556
|
+
}
|
|
557
|
+
/**
|
|
558
|
+
* @description 从数据库中随机返回一个键
|
|
559
|
+
*/
|
|
560
|
+
async randomKey() {
|
|
561
|
+
const keys = Object.keys(this.store);
|
|
562
|
+
if (keys.length === 0) return null;
|
|
563
|
+
keys.forEach((key) => this.checkExpire(key));
|
|
564
|
+
const validKeys = Object.keys(this.store);
|
|
565
|
+
if (validKeys.length === 0) return null;
|
|
566
|
+
return validKeys[Math.floor(Math.random() * validKeys.length)];
|
|
567
|
+
}
|
|
568
|
+
/**
|
|
569
|
+
* @description 获取所有键
|
|
570
|
+
* @param pattern 匹配规则
|
|
571
|
+
*/
|
|
572
|
+
async keys(pattern) {
|
|
573
|
+
const reg = new RegExp(pattern.replace(/\*/g, ".*"));
|
|
574
|
+
const keys = Object.keys(this.store);
|
|
575
|
+
const result = [];
|
|
576
|
+
await Promise.all(keys.map(async (key) => {
|
|
577
|
+
if (this.checkExpire(key)) return;
|
|
578
|
+
if (reg.test(key)) result.push(key);
|
|
579
|
+
}));
|
|
580
|
+
return result;
|
|
581
|
+
}
|
|
582
|
+
/**
|
|
583
|
+
* @description 清空所有键
|
|
584
|
+
*/
|
|
585
|
+
async flushAll() {
|
|
586
|
+
this.#str = {};
|
|
587
|
+
this.#num = {};
|
|
588
|
+
this.#hash = {};
|
|
589
|
+
this.#list = {};
|
|
590
|
+
this.#set = {};
|
|
591
|
+
this.#zset = {};
|
|
592
|
+
this.#pf = {};
|
|
593
|
+
this.#bit = {};
|
|
594
|
+
this.store = {};
|
|
595
|
+
return "OK";
|
|
596
|
+
}
|
|
597
|
+
/**
|
|
598
|
+
* @description 自增
|
|
599
|
+
* @param key 键
|
|
600
|
+
*/
|
|
601
|
+
async incr(key) {
|
|
602
|
+
if (!this.#num[key]) {
|
|
603
|
+
this.#num[key] = 0;
|
|
604
|
+
this.store[key] = {
|
|
605
|
+
type: "num",
|
|
606
|
+
expire: -1
|
|
607
|
+
};
|
|
608
|
+
} else if (this.checkExpire(key, false)) {
|
|
609
|
+
this.store[key].expire = -1;
|
|
610
|
+
this.#num[key] = 0;
|
|
611
|
+
}
|
|
612
|
+
this.#num[key] += 1;
|
|
613
|
+
this.#sqlite.set(key, String(this.#num[key]), "num", this.store[key].expire);
|
|
614
|
+
return this.#num[key];
|
|
615
|
+
}
|
|
616
|
+
/**
|
|
617
|
+
* @description 自增指定值
|
|
618
|
+
* @param key 键
|
|
619
|
+
* @param increment 增量
|
|
620
|
+
*/
|
|
621
|
+
async incrBy(key, increment) {
|
|
622
|
+
if (!this.#num[key]) {
|
|
623
|
+
this.#num[key] = 0;
|
|
624
|
+
this.store[key] = {
|
|
625
|
+
type: "num",
|
|
626
|
+
expire: -1
|
|
627
|
+
};
|
|
628
|
+
} else if (this.checkExpire(key, false)) {
|
|
629
|
+
this.store[key].expire = -1;
|
|
630
|
+
this.#num[key] = 0;
|
|
631
|
+
}
|
|
632
|
+
this.#num[key] += increment;
|
|
633
|
+
this.#sqlite.set(key, String(this.#num[key]), "num", this.store[key].expire);
|
|
634
|
+
return this.#num[key];
|
|
635
|
+
}
|
|
636
|
+
/**
|
|
637
|
+
* @description 自增指定浮点值
|
|
638
|
+
* @param key 键
|
|
639
|
+
* @param increment 增量(浮点数)
|
|
640
|
+
*/
|
|
641
|
+
async incrByFloat(key, increment) {
|
|
642
|
+
if (!this.#num[key]) {
|
|
643
|
+
this.#num[key] = 0;
|
|
644
|
+
this.store[key] = {
|
|
645
|
+
type: "num",
|
|
646
|
+
expire: -1
|
|
647
|
+
};
|
|
648
|
+
} else if (this.checkExpire(key, false)) {
|
|
649
|
+
this.store[key].expire = -1;
|
|
650
|
+
this.#num[key] = 0;
|
|
651
|
+
}
|
|
652
|
+
this.#num[key] += increment;
|
|
653
|
+
this.#sqlite.set(key, String(this.#num[key]), "num", this.store[key].expire);
|
|
654
|
+
return this.#num[key];
|
|
655
|
+
}
|
|
656
|
+
/**
|
|
657
|
+
* @description 自减
|
|
658
|
+
* @param key 键
|
|
659
|
+
*/
|
|
660
|
+
async decr(key) {
|
|
661
|
+
if (!this.#num[key]) {
|
|
662
|
+
this.#num[key] = 0;
|
|
663
|
+
this.store[key] = {
|
|
664
|
+
type: "num",
|
|
665
|
+
expire: -1
|
|
666
|
+
};
|
|
667
|
+
} else if (this.checkExpire(key, false)) {
|
|
668
|
+
this.store[key].expire = -1;
|
|
669
|
+
this.#num[key] = 0;
|
|
670
|
+
}
|
|
671
|
+
this.#num[key] -= 1;
|
|
672
|
+
this.#sqlite.set(key, String(this.#num[key]), "num", this.store[key].expire);
|
|
673
|
+
return this.#num[key];
|
|
674
|
+
}
|
|
675
|
+
/**
|
|
676
|
+
* @description 自减指定值
|
|
677
|
+
* @param key 键
|
|
678
|
+
* @param decrement 减量
|
|
679
|
+
*/
|
|
680
|
+
async decrBy(key, decrement) {
|
|
681
|
+
if (!this.#num[key]) {
|
|
682
|
+
this.#num[key] = 0;
|
|
683
|
+
this.store[key] = {
|
|
684
|
+
type: "num",
|
|
685
|
+
expire: -1
|
|
686
|
+
};
|
|
687
|
+
} else if (this.checkExpire(key, false)) {
|
|
688
|
+
this.store[key].expire = -1;
|
|
689
|
+
this.#num[key] = 0;
|
|
690
|
+
}
|
|
691
|
+
this.#num[key] -= decrement;
|
|
692
|
+
this.#sqlite.set(key, String(this.#num[key]), "num", this.store[key].expire);
|
|
693
|
+
return this.#num[key];
|
|
694
|
+
}
|
|
695
|
+
/**
|
|
696
|
+
* @description 追加字符串
|
|
697
|
+
* @param key 键
|
|
698
|
+
* @param value 值
|
|
699
|
+
*/
|
|
700
|
+
async append(key, value) {
|
|
701
|
+
if (!this.#str[key]) {
|
|
702
|
+
this.#str[key] = "";
|
|
703
|
+
this.store[key] = {
|
|
704
|
+
type: "str",
|
|
705
|
+
expire: -1
|
|
706
|
+
};
|
|
707
|
+
} else if (this.checkExpire(key, false)) {
|
|
708
|
+
this.store[key].expire = -1;
|
|
709
|
+
this.#str[key] = "";
|
|
710
|
+
}
|
|
711
|
+
if (Buffer.isBuffer(value)) this.#str[key] += Buffer.concat([Buffer.from(this.#str[key]), value]).toString();
|
|
712
|
+
else this.#str[key] += value;
|
|
713
|
+
this.#sqlite.set(key, this.#str[key], "str", this.store[key].expire);
|
|
714
|
+
return this.#str[key].length;
|
|
715
|
+
}
|
|
716
|
+
/**
|
|
717
|
+
* @description
|
|
718
|
+
* @param key
|
|
719
|
+
* @param field
|
|
720
|
+
* @param value
|
|
721
|
+
* @returns
|
|
722
|
+
*/
|
|
723
|
+
/**
|
|
724
|
+
* 将字段和值设置到指定键的哈希表中
|
|
725
|
+
* 如果键不存在,则创建一个新的哈希表
|
|
726
|
+
* @param key 哈希表的键
|
|
727
|
+
* @param field 哈希表中的字段
|
|
728
|
+
* @param value 要设置的值,可以是字符串或缓冲区
|
|
729
|
+
* @returns 返回 1 表示设置成功,0 表示设置失败
|
|
730
|
+
*/
|
|
731
|
+
async hSet(key, field, value) {
|
|
732
|
+
if (!this.#hash[key]) {
|
|
733
|
+
this.store[key] = {
|
|
734
|
+
type: "hash",
|
|
735
|
+
expire: -1
|
|
736
|
+
};
|
|
737
|
+
this.#hash[key] = {};
|
|
738
|
+
} else if (this.checkExpire(key, false)) {
|
|
739
|
+
this.store[key].expire = -1;
|
|
740
|
+
this.#hash[key] = {};
|
|
741
|
+
}
|
|
742
|
+
this.#hash[key][field] = value;
|
|
743
|
+
this.#sqlite.set(key, JSON.stringify(this.#hash[key]), "hash", this.store[key].expire);
|
|
744
|
+
return 1;
|
|
745
|
+
}
|
|
746
|
+
/**
|
|
747
|
+
* 获取哈希表字段的值
|
|
748
|
+
* @param key 哈希表的键
|
|
749
|
+
* @param field 哈希表中的字段
|
|
750
|
+
* @returns 返回字段的值,如果字段不存在则返回 null
|
|
751
|
+
*/
|
|
752
|
+
async hGet(key, field) {
|
|
753
|
+
if (!this.#hash[key] || !this.#hash[key][field]) return null;
|
|
754
|
+
if (this.checkExpire(key)) return null;
|
|
755
|
+
return this.#hash[key][field].toString();
|
|
756
|
+
}
|
|
757
|
+
/**
|
|
758
|
+
* 删除哈希表中的一个或多个字段
|
|
759
|
+
* @param key 哈希表的键
|
|
760
|
+
* @param field 要删除的字段
|
|
761
|
+
* @returns 返回成功删除的字段数量
|
|
762
|
+
*/
|
|
763
|
+
async hDel(key, field) {
|
|
764
|
+
if (!this.#hash[key] || !this.#hash[key][field]) return 0;
|
|
765
|
+
if (this.checkExpire(key)) return 0;
|
|
766
|
+
delete this.#hash[key][field];
|
|
767
|
+
this.#sqlite.set(key, JSON.stringify(this.#hash[key]), "hash", this.store[key].expire);
|
|
768
|
+
return 1;
|
|
769
|
+
}
|
|
770
|
+
/**
|
|
771
|
+
* 获取哈希表中所有字段的值
|
|
772
|
+
* @param key 哈希表的键
|
|
773
|
+
* @returns 返回所有字段的值
|
|
774
|
+
*/
|
|
775
|
+
async hGetAll(key) {
|
|
776
|
+
if (!this.#hash[key]) return {};
|
|
777
|
+
if (this.checkExpire(key)) return {};
|
|
778
|
+
return lodash.mapValues(this.#hash[key], (value) => value.toString());
|
|
779
|
+
}
|
|
780
|
+
/**
|
|
781
|
+
* 将一个或多个值插入到列表的头部
|
|
782
|
+
* @param key 列表的键
|
|
783
|
+
* @param values 要插入的值
|
|
784
|
+
*/
|
|
785
|
+
async lPush(key, ...values) {
|
|
786
|
+
if (!this.#list[key]) {
|
|
787
|
+
this.store[key] = {
|
|
788
|
+
type: "list",
|
|
789
|
+
expire: -1
|
|
790
|
+
};
|
|
791
|
+
this.#list[key] = [];
|
|
792
|
+
} else if (this.checkExpire(key, false)) {
|
|
793
|
+
this.store[key].expire = -1;
|
|
794
|
+
this.#list[key] = [];
|
|
795
|
+
}
|
|
796
|
+
this.#list[key].unshift(...values);
|
|
797
|
+
this.#sqlite.set(key, JSON.stringify(this.#list[key]), "list", this.store[key].expire);
|
|
798
|
+
return this.#list[key].length;
|
|
799
|
+
}
|
|
800
|
+
/**
|
|
801
|
+
* 将一个或多个值插入到列表的尾部
|
|
802
|
+
* @param key 列表的键
|
|
803
|
+
* @param values 要插入的值
|
|
804
|
+
*/
|
|
805
|
+
async rPush(key, ...values) {
|
|
806
|
+
if (!this.#list[key]) {
|
|
807
|
+
this.store[key] = {
|
|
808
|
+
type: "list",
|
|
809
|
+
expire: -1
|
|
810
|
+
};
|
|
811
|
+
this.#list[key] = [];
|
|
812
|
+
} else if (this.checkExpire(key, false)) {
|
|
813
|
+
this.store[key].expire = -1;
|
|
814
|
+
this.#list[key] = [];
|
|
815
|
+
}
|
|
816
|
+
this.#list[key].push(...values);
|
|
817
|
+
this.#sqlite.set(key, JSON.stringify(this.#list[key]), "list", this.store[key].expire);
|
|
818
|
+
return this.#list[key].length;
|
|
819
|
+
}
|
|
820
|
+
/**
|
|
821
|
+
* 移除并返回列表的第一个元素
|
|
822
|
+
* @param key 列表的键
|
|
823
|
+
* @returns 返回列表的第一个元素,如果列表为空则返回 null
|
|
824
|
+
*/
|
|
825
|
+
async lPop(key) {
|
|
826
|
+
if (!this.#list[key] || this.#list[key].length === 0) return null;
|
|
827
|
+
if (this.checkExpire(key)) return null;
|
|
828
|
+
const value = this.#list[key].shift();
|
|
829
|
+
this.#sqlite.set(key, JSON.stringify(this.#list[key]), "list", this.store[key].expire);
|
|
830
|
+
return value ? value.toString() : null;
|
|
831
|
+
}
|
|
832
|
+
/**
|
|
833
|
+
* 移除并返回列表的最后一个元素
|
|
834
|
+
* @param key 列表的键
|
|
835
|
+
* @returns 返回列表的最后一个元素,如果列表为空则返回 null
|
|
836
|
+
*/
|
|
837
|
+
async rPop(key) {
|
|
838
|
+
if (!this.#list[key] || this.#list[key].length === 0) return null;
|
|
839
|
+
if (this.checkExpire(key)) return null;
|
|
840
|
+
const value = this.#list[key].pop();
|
|
841
|
+
this.#sqlite.set(key, JSON.stringify(this.#list[key]), "list", this.store[key].expire);
|
|
842
|
+
return value ? value.toString() : null;
|
|
843
|
+
}
|
|
844
|
+
/**
|
|
845
|
+
* 返回列表指定范围内的元素
|
|
846
|
+
* @param key 列表的键
|
|
847
|
+
* @returns 返回列表指定范围内的元素
|
|
848
|
+
*/
|
|
849
|
+
async lRange(key, start, stop) {
|
|
850
|
+
if (!this.#list[key]) return [];
|
|
851
|
+
if (this.checkExpire(key)) return [];
|
|
852
|
+
const value = this.#list[key].slice(start, stop + 1).map((value) => value.toString());
|
|
853
|
+
this.#sqlite.set(key, JSON.stringify(this.#list[key]), "list", this.store[key].expire);
|
|
854
|
+
return value;
|
|
855
|
+
}
|
|
856
|
+
/**
|
|
857
|
+
* 向集合添加一个或多个成员
|
|
858
|
+
* @param key 集合的键
|
|
859
|
+
* @param members 要添加的成员
|
|
860
|
+
* @returns 返回成功添加的成员数量
|
|
861
|
+
*/
|
|
862
|
+
async sAdd(key, ...members) {
|
|
863
|
+
if (!this.#set[key]) {
|
|
864
|
+
this.store[key] = {
|
|
865
|
+
type: "set",
|
|
866
|
+
expire: -1
|
|
867
|
+
};
|
|
868
|
+
this.#set[key] = /* @__PURE__ */ new Set();
|
|
869
|
+
} else if (this.checkExpire(key, false)) {
|
|
870
|
+
this.store[key].expire = -1;
|
|
871
|
+
this.#set[key] = /* @__PURE__ */ new Set();
|
|
872
|
+
}
|
|
873
|
+
let added = 0;
|
|
874
|
+
for (const member of members) if (!this.#set[key].has(member.toString())) {
|
|
875
|
+
this.#set[key].add(member.toString());
|
|
876
|
+
added++;
|
|
877
|
+
}
|
|
878
|
+
this.#sqlite.set(key, JSON.stringify(this.#set[key]), "set", this.store[key].expire);
|
|
879
|
+
return added;
|
|
880
|
+
}
|
|
881
|
+
/**
|
|
882
|
+
* 从集合中移除一个或多个成员
|
|
883
|
+
* @param key 集合的键
|
|
884
|
+
* @param members 要移除的成员
|
|
885
|
+
* @returns 返回成功移除的成员数量
|
|
886
|
+
*/
|
|
887
|
+
async sRem(key, ...members) {
|
|
888
|
+
if (!this.#set[key]) return 0;
|
|
889
|
+
if (this.checkExpire(key)) return 0;
|
|
890
|
+
let removed = 0;
|
|
891
|
+
for (const member of members) if (this.#set[key].has(member.toString())) {
|
|
892
|
+
this.#set[key].delete(member.toString());
|
|
893
|
+
removed++;
|
|
894
|
+
}
|
|
895
|
+
this.#sqlite.set(key, JSON.stringify(this.#set[key]), "set", this.store[key].expire);
|
|
896
|
+
return removed;
|
|
897
|
+
}
|
|
898
|
+
/**
|
|
899
|
+
* 返回集合的所有成员
|
|
900
|
+
* @param key 集合的键
|
|
901
|
+
* @returns 返回集合的所有成员
|
|
902
|
+
*/
|
|
903
|
+
async sMembers(key) {
|
|
904
|
+
if (!this.#set[key]) return [];
|
|
905
|
+
if (this.checkExpire(key)) return [];
|
|
906
|
+
return Array.from(this.#set[key]).map((member) => member.toString());
|
|
907
|
+
}
|
|
908
|
+
/**
|
|
909
|
+
* 检查成员是否是集合的成员
|
|
910
|
+
* @param key 集合的键
|
|
911
|
+
* @param member 要检查的成员
|
|
912
|
+
* @returns 返回 1 表示是集合的成员,0 表示不是集合的成员
|
|
913
|
+
*/
|
|
914
|
+
async sismember(key, member) {
|
|
915
|
+
if (!this.#set[key]) return 0;
|
|
916
|
+
if (this.checkExpire(key)) return 0;
|
|
917
|
+
return this.#set[key].has(member.toString()) ? 1 : 0;
|
|
918
|
+
}
|
|
919
|
+
/**
|
|
920
|
+
* 向有序集合添加一个或多个成员
|
|
921
|
+
* @param key 有序集合的键
|
|
922
|
+
* @param score 分数
|
|
923
|
+
* @param member 成员
|
|
924
|
+
* @returns 返回成功添加的成员数量
|
|
925
|
+
*/
|
|
926
|
+
async zAdd(key, score, member) {
|
|
927
|
+
if (!this.#zset[key]) {
|
|
928
|
+
this.store[key] = {
|
|
929
|
+
type: "zset",
|
|
930
|
+
expire: -1
|
|
931
|
+
};
|
|
932
|
+
this.#zset[key] = [];
|
|
933
|
+
} else if (this.checkExpire(key, false)) {
|
|
934
|
+
this.store[key].expire = -1;
|
|
935
|
+
this.#zset[key] = [];
|
|
936
|
+
}
|
|
937
|
+
const index = this.#zset[key].findIndex((entry) => entry.member === member.toString());
|
|
938
|
+
if (index === -1) this.#zset[key].push({
|
|
939
|
+
score,
|
|
940
|
+
member
|
|
941
|
+
});
|
|
942
|
+
else this.#zset[key][index] = {
|
|
943
|
+
score,
|
|
944
|
+
member
|
|
945
|
+
};
|
|
946
|
+
this.#sqlite.set(key, JSON.stringify(this.#zset[key]), "zset", this.store[key].expire);
|
|
947
|
+
return 1;
|
|
948
|
+
}
|
|
949
|
+
/**
|
|
950
|
+
* 返回有序集合的成员数量
|
|
951
|
+
* @param key 有序集合的键
|
|
952
|
+
* @returns 返回有序集合的成员数量
|
|
953
|
+
*/
|
|
954
|
+
async zcard(key) {
|
|
955
|
+
if (!this.#list[key]) return 0;
|
|
956
|
+
if (this.checkExpire(key)) return 0;
|
|
957
|
+
return this.#list[key].length;
|
|
958
|
+
}
|
|
959
|
+
/**
|
|
960
|
+
* 返回有序集合中指定成员的排名
|
|
961
|
+
* @param key 有序集合的键
|
|
962
|
+
* @param member 成员
|
|
963
|
+
* @returns 返回成员的排名,如果成员不存在则返回 null
|
|
964
|
+
*/
|
|
965
|
+
async zRank(key, member) {
|
|
966
|
+
if (!this.#list[key]) return null;
|
|
967
|
+
if (this.checkExpire(key)) return null;
|
|
968
|
+
const index = this.#list[key].findIndex((entry) => entry === member.toString());
|
|
969
|
+
return index !== -1 ? index : null;
|
|
970
|
+
}
|
|
971
|
+
/**
|
|
972
|
+
* 返回有序集合中指定成员的分数
|
|
973
|
+
* @param key 有序集合的键
|
|
974
|
+
* @param member 成员
|
|
975
|
+
* @returns 返回成员的分数,如果成员不存在则返回 null
|
|
976
|
+
*/
|
|
977
|
+
async zScore(key, member) {
|
|
978
|
+
if (!this.#list[key]) return null;
|
|
979
|
+
if (this.checkExpire(key)) return null;
|
|
980
|
+
const entry = this.#list[key].find((entry) => entry === member.toString());
|
|
981
|
+
return entry ? entry.length : null;
|
|
982
|
+
}
|
|
983
|
+
/**
|
|
984
|
+
* 从 HyperLogLog 中添加一个或多个元素
|
|
985
|
+
* @param key HyperLogLog 的键
|
|
986
|
+
* @param elements 要添加的元素
|
|
987
|
+
* @returns 返回 1 表示添加成功,0 表示添加失败
|
|
988
|
+
*/
|
|
989
|
+
async pfAdd(key, ...elements) {
|
|
990
|
+
if (!this.#pf[key]) {
|
|
991
|
+
this.store[key] = {
|
|
992
|
+
type: "pf",
|
|
993
|
+
expire: -1
|
|
994
|
+
};
|
|
995
|
+
this.#pf[key] = /* @__PURE__ */ new Set();
|
|
996
|
+
} else if (this.checkExpire(key, false)) {
|
|
997
|
+
this.store[key].expire = -1;
|
|
998
|
+
this.#pf[key] = /* @__PURE__ */ new Set();
|
|
999
|
+
}
|
|
1000
|
+
let added = 0;
|
|
1001
|
+
for (const element of elements) if (!this.#pf[key].has(element.toString())) {
|
|
1002
|
+
this.#pf[key].add(element.toString());
|
|
1003
|
+
added++;
|
|
1004
|
+
}
|
|
1005
|
+
this.#sqlite.set(key, JSON.stringify(this.#pf[key]), "pf", this.store[key].expire);
|
|
1006
|
+
return added > 0;
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* 返回 HyperLogLog 的基数估算值
|
|
1010
|
+
* @param key HyperLogLog 的键
|
|
1011
|
+
* @returns 返回基数估算值
|
|
1012
|
+
*/
|
|
1013
|
+
async pfCount(key) {
|
|
1014
|
+
if (!this.#pf[key]) return 0;
|
|
1015
|
+
if (this.checkExpire(key)) return 0;
|
|
1016
|
+
return this.#pf[key].size;
|
|
1017
|
+
}
|
|
1018
|
+
/**
|
|
1019
|
+
* @description 发布消息到频道
|
|
1020
|
+
* @param channel 频道
|
|
1021
|
+
* @param message 消息
|
|
1022
|
+
* @returns 返回订阅者数量
|
|
1023
|
+
*/
|
|
1024
|
+
async publish(channel, message) {
|
|
1025
|
+
this.emit(channel, message.toString());
|
|
1026
|
+
return this.listenerCount(channel);
|
|
1027
|
+
}
|
|
1028
|
+
/**
|
|
1029
|
+
* @description 订阅一个或多个频道
|
|
1030
|
+
* @param channels 频道
|
|
1031
|
+
* @param listener 监听器
|
|
1032
|
+
* @returns 返回订阅的频道数量
|
|
1033
|
+
*/
|
|
1034
|
+
async subscribe(channels, listener) {
|
|
1035
|
+
channels.forEach((channel) => this.on(channel, listener));
|
|
1036
|
+
return channels.length;
|
|
1037
|
+
}
|
|
1038
|
+
/**
|
|
1039
|
+
* @description 取消订阅一个或多个频道
|
|
1040
|
+
* @param channels 频道
|
|
1041
|
+
* @param listener 监听器
|
|
1042
|
+
* @returns 返回取消订阅的频道数量
|
|
1043
|
+
*/
|
|
1044
|
+
async unsubscribe(channels, listener) {
|
|
1045
|
+
channels.forEach((channel) => this.off(channel, listener));
|
|
1046
|
+
return channels.length;
|
|
1047
|
+
}
|
|
1048
|
+
/**
|
|
1049
|
+
* @description 设置位图指定偏移量的值
|
|
1050
|
+
* @param key 键
|
|
1051
|
+
* @param offset 偏移量
|
|
1052
|
+
* @param value 值
|
|
1053
|
+
* @returns 返回设置前的位
|
|
1054
|
+
*/
|
|
1055
|
+
async setBit(key, offset, value) {
|
|
1056
|
+
if (!this.#bit[key]) {
|
|
1057
|
+
this.store[key] = {
|
|
1058
|
+
type: "bit",
|
|
1059
|
+
expire: -1
|
|
1060
|
+
};
|
|
1061
|
+
this.#bit[key] = Buffer.alloc(0);
|
|
1062
|
+
} else if (this.checkExpire(key, false)) {
|
|
1063
|
+
this.store[key].expire = -1;
|
|
1064
|
+
this.#bit[key] = Buffer.alloc(0);
|
|
1065
|
+
}
|
|
1066
|
+
const byteOffset = Math.floor(offset / 8);
|
|
1067
|
+
const bitOffset = offset % 8;
|
|
1068
|
+
const oldValue = this.#bit[key].readUInt8(byteOffset);
|
|
1069
|
+
const newValue = value ? oldValue | 1 << bitOffset : oldValue & ~(1 << bitOffset);
|
|
1070
|
+
this.#bit[key].writeUInt8(newValue, byteOffset);
|
|
1071
|
+
this.#sqlite.set(key, this.#bit[key].toString("base64"), "bit", this.store[key].expire);
|
|
1072
|
+
return oldValue;
|
|
1073
|
+
}
|
|
1074
|
+
/**
|
|
1075
|
+
* @description 获取位图指定偏移量的值
|
|
1076
|
+
* @param key 键
|
|
1077
|
+
* @param offset 偏移量
|
|
1078
|
+
* @returns 返回位
|
|
1079
|
+
*/
|
|
1080
|
+
async getBit(key, offset) {
|
|
1081
|
+
if (!this.#bit[key]) return 0;
|
|
1082
|
+
if (this.checkExpire(key)) return 0;
|
|
1083
|
+
const byteOffset = Math.floor(offset / 8);
|
|
1084
|
+
const bitOffset = offset % 8;
|
|
1085
|
+
return this.#bit[key].readUInt8(byteOffset) & 1 << bitOffset ? 1 : 0;
|
|
1086
|
+
}
|
|
1087
|
+
/**
|
|
1088
|
+
* @description 获取位图的指定范围内的位
|
|
1089
|
+
* @param key 键
|
|
1090
|
+
* @param start 起始偏移量
|
|
1091
|
+
* @param end 结束偏移量
|
|
1092
|
+
* @returns 返回位数组
|
|
1093
|
+
*/
|
|
1094
|
+
async getRange(key, start, end) {
|
|
1095
|
+
if (!this.#bit[key]) return [];
|
|
1096
|
+
if (this.checkExpire(key)) return [];
|
|
1097
|
+
const result = [];
|
|
1098
|
+
for (let i = start; i <= end; i++) result.push(await this.getBit(key, i));
|
|
1099
|
+
return result;
|
|
1100
|
+
}
|
|
1101
|
+
/**
|
|
1102
|
+
* 获取哈希表中字段是否存在
|
|
1103
|
+
* @param key 哈希表的键
|
|
1104
|
+
* @param field 哈希表中的字段
|
|
1105
|
+
* @returns 返回1表示字段存在,0表示字段不存在
|
|
1106
|
+
*/
|
|
1107
|
+
async hExists(key, field) {
|
|
1108
|
+
if (!this.#hash[key]) return 0;
|
|
1109
|
+
if (this.checkExpire(key)) return 0;
|
|
1110
|
+
return this.#hash[key][field] !== void 0 ? 1 : 0;
|
|
1111
|
+
}
|
|
1112
|
+
/**
|
|
1113
|
+
* 获取哈希表中所有字段名
|
|
1114
|
+
* @param key 哈希表的键
|
|
1115
|
+
* @returns 返回所有字段名数组
|
|
1116
|
+
*/
|
|
1117
|
+
async hKeys(key) {
|
|
1118
|
+
if (!this.#hash[key]) return [];
|
|
1119
|
+
if (this.checkExpire(key)) return [];
|
|
1120
|
+
return Object.keys(this.#hash[key]);
|
|
1121
|
+
}
|
|
1122
|
+
/**
|
|
1123
|
+
* 获取哈希表中所有值
|
|
1124
|
+
* @param key 哈希表的键
|
|
1125
|
+
* @returns 返回所有值数组
|
|
1126
|
+
*/
|
|
1127
|
+
async hVals(key) {
|
|
1128
|
+
if (!this.#hash[key]) return [];
|
|
1129
|
+
if (this.checkExpire(key)) return [];
|
|
1130
|
+
return Object.values(this.#hash[key]).map((value) => value.toString());
|
|
1131
|
+
}
|
|
1132
|
+
/**
|
|
1133
|
+
* 获取哈希表中字段数量
|
|
1134
|
+
* @param key 哈希表的键
|
|
1135
|
+
* @returns 返回字段数量
|
|
1136
|
+
*/
|
|
1137
|
+
async hLen(key) {
|
|
1138
|
+
if (!this.#hash[key]) return 0;
|
|
1139
|
+
if (this.checkExpire(key)) return 0;
|
|
1140
|
+
return Object.keys(this.#hash[key]).length;
|
|
1141
|
+
}
|
|
1142
|
+
/**
|
|
1143
|
+
* 批量获取哈希表中字段的值
|
|
1144
|
+
* @param key 哈希表的键
|
|
1145
|
+
* @param fields 要获取的字段数组
|
|
1146
|
+
* @returns 返回字段值数组,不存在的字段返回null
|
|
1147
|
+
*/
|
|
1148
|
+
async hMGet(key, ...fields) {
|
|
1149
|
+
if (!this.#hash[key]) return fields.map(() => null);
|
|
1150
|
+
if (this.checkExpire(key)) return fields.map(() => null);
|
|
1151
|
+
return fields.map((field) => {
|
|
1152
|
+
const value = this.#hash[key][field];
|
|
1153
|
+
return value !== void 0 ? value.toString() : null;
|
|
1154
|
+
});
|
|
1155
|
+
}
|
|
1156
|
+
/**
|
|
1157
|
+
* 批量设置哈希表中字段的值
|
|
1158
|
+
* @param key 哈希表的键
|
|
1159
|
+
* @param fieldValues 字段和值的数组,格式为[field1, value1, field2, value2, ...]
|
|
1160
|
+
* @returns 返回"OK"
|
|
1161
|
+
*/
|
|
1162
|
+
async hMSet(key, ...fieldValues) {
|
|
1163
|
+
if (!this.#hash[key]) {
|
|
1164
|
+
this.store[key] = {
|
|
1165
|
+
type: "hash",
|
|
1166
|
+
expire: -1
|
|
1167
|
+
};
|
|
1168
|
+
this.#hash[key] = {};
|
|
1169
|
+
} else if (this.checkExpire(key, false)) {
|
|
1170
|
+
this.store[key].expire = -1;
|
|
1171
|
+
this.#hash[key] = {};
|
|
1172
|
+
}
|
|
1173
|
+
for (let i = 0; i < fieldValues.length; i += 2) if (i + 1 < fieldValues.length) {
|
|
1174
|
+
const field = fieldValues[i].toString();
|
|
1175
|
+
const value = fieldValues[i + 1];
|
|
1176
|
+
this.#hash[key][field] = value;
|
|
1177
|
+
}
|
|
1178
|
+
this.#sqlite.set(key, JSON.stringify(this.#hash[key]), "hash", this.store[key].expire);
|
|
1179
|
+
return "OK";
|
|
1180
|
+
}
|
|
1181
|
+
/**
|
|
1182
|
+
* 获取列表长度
|
|
1183
|
+
* @param key 列表的键
|
|
1184
|
+
* @returns 返回列表长度
|
|
1185
|
+
*/
|
|
1186
|
+
async lLen(key) {
|
|
1187
|
+
if (!this.#list[key]) return 0;
|
|
1188
|
+
if (this.checkExpire(key)) return 0;
|
|
1189
|
+
return this.#list[key].length;
|
|
1190
|
+
}
|
|
1191
|
+
/**
|
|
1192
|
+
* 获取列表指定索引的元素
|
|
1193
|
+
* @param key 列表的键
|
|
1194
|
+
* @param index 索引,0表示第一个元素,-1表示最后一个元素
|
|
1195
|
+
* @returns 返回元素值,索引超出范围返回null
|
|
1196
|
+
*/
|
|
1197
|
+
async lIndex(key, index) {
|
|
1198
|
+
if (!this.#list[key]) return null;
|
|
1199
|
+
if (this.checkExpire(key)) return null;
|
|
1200
|
+
const list = this.#list[key];
|
|
1201
|
+
if (index < 0) index = list.length + index;
|
|
1202
|
+
if (index < 0 || index >= list.length) return null;
|
|
1203
|
+
return list[index].toString();
|
|
1204
|
+
}
|
|
1205
|
+
/**
|
|
1206
|
+
* 设置列表指定索引的元素值
|
|
1207
|
+
* @param key 列表的键
|
|
1208
|
+
* @param index 索引
|
|
1209
|
+
* @param value 值
|
|
1210
|
+
* @returns 成功返回"OK",失败返回错误
|
|
1211
|
+
*/
|
|
1212
|
+
async lSet(key, index, value) {
|
|
1213
|
+
if (!this.#list[key]) return null;
|
|
1214
|
+
if (this.checkExpire(key)) return null;
|
|
1215
|
+
const list = this.#list[key];
|
|
1216
|
+
if (index < 0) index = list.length + index;
|
|
1217
|
+
if (index < 0 || index >= list.length) return null;
|
|
1218
|
+
list[index] = value;
|
|
1219
|
+
this.#sqlite.set(key, JSON.stringify(list), "list", this.store[key].expire);
|
|
1220
|
+
return "OK";
|
|
1221
|
+
}
|
|
1222
|
+
/**
|
|
1223
|
+
* 移除列表中与指定值相等的元素
|
|
1224
|
+
* @param key 列表的键
|
|
1225
|
+
* @param count 移除的数量,0表示移除所有匹配的元素,正数表示从头部开始移除,负数表示从尾部开始移除
|
|
1226
|
+
* @param value 要移除的值
|
|
1227
|
+
* @returns 返回移除的元素数量
|
|
1228
|
+
*/
|
|
1229
|
+
async lRem(key, count, value) {
|
|
1230
|
+
if (!this.#list[key]) return 0;
|
|
1231
|
+
if (this.checkExpire(key)) return 0;
|
|
1232
|
+
const list = this.#list[key];
|
|
1233
|
+
const strValue = value.toString();
|
|
1234
|
+
let removed = 0;
|
|
1235
|
+
if (count === 0) {
|
|
1236
|
+
const newList = list.filter((item) => item.toString() !== strValue);
|
|
1237
|
+
removed = list.length - newList.length;
|
|
1238
|
+
this.#list[key] = newList;
|
|
1239
|
+
} else if (count > 0) {
|
|
1240
|
+
for (let i = 0; i < list.length && removed < count; i++) if (list[i].toString() === strValue) {
|
|
1241
|
+
list.splice(i, 1);
|
|
1242
|
+
removed++;
|
|
1243
|
+
i--;
|
|
1244
|
+
}
|
|
1245
|
+
} else {
|
|
1246
|
+
count = Math.abs(count);
|
|
1247
|
+
for (let i = list.length - 1; i >= 0 && removed < count; i--) if (list[i].toString() === strValue) {
|
|
1248
|
+
list.splice(i, 1);
|
|
1249
|
+
removed++;
|
|
1250
|
+
}
|
|
1251
|
+
}
|
|
1252
|
+
this.#sqlite.set(key, JSON.stringify(list), "list", this.store[key].expire);
|
|
1253
|
+
return removed;
|
|
1254
|
+
}
|
|
1255
|
+
/**
|
|
1256
|
+
* 获取集合中元素数量
|
|
1257
|
+
* @param key 集合的键
|
|
1258
|
+
* @returns 返回集合中元素数量
|
|
1259
|
+
*/
|
|
1260
|
+
async sCard(key) {
|
|
1261
|
+
if (!this.#set[key]) return 0;
|
|
1262
|
+
if (this.checkExpire(key)) return 0;
|
|
1263
|
+
return this.#set[key].size;
|
|
1264
|
+
}
|
|
1265
|
+
/**
|
|
1266
|
+
* 计算集合的差集
|
|
1267
|
+
* @param keys 集合的键数组
|
|
1268
|
+
* @returns 返回差集数组
|
|
1269
|
+
*/
|
|
1270
|
+
async sDiff(...keys) {
|
|
1271
|
+
if (keys.length === 0) return [];
|
|
1272
|
+
const firstKey = keys[0];
|
|
1273
|
+
if (!this.#set[firstKey]) return [];
|
|
1274
|
+
if (this.checkExpire(firstKey)) return [];
|
|
1275
|
+
const result = /* @__PURE__ */ new Set();
|
|
1276
|
+
for (const item of this.#set[firstKey]) result.add(item.toString());
|
|
1277
|
+
for (let i = 1; i < keys.length; i++) {
|
|
1278
|
+
const key = keys[i];
|
|
1279
|
+
if (!this.#set[key] || this.checkExpire(key)) continue;
|
|
1280
|
+
for (const item of this.#set[key]) result.delete(item.toString());
|
|
1281
|
+
}
|
|
1282
|
+
return Array.from(result);
|
|
1283
|
+
}
|
|
1284
|
+
/**
|
|
1285
|
+
* 计算集合的交集
|
|
1286
|
+
* @param keys 集合的键数组
|
|
1287
|
+
* @returns 返回交集数组
|
|
1288
|
+
*/
|
|
1289
|
+
async sInter(...keys) {
|
|
1290
|
+
if (keys.length === 0) return [];
|
|
1291
|
+
const validKeys = keys.filter((key) => this.#set[key] && !this.checkExpire(key));
|
|
1292
|
+
if (validKeys.length === 0) return [];
|
|
1293
|
+
const result = /* @__PURE__ */ new Set();
|
|
1294
|
+
const firstKey = validKeys[0];
|
|
1295
|
+
for (const item of this.#set[firstKey]) {
|
|
1296
|
+
let inAllSets = true;
|
|
1297
|
+
for (let i = 1; i < validKeys.length; i++) {
|
|
1298
|
+
const key = validKeys[i];
|
|
1299
|
+
if (!this.#set[key].has(item.toString())) {
|
|
1300
|
+
inAllSets = false;
|
|
1301
|
+
break;
|
|
1302
|
+
}
|
|
1303
|
+
}
|
|
1304
|
+
if (inAllSets) result.add(item.toString());
|
|
1305
|
+
}
|
|
1306
|
+
return Array.from(result);
|
|
1307
|
+
}
|
|
1308
|
+
/**
|
|
1309
|
+
* 计算集合的并集
|
|
1310
|
+
* @param keys 集合的键数组
|
|
1311
|
+
* @returns 返回并集数组
|
|
1312
|
+
*/
|
|
1313
|
+
async sUnion(...keys) {
|
|
1314
|
+
const result = /* @__PURE__ */ new Set();
|
|
1315
|
+
for (const key of keys) {
|
|
1316
|
+
if (!this.#set[key] || this.checkExpire(key)) continue;
|
|
1317
|
+
for (const item of this.#set[key]) result.add(item.toString());
|
|
1318
|
+
}
|
|
1319
|
+
return Array.from(result);
|
|
1320
|
+
}
|
|
1321
|
+
/**
|
|
1322
|
+
* 将多个键的值同时获取
|
|
1323
|
+
* @param keys 要获取的键数组
|
|
1324
|
+
* @returns 返回值数组,不存在的键返回null
|
|
1325
|
+
*/
|
|
1326
|
+
async mGet(...keys) {
|
|
1327
|
+
return Promise.all(keys.map((key) => this.get(key)));
|
|
1328
|
+
}
|
|
1329
|
+
/**
|
|
1330
|
+
* 同时设置多个键值对
|
|
1331
|
+
* @param keyValues 键值对数组,格式为[key1, value1, key2, value2, ...]
|
|
1332
|
+
* @returns 返回"OK"
|
|
1333
|
+
*/
|
|
1334
|
+
async mSet(...keyValues) {
|
|
1335
|
+
for (let i = 0; i < keyValues.length; i += 2) if (i + 1 < keyValues.length) {
|
|
1336
|
+
const key = keyValues[i].toString();
|
|
1337
|
+
const value = keyValues[i + 1];
|
|
1338
|
+
await this.set(key, value);
|
|
1339
|
+
}
|
|
1340
|
+
return "OK";
|
|
1341
|
+
}
|
|
1342
|
+
/**
|
|
1343
|
+
* 设置键的新值并返回旧值
|
|
1344
|
+
* @param key 键
|
|
1345
|
+
* @param value 新值
|
|
1346
|
+
* @returns 返回旧值,如果键不存在则返回null
|
|
1347
|
+
*/
|
|
1348
|
+
async getSet(key, value) {
|
|
1349
|
+
const oldValue = await this.get(key);
|
|
1350
|
+
await this.set(key, value);
|
|
1351
|
+
return oldValue;
|
|
1352
|
+
}
|
|
1353
|
+
/**
|
|
1354
|
+
* 修复zRange方法,使用正确的zset数据结构
|
|
1355
|
+
* 返回有序集合的成员数量
|
|
1356
|
+
* @param key 有序集合的键
|
|
1357
|
+
* @returns 返回有序集合的成员数量
|
|
1358
|
+
*/
|
|
1359
|
+
async zRange(key, start, stop) {
|
|
1360
|
+
if (!this.#zset[key]) return [];
|
|
1361
|
+
if (this.checkExpire(key)) return [];
|
|
1362
|
+
return [...this.#zset[key]].sort((a, b) => a.score - b.score).slice(start, stop + 1).map((entry) => entry.member.toString());
|
|
1363
|
+
}
|
|
1364
|
+
/**
|
|
1365
|
+
* 按分数范围返回有序集合的成员
|
|
1366
|
+
* @param key 有序集合的键
|
|
1367
|
+
* @param min 最小分数
|
|
1368
|
+
* @param max 最大分数
|
|
1369
|
+
* @returns 返回指定分数范围的成员数组
|
|
1370
|
+
*/
|
|
1371
|
+
async zRangeByScore(key, min, max) {
|
|
1372
|
+
if (!this.#zset[key]) return [];
|
|
1373
|
+
if (this.checkExpire(key)) return [];
|
|
1374
|
+
return this.#zset[key].filter((entry) => entry.score >= min && entry.score <= max).sort((a, b) => a.score - b.score).map((entry) => entry.member.toString());
|
|
1375
|
+
}
|
|
1376
|
+
/**
|
|
1377
|
+
* 按分数降序返回有序集合的成员
|
|
1378
|
+
* @param key 有序集合的键
|
|
1379
|
+
* @param start 起始索引
|
|
1380
|
+
* @param stop 结束索引
|
|
1381
|
+
* @returns 返回指定范围的成员数组(按分数降序)
|
|
1382
|
+
*/
|
|
1383
|
+
async zRevRange(key, start, stop) {
|
|
1384
|
+
if (!this.#zset[key]) return [];
|
|
1385
|
+
if (this.checkExpire(key)) return [];
|
|
1386
|
+
return [...this.#zset[key]].sort((a, b) => b.score - a.score).slice(start, stop + 1).map((entry) => entry.member.toString());
|
|
1387
|
+
}
|
|
1388
|
+
/**
|
|
1389
|
+
* 获取有序集合中指定成员的排名(按分数降序)
|
|
1390
|
+
* @param key 有序集合的键
|
|
1391
|
+
* @param member 成员
|
|
1392
|
+
* @returns 返回成员的排名,不存在返回null
|
|
1393
|
+
*/
|
|
1394
|
+
async zRevRank(key, member) {
|
|
1395
|
+
if (!this.#zset[key]) return null;
|
|
1396
|
+
if (this.checkExpire(key)) return null;
|
|
1397
|
+
const strMember = member.toString();
|
|
1398
|
+
const sortedEntries = [...this.#zset[key]].sort((a, b) => b.score - a.score);
|
|
1399
|
+
for (let i = 0; i < sortedEntries.length; i++) if (sortedEntries[i].member.toString() === strMember) return i;
|
|
1400
|
+
return null;
|
|
1401
|
+
}
|
|
1402
|
+
/**
|
|
1403
|
+
* 从有序集合中移除一个或多个成员
|
|
1404
|
+
* @param key 有序集合的键
|
|
1405
|
+
* @param member 要移除的成员
|
|
1406
|
+
* @returns 返回成功移除的成员数量
|
|
1407
|
+
*/
|
|
1408
|
+
async zRem(key, member) {
|
|
1409
|
+
if (!this.#zset[key]) return 0;
|
|
1410
|
+
if (this.checkExpire(key)) return 0;
|
|
1411
|
+
const strMember = member.toString();
|
|
1412
|
+
const index = this.#zset[key].findIndex((entry) => entry.member.toString() === strMember);
|
|
1413
|
+
if (index !== -1) {
|
|
1414
|
+
this.#zset[key].splice(index, 1);
|
|
1415
|
+
this.#sqlite.set(key, JSON.stringify(this.#zset[key]), "zset", this.store[key].expire);
|
|
1416
|
+
return 1;
|
|
1417
|
+
}
|
|
1418
|
+
return 0;
|
|
1419
|
+
}
|
|
1420
|
+
/**
|
|
1421
|
+
* 根据键类型获取值的字符串表示
|
|
1422
|
+
* @param key 键名
|
|
1423
|
+
* @returns 值的字符串表示
|
|
1424
|
+
*/
|
|
1425
|
+
getValueStringByKey(key) {
|
|
1426
|
+
const { type } = this.store[key];
|
|
1427
|
+
switch (type) {
|
|
1428
|
+
case "str": return this.#str[key];
|
|
1429
|
+
case "num": return String(this.#num[key]);
|
|
1430
|
+
case "hash": return JSON.stringify(this.#hash[key]);
|
|
1431
|
+
case "list": return JSON.stringify(this.#list[key]);
|
|
1432
|
+
case "set": return JSON.stringify(Array.from(this.#set[key]));
|
|
1433
|
+
case "zset": return JSON.stringify(this.#zset[key]);
|
|
1434
|
+
case "pf": return JSON.stringify(Array.from(this.#pf[key]));
|
|
1435
|
+
case "bit": return this.#bit[key].toString("base64");
|
|
1436
|
+
default: return "";
|
|
1437
|
+
}
|
|
1438
|
+
}
|
|
1439
|
+
async save() {
|
|
1440
|
+
const { removed, added, common } = diffSimpleArray(await this.#sqlite.keys(), Object.keys(this.store));
|
|
1441
|
+
removed.forEach((key) => {
|
|
1442
|
+
this.#sqlite.del(key);
|
|
1443
|
+
});
|
|
1444
|
+
added.forEach((key) => {
|
|
1445
|
+
const { type, expire } = this.store[key];
|
|
1446
|
+
const value = this.getValueStringByKey(key);
|
|
1447
|
+
this.#sqlite.set(key, value, type, expire);
|
|
1448
|
+
});
|
|
1449
|
+
await Promise.all(common.map(async (key) => {
|
|
1450
|
+
const data = this.store[key];
|
|
1451
|
+
if (!data) return;
|
|
1452
|
+
const { type, expire } = data;
|
|
1453
|
+
const currentValue = this.getValueStringByKey(key);
|
|
1454
|
+
const sql = await this.#sqlite.get(key);
|
|
1455
|
+
if (sql?.expire !== expire || sql?.value !== currentValue) this.#sqlite.set(key, currentValue, type, expire);
|
|
1456
|
+
}));
|
|
1457
|
+
return "OK";
|
|
1458
|
+
}
|
|
1459
|
+
};
|
|
1460
|
+
|
|
1461
|
+
//#endregion
|
|
1462
|
+
//#region src/core/db/redis/redis.ts
|
|
1463
|
+
var redis_exports = /* @__PURE__ */ __exportAll({
|
|
1464
|
+
createRedis: () => createRedis,
|
|
1465
|
+
redis: () => redis
|
|
1466
|
+
});
|
|
1467
|
+
/**
|
|
1468
|
+
* @public
|
|
1469
|
+
* @description Redis数据库
|
|
1470
|
+
*/
|
|
1471
|
+
let redis;
|
|
1472
|
+
/**
|
|
1473
|
+
* @description 创建 Redis 客户端
|
|
1474
|
+
* @param options Redis 客户端配置
|
|
1475
|
+
* @returns Redis 客户端
|
|
1476
|
+
*/
|
|
1477
|
+
const create = async (options) => {
|
|
1478
|
+
try {
|
|
1479
|
+
const client = createClient(options);
|
|
1480
|
+
await client.connect();
|
|
1481
|
+
Object.defineProperty(client, "id", { value: "Redis" });
|
|
1482
|
+
return client;
|
|
1483
|
+
} catch (error) {
|
|
1484
|
+
logger.debug(`${logger.red("[redis] 启动失败:")} ${JSON.stringify(options)}`);
|
|
1485
|
+
logger.debug(error);
|
|
1486
|
+
}
|
|
1487
|
+
};
|
|
1488
|
+
/**
|
|
1489
|
+
* @description 尝试主动拉起 Redis
|
|
1490
|
+
* @returns 是否成功
|
|
1491
|
+
*/
|
|
1492
|
+
const start = async () => {
|
|
1493
|
+
logger.debug("[redis] 正在尝试启动 Redis...");
|
|
1494
|
+
if (isWin()) {
|
|
1495
|
+
const result = await exec("redis-server.exe redis.conf", { booleanResult: true });
|
|
1496
|
+
if (result) return result;
|
|
1497
|
+
return await exec("net start Redis", { booleanResult: true });
|
|
1498
|
+
}
|
|
1499
|
+
return await exec("redis-server --save 300 10 --daemonize yes" + await isArm64(), { booleanResult: true });
|
|
1500
|
+
};
|
|
1501
|
+
/**
|
|
1502
|
+
* @description Redis 服务
|
|
1503
|
+
* @returns Redis 客户端
|
|
1504
|
+
*/
|
|
1505
|
+
const createRedis = async () => {
|
|
1506
|
+
try {
|
|
1507
|
+
const options = redis$1();
|
|
1508
|
+
let client = await create(options);
|
|
1509
|
+
if (client) {
|
|
1510
|
+
logger.info(`[redis] ${logger.green("Redis 连接成功")}`);
|
|
1511
|
+
redis = client;
|
|
1512
|
+
return redis;
|
|
1513
|
+
}
|
|
1514
|
+
if (await start()) {
|
|
1515
|
+
logger.debug(logger.green("[redis] 主动拉起 Redis 成功"));
|
|
1516
|
+
client = await create(options);
|
|
1517
|
+
} else logger.debug(logger.red("[redis] 主动拉起 Redis 失败"));
|
|
1518
|
+
if (client) {
|
|
1519
|
+
redis = client;
|
|
1520
|
+
return redis;
|
|
1521
|
+
}
|
|
1522
|
+
throw new Error("Redis 启动失败");
|
|
1523
|
+
} catch (error) {
|
|
1524
|
+
logger.debug(`[redis] ${logger.red("Redis 连接失败")}`);
|
|
1525
|
+
logger.debug(error);
|
|
1526
|
+
logger.debug(logger.yellow("[redis] 将降级为 redis-mock 实现"));
|
|
1527
|
+
redis = await mock();
|
|
1528
|
+
return redis;
|
|
1529
|
+
}
|
|
1530
|
+
};
|
|
1531
|
+
/**
|
|
1532
|
+
* @description 判断是否为 arm64 架构
|
|
1533
|
+
* @returns 返回 arm64 专属参数
|
|
1534
|
+
*/
|
|
1535
|
+
const isArm64 = async () => {
|
|
1536
|
+
if (os.arch() !== "arm64") return "";
|
|
1537
|
+
/** 提取版本 只有>=6的版本才忽略警告 */
|
|
1538
|
+
const { stdout } = await exec("redis-server -v");
|
|
1539
|
+
const version = stdout.toString();
|
|
1540
|
+
if (!version) return "";
|
|
1541
|
+
const RedisVersion = version.match(/v=(\d)./);
|
|
1542
|
+
if (RedisVersion && Number(RedisVersion[1]) >= 6) return " --ignore-warnings ARM64-COW-BUG";
|
|
1543
|
+
return "";
|
|
1544
|
+
};
|
|
1545
|
+
/**
|
|
1546
|
+
* @description 伪 Redis 实现
|
|
1547
|
+
* @returns Redis 客户端
|
|
1548
|
+
*/
|
|
1549
|
+
const mock = async () => {
|
|
1550
|
+
const redis = await new RedisClient(await new SQLiteWrapper(path.join(redisSqlite3Path, "redis.db")).init()).init();
|
|
1551
|
+
Object.defineProperty(redis, "id", { value: "mock" });
|
|
1552
|
+
return redis;
|
|
1553
|
+
};
|
|
1554
|
+
|
|
1555
|
+
//#endregion
|
|
1556
|
+
export { redis as n, redis_exports as r, createRedis as t };
|