@mulingai-npm/redis 3.7.0 → 3.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.
@@ -5,6 +5,9 @@ export interface CachedPageContent {
5
5
  locale: string;
6
6
  slug: string;
7
7
  payload: string;
8
+ createdAt: number;
9
+ lastUsedAt: number | null;
10
+ totalUsage: number;
8
11
  }
9
12
  export declare class PageContentManager {
10
13
  private redis;
@@ -12,7 +15,7 @@ export declare class PageContentManager {
12
15
  constructor(redis: RedisClient);
13
16
  private key;
14
17
  private build;
15
- private scanAll;
18
+ private scan;
16
19
  addPageContent(locale: string, slug: string, payload: unknown, ttl?: number): Promise<void>;
17
20
  getPageContent(locale: string, slug: string): Promise<CachedPageContent | null>;
18
21
  getAllPageContents(): Promise<CachedPageContent[]>;
@@ -20,4 +23,5 @@ export declare class PageContentManager {
20
23
  clearPageContent(locale: string, slug: string): Promise<void>;
21
24
  clearAllPageContents(): Promise<void>;
22
25
  clearAllPageContentsByLocale(locale: string): Promise<void>;
26
+ incrementUsage(locale: string, slug: string): Promise<void>;
23
27
  }
@@ -10,17 +10,27 @@ class PageContentManager {
10
10
  }
11
11
  build(locale, slug, payload) {
12
12
  const id = this.key(locale, slug);
13
- return { id, path: `/${locale}/${slug}`, locale, slug, payload: JSON.stringify(payload) };
13
+ const now = Date.now();
14
+ return {
15
+ id,
16
+ path: `/${locale}/${slug}`,
17
+ locale,
18
+ slug,
19
+ payload: JSON.stringify(payload),
20
+ createdAt: now,
21
+ lastUsedAt: null,
22
+ totalUsage: 0
23
+ };
14
24
  }
15
- async scanAll() {
16
- let cursor = '0';
17
- const out = [];
25
+ async scan(pattern) {
26
+ let cursor = 0;
27
+ const keys = [];
18
28
  do {
19
- const [next, keys] = await this.redis.scan(Number(cursor), 'page-content--[*');
20
- cursor = next;
21
- out.push(...keys);
22
- } while (cursor !== '0');
23
- return out;
29
+ const [next, batch] = await this.redis.scan(cursor, pattern);
30
+ cursor = Number(next);
31
+ keys.push(...batch);
32
+ } while (cursor !== 0);
33
+ return keys;
24
34
  }
25
35
  async addPageContent(locale, slug, payload, ttl = PageContentManager.DEFAULT_TTL) {
26
36
  const obj = this.build(locale, slug, payload);
@@ -32,31 +42,53 @@ class PageContentManager {
32
42
  return raw ? JSON.parse(raw) : null;
33
43
  }
34
44
  async getAllPageContents() {
35
- const keys = await this.scanAll();
45
+ const keys = await this.scan('page-content--[*');
36
46
  if (!keys.length)
37
47
  return [];
38
48
  const p = this.redis.pipeline();
39
49
  keys.forEach((k) => p.get(k));
40
- const r = await p.exec();
41
- return r.flatMap(([_, v]) => (v ? [JSON.parse(v)] : []));
50
+ const res = await p.exec();
51
+ return res.flatMap(([_, v]) => (v ? [JSON.parse(v)] : []));
42
52
  }
43
53
  async getAllPageContentsByLocale(locale) {
44
- const all = await this.getAllPageContents();
45
- return all.filter((c) => c.locale.toLowerCase() === locale.toLowerCase());
54
+ const keys = await this.scan(`page-content--[${locale.toLowerCase()}]-[*`);
55
+ if (!keys.length)
56
+ return [];
57
+ const p = this.redis.pipeline();
58
+ keys.forEach((k) => p.get(k));
59
+ const res = await p.exec();
60
+ return res.flatMap(([_, v]) => (v ? [JSON.parse(v)] : []));
46
61
  }
47
62
  async clearPageContent(locale, slug) {
48
63
  await this.redis.unlink(this.key(locale, slug));
49
64
  }
50
65
  async clearAllPageContents() {
51
- const keys = await this.scanAll();
66
+ const keys = await this.scan('page-content--[*');
52
67
  if (keys.length)
53
68
  await this.redis.unlink(...keys);
54
69
  }
55
70
  async clearAllPageContentsByLocale(locale) {
56
- const keys = await this.scanAll();
57
- const toDel = keys.filter((k) => k.startsWith(`page-content--[${locale.toLowerCase()}]-[`));
58
- if (toDel.length)
59
- await this.redis.unlink(...toDel);
71
+ const keys = await this.scan(`page-content--[${locale.toLowerCase()}]-[*`);
72
+ if (keys.length)
73
+ await this.redis.unlink(...keys);
74
+ }
75
+ async incrementUsage(locale, slug) {
76
+ var _a;
77
+ const k = this.key(locale, slug);
78
+ const pipe = this.redis.pipeline();
79
+ pipe.get(k);
80
+ pipe.ttl(k);
81
+ const [getRes, ttlRes] = (await pipe.exec());
82
+ const raw = getRes === null || getRes === void 0 ? void 0 : getRes[1];
83
+ if (!raw)
84
+ return;
85
+ const ttl = Number((_a = ttlRes === null || ttlRes === void 0 ? void 0 : ttlRes[1]) !== null && _a !== void 0 ? _a : -1);
86
+ const obj = JSON.parse(raw);
87
+ obj.totalUsage += 1;
88
+ obj.lastUsedAt = Date.now();
89
+ await this.redis.set(k, JSON.stringify(obj));
90
+ if (ttl > 0)
91
+ await this.redis.expire(k, ttl);
60
92
  }
61
93
  }
62
94
  exports.PageContentManager = PageContentManager;
@@ -5,18 +5,10 @@ export interface RedisConfig {
5
5
  password?: string;
6
6
  db: number;
7
7
  }
8
- /**
9
- * Thin wrapper around ioredis that exposes exactly the helpers used by the two
10
- * managers. All helpers are 1-to-1 pass-through so there is no behavioural
11
- * change – this merely centralises typing and keeps the rest of the codebase
12
- * clean.
13
- */
14
8
  export declare class RedisClient {
15
9
  private client;
16
10
  constructor(config: RedisConfig);
17
11
  set(key: string, value: string): Promise<void>;
18
- /** Atomic SET with NX + EX.
19
- * @returns true if the key was set, false when it already existed */
20
12
  setNxEx(key: string, value: string, expireSeconds: number): Promise<boolean>;
21
13
  get(key: string): Promise<string | null>;
22
14
  del(key: string): Promise<number>;
@@ -5,12 +5,6 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
5
5
  Object.defineProperty(exports, "__esModule", { value: true });
6
6
  exports.RedisClient = void 0;
7
7
  const ioredis_1 = __importDefault(require("ioredis"));
8
- /**
9
- * Thin wrapper around ioredis that exposes exactly the helpers used by the two
10
- * managers. All helpers are 1-to-1 pass-through so there is no behavioural
11
- * change – this merely centralises typing and keeps the rest of the codebase
12
- * clean.
13
- */
14
8
  class RedisClient {
15
9
  constructor(config) {
16
10
  this.client = new ioredis_1.default({
@@ -23,14 +17,9 @@ class RedisClient {
23
17
  console.error('Redis error:', err);
24
18
  });
25
19
  }
26
- /* ----------------------------------------------------------------------- */
27
- /* Primitive string helpers */
28
- /* ----------------------------------------------------------------------- */
29
20
  async set(key, value) {
30
21
  await this.client.set(key, value);
31
22
  }
32
- /** Atomic SET with NX + EX.
33
- * @returns true if the key was set, false when it already existed */
34
23
  async setNxEx(key, value, expireSeconds) {
35
24
  // correct order: 'EX', ttl, 'NX'
36
25
  const res = (await this.client.set(key, value, 'EX', expireSeconds, 'NX'));
@@ -48,9 +37,6 @@ class RedisClient {
48
37
  async expire(key, seconds) {
49
38
  return this.client.expire(key, seconds);
50
39
  }
51
- /* ----------------------------------------------------------------------- */
52
- /* Set helpers */
53
- /* ----------------------------------------------------------------------- */
54
40
  async sadd(key, ...values) {
55
41
  return this.client.sadd(key, values);
56
42
  }
@@ -60,18 +46,12 @@ class RedisClient {
60
46
  async smembers(key) {
61
47
  return this.client.smembers(key);
62
48
  }
63
- /* ----------------------------------------------------------------------- */
64
- /* Hash helpers */
65
- /* ----------------------------------------------------------------------- */
66
49
  async hset(key, data) {
67
50
  return this.client.hset(key, data);
68
51
  }
69
52
  async hgetall(key) {
70
53
  return this.client.hgetall(key);
71
54
  }
72
- /* ----------------------------------------------------------------------- */
73
- /* Sorted-set helpers */
74
- /* ----------------------------------------------------------------------- */
75
55
  async zadd(key, score, member) {
76
56
  return this.client.zadd(key, score, member);
77
57
  }
@@ -87,24 +67,15 @@ class RedisClient {
87
67
  async zrem(key, ...members) {
88
68
  return this.client.zrem(key, members);
89
69
  }
90
- /* ----------------------------------------------------------------------- */
91
- /* Scan (pattern search) */
92
- /* ----------------------------------------------------------------------- */
93
70
  async scan(cursor, pattern, count = 1000) {
94
71
  return this.client.scan(cursor, 'MATCH', pattern, 'COUNT', count);
95
72
  }
96
73
  async keys(pattern) {
97
74
  return this.client.keys(pattern);
98
75
  }
99
- /* ----------------------------------------------------------------------- */
100
- /* Pipeline */
101
- /* ----------------------------------------------------------------------- */
102
76
  pipeline() {
103
77
  return this.client.pipeline();
104
78
  }
105
- /* ----------------------------------------------------------------------- */
106
- /* Misc */
107
- /* ----------------------------------------------------------------------- */
108
79
  async unlink(...keys) {
109
80
  // unlink exists on Redis ≥4 and is supported by ioredis but not typed in
110
81
  // older versions – we cast to any.
@@ -112,7 +83,6 @@ class RedisClient {
112
83
  // @ts-ignore – runtime command exists even if typings lag behind.
113
84
  return this.client.unlink(...keys);
114
85
  }
115
- /* JSON commands (RedisJSON module) -------------------------------------- */
116
86
  async jsonSet(key, path, value) {
117
87
  const result = await this.client.call('JSON.SET', key, path, JSON.stringify(value));
118
88
  return result;
@@ -135,9 +105,6 @@ class RedisClient {
135
105
  async jsonArrPop(key, path, index) {
136
106
  return index !== undefined ? this.client.call('JSON.ARRPOP', key, path, index) : this.client.call('JSON.ARRPOP', key, path);
137
107
  }
138
- /* ----------------------------------------------------------------------- */
139
- /* Dangerous! – flush helpers */
140
- /* ----------------------------------------------------------------------- */
141
108
  async flushAll() {
142
109
  console.warn('Flushing all Redis data!');
143
110
  return this.client.flushall();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mulingai-npm/redis",
3
- "version": "3.7.0",
3
+ "version": "3.8.0",
4
4
  "main": "dist/index.js",
5
5
  "types": "dist/index.d.ts",
6
6
  "repository": {