@zintrust/queue-redis 0.4.27 → 0.4.39

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.
@@ -28,6 +28,53 @@ export const BullMQRedisQueue = (() => {
28
28
  const queues = new Map();
29
29
  let sharedConnection = null;
30
30
  let lockProviderCache = null;
31
+ const isRedisProxyEnabled = () => {
32
+ return Env.USE_REDIS_PROXY === true || Env.get('REDIS_PROXY_URL', '').trim() !== '';
33
+ };
34
+ const assertProxyAndWorkersCompatibility = (isWorkersRuntime) => {
35
+ if (isRedisProxyEnabled() && shouldUseHttpProxyDriver() === false) {
36
+ throw ErrorFactory.createConfigError('BullMQ Redis driver does not support REDIS proxy transport directly. Enable QUEUE_HTTP_PROXY_ENABLED=true for queue proxy mode, or disable REDIS proxy mode for direct BullMQ access.');
37
+ }
38
+ if (isWorkersRuntime && Cloudflare.isCloudflareSocketsEnabled() === false) {
39
+ throw ErrorFactory.createConfigError('BullMQ Redis driver requires ENABLE_CLOUDFLARE_SOCKETS=true in Cloudflare Workers. To use HTTP queue proxy mode, set QUEUE_HTTP_PROXY_ENABLED=true and QUEUE_HTTP_PROXY_URL.');
40
+ }
41
+ };
42
+ const resolveQueueRedisConfig = () => {
43
+ const workersHost = Cloudflare.getWorkersVar('WORKERS_REDIS_HOST');
44
+ const workersPortRaw = Cloudflare.getWorkersVar('WORKERS_REDIS_PORT');
45
+ const workersPassword = Cloudflare.getWorkersVar('WORKERS_REDIS_PASSWORD');
46
+ const workersDbRaw = Cloudflare.getWorkersVar('WORKERS_REDIS_QUEUE_DB');
47
+ return {
48
+ host: workersHost !== null && workersHost.trim() !== '' ? workersHost.trim() : Env.REDIS_HOST,
49
+ port: workersPortRaw !== null && Number.isFinite(Number.parseInt(workersPortRaw, 10))
50
+ ? Number.parseInt(workersPortRaw, 10)
51
+ : Env.REDIS_PORT,
52
+ password: workersPassword !== null && workersPassword.trim() !== ''
53
+ ? workersPassword
54
+ : Env.REDIS_PASSWORD,
55
+ database: workersDbRaw !== null && Number.isFinite(Number.parseInt(workersDbRaw, 10))
56
+ ? Number.parseInt(workersDbRaw, 10)
57
+ : Env.getInt('REDIS_QUEUE_DB', 0),
58
+ };
59
+ };
60
+ const assertWorkersHostIsReachable = (isWorkersRuntime, redisConfig) => {
61
+ if (isWorkersRuntime &&
62
+ (redisConfig.host === 'localhost' || redisConfig.host === '127.0.0.1')) {
63
+ throw ErrorFactory.createConfigError('Redis host cannot be localhost in Cloudflare Workers. Use a public Redis host, or enable queue HTTP proxy mode with QUEUE_HTTP_PROXY_ENABLED=true and QUEUE_HTTP_PROXY_URL.');
64
+ }
65
+ };
66
+ const createSharedBullMqConnection = () => {
67
+ const isWorkersRuntime = Cloudflare.getWorkersEnv() !== null;
68
+ assertProxyAndWorkersCompatibility(isWorkersRuntime);
69
+ const redisConfig = resolveQueueRedisConfig();
70
+ assertWorkersHostIsReachable(isWorkersRuntime, redisConfig);
71
+ return createRedisConnection({
72
+ host: redisConfig.host,
73
+ port: redisConfig.port,
74
+ password: redisConfig.password,
75
+ db: redisConfig.database,
76
+ }, 3, { subsystem: 'queue-bullmq' });
77
+ };
31
78
  const getDefaultLockDriveName = () => {
32
79
  const driver = queueConfig.default;
33
80
  return driver.length > 0 ? driver : ZintrustLang.REDIS;
@@ -59,40 +106,7 @@ export const BullMQRedisQueue = (() => {
59
106
  const getSharedConnection = () => {
60
107
  if (sharedConnection)
61
108
  return sharedConnection;
62
- const isWorkersRuntime = Cloudflare.getWorkersEnv() !== null;
63
- if (isWorkersRuntime && Cloudflare.isCloudflareSocketsEnabled() === false) {
64
- throw ErrorFactory.createConfigError('BullMQ Redis driver requires ENABLE_CLOUDFLARE_SOCKETS=true in Cloudflare Workers. To use HTTP queue proxy mode, set QUEUE_HTTP_PROXY_ENABLED=true and QUEUE_HTTP_PROXY_URL.');
65
- }
66
- const workersHost = Cloudflare.getWorkersVar('WORKERS_REDIS_HOST');
67
- const workersPortRaw = Cloudflare.getWorkersVar('WORKERS_REDIS_PORT');
68
- const workersPassword = Cloudflare.getWorkersVar('WORKERS_REDIS_PASSWORD');
69
- const workersDbRaw = Cloudflare.getWorkersVar('WORKERS_REDIS_QUEUE_DB');
70
- const resolvedHost = workersHost !== null && workersHost.trim() !== '' ? workersHost.trim() : Env.REDIS_HOST;
71
- const resolvedPort = workersPortRaw !== null && Number.isFinite(Number.parseInt(workersPortRaw, 10))
72
- ? Number.parseInt(workersPortRaw, 10)
73
- : Env.REDIS_PORT;
74
- const resolvedPassword = workersPassword !== null && workersPassword.trim() !== ''
75
- ? workersPassword
76
- : Env.REDIS_PASSWORD;
77
- const resolvedDb = workersDbRaw !== null && Number.isFinite(Number.parseInt(workersDbRaw, 10))
78
- ? Number.parseInt(workersDbRaw, 10)
79
- : Env.getInt('REDIS_QUEUE_DB', 0);
80
- const redisConfig = {
81
- host: resolvedHost,
82
- port: resolvedPort,
83
- password: resolvedPassword,
84
- database: resolvedDb,
85
- };
86
- if (isWorkersRuntime &&
87
- (redisConfig.host === 'localhost' || redisConfig.host === '127.0.0.1')) {
88
- throw ErrorFactory.createConfigError('Redis host cannot be localhost in Cloudflare Workers. Use a public Redis host, or enable queue HTTP proxy mode with QUEUE_HTTP_PROXY_ENABLED=true and QUEUE_HTTP_PROXY_URL.');
89
- }
90
- sharedConnection = createRedisConnection({
91
- host: redisConfig.host,
92
- port: redisConfig.port,
93
- password: redisConfig.password,
94
- db: redisConfig.database,
95
- });
109
+ sharedConnection = createSharedBullMqConnection();
96
110
  return sharedConnection; // sharedConnection is IoRedis (compatible with BullMQ)
97
111
  };
98
112
  const waitForRedisReady = async (client, timeoutMs) => {
@@ -308,13 +322,15 @@ export const BullMQRedisQueue = (() => {
308
322
  ? deduplication.ttl
309
323
  : undefined;
310
324
  const replace = deduplication.replace === true;
325
+ const jobId = jobOptions.jobId ?? generateUuid();
326
+ jobOptions.jobId = jobId;
311
327
  // Check existing lock
312
- const hasExistingLock = await checkExistingLock(deduplicationId, provider, replace, queue, jobOptions.jobId);
328
+ const hasExistingLock = await checkExistingLock(deduplicationId, provider, replace, queue, jobId);
313
329
  if (hasExistingLock) {
314
330
  return { payloadToSend: payloadData, shouldReturn: true, returnValue: deduplicationId };
315
331
  }
316
332
  // Acquire lock
317
- const lockAcquired = await acquireDeduplicationLock(deduplicationId, provider, ttl, queue, jobOptions.jobId);
333
+ const lockAcquired = await acquireDeduplicationLock(deduplicationId, provider, ttl, queue, jobId);
318
334
  if (!lockAcquired) {
319
335
  return { payloadToSend: payloadData, shouldReturn: true, returnValue: deduplicationId };
320
336
  }
@@ -1,113 +1,19 @@
1
- import { Env, ErrorFactory, SignedRequest, ZintrustLang } from '@zintrust/core';
2
- const resolveSigningPrefix = (baseUrl) => {
3
- try {
4
- const parsed = new URL(baseUrl);
5
- const path = parsed.pathname.endsWith('/') ? parsed.pathname.slice(0, -1) : parsed.pathname;
6
- if (path === '' || path === '/')
7
- return undefined;
8
- return path;
9
- }
10
- catch {
11
- return undefined;
12
- }
13
- };
14
- const buildRequestUrl = (baseUrl, path) => {
15
- const url = new URL(baseUrl);
16
- const basePath = url.pathname.endsWith('/') ? url.pathname.slice(0, -1) : url.pathname;
17
- const requestPath = path.startsWith('/') ? path : `/${path}`;
18
- url.pathname = `${basePath}${requestPath}`;
19
- return url;
20
- };
21
- const buildSigningUrl = (requestUrl, baseUrl) => {
22
- const prefix = resolveSigningPrefix(baseUrl);
23
- if (!prefix)
24
- return requestUrl;
25
- if (requestUrl.pathname === prefix || requestUrl.pathname.startsWith(`${prefix}/`)) {
26
- const signingUrl = new URL(requestUrl.toString());
27
- const stripped = requestUrl.pathname.slice(prefix.length);
28
- signingUrl.pathname = stripped.startsWith('/') ? stripped : `/${stripped}`;
29
- return signingUrl;
30
- }
31
- return requestUrl;
32
- };
1
+ import { Env, ErrorFactory, ZintrustLang, createRedisConnection } from '@zintrust/core';
2
+ const createSharedRedisConnection = createRedisConnection;
33
3
  let publishClientInstance = null;
34
4
  let publishClientConnected = false;
35
- const resolveProxyBaseUrl = () => {
36
- const explicit = Env.REDIS_PROXY_URL.trim();
37
- if (explicit !== '')
38
- return explicit;
39
- if (Env.USE_REDIS_PROXY === false)
40
- return '';
41
- const host = Env.REDIS_PROXY_HOST || '127.0.0.1';
42
- const port = Env.REDIS_PROXY_PORT;
43
- return `http://${host}:${port}`;
44
- };
45
- const buildProxySettings = () => {
46
- const baseUrl = resolveProxyBaseUrl();
47
- const keyId = Env.REDIS_PROXY_KEY_ID || undefined;
48
- const secret = Env.REDIS_PROXY_SECRET || Env.APP_KEY || undefined;
49
- const timeoutMs = Env.REDIS_PROXY_TIMEOUT_MS;
50
- return { baseUrl, keyId, secret, timeoutMs };
51
- };
52
- const buildHeaders = async (settings, requestUrl, body) => {
53
- const headers = {
54
- 'Content-Type': 'application/json',
55
- };
56
- if (settings.keyId && settings.secret) {
57
- const signingUrl = buildSigningUrl(requestUrl, settings.baseUrl);
58
- const signed = await SignedRequest.createHeaders({
59
- method: 'POST',
60
- url: signingUrl,
61
- body,
62
- keyId: settings.keyId,
63
- secret: settings.secret,
64
- });
65
- Object.assign(headers, signed);
66
- }
67
- return headers;
68
- };
69
- const requestProxy = async (settings, path, payload) => {
70
- if (settings.baseUrl.trim() === '') {
71
- throw ErrorFactory.createConfigError('Redis proxy URL is missing (REDIS_PROXY_URL)');
72
- }
73
- const body = JSON.stringify(payload);
74
- const url = buildRequestUrl(settings.baseUrl, path);
75
- const headers = await buildHeaders(settings, url, body);
76
- const timeoutSignal = typeof AbortSignal !== 'undefined' && 'timeout' in AbortSignal;
77
- const signal = timeoutSignal ? AbortSignal.timeout(settings.timeoutMs) : undefined;
78
- const response = await fetch(url.toString(), {
79
- method: 'POST',
80
- headers,
81
- body,
82
- signal,
83
- });
84
- if (!response.ok) {
85
- const text = await response.text();
86
- throw ErrorFactory.createTryCatchError(`Redis proxy request failed (${response.status})`, text);
87
- }
88
- return (await response.json());
89
- };
90
- const toNumber = (value) => {
91
- if (typeof value === 'number')
92
- return value;
93
- if (typeof value === 'string') {
94
- const parsed = Number(value);
95
- return Number.isFinite(parsed) ? parsed : 0;
96
- }
97
- return 0;
98
- };
99
5
  const tryCreateProxyPublishClient = async () => {
100
- const settings = buildProxySettings();
101
- if (settings.baseUrl.trim() === '')
6
+ if (Env.REDIS_PROXY_URL.trim() === '' && Env.USE_REDIS_PROXY !== true)
102
7
  return null;
8
+ const client = createSharedRedisConnection({
9
+ host: Env.get('REDIS_HOST', 'localhost'),
10
+ port: Env.getInt('REDIS_PORT', ZintrustLang.REDIS_DEFAULT_PORT),
11
+ password: Env.get('REDIS_PASSWORD'),
12
+ db: Env.getInt('REDIS_QUEUE_DB', ZintrustLang.REDIS_DEFAULT_DB),
13
+ }, 3, { subsystem: 'broadcast-publish' });
103
14
  return {
104
- publish: async (channel, message) => {
105
- const response = await requestProxy(settings, '/zin/redis/command', {
106
- command: 'PUBLISH',
107
- args: [channel, message],
108
- });
109
- return toNumber(response.result);
110
- },
15
+ connect: client.connect,
16
+ publish: (channel, message) => client.publish(channel, message),
111
17
  };
112
18
  };
113
19
  /**
@@ -1,15 +1,15 @@
1
1
  {
2
2
  "name": "@zintrust/queue-redis",
3
- "version": "0.4.22",
4
- "buildDate": "2026-03-26T14:37:41.614Z",
3
+ "version": "0.4.39",
4
+ "buildDate": "2026-03-30T20:12:42.664Z",
5
5
  "buildEnvironment": {
6
6
  "node": "v22.22.1",
7
7
  "platform": "darwin",
8
8
  "arch": "arm64"
9
9
  },
10
10
  "git": {
11
- "commit": "94caf69e",
12
- "branch": "dev"
11
+ "commit": "0d093bc6",
12
+ "branch": "release"
13
13
  },
14
14
  "package": {
15
15
  "engines": {
@@ -29,8 +29,8 @@
29
29
  "sha256": "52fb0f688cd17cc7d8e8128e60df6f5eea4c803282382ac75550e6aacee7cbb0"
30
30
  },
31
31
  "BullMQRedisQueue.js": {
32
- "size": 20130,
33
- "sha256": "25e6fb6928236fe10959d2d6a3849ee7845d89e7469b12e57d4f945d32f7304f"
32
+ "size": 21034,
33
+ "sha256": "4432bacf5e54fe24a8d7aec9f228db5f8c1ea827e9584709b922aecb126942da"
34
34
  },
35
35
  "HttpQueueDriver.d.ts": {
36
36
  "size": 835,
@@ -53,8 +53,8 @@
53
53
  "sha256": "341a68a3b8603b453146dc721f9d2eaf0d65bb6a1acb67908a9c6f9542b9fd2d"
54
54
  },
55
55
  "RedisPublishClient.js": {
56
- "size": 8688,
57
- "sha256": "4252875ce0326c74fb96bd6901e328bff4a4f6cd894681a3378f990a528880ff"
56
+ "size": 5464,
57
+ "sha256": "18b785c47b4df689f9969625880d1b559e6cf8c95bb8c3cd3a4112fa75e0d870"
58
58
  },
59
59
  "RedisQueue.d.ts": {
60
60
  "size": 438,
@@ -65,8 +65,8 @@
65
65
  "sha256": "dc8b2c28b2e288e048423067f90ffbe0389ac813086246a1c8fafeeeab5c142d"
66
66
  },
67
67
  "build-manifest.json": {
68
- "size": 2509,
69
- "sha256": "dce980010169a58b82a991c024fb5e407cdb44e10b2194469344a6a8aea6c015"
68
+ "size": 2513,
69
+ "sha256": "91ec17322539b8ed460e0d916878c0ed23ba67537d3c68c08488e3776ac3e35b"
70
70
  },
71
71
  "index.d.ts": {
72
72
  "size": 565,
@@ -74,7 +74,7 @@
74
74
  },
75
75
  "index.js": {
76
76
  "size": 677,
77
- "sha256": "0ac4f592c2509e4f328ec4b4b77c7f85bf020bc1fb7c537860cc16714d6ce4f8"
77
+ "sha256": "af7ad24e2f89f39773f97c4f7bd1998c8f1507eb48ab5a563bd3a6cf86a11ef3"
78
78
  },
79
79
  "register.d.ts": {
80
80
  "size": 169,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@zintrust/queue-redis",
3
- "version": "0.4.27",
3
+ "version": "0.4.39",
4
4
  "description": "Redis queue 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.27"
26
+ "@zintrust/core": "^0.4.39"
27
27
  },
28
28
  "devDependencies": {
29
29
  "@zintrust/core": "file:../../dist"
@@ -43,7 +43,7 @@
43
43
  "prepublishOnly": "npm run build"
44
44
  },
45
45
  "dependencies": {
46
- "ioredis": "^5.10.0",
46
+ "ioredis": "^5.10.1",
47
47
  "redis": "^5.11.0"
48
48
  }
49
49
  }