@zintrust/cache-redis 0.4.70 → 0.4.71

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.
@@ -0,0 +1,5 @@
1
+ import type { CacheDriver } from './index.js';
2
+ export declare const RedisWorkersDurableObjectAdapter: Readonly<{
3
+ create(): CacheDriver;
4
+ }>;
5
+ export default RedisWorkersDurableObjectAdapter;
@@ -0,0 +1,129 @@
1
+ import { ErrorFactory, Logger } from '@zintrust/core';
2
+ const createSendCommandFunction = (getStub, connect) => {
3
+ return async (command, args) => {
4
+ await connect();
5
+ const stub = getStub();
6
+ const executePath = 'http://do/execute'; //NOSONAR
7
+ const payload = JSON.stringify({
8
+ command,
9
+ params: args,
10
+ });
11
+ const response = await stub.fetch(executePath, {
12
+ method: 'POST',
13
+ headers: { 'Content-Type': 'application/json' },
14
+ body: payload,
15
+ });
16
+ if (!response.ok) {
17
+ const text = await response.text();
18
+ let errDetail;
19
+ try {
20
+ errDetail = JSON.parse(text);
21
+ }
22
+ catch {
23
+ errDetail = { error: text };
24
+ }
25
+ const msg = errDetail.error ||
26
+ errDetail.message ||
27
+ response.statusText;
28
+ throw ErrorFactory.createGeneralError(`DO Command Failed: ${msg}`);
29
+ }
30
+ const json = (await response.json());
31
+ if (json !== null && typeof json === 'object' && 'result' in json) {
32
+ return json.result;
33
+ }
34
+ return json;
35
+ };
36
+ };
37
+ const createConnectionManager = (getNamespace) => {
38
+ let connected = false;
39
+ const getStub = () => {
40
+ const namespace = getNamespace();
41
+ if (!namespace) {
42
+ throw ErrorFactory.createConfigError('REDIS_POOL binding not found. Cannot connect to Durable Object pool.');
43
+ }
44
+ const id = namespace.idFromName('default');
45
+ return namespace.get(id);
46
+ };
47
+ const connect = async () => {
48
+ if (connected)
49
+ return;
50
+ try {
51
+ const stub = getStub();
52
+ const health = 'http://do/health'; //NOSONAR
53
+ const res = await stub.fetch(health, {
54
+ method: 'POST',
55
+ });
56
+ if (!res.ok) {
57
+ throw ErrorFactory.createGeneralError(`DO health check failed: ${res.status}`);
58
+ }
59
+ const body = (await res.json());
60
+ if (!body.connected) {
61
+ Logger.info('[RedisWorkersDurableObjectAdapter] DO not connected yet, will init on first command');
62
+ }
63
+ connected = true;
64
+ }
65
+ catch (err) {
66
+ Logger.error('[RedisWorkersDurableObjectAdapter] Connection failed', err);
67
+ throw ErrorFactory.createGeneralError('Failed to connect to Redis DO', err);
68
+ }
69
+ };
70
+ const sendCommand = createSendCommandFunction(getStub, connect);
71
+ return {
72
+ connect,
73
+ sendCommand,
74
+ disconnect: () => {
75
+ connected = false;
76
+ },
77
+ isConnected: () => connected,
78
+ };
79
+ };
80
+ const toNumber = (value) => {
81
+ if (typeof value === 'number')
82
+ return value;
83
+ if (typeof value === 'string') {
84
+ const parsed = Number(value);
85
+ return Number.isFinite(parsed) ? parsed : 0;
86
+ }
87
+ return 0;
88
+ };
89
+ export const RedisWorkersDurableObjectAdapter = Object.freeze({
90
+ create() {
91
+ const connectionManager = createConnectionManager(() => {
92
+ const globalEnv = globalThis.env;
93
+ return globalEnv?.['REDIS_POOL'];
94
+ });
95
+ return {
96
+ async get(key) {
97
+ const raw = await connectionManager.sendCommand('GET', [key]);
98
+ if (raw === null || raw === undefined)
99
+ return null;
100
+ try {
101
+ return JSON.parse(String(raw));
102
+ }
103
+ catch {
104
+ return null;
105
+ }
106
+ },
107
+ async set(key, value, ttl) {
108
+ const json = JSON.stringify(value);
109
+ if (Number.isFinite(ttl) && (ttl ?? 0) > 0) {
110
+ await connectionManager.sendCommand('SET', [key, json, 'EX', ttl]);
111
+ }
112
+ else {
113
+ await connectionManager.sendCommand('SET', [key, json]);
114
+ }
115
+ },
116
+ async delete(key) {
117
+ await connectionManager.sendCommand('DEL', [key]);
118
+ },
119
+ async clear() {
120
+ await connectionManager.sendCommand('FLUSHDB', []);
121
+ },
122
+ async has(key) {
123
+ const count = await connectionManager.sendCommand('EXISTS', [key]);
124
+ return toNumber(count) > 0;
125
+ },
126
+ };
127
+ },
128
+ });
129
+ export default RedisWorkersDurableObjectAdapter;
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@zintrust/cache-redis",
3
- "version": "0.4.70",
4
- "buildDate": "2026-04-06T16:04:58.363Z",
3
+ "version": "0.4.67",
4
+ "buildDate": "2026-04-06T10:05:15.346Z",
5
5
  "buildEnvironment": {
6
- "node": "v20.20.2",
7
- "platform": "linux",
8
- "arch": "x64"
6
+ "node": "v22.22.1",
7
+ "platform": "darwin",
8
+ "arch": "arm64"
9
9
  },
10
10
  "git": {
11
- "commit": "b92b2b05",
12
- "branch": "master"
11
+ "commit": "f5a9d786",
12
+ "branch": "release"
13
13
  },
14
14
  "package": {
15
15
  "engines": {
@@ -31,13 +31,25 @@
31
31
  "size": 1693,
32
32
  "sha256": "b1cd4417d827598b41226597355559eef423eac869448df5fd94d338d16a09f2"
33
33
  },
34
+ "RedisWorkersDurableObjectAdapter.d.ts": {
35
+ "size": 193,
36
+ "sha256": "859d3177d302c65f93add57216eae91d8c45874801bb75bdea3d6f6b00fb00c3"
37
+ },
38
+ "RedisWorkersDurableObjectAdapter.js": {
39
+ "size": 4482,
40
+ "sha256": "5b0ae2883872ce974ecaf01f07d35b72e6da468439912a57734ccd5222705bfd"
41
+ },
42
+ "build-manifest.json": {
43
+ "size": 1701,
44
+ "sha256": "b5b1dd88e1a45ad9ea5e2aa01d63212eaf3199c2d27c80dbdeb62cafbb08b801"
45
+ },
34
46
  "index.d.ts": {
35
47
  "size": 842,
36
48
  "sha256": "b47614dfdb736524165785958f4a685818c055315961c2e2b48d99ef6ca491e2"
37
49
  },
38
50
  "index.js": {
39
51
  "size": 5371,
40
- "sha256": "9caa2ed06025012497e5d62fd6451ff6bef9731e9545493644616b90aa0da7ad"
52
+ "sha256": "b45495a3a32724f5181ecb82558e0e3f6afc6ab9fb106163709d8980f26161ea"
41
53
  },
42
54
  "register.d.ts": {
43
55
  "size": 184,
package/dist/index.js CHANGED
@@ -11,9 +11,27 @@ const safeJsonParse = (value) => {
11
11
  async function importRedis() {
12
12
  return (await import('redis'));
13
13
  }
14
+ const logCacheFailure = (action, error) => {
15
+ Logger.error(`Redis cache ${action} failed`, error);
16
+ };
17
+ const createCacheFailureState = () => {
18
+ let disabled = false;
19
+ return {
20
+ isDisabled: () => disabled,
21
+ disableAfterFailure: (action, error) => {
22
+ if (!disabled) {
23
+ logCacheFailure(action, error);
24
+ }
25
+ disabled = true;
26
+ },
27
+ };
28
+ };
14
29
  const createCacheOperations = (ensureClient, operations, defaultTtl) => {
30
+ const failureState = createCacheFailureState();
15
31
  return {
16
32
  async get(key) {
33
+ if (failureState.isDisabled())
34
+ return null;
17
35
  try {
18
36
  const client = await ensureClient();
19
37
  const value = await operations.get(client, key);
@@ -22,28 +40,57 @@ const createCacheOperations = (ensureClient, operations, defaultTtl) => {
22
40
  return safeJsonParse(value);
23
41
  }
24
42
  catch (error) {
25
- Logger.error('Redis cache GET failed', error);
43
+ failureState.disableAfterFailure('GET', error);
26
44
  return null;
27
45
  }
28
46
  },
29
47
  async set(key, value, ttl) {
30
- const client = await ensureClient();
31
- const json = JSON.stringify(value);
32
- const effectiveTtl = ttl ?? defaultTtl;
33
- await operations.set(client, key, json, effectiveTtl);
48
+ if (failureState.isDisabled())
49
+ return;
50
+ try {
51
+ const client = await ensureClient();
52
+ const json = JSON.stringify(value);
53
+ const effectiveTtl = ttl ?? defaultTtl;
54
+ await operations.set(client, key, json, effectiveTtl);
55
+ }
56
+ catch (error) {
57
+ failureState.disableAfterFailure('SET', error);
58
+ }
34
59
  },
35
60
  async delete(key) {
36
- const client = await ensureClient();
37
- await operations.del(client, key);
61
+ if (failureState.isDisabled())
62
+ return;
63
+ try {
64
+ const client = await ensureClient();
65
+ await operations.del(client, key);
66
+ }
67
+ catch (error) {
68
+ failureState.disableAfterFailure('DEL', error);
69
+ }
38
70
  },
39
71
  async clear() {
40
- const client = await ensureClient();
41
- await operations.clear(client);
72
+ if (failureState.isDisabled())
73
+ return;
74
+ try {
75
+ const client = await ensureClient();
76
+ await operations.clear(client);
77
+ }
78
+ catch (error) {
79
+ failureState.disableAfterFailure('FLUSHDB', error);
80
+ }
42
81
  },
43
82
  async has(key) {
44
- const client = await ensureClient();
45
- const count = await operations.exists(client, key);
46
- return count > 0;
83
+ if (failureState.isDisabled())
84
+ return false;
85
+ try {
86
+ const client = await ensureClient();
87
+ const count = await operations.exists(client, key);
88
+ return count > 0;
89
+ }
90
+ catch (error) {
91
+ failureState.disableAfterFailure('EXISTS', error);
92
+ return false;
93
+ }
47
94
  },
48
95
  };
49
96
  };
@@ -73,9 +120,8 @@ const createWorkersCacheDriver = (config) => {
73
120
  return redisClient.set(key, json);
74
121
  }
75
122
  },
76
- del: (redisClient, key) => {
77
- redisClient.del(key);
78
- return Promise.resolve();
123
+ del: async (redisClient, key) => {
124
+ await redisClient.del(key);
79
125
  },
80
126
  clear: (redisClient) => {
81
127
  if (typeof redisClient.flushDb === 'function') {
@@ -113,14 +159,10 @@ const createNodeCacheDriver = (config) => {
113
159
  return redisClient.set(key, json);
114
160
  }
115
161
  },
116
- del: (redisClient, key) => {
117
- redisClient.del(key);
118
- return Promise.resolve();
119
- },
120
- clear: (redisClient) => {
121
- redisClient.flushDb();
122
- return Promise.resolve();
162
+ del: async (redisClient, key) => {
163
+ await redisClient.del(key);
123
164
  },
165
+ clear: (redisClient) => redisClient.flushDb(),
124
166
  exists: (redisClient, key) => redisClient.exists(key),
125
167
  }, config.ttl ?? 300);
126
168
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zintrust/cache-redis",
3
- "version": "0.4.70",
3
+ "version": "0.4.71",
4
4
  "description": "Redis cache driver for ZinTrust.",
5
5
  "private": false,
6
6
  "type": "module",
@@ -23,7 +23,7 @@
23
23
  "node": ">=20.0.0"
24
24
  },
25
25
  "peerDependencies": {
26
- "@zintrust/core": "^0.4.70"
26
+ "@zintrust/core": "^0.4.71"
27
27
  },
28
28
  "publishConfig": {
29
29
  "access": "public"
@@ -42,4 +42,4 @@
42
42
  "build": "tsc -p tsconfig.json",
43
43
  "prepublishOnly": "npm run build"
44
44
  }
45
- }
45
+ }