@h-ai/cache 0.1.0-alpha.66 → 0.1.0-alpha.68
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.en.md +71 -41
- package/README.ja.md +72 -42
- package/README.md +83 -42
- package/dist/index.d.ts +291 -93
- package/dist/index.js +423 -133
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.d.ts
CHANGED
|
@@ -9,14 +9,35 @@ import { HaiResult } from '@h-ai/core';
|
|
|
9
9
|
* @module cache-config
|
|
10
10
|
*/
|
|
11
11
|
|
|
12
|
-
/**
|
|
12
|
+
/**
|
|
13
|
+
* Redis 集群节点配置
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* import { RedisClusterNodeSchema } from '@h-ai/cache'
|
|
17
|
+
* const config = RedisClusterNodeSchema.parse({ host: 'redis.internal', port: 6379 })
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
13
20
|
declare const RedisClusterNodeSchema: z.ZodObject<{
|
|
14
21
|
host: z.ZodString;
|
|
15
22
|
port: z.ZodNumber;
|
|
16
23
|
}, z.core.$strip>;
|
|
17
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* Redis 集群节点类型
|
|
26
|
+
* @example
|
|
27
|
+
* ```ts
|
|
28
|
+
* import type { RedisClusterNode } from '@h-ai/cache'
|
|
29
|
+
* const options: RedisClusterNode = { host: 'redis.internal', port: 6379 }
|
|
30
|
+
* ```
|
|
31
|
+
*/
|
|
18
32
|
type RedisClusterNode = z.infer<typeof RedisClusterNodeSchema>;
|
|
19
|
-
/**
|
|
33
|
+
/**
|
|
34
|
+
* Redis 哨兵模式配置
|
|
35
|
+
* @example
|
|
36
|
+
* ```ts
|
|
37
|
+
* import { RedisSentinelConfigSchema } from '@h-ai/cache'
|
|
38
|
+
* const config = RedisSentinelConfigSchema.parse({ name: 'primary', sentinels: [{ host: 'sentinel.internal', port: 26379 }] })
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
20
41
|
declare const RedisSentinelConfigSchema: z.ZodObject<{
|
|
21
42
|
sentinels: z.ZodArray<z.ZodObject<{
|
|
22
43
|
host: z.ZodString;
|
|
@@ -24,9 +45,23 @@ declare const RedisSentinelConfigSchema: z.ZodObject<{
|
|
|
24
45
|
}, z.core.$strip>>;
|
|
25
46
|
name: z.ZodString;
|
|
26
47
|
}, z.core.$strip>;
|
|
27
|
-
/**
|
|
48
|
+
/**
|
|
49
|
+
* Redis 哨兵模式配置类型
|
|
50
|
+
* @example
|
|
51
|
+
* ```ts
|
|
52
|
+
* import type { RedisSentinelConfig } from '@h-ai/cache'
|
|
53
|
+
* const options: RedisSentinelConfig = { name: 'primary', sentinels: [{ host: 'sentinel.internal', port: 26379 }] }
|
|
54
|
+
* ```
|
|
55
|
+
*/
|
|
28
56
|
type RedisSentinelConfig = z.infer<typeof RedisSentinelConfigSchema>;
|
|
29
|
-
/**
|
|
57
|
+
/**
|
|
58
|
+
* 内存缓存配置(仅用于开发/测试,不支持跨进程共享)
|
|
59
|
+
* @example
|
|
60
|
+
* ```ts
|
|
61
|
+
* import { MemoryConfigSchema } from '@h-ai/cache'
|
|
62
|
+
* const config = MemoryConfigSchema.parse({ type: 'memory' })
|
|
63
|
+
* ```
|
|
64
|
+
*/
|
|
30
65
|
declare const MemoryConfigSchema: z.ZodObject<{
|
|
31
66
|
type: z.ZodLiteral<"memory">;
|
|
32
67
|
}, z.core.$strip>;
|
|
@@ -34,6 +69,11 @@ declare const MemoryConfigSchema: z.ZodObject<{
|
|
|
34
69
|
* Redis 缓存配置
|
|
35
70
|
*
|
|
36
71
|
* 连接优先级:url > cluster > sentinel > host
|
|
72
|
+
* @example
|
|
73
|
+
* ```ts
|
|
74
|
+
* import { RedisConfigSchema } from '@h-ai/cache'
|
|
75
|
+
* const config = RedisConfigSchema.parse({ type: 'redis', host: 'redis.internal' })
|
|
76
|
+
* ```
|
|
37
77
|
*/
|
|
38
78
|
declare const RedisConfigSchema: z.ZodObject<{
|
|
39
79
|
type: z.ZodLiteral<"redis">;
|
|
@@ -61,7 +101,14 @@ declare const RedisConfigSchema: z.ZodObject<{
|
|
|
61
101
|
retryDelay: z.ZodDefault<z.ZodNumber>;
|
|
62
102
|
readOnly: z.ZodDefault<z.ZodBoolean>;
|
|
63
103
|
}, z.core.$strip>;
|
|
64
|
-
/**
|
|
104
|
+
/**
|
|
105
|
+
* 缓存配置 Schema;通过 type 字段区分 memory / redis
|
|
106
|
+
* @example
|
|
107
|
+
* ```ts
|
|
108
|
+
* import { CacheConfigSchema } from '@h-ai/cache'
|
|
109
|
+
* const config = CacheConfigSchema.parse({ type: 'redis', host: 'redis.internal' })
|
|
110
|
+
* ```
|
|
111
|
+
*/
|
|
65
112
|
declare const CacheConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
66
113
|
type: z.ZodLiteral<"memory">;
|
|
67
114
|
}, z.core.$strip>, z.ZodObject<{
|
|
@@ -90,11 +137,35 @@ declare const CacheConfigSchema: z.ZodDiscriminatedUnion<[z.ZodObject<{
|
|
|
90
137
|
retryDelay: z.ZodDefault<z.ZodNumber>;
|
|
91
138
|
readOnly: z.ZodDefault<z.ZodBoolean>;
|
|
92
139
|
}, z.core.$strip>], "type">;
|
|
93
|
-
/**
|
|
140
|
+
/**
|
|
141
|
+
* 缓存配置类型(Zod parse 后的完整类型,所有默认值已填充)
|
|
142
|
+
* @example
|
|
143
|
+
* ```ts
|
|
144
|
+
* import { CacheConfigSchema, type CacheConfig } from '@h-ai/cache'
|
|
145
|
+
* const config: CacheConfig = CacheConfigSchema.parse({ type: 'redis', host: 'redis.internal' })
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
94
148
|
type CacheConfig = z.infer<typeof CacheConfigSchema>;
|
|
95
|
-
/**
|
|
149
|
+
/**
|
|
150
|
+
* 缓存配置输入类型(Zod parse 前,允许省略带默认值的字段)
|
|
151
|
+
* @example
|
|
152
|
+
* ```ts
|
|
153
|
+
* import type { CacheConfigInput } from '@h-ai/cache'
|
|
154
|
+
* const options: CacheConfigInput = { type: 'redis', host: 'redis.internal', keyPrefix: 'orders:' }
|
|
155
|
+
* ```
|
|
156
|
+
*/
|
|
96
157
|
type CacheConfigInput = z.input<typeof CacheConfigSchema>;
|
|
97
158
|
|
|
159
|
+
/**
|
|
160
|
+
* 缓存连接、序列化、输入与分布式操作的稳定错误定义。
|
|
161
|
+
* @example
|
|
162
|
+
* ```ts
|
|
163
|
+
* import { HaiCacheError } from '@h-ai/cache'
|
|
164
|
+
* import { err } from '@h-ai/core'
|
|
165
|
+
* const failure = err(HaiCacheError.INVALID_SET_OPTIONS, 'Conflicting expiry options')
|
|
166
|
+
* if (!failure.success) failure.error.code
|
|
167
|
+
* ```
|
|
168
|
+
*/
|
|
98
169
|
declare const HaiCacheError: {
|
|
99
170
|
readonly CONNECTION_FAILED: _h_ai_core.HaiErrorDef;
|
|
100
171
|
readonly OPERATION_FAILED: _h_ai_core.HaiErrorDef;
|
|
@@ -105,38 +176,82 @@ declare const HaiCacheError: {
|
|
|
105
176
|
readonly NOT_INITIALIZED: _h_ai_core.HaiErrorDef;
|
|
106
177
|
readonly UNSUPPORTED_TYPE: _h_ai_core.HaiErrorDef;
|
|
107
178
|
readonly CONFIG_ERROR: _h_ai_core.HaiErrorDef;
|
|
179
|
+
readonly INVALID_SCAN: _h_ai_core.HaiErrorDef;
|
|
180
|
+
readonly SCAN_TOPOLOGY_CHANGED: _h_ai_core.HaiErrorDef;
|
|
181
|
+
readonly CROSS_SLOT: _h_ai_core.HaiErrorDef;
|
|
182
|
+
readonly INVALID_SET_OPTIONS: _h_ai_core.HaiErrorDef;
|
|
108
183
|
};
|
|
109
184
|
/**
|
|
110
185
|
* 可缓存的值类型
|
|
111
186
|
*
|
|
112
187
|
* 说明:`object` 包含普通对象与数组;函数、Symbol、BigInt 等不建议直接缓存。
|
|
188
|
+
* @example
|
|
189
|
+
* ```ts
|
|
190
|
+
* import type { CacheValue } from '@h-ai/cache'
|
|
191
|
+
* const options: CacheValue = { id: 'order-1', paid: false }
|
|
192
|
+
* ```
|
|
113
193
|
*/
|
|
114
194
|
type CacheValue = string | number | boolean | object | null;
|
|
115
|
-
/**
|
|
195
|
+
/**
|
|
196
|
+
* SET 选项;四种期限彼此互斥且与 keepTtl 互斥,nx/xx 互斥。
|
|
197
|
+
* @example
|
|
198
|
+
* ```ts
|
|
199
|
+
* import type { SetOptions } from '@h-ai/cache'
|
|
200
|
+
* const options: SetOptions = { px: 60_000, nx: true }
|
|
201
|
+
* ```
|
|
202
|
+
*/
|
|
116
203
|
interface SetOptions {
|
|
117
|
-
/**
|
|
204
|
+
/** 过期时间(正安全整数秒),零/负数无效。 */
|
|
118
205
|
ex?: number;
|
|
119
|
-
/**
|
|
206
|
+
/** 过期时间(正安全整数毫秒),零/负数无效。 */
|
|
120
207
|
px?: number;
|
|
121
|
-
/**
|
|
208
|
+
/** Unix 正安全整数秒;过去时间会立即过期,乘1000后须仍为安全整数。 */
|
|
122
209
|
exat?: number;
|
|
123
|
-
/**
|
|
210
|
+
/** Unix 正安全整数毫秒;过去时间会立即过期。 */
|
|
124
211
|
pxat?: number;
|
|
125
212
|
/** 仅在键不存在时设置 */
|
|
126
213
|
nx?: boolean;
|
|
127
214
|
/** 仅在键存在时设置 */
|
|
128
215
|
xx?: boolean;
|
|
129
|
-
/**
|
|
216
|
+
/** 保留原 TTL;新键无 TTL,与所有显式期限互斥。 */
|
|
130
217
|
keepTtl?: boolean;
|
|
131
218
|
}
|
|
132
|
-
/**
|
|
219
|
+
/**
|
|
220
|
+
* 扫描选项
|
|
221
|
+
* @example
|
|
222
|
+
* ```ts
|
|
223
|
+
* import type { ScanOptions } from '@h-ai/cache'
|
|
224
|
+
* const options: ScanOptions = { match: 'orders:*', count: 100 }
|
|
225
|
+
* ```
|
|
226
|
+
*/
|
|
133
227
|
interface ScanOptions {
|
|
134
228
|
/** 匹配模式 */
|
|
135
229
|
match?: string;
|
|
136
230
|
/** 每次扫描数量 */
|
|
137
231
|
count?: number;
|
|
138
232
|
}
|
|
139
|
-
/**
|
|
233
|
+
/**
|
|
234
|
+
* 条件替换的过期策略;px 与 keepTtl 互斥,省略时新值永久保存。
|
|
235
|
+
* @example
|
|
236
|
+
* ```ts
|
|
237
|
+
* import type { CompareAndSwapOptions } from '@h-ai/cache'
|
|
238
|
+
* const options: CompareAndSwapOptions = { px: 60_000 }
|
|
239
|
+
* ```
|
|
240
|
+
*/
|
|
241
|
+
interface CompareAndSwapOptions {
|
|
242
|
+
/** 新值存续的正整数毫秒数。 */
|
|
243
|
+
px?: number;
|
|
244
|
+
/** 成功替换时保留已有 TTL;原键不存在时新值无 TTL。 */
|
|
245
|
+
keepTtl?: boolean;
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* 有序集合成员(score + member 对)
|
|
249
|
+
* @example
|
|
250
|
+
* ```ts
|
|
251
|
+
* import type { ZMember } from '@h-ai/cache'
|
|
252
|
+
* const options: ZMember = { score: 100, member: 'player-1' }
|
|
253
|
+
* ```
|
|
254
|
+
*/
|
|
140
255
|
interface ZMember {
|
|
141
256
|
/** 分数,用于排序 */
|
|
142
257
|
score: number;
|
|
@@ -145,21 +260,56 @@ interface ZMember {
|
|
|
145
260
|
}
|
|
146
261
|
/**
|
|
147
262
|
* 基础键值操作接口
|
|
148
|
-
*
|
|
149
263
|
* @example
|
|
150
264
|
* ```ts
|
|
151
|
-
*
|
|
152
|
-
*
|
|
265
|
+
* import type { KvOperations } from '@h-ai/cache'
|
|
266
|
+
* // 传入已初始化 cache 的对应操作;连接生命周期由创建者管理。
|
|
267
|
+
* async function example(operations: KvOperations) {
|
|
268
|
+
* const claimed = await operations.set('orders:claim', 'worker-1', { nx: true, px: 60_000 })
|
|
269
|
+
* if (!claimed.success) return claimed
|
|
270
|
+
* if (!claimed.data) return 'already claimed'
|
|
271
|
+
* const read = await operations.get<string>('orders:claim')
|
|
272
|
+
* return read.success ? read.data : read.error.message
|
|
273
|
+
* }
|
|
153
274
|
* ```
|
|
154
275
|
*/
|
|
155
276
|
interface KvOperations {
|
|
156
277
|
/** 获取值;键不存在时返回 null */
|
|
157
278
|
get: <T = CacheValue>(key: string) => Promise<HaiResult<T | null>>;
|
|
158
|
-
/**
|
|
159
|
-
|
|
160
|
-
|
|
279
|
+
/**
|
|
280
|
+
* 设置 JSON 快照,返回实际是否写入;NX/XX 未命中为 success:true/data:false,原值与TTL不变。
|
|
281
|
+
* 参数非法返回 INVALID_SET_OPTIONS;省略期限永久保存,keepTtl 显式保留原期限。
|
|
282
|
+
* @example
|
|
283
|
+
* ```ts
|
|
284
|
+
* import type { KvOperations } from '@h-ai/cache'
|
|
285
|
+
* async function reserve(kv: KvOperations) {
|
|
286
|
+
* const applied = await kv.set('orders:claim', 'worker-1', { nx: true, px: 1000 })
|
|
287
|
+
* return applied.success ? applied.data : applied.error.message
|
|
288
|
+
* }
|
|
289
|
+
* ```
|
|
290
|
+
*/
|
|
291
|
+
set: (key: string, value: CacheValue, options?: SetOptions) => Promise<HaiResult<boolean>>;
|
|
292
|
+
/**
|
|
293
|
+
* 对单个 key 原子比较 JSON 序列化值并替换;比较失败返回 false,不修改值/TTL。
|
|
294
|
+
* expected=undefined 表示键不存在;value=undefined 表示删除,null 是实际 JSON 值。
|
|
295
|
+
* 适合跨节点状态版本更新,Cluster 只涉及一个 slot;调用方必须检查 success 和 data。
|
|
296
|
+
* @example
|
|
297
|
+
* ```ts
|
|
298
|
+
* import type { KvOperations } from '@h-ai/cache'
|
|
299
|
+
* async function advance(kv: KvOperations) {
|
|
300
|
+
* const read = await kv.get<number>('orders:version')
|
|
301
|
+
* if (!read.success) return read
|
|
302
|
+
* const expected = read.data ?? undefined
|
|
303
|
+
* const changed = await kv.compareAndSwap('orders:version', expected, (expected ?? 0) + 1, { px: 60_000 })
|
|
304
|
+
* // data=false 时需要重新读取后再决定是否重试,不能按成功提交处理。
|
|
305
|
+
* return changed.success ? changed.data : changed.error.message
|
|
306
|
+
* }
|
|
307
|
+
* ```
|
|
308
|
+
*/
|
|
309
|
+
compareAndSwap: (key: string, expected: CacheValue | undefined, value: CacheValue | undefined, options?: CompareAndSwapOptions) => Promise<HaiResult<boolean>>;
|
|
310
|
+
/** 删除一个或多个键;返回实际删除的数量。Cluster 跨 slot 按独立键有界执行,失败可能已有部分删除,重试幂等。 */
|
|
161
311
|
del: (...keys: string[]) => Promise<HaiResult<number>>;
|
|
162
|
-
/**
|
|
312
|
+
/** 检查一个或多个键是否存在;返回存在的数量。Cluster 跨 slot 独立读取,不保证同一时刻快照。 */
|
|
163
313
|
exists: (...keys: string[]) => Promise<HaiResult<number>>;
|
|
164
314
|
/** 设置过期时间(秒) */
|
|
165
315
|
expire: (key: string, seconds: number) => Promise<HaiResult<boolean>>;
|
|
@@ -177,12 +327,25 @@ interface KvOperations {
|
|
|
177
327
|
decr: (key: string) => Promise<HaiResult<number>>;
|
|
178
328
|
/** 自减指定值;键不存在时从 0 开始 */
|
|
179
329
|
decrBy: (key: string, decrement: number) => Promise<HaiResult<number>>;
|
|
180
|
-
/** 批量获取;不存在的键对应位置返回 null */
|
|
330
|
+
/** 批量获取;不存在的键对应位置返回 null。Cluster 跨 slot 保留输入顺序,不保证同一时刻快照。 */
|
|
181
331
|
mget: <T = CacheValue>(...keys: string[]) => Promise<HaiResult<(T | null)[]>>;
|
|
182
|
-
/**
|
|
332
|
+
/** 原子批量设置;Cluster 中所有 key 必须同 slot,可使用共同的 {hash-tag}。 */
|
|
183
333
|
mset: (entries: Array<[string, CacheValue]>) => Promise<HaiResult<void>>;
|
|
184
|
-
/**
|
|
185
|
-
|
|
334
|
+
/**
|
|
335
|
+
* 游标扫描逻辑 key,首次传 '0',后续原样传回不透明字符串;'0' 表示完成。
|
|
336
|
+
* Cluster 遍历全部主节点,拓扑/match/keyPrefix 变化返回 SCAN_TOPOLOGY_CHANGED,须重启遍历。
|
|
337
|
+
* 与 Redis SCAN 一致,不提供快照,可能返回重复项;调用方按需去重。
|
|
338
|
+
* @example
|
|
339
|
+
* ```ts
|
|
340
|
+
* import type { KvOperations } from '@h-ai/cache'
|
|
341
|
+
* async function readPage(kv: KvOperations, cursor = '0') {
|
|
342
|
+
* const page = await kv.scan(cursor, { match: 'orders:*', count: 100 })
|
|
343
|
+
* if (!page.success) return page
|
|
344
|
+
* return { next: page.data[0], keys: page.data[1] }
|
|
345
|
+
* }
|
|
346
|
+
* ```
|
|
347
|
+
*/
|
|
348
|
+
scan: (cursor: string, options?: ScanOptions) => Promise<HaiResult<[string, string[]]>>;
|
|
186
349
|
/** 获取匹配模式的所有键(慎用,生产环境请用 scan 迭代) */
|
|
187
350
|
keys: (pattern: string) => Promise<HaiResult<string[]>>;
|
|
188
351
|
/** 获取键的数据类型;不存在时返回 "none" */
|
|
@@ -190,11 +353,16 @@ interface KvOperations {
|
|
|
190
353
|
}
|
|
191
354
|
/**
|
|
192
355
|
* Hash 操作接口
|
|
193
|
-
*
|
|
194
356
|
* @example
|
|
195
357
|
* ```ts
|
|
196
|
-
*
|
|
197
|
-
*
|
|
358
|
+
* import type { HashOperations } from '@h-ai/cache'
|
|
359
|
+
* // 传入已初始化 cache 的对应操作;连接生命周期由创建者管理。
|
|
360
|
+
* async function example(operations: HashOperations) {
|
|
361
|
+
* const written = await operations.hset('orders:1', { status: 'pending', version: 1 })
|
|
362
|
+
* if (!written.success) return written
|
|
363
|
+
* const status = await operations.hget<string>('orders:1', 'status')
|
|
364
|
+
* return status.success ? status.data : status.error.message
|
|
365
|
+
* }
|
|
198
366
|
* ```
|
|
199
367
|
*/
|
|
200
368
|
interface HashOperations {
|
|
@@ -221,11 +389,16 @@ interface HashOperations {
|
|
|
221
389
|
}
|
|
222
390
|
/**
|
|
223
391
|
* List 操作接口
|
|
224
|
-
*
|
|
225
392
|
* @example
|
|
226
393
|
* ```ts
|
|
227
|
-
*
|
|
228
|
-
*
|
|
394
|
+
* import type { ListOperations } from '@h-ai/cache'
|
|
395
|
+
* // 传入已初始化 cache 的对应操作;连接生命周期由创建者管理。
|
|
396
|
+
* async function example(operations: ListOperations) {
|
|
397
|
+
* const queued = await operations.rpush('orders:queue', 'order-1')
|
|
398
|
+
* if (!queued.success) return queued
|
|
399
|
+
* const next = await operations.lpop<string>('orders:queue')
|
|
400
|
+
* return next.success ? next.data : next.error.message
|
|
401
|
+
* }
|
|
229
402
|
* ```
|
|
230
403
|
*/
|
|
231
404
|
interface ListOperations {
|
|
@@ -247,18 +420,23 @@ interface ListOperations {
|
|
|
247
420
|
lset: (key: string, index: number, value: CacheValue) => Promise<HaiResult<void>>;
|
|
248
421
|
/** 保留指定范围的元素,范围外的元素被删除 */
|
|
249
422
|
ltrim: (key: string, start: number, stop: number) => Promise<HaiResult<void>>;
|
|
250
|
-
/** 阻塞式从左侧弹出;返回 [key, value] 或超时返回 null。memory
|
|
423
|
+
/** 阻塞式从左侧弹出;返回 [key, value] 或超时返回 null。Cluster 多键须同 slot;memory 非阻塞立即返回。 */
|
|
251
424
|
blpop: <T = CacheValue>(timeout: number, ...keys: string[]) => Promise<HaiResult<[string, T] | null>>;
|
|
252
|
-
/** 阻塞式从右侧弹出;返回 [key, value] 或超时返回 null。memory
|
|
425
|
+
/** 阻塞式从右侧弹出;返回 [key, value] 或超时返回 null。Cluster 多键须同 slot;memory 非阻塞立即返回。 */
|
|
253
426
|
brpop: <T = CacheValue>(timeout: number, ...keys: string[]) => Promise<HaiResult<[string, T] | null>>;
|
|
254
427
|
}
|
|
255
428
|
/**
|
|
256
429
|
* Set 操作接口
|
|
257
|
-
*
|
|
258
430
|
* @example
|
|
259
431
|
* ```ts
|
|
260
|
-
*
|
|
261
|
-
*
|
|
432
|
+
* import type { SetOperations } from '@h-ai/cache'
|
|
433
|
+
* // 传入已初始化 cache 的对应操作;连接生命周期由创建者管理。
|
|
434
|
+
* async function example(operations: SetOperations) {
|
|
435
|
+
* const added = await operations.sadd('orders:tags', 'paid', 'priority')
|
|
436
|
+
* if (!added.success) return added
|
|
437
|
+
* const members = await operations.smembers<string>('orders:tags')
|
|
438
|
+
* return members.success ? members.data : members.error.message
|
|
439
|
+
* }
|
|
262
440
|
* ```
|
|
263
441
|
*/
|
|
264
442
|
interface SetOperations {
|
|
@@ -276,20 +454,25 @@ interface SetOperations {
|
|
|
276
454
|
srandmember: <T = CacheValue>(key: string, count?: number) => Promise<HaiResult<T | T[] | null>>;
|
|
277
455
|
/** 随机弹出成员(会从集合中移除);不指定 count 返回单个值,指定 count 返回数组;空集合返回 null */
|
|
278
456
|
spop: <T = CacheValue>(key: string, count?: number) => Promise<HaiResult<T | T[] | null>>;
|
|
279
|
-
/** 集合交集;任一 key
|
|
457
|
+
/** 集合交集;任一 key 不存在时返回空数组。Cluster 多键须同 slot,否则返回 CROSS_SLOT。 */
|
|
280
458
|
sinter: <T = CacheValue>(...keys: string[]) => Promise<HaiResult<T[]>>;
|
|
281
|
-
/**
|
|
459
|
+
/** 集合并集;Cluster 多键须同 slot,否则返回 CROSS_SLOT。 */
|
|
282
460
|
sunion: <T = CacheValue>(...keys: string[]) => Promise<HaiResult<T[]>>;
|
|
283
|
-
/**
|
|
461
|
+
/** 集合差集(第一个集合减去其余集合);Cluster 多键须同 slot,否则返回 CROSS_SLOT。 */
|
|
284
462
|
sdiff: <T = CacheValue>(...keys: string[]) => Promise<HaiResult<T[]>>;
|
|
285
463
|
}
|
|
286
464
|
/**
|
|
287
465
|
* 有序集合操作接口
|
|
288
|
-
*
|
|
289
466
|
* @example
|
|
290
467
|
* ```ts
|
|
291
|
-
*
|
|
292
|
-
*
|
|
468
|
+
* import type { ZSetOperations } from '@h-ai/cache'
|
|
469
|
+
* // 传入已初始化 cache 的对应操作;连接生命周期由创建者管理。
|
|
470
|
+
* async function example(operations: ZSetOperations) {
|
|
471
|
+
* const added = await operations.zadd('orders:rank', { member: 'order-1', score: 100 })
|
|
472
|
+
* if (!added.success) return added
|
|
473
|
+
* const top = await operations.zrevrange('orders:rank', 0, 9, true)
|
|
474
|
+
* return top.success ? top.data : top.error.message
|
|
475
|
+
* }
|
|
293
476
|
* ```
|
|
294
477
|
*/
|
|
295
478
|
interface ZSetOperations {
|
|
@@ -328,16 +511,16 @@ interface ZSetOperations {
|
|
|
328
511
|
* 分布式锁操作接口
|
|
329
512
|
*
|
|
330
513
|
* 基于 SET NX EX 模式实现分布式互斥锁,适用于多节点部署场景。
|
|
331
|
-
*
|
|
332
514
|
* @example
|
|
333
515
|
* ```ts
|
|
334
|
-
*
|
|
335
|
-
*
|
|
336
|
-
*
|
|
337
|
-
*
|
|
338
|
-
*
|
|
339
|
-
*
|
|
340
|
-
*
|
|
516
|
+
* import type { LockOperations } from '@h-ai/cache'
|
|
517
|
+
* // 传入已初始化 cache 的对应操作;连接生命周期由创建者管理。
|
|
518
|
+
* async function example(operations: LockOperations) {
|
|
519
|
+
* const acquired = await operations.acquire('orders:lock', { ttl: 30 })
|
|
520
|
+
* if (!acquired.success || !acquired.data) return acquired
|
|
521
|
+
* const released = await operations.release('orders:lock', acquired.data)
|
|
522
|
+
* // owner 不是 fencing token;业务最终写入仍需自己的数据库/CAS条件。
|
|
523
|
+
* return released.success ? released.data : released.error.message
|
|
341
524
|
* }
|
|
342
525
|
* ```
|
|
343
526
|
*/
|
|
@@ -345,23 +528,25 @@ interface LockOperations {
|
|
|
345
528
|
/**
|
|
346
529
|
* 尝试获取分布式锁
|
|
347
530
|
*
|
|
348
|
-
* 通过 SET NX EX
|
|
531
|
+
* 通过 SET NX EX 原子操作实现。每次生成不可复用的所有权凭据;占用时返回 null。
|
|
532
|
+
* owner 仅用于租约续期/释放,不是资源的 fencing token;暂停/超时后旧操作仍可能继续。
|
|
533
|
+
* 最终资源写入必须自行使用 CAS/代际条件或数据库事务约束,不能只检查续期或返回失败。
|
|
349
534
|
*
|
|
350
535
|
* @param key - 锁键名
|
|
351
536
|
* @param options - 锁选项
|
|
352
|
-
* @returns
|
|
537
|
+
* @returns 获锁成功返回唯一凭据,锁占用时返回 null
|
|
353
538
|
*/
|
|
354
|
-
acquire: (key: string, options?: LockOptions) => Promise<HaiResult<
|
|
539
|
+
acquire: (key: string, options?: LockOptions) => Promise<HaiResult<string | null>>;
|
|
355
540
|
/**
|
|
356
541
|
* 释放分布式锁
|
|
357
542
|
*
|
|
358
543
|
* 仅当锁由当前持有者持有时才释放(通过 owner 比对),防止误释放他人的锁。
|
|
359
544
|
*
|
|
360
545
|
* @param key - 锁键名
|
|
361
|
-
* @param owner -
|
|
546
|
+
* @param owner - 本次 acquire 返回的唯一凭据,不能使用节点 ID
|
|
362
547
|
* @returns true 表示释放成功,false 表示锁不存在或非当前持有者
|
|
363
548
|
*/
|
|
364
|
-
release: (key: string, owner
|
|
549
|
+
release: (key: string, owner: string) => Promise<HaiResult<boolean>>;
|
|
365
550
|
/**
|
|
366
551
|
* 检查锁是否被持有
|
|
367
552
|
*
|
|
@@ -376,22 +561,38 @@ interface LockOperations {
|
|
|
376
561
|
*
|
|
377
562
|
* @param key - 锁键名
|
|
378
563
|
* @param ttl - 新的过期时间(秒)
|
|
379
|
-
* @param owner -
|
|
564
|
+
* @param owner - 本次 acquire 返回的唯一凭据
|
|
380
565
|
* @returns true 表示续期成功,false 表示锁不存在或非当前持有者
|
|
381
566
|
*/
|
|
382
|
-
extend: (key: string, ttl: number, owner
|
|
567
|
+
extend: (key: string, ttl: number, owner: string) => Promise<HaiResult<boolean>>;
|
|
383
568
|
}
|
|
384
|
-
/**
|
|
569
|
+
/**
|
|
570
|
+
* 分布式锁选项
|
|
571
|
+
* @example
|
|
572
|
+
* ```ts
|
|
573
|
+
* import type { LockOptions } from '@h-ai/cache'
|
|
574
|
+
* const options: LockOptions = { ttl: 30 }
|
|
575
|
+
* ```
|
|
576
|
+
*/
|
|
385
577
|
interface LockOptions {
|
|
386
578
|
/** 锁过期时间(秒);默认 30 */
|
|
387
579
|
ttl?: number;
|
|
388
|
-
/** 锁持有者标识;默认 'default'(多节点部署时建议设置固定值以便审计) */
|
|
389
|
-
owner?: string;
|
|
390
580
|
}
|
|
391
581
|
/**
|
|
392
582
|
* 复合缓存操作接口,聚合所有数据结构的操作子接口
|
|
393
583
|
*
|
|
394
584
|
* 通过 `cache.kv` / `cache.hash` / `cache.list` / `cache.set_` / `cache.zset` / `cache.lock` 分别访问
|
|
585
|
+
* @example
|
|
586
|
+
* ```ts
|
|
587
|
+
* import type { CacheCompositeOperations } from '@h-ai/cache'
|
|
588
|
+
* // 传入已初始化 cache 的对应操作;连接生命周期由创建者管理。
|
|
589
|
+
* async function example(operations: CacheCompositeOperations) {
|
|
590
|
+
* const health = await operations.ping()
|
|
591
|
+
* if (!health.success) return health
|
|
592
|
+
* const result = await operations.kv.get<string>('orders:1')
|
|
593
|
+
* return result.success ? result.data : result.error.message
|
|
594
|
+
* }
|
|
595
|
+
* ```
|
|
395
596
|
*/
|
|
396
597
|
interface CacheCompositeOperations {
|
|
397
598
|
/** KV 操作 */
|
|
@@ -411,15 +612,16 @@ interface CacheCompositeOperations {
|
|
|
411
612
|
}
|
|
412
613
|
/**
|
|
413
614
|
* 缓存函数接口
|
|
414
|
-
*
|
|
415
615
|
* @example
|
|
416
616
|
* ```ts
|
|
417
|
-
* import {
|
|
418
|
-
*
|
|
419
|
-
*
|
|
420
|
-
* await
|
|
421
|
-
*
|
|
422
|
-
* await
|
|
617
|
+
* import type { CacheFunctions } from '@h-ai/cache'
|
|
618
|
+
* // 传入已初始化 cache 的对应操作;连接生命周期由创建者管理。
|
|
619
|
+
* async function example(operations: CacheFunctions) {
|
|
620
|
+
* const initialized = await operations.init({ type: 'redis', host: 'localhost' })
|
|
621
|
+
* if (!initialized.success) return initialized
|
|
622
|
+
* try { return await operations.kv.get<string>('orders:1') }
|
|
623
|
+
* finally { const closed = await operations.close(); if (!closed.success) return closed }
|
|
624
|
+
* }
|
|
423
625
|
* ```
|
|
424
626
|
*/
|
|
425
627
|
interface CacheFunctions extends CacheCompositeOperations {
|
|
@@ -429,23 +631,14 @@ interface CacheFunctions extends CacheCompositeOperations {
|
|
|
429
631
|
readonly config: CacheConfig | null;
|
|
430
632
|
/** 是否已初始化并连接 */
|
|
431
633
|
readonly isInitialized: boolean;
|
|
432
|
-
/**
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
interface CacheProvider extends CacheCompositeOperations {
|
|
441
|
-
/** Provider 名称(如 'memory' / 'redis') */
|
|
442
|
-
readonly name: string;
|
|
443
|
-
/** 连接缓存服务;config 已经过 Zod Schema 校验 */
|
|
444
|
-
connect: (config: CacheConfig) => Promise<HaiResult<void>>;
|
|
445
|
-
/** 关闭连接并释放资源 */
|
|
446
|
-
close: () => Promise<void>;
|
|
447
|
-
/** 是否处于已连接状态 */
|
|
448
|
-
isConnected: () => boolean;
|
|
634
|
+
/**
|
|
635
|
+
* 关闭连接并清理资源;失败时停止提供业务操作并保留资源,可再次 close/init 重试。
|
|
636
|
+
* @returns 资源全部释放时成功;释放失败时返回 CONNECTION_FAILED。
|
|
637
|
+
* @example
|
|
638
|
+
* const result = await cache.close()
|
|
639
|
+
* if (!result.success) await cache.close()
|
|
640
|
+
*/
|
|
641
|
+
close: () => Promise<HaiResult<void>>;
|
|
449
642
|
}
|
|
450
643
|
|
|
451
644
|
/**
|
|
@@ -457,17 +650,22 @@ interface CacheProvider extends CacheCompositeOperations {
|
|
|
457
650
|
|
|
458
651
|
/**
|
|
459
652
|
* 缓存服务对象
|
|
460
|
-
*
|
|
461
653
|
* @example
|
|
462
654
|
* ```ts
|
|
463
655
|
* import { cache } from '@h-ai/cache'
|
|
464
|
-
*
|
|
465
|
-
* await cache.init({ type: 'redis', host: 'localhost'
|
|
466
|
-
*
|
|
467
|
-
*
|
|
468
|
-
* await cache.
|
|
656
|
+
* import { core } from '@h-ai/core'
|
|
657
|
+
* const initialized = await cache.init({ type: 'redis', host: 'localhost' })
|
|
658
|
+
* if (initialized.success) {
|
|
659
|
+
* try {
|
|
660
|
+
* const claimed = await cache.kv.set('orders:claim', 'worker-1', { nx: true, px: 60_000 })
|
|
661
|
+
* if (claimed.success && claimed.data) core.logger.info('Claim created')
|
|
662
|
+
* } finally {
|
|
663
|
+
* const closed = await cache.close()
|
|
664
|
+
* if (!closed.success) core.logger.error('Cache cleanup pending', { code: closed.error.code })
|
|
665
|
+
* }
|
|
666
|
+
* }
|
|
469
667
|
* ```
|
|
470
668
|
*/
|
|
471
669
|
declare const cache: CacheFunctions;
|
|
472
670
|
|
|
473
|
-
export { type CacheCompositeOperations, type CacheConfig, type CacheConfigInput, CacheConfigSchema, type CacheFunctions, type
|
|
671
|
+
export { type CacheCompositeOperations, type CacheConfig, type CacheConfigInput, CacheConfigSchema, type CacheFunctions, type CacheValue, type CompareAndSwapOptions, HaiCacheError, type HashOperations, type KvOperations, type ListOperations, type LockOperations, type LockOptions, MemoryConfigSchema, type RedisClusterNode, RedisClusterNodeSchema, RedisConfigSchema, type RedisSentinelConfig, RedisSentinelConfigSchema, type ScanOptions, type SetOperations, type SetOptions, type ZMember, type ZSetOperations, cache };
|