@zintrust/redis-rpc 2.4.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.
@@ -0,0 +1,557 @@
1
+ import { ErrorFactory } from '@zintrust/core/errors';
2
+ import { isArray, isNonEmptyString, isObject, isUndefinedOrNull } from '@zintrust/core/helper';
3
+ import { Logger } from '@zintrust/core/logger';
4
+ import { Sanitizer } from '@zintrust/core/security';
5
+ import { Queue, QueueEvents, Worker, } from 'bullmq';
6
+ import IORedis from 'ioredis';
7
+ import { redisConnectionOptions, rpcServerOptions } from './env.js';
8
+ import { createRpcValidationError } from './errors.js';
9
+ const DEFAULT_JOB_STATES = [
10
+ 'waiting',
11
+ 'active',
12
+ 'completed',
13
+ 'failed',
14
+ 'delayed',
15
+ 'paused',
16
+ 'prioritized',
17
+ 'waiting-children',
18
+ ];
19
+ const CLEAN_JOB_TYPES = new Set([
20
+ 'completed',
21
+ 'failed',
22
+ 'active',
23
+ 'delayed',
24
+ 'prioritized',
25
+ 'waiting',
26
+ 'paused',
27
+ 'wait',
28
+ ]);
29
+ const EVENT_NAMES = [
30
+ 'added',
31
+ 'waiting',
32
+ 'active',
33
+ 'completed',
34
+ 'failed',
35
+ 'delayed',
36
+ 'removed',
37
+ 'drained',
38
+ 'paused',
39
+ 'resumed',
40
+ ];
41
+ const PULL_WORKER_TOKEN = 'pull-worker';
42
+ const isRecord = (value) => isObject(value) && !isArray(value);
43
+ const sanitizeKey = (value) => {
44
+ const sanitized = Sanitizer.keyLike(value);
45
+ return sanitized.length > 0 ? sanitized : value.trim();
46
+ };
47
+ const requireRecord = (value, name) => {
48
+ if (!isRecord(value)) {
49
+ throw createRpcValidationError(`${name} must be an object`);
50
+ }
51
+ return value;
52
+ };
53
+ const requireString = (value, name) => {
54
+ if (!isNonEmptyString(value)) {
55
+ throw createRpcValidationError(`${name} is required`);
56
+ }
57
+ return sanitizeKey(value);
58
+ };
59
+ const asArgs = (payload) => (isArray(payload.args) ? payload.args : []);
60
+ const firstDefined = (...values) => values.find((value) => !isUndefinedOrNull(value));
61
+ const redisCommandArgs = (values) => values.flatMap((value) => {
62
+ if (!isRecord(value))
63
+ return [Buffer.isBuffer(value) ? value : String(value)];
64
+ return Object.entries(value).flatMap(([key, entry]) => {
65
+ if (typeof entry !== 'boolean')
66
+ return [key, String(entry)];
67
+ return entry ? [key] : [];
68
+ });
69
+ });
70
+ const redisPipelineCommands = (payload) => {
71
+ const commands = payload.commands;
72
+ if (!isArray(commands)) {
73
+ throw createRpcValidationError('commands must be an array');
74
+ }
75
+ return commands.map((command) => {
76
+ if (!isRecord(command)) {
77
+ throw createRpcValidationError('commands entries must be objects');
78
+ }
79
+ return command;
80
+ });
81
+ };
82
+ const queuePipelineCommand = (pipeline, command, args) => {
83
+ const lower = command.toLowerCase();
84
+ const candidate = pipeline[lower];
85
+ if (typeof candidate === 'function') {
86
+ candidate.apply(pipeline, args);
87
+ return;
88
+ }
89
+ if (typeof pipeline.call === 'function') {
90
+ pipeline.call(command, ...args);
91
+ return;
92
+ }
93
+ throw createRpcValidationError(`Unsupported redis command: ${command}`);
94
+ };
95
+ const serializePipelineResults = (results) => results.map(([error, result]) => [error === null ? null : error.message, result]);
96
+ const queueNameFromPayload = (payload) => {
97
+ return requireString(firstDefined(payload.queueName, payload.queue, payload.target), 'queueName');
98
+ };
99
+ const numberFrom = (value, fallback) => {
100
+ const parsed = Number(value);
101
+ return Number.isFinite(parsed) ? parsed : fallback;
102
+ };
103
+ const boolFrom = (value, fallback = false) => typeof value === 'boolean' ? value : fallback;
104
+ const normalizeStates = (value) => {
105
+ if (!isArray(value)) {
106
+ return DEFAULT_JOB_STATES;
107
+ }
108
+ const states = value.map((state) => String(state).trim()).filter(Boolean);
109
+ return states.length > 0 ? states : DEFAULT_JOB_STATES;
110
+ };
111
+ const withTimeout = async (operation, timeoutMs = 5000) => {
112
+ let timeout;
113
+ try {
114
+ return await Promise.race([
115
+ operation(),
116
+ new Promise((_, reject) => {
117
+ timeout = globalThis.setTimeout(() => reject(ErrorFactory.createTryCatchError('Redis RPC close timed out')), timeoutMs);
118
+ timeout.unref?.();
119
+ }),
120
+ ]);
121
+ }
122
+ finally {
123
+ if (timeout)
124
+ clearTimeout(timeout);
125
+ }
126
+ };
127
+ const serializeJob = async (job) => {
128
+ if (!job)
129
+ return null;
130
+ let state;
131
+ try {
132
+ state = await job.getState();
133
+ }
134
+ catch (error) {
135
+ Logger.warn('Redis RPC failed to read job state', { error });
136
+ }
137
+ return {
138
+ id: isUndefinedOrNull(job.id) ? undefined : String(job.id),
139
+ name: job.name,
140
+ queueName: job.queueName,
141
+ data: job.data,
142
+ opts: job.opts,
143
+ progress: job.progress,
144
+ attemptsMade: job.attemptsMade,
145
+ failedReason: job.failedReason,
146
+ stacktrace: job.stacktrace,
147
+ returnvalue: job.returnvalue,
148
+ timestamp: job.timestamp,
149
+ processedOn: job.processedOn,
150
+ finishedOn: job.finishedOn,
151
+ delay: job.delay,
152
+ priority: job.priority,
153
+ state,
154
+ };
155
+ };
156
+ const createConnection = (state, extra = {}) => {
157
+ const connection = new IORedis({ ...state.connectionOptions, ...extra });
158
+ state.connections.add(connection);
159
+ connection.once('end', () => state.connections.delete(connection));
160
+ return connection;
161
+ };
162
+ const queueOptions = (state) => ({
163
+ connection: createConnection(state),
164
+ prefix: state.prefix,
165
+ });
166
+ const getQueue = (state, queueName) => {
167
+ const name = requireString(queueName, 'queueName');
168
+ const existing = state.queues.get(name);
169
+ if (existing)
170
+ return existing;
171
+ const queue = new Queue(name, queueOptions(state));
172
+ state.queues.set(name, queue);
173
+ return queue;
174
+ };
175
+ const getQueueEvents = (state, queueName) => {
176
+ const name = requireString(queueName, 'queueName');
177
+ const existing = state.queueEvents.get(name);
178
+ if (existing)
179
+ return existing;
180
+ const events = new QueueEvents(name, queueOptions(state));
181
+ const log = [];
182
+ state.eventLogs.set(name, log);
183
+ for (const eventName of EVENT_NAMES) {
184
+ events.on(eventName, (payload) => {
185
+ log.push({ event: eventName, payload, at: Date.now() });
186
+ if (log.length > 200)
187
+ log.shift();
188
+ });
189
+ }
190
+ state.queueEvents.set(name, events);
191
+ return events;
192
+ };
193
+ const getJob = async (state, queueName, jobId) => {
194
+ const queue = getQueue(state, queueName);
195
+ return queue.getJob(requireString(jobId, 'jobId'));
196
+ };
197
+ const addJob = async (state, payload) => {
198
+ const queue = getQueue(state, queueNameFromPayload(payload));
199
+ const args = asArgs(payload);
200
+ let name = 'default';
201
+ if (isNonEmptyString(args[0])) {
202
+ name = args[0].trim();
203
+ }
204
+ else if (isNonEmptyString(payload.name)) {
205
+ name = payload.name.trim();
206
+ }
207
+ const data = args.length > 1 ? args[1] : firstDefined(payload.data, payload.payload, {});
208
+ let opts = {};
209
+ if (isRecord(args[2])) {
210
+ opts = args[2];
211
+ }
212
+ else if (isRecord(payload.opts)) {
213
+ opts = payload.opts;
214
+ }
215
+ return serializeJob(await queue.add(name, data, opts));
216
+ };
217
+ const getQueueJob = async (state, queueName, payload) => {
218
+ const args = asArgs(payload);
219
+ return serializeJob(await getJob(state, queueName, firstDefined(args[0], payload.jobId, payload.id)));
220
+ };
221
+ const listQueueJobs = async (state, queueName, payload) => {
222
+ const args = asArgs(payload);
223
+ const queue = getQueue(state, queueName);
224
+ const jobs = await queue.getJobs(normalizeStates(firstDefined(args[0], payload.states)), numberFrom(firstDefined(args[1], payload.start), 0), numberFrom(firstDefined(args[2], payload.end), 99), boolFrom(firstDefined(args[3], payload.asc), false));
225
+ return Promise.all(jobs.map((job) => serializeJob(job)));
226
+ };
227
+ const getQueueJobCounts = async (state, queueName, payload) => {
228
+ const args = asArgs(payload);
229
+ const queue = getQueue(state, queueName);
230
+ let types = [];
231
+ if (args.length > 0) {
232
+ types = args.map(String);
233
+ }
234
+ else if (isArray(payload.types)) {
235
+ types = payload.types.map(String);
236
+ }
237
+ return types.length > 0 ? queue.getJobCounts(...types) : queue.getJobCounts();
238
+ };
239
+ const closeQueue = async (state, queueName) => {
240
+ await state.queues.get(queueName)?.close();
241
+ await state.queueEvents.get(queueName)?.close();
242
+ state.queues.delete(queueName);
243
+ state.queueEvents.delete(queueName);
244
+ state.eventLogs.delete(queueName);
245
+ return true;
246
+ };
247
+ const getObliterateOptions = (payload) => {
248
+ const args = asArgs(payload);
249
+ const options = args[0];
250
+ return isRecord(options) ? options : { force: payload.force !== false };
251
+ };
252
+ const getQueueJobById = async (state, queueName, payload) => {
253
+ const args = asArgs(payload);
254
+ return getJob(state, queueName, firstDefined(args[0], payload.jobId, payload.id));
255
+ };
256
+ const dequeueQueueJob = async (state, queueName, payload) => {
257
+ const queue = getQueue(state, queueName);
258
+ const jobs = await queue.getJobs(['waiting'], 0, 0);
259
+ const job = jobs[0];
260
+ if (!job)
261
+ return undefined;
262
+ const visibilityTimeoutMs = numberFrom(payload.visibilityTimeoutMs, 30_000);
263
+ await job.moveToDelayed(Date.now() + Math.max(1, visibilityTimeoutMs), PULL_WORKER_TOKEN);
264
+ return {
265
+ id: isUndefinedOrNull(job.id) ? undefined : String(job.id),
266
+ payload: job.data,
267
+ attempts: job.attemptsMade || 0,
268
+ };
269
+ };
270
+ const ackQueueJob = async (state, queueName, payload) => {
271
+ const job = await getQueueJobById(state, queueName, payload);
272
+ if (!job)
273
+ return false;
274
+ await job.moveToCompleted('acknowledged', PULL_WORKER_TOKEN, false);
275
+ return true;
276
+ };
277
+ const removeQueueJob = async (state, queueName, payload) => {
278
+ const job = await getQueueJobById(state, queueName, payload);
279
+ if (!job)
280
+ return false;
281
+ await job.remove();
282
+ return true;
283
+ };
284
+ const retryQueueJob = async (state, queueName, payload) => {
285
+ const args = asArgs(payload);
286
+ const job = await getQueueJobById(state, queueName, payload);
287
+ if (!job)
288
+ return { ok: false, status: 'missing' };
289
+ await job.retry(firstDefined(args[1], payload.state));
290
+ return { ok: true, status: 'retried' };
291
+ };
292
+ const promoteQueueJob = async (state, queueName, payload) => {
293
+ const job = await getQueueJobById(state, queueName, payload);
294
+ if (!job)
295
+ return { ok: false, status: 'missing' };
296
+ await job.promote();
297
+ return { ok: true, status: 'promoted' };
298
+ };
299
+ /* eslint-disable complexity */
300
+ const dispatchQueue = async (state, method, payload) => {
301
+ const queueName = queueNameFromPayload(payload);
302
+ switch (method) {
303
+ case 'add':
304
+ case 'enqueue':
305
+ return addJob(state, payload);
306
+ case 'get':
307
+ case 'getJob':
308
+ return getQueueJob(state, queueName, payload);
309
+ case 'getJobs':
310
+ return listQueueJobs(state, queueName, payload);
311
+ case 'getJobCounts':
312
+ case 'counts':
313
+ return getQueueJobCounts(state, queueName, payload);
314
+ case 'count':
315
+ case 'length':
316
+ return getQueue(state, queueName).count();
317
+ case 'dequeue':
318
+ return dequeueQueueJob(state, queueName, payload);
319
+ case 'ack':
320
+ return ackQueueJob(state, queueName, payload);
321
+ case 'pause':
322
+ await getQueue(state, queueName).pause();
323
+ return true;
324
+ case 'resume':
325
+ await getQueue(state, queueName).resume();
326
+ return true;
327
+ case 'drain':
328
+ await getQueue(state, queueName).drain(Boolean(firstDefined(asArgs(payload)[0], payload.delayed)));
329
+ return true;
330
+ case 'obliterate':
331
+ await getQueue(state, queueName).obliterate(getObliterateOptions(payload));
332
+ return true;
333
+ case 'clean': {
334
+ const args = asArgs(payload);
335
+ const cleanType = String(firstDefined(args[2], payload.type, 'completed'));
336
+ return getQueue(state, queueName).clean(numberFrom(firstDefined(args[0], payload.grace), 0), numberFrom(firstDefined(args[1], payload.limit), 1000), (CLEAN_JOB_TYPES.has(cleanType) ? cleanType : 'completed'));
337
+ }
338
+ case 'removeJob':
339
+ return removeQueueJob(state, queueName, payload);
340
+ case 'retryJob':
341
+ return retryQueueJob(state, queueName, payload);
342
+ case 'promoteJob':
343
+ return promoteQueueJob(state, queueName, payload);
344
+ case 'closeQueue':
345
+ return closeQueue(state, queueName);
346
+ default:
347
+ throw createRpcValidationError(`Unsupported queue method: ${method}`);
348
+ }
349
+ };
350
+ /* eslint-enable complexity */
351
+ const createProcessor = (kind) => {
352
+ switch (kind) {
353
+ case 'echo':
354
+ case undefined:
355
+ case null:
356
+ return async (job) => ({ echo: job.data, jobId: job.id, name: job.name });
357
+ case 'sum':
358
+ return async (job) => {
359
+ const data = job.data;
360
+ const values = isArray(data.values) ? data.values : [];
361
+ return values.reduce((total, value) => total + Number(value || 0), 0);
362
+ };
363
+ case 'fail':
364
+ return async () => {
365
+ throw ErrorFactory.createWorkerError('redis-rpc test processor failure');
366
+ };
367
+ default:
368
+ throw createRpcValidationError(`Unsupported worker processor: ${String(kind)}`);
369
+ }
370
+ };
371
+ const dispatchWorker = async (state, method, payload) => {
372
+ const args = asArgs(payload);
373
+ switch (method) {
374
+ case 'start':
375
+ case 'startWorker': {
376
+ const queueName = requireString(firstDefined(args[0], payload.queueName, payload.queue), 'queueName');
377
+ const defaultWorkerName = `${queueName}:${String(payload.processor || 'echo')}`;
378
+ const workerName = requireString(firstDefined(args[1], payload.workerName, payload.name, defaultWorkerName), 'workerName');
379
+ if (state.workers.has(workerName))
380
+ return { workerName, queueName, status: 'already-running' };
381
+ const options = isRecord(args[2]) ? args[2] : payload;
382
+ const worker = new Worker(queueName, createProcessor(options.processor), {
383
+ connection: createConnection(state),
384
+ prefix: state.prefix,
385
+ concurrency: numberFrom(options.concurrency, 1),
386
+ });
387
+ state.workers.set(workerName, { queueName, worker });
388
+ await worker.waitUntilReady();
389
+ return { workerName, queueName, status: 'running' };
390
+ }
391
+ case 'stop':
392
+ case 'stopWorker': {
393
+ const workerName = requireString(firstDefined(args[0], payload.workerName, payload.name), 'workerName');
394
+ const entry = state.workers.get(workerName);
395
+ if (!entry)
396
+ return false;
397
+ await entry.worker.close(true);
398
+ state.workers.delete(workerName);
399
+ return true;
400
+ }
401
+ case 'list':
402
+ return Array.from(state.workers.entries()).map(([workerName, entry]) => ({
403
+ workerName,
404
+ queueName: entry.queueName,
405
+ }));
406
+ default:
407
+ throw createRpcValidationError(`Unsupported worker method: ${method}`);
408
+ }
409
+ };
410
+ const dispatchMonitor = async (state, method, payload) => {
411
+ const args = asArgs(payload);
412
+ switch (method) {
413
+ case 'snapshot':
414
+ case 'getSnapshot': {
415
+ let queueNames;
416
+ if (isArray(args[0])) {
417
+ queueNames = args[0];
418
+ }
419
+ else if (isArray(payload.queueNames)) {
420
+ queueNames = payload.queueNames;
421
+ }
422
+ else {
423
+ queueNames = Array.from(state.queues.keys());
424
+ }
425
+ const queues = await Promise.all(queueNames.map(async (name) => {
426
+ const queueName = requireString(name, 'queueName');
427
+ return { name: queueName, counts: await getQueue(state, queueName).getJobCounts() };
428
+ }));
429
+ return { status: 'ok', startedAt: new Date().toISOString(), queues };
430
+ }
431
+ case 'events':
432
+ case 'getEvents': {
433
+ const queueName = requireString(firstDefined(args[0], payload.queueName, payload.queue), 'queueName');
434
+ getQueueEvents(state, queueName);
435
+ return state.eventLogs.get(queueName) ?? [];
436
+ }
437
+ case 'getRecentJobsForQueue': {
438
+ const queueName = requireString(firstDefined(args[0], payload.queueName, payload.queue), 'queueName');
439
+ return dispatchQueue(state, 'getJobs', {
440
+ ...payload,
441
+ args: [],
442
+ queueName,
443
+ states: DEFAULT_JOB_STATES,
444
+ start: 0,
445
+ end: firstDefined(args[1], payload.limit, 99),
446
+ });
447
+ }
448
+ default:
449
+ throw createRpcValidationError(`Unsupported queue-monitor method: ${method}`);
450
+ }
451
+ };
452
+ const dispatchRedis = async (state, method, payload) => {
453
+ const args = asArgs(payload);
454
+ const connection = createConnection(state, { maxRetriesPerRequest: 1 });
455
+ try {
456
+ if (method === 'ping')
457
+ return await connection.ping();
458
+ if (method === 'call') {
459
+ const command = requireString(firstDefined(args[0], payload.command), 'command');
460
+ const rawCommandArgs = args.length > 0 ? args.slice(1) : asArgs(payload);
461
+ const commandArgs = redisCommandArgs(rawCommandArgs);
462
+ return await connection.call(command, ...commandArgs);
463
+ }
464
+ if (method === 'pipeline' || method === 'multi') {
465
+ const commands = redisPipelineCommands(payload);
466
+ const transaction = method === 'multi' || boolFrom(payload.transaction, false);
467
+ const pipeline = (transaction
468
+ ? connection.multi()
469
+ : connection.pipeline());
470
+ for (const entry of commands) {
471
+ const command = requireString(entry.command, 'command');
472
+ queuePipelineCommand(pipeline, command, redisCommandArgs(isArray(entry.args) ? entry.args : []));
473
+ }
474
+ return serializePipelineResults(await pipeline.exec());
475
+ }
476
+ throw createRpcValidationError(`Unsupported redis method: ${method}`);
477
+ }
478
+ finally {
479
+ connection.disconnect();
480
+ }
481
+ };
482
+ const dispatchCustom = async (backend, state, service, method, payload) => {
483
+ const handler = state.services.get(service);
484
+ if (!handler) {
485
+ throw createRpcValidationError(`Unsupported Redis RPC service: ${service}`);
486
+ }
487
+ return handler({ method, payload, backend });
488
+ };
489
+ const closeBackend = async (state) => {
490
+ await Promise.allSettled(Array.from(state.workers.values()).map(async ({ worker }) => {
491
+ try {
492
+ await withTimeout(() => worker.close(true));
493
+ }
494
+ catch {
495
+ await worker.disconnect();
496
+ }
497
+ }));
498
+ await Promise.allSettled(Array.from(state.queueEvents.values()).map(async (events) => {
499
+ try {
500
+ await withTimeout(() => events.close());
501
+ }
502
+ catch {
503
+ await events.disconnect();
504
+ }
505
+ }));
506
+ await Promise.allSettled(Array.from(state.queues.values()).map(async (queue) => {
507
+ try {
508
+ await withTimeout(() => queue.close());
509
+ }
510
+ catch {
511
+ await queue.disconnect();
512
+ }
513
+ }));
514
+ for (const connection of state.connections) {
515
+ connection.disconnect();
516
+ }
517
+ state.workers.clear();
518
+ state.queues.clear();
519
+ state.queueEvents.clear();
520
+ state.eventLogs.clear();
521
+ state.connections.clear();
522
+ };
523
+ export const createRedisRpcBackend = (options = {}) => {
524
+ const serverOptions = rpcServerOptions();
525
+ const state = {
526
+ prefix: options.prefix || serverOptions.prefix,
527
+ connectionOptions: options.redis || redisConnectionOptions(),
528
+ queues: new Map(),
529
+ queueEvents: new Map(),
530
+ workers: new Map(),
531
+ eventLogs: new Map(),
532
+ connections: new Set(),
533
+ services: new Map(Object.entries(options.services ?? {})),
534
+ };
535
+ const backend = Object.freeze({
536
+ prefix: state.prefix,
537
+ dispatch: async (service, method, payload = {}) => {
538
+ const normalizedService = requireString(service, 'service');
539
+ const normalizedMethod = requireString(method, 'method');
540
+ const body = requireRecord(payload, 'payload');
541
+ if (normalizedService === 'queue' || normalizedService === 'bullmq')
542
+ return dispatchQueue(state, normalizedMethod, body);
543
+ if (normalizedService === 'worker')
544
+ return dispatchWorker(state, normalizedMethod, body);
545
+ if (normalizedService === 'queue-monitor')
546
+ return dispatchMonitor(state, normalizedMethod, body);
547
+ if (normalizedService === 'redis')
548
+ return dispatchRedis(state, normalizedMethod, body);
549
+ return dispatchCustom(backend, state, normalizedService, normalizedMethod, body);
550
+ },
551
+ registerService: (service, handler) => {
552
+ state.services.set(requireString(service, 'service'), handler);
553
+ },
554
+ close: () => closeBackend(state),
555
+ });
556
+ return backend;
557
+ };
@@ -0,0 +1,94 @@
1
+ {
2
+ "name": "@zintrust/redis-rpc",
3
+ "version": "2.4.1",
4
+ "buildDate": "2026-05-31T11:41:49.490Z",
5
+ "buildEnvironment": {
6
+ "node": "v22.22.1",
7
+ "platform": "darwin",
8
+ "arch": "arm64"
9
+ },
10
+ "git": {
11
+ "commit": "e97b7b3d",
12
+ "branch": "release"
13
+ },
14
+ "package": {
15
+ "engines": {
16
+ "node": ">=20.0.0"
17
+ },
18
+ "dependencies": [
19
+ "@zintrust/core",
20
+ "bullmq",
21
+ "dotenv",
22
+ "ioredis"
23
+ ],
24
+ "peerDependencies": [
25
+ "@zintrust/core"
26
+ ]
27
+ },
28
+ "files": {
29
+ "adapters.d.ts": {
30
+ "size": 2331,
31
+ "sha256": "4080a5f54655beac7aa22f271aae61e03611230abf75491cd269d83278ea07c7"
32
+ },
33
+ "adapters.js": {
34
+ "size": 2817,
35
+ "sha256": "f7a732072fa8b1c0a288cdea9e6304f0e884179ed924eb1530a2dbd57e5bb087"
36
+ },
37
+ "backend.d.ts": {
38
+ "size": 183,
39
+ "sha256": "c2f1dd71fe2c5a1dc73a7fba855b5e8f8b1700c9f05baeab2c5d9aa8f31f4c60"
40
+ },
41
+ "backend.js": {
42
+ "size": 21160,
43
+ "sha256": "85777d42545d97624a0973fccc33b91a0f8e6a6345c940a8581845a4670cce2e"
44
+ },
45
+ "client.d.ts": {
46
+ "size": 166,
47
+ "sha256": "f58e91ca403a526864cca16eba5caa34a947835d403a20fe9e02e7cdd7ec55ca"
48
+ },
49
+ "client.js": {
50
+ "size": 3528,
51
+ "sha256": "79322c1c1265d34a02260f0d51b99e0e549e02978a40208faaf4e6fdf56e92e3"
52
+ },
53
+ "env.d.ts": {
54
+ "size": 575,
55
+ "sha256": "640907189918c080ac4bdce3f9a09771ab417ab266d9d5096071ce03e95f36a0"
56
+ },
57
+ "env.js": {
58
+ "size": 1282,
59
+ "sha256": "4612b16a360e9a0644718f533c94dd80f2f4e1134c396348cf86814f54387ba7"
60
+ },
61
+ "errors.d.ts": {
62
+ "size": 484,
63
+ "sha256": "178c171224b78c62b2347f58922906fa6903cdff27731db14b4f9c88af4e86d1"
64
+ },
65
+ "errors.js": {
66
+ "size": 1202,
67
+ "sha256": "eea48bcb9a1fe0f4f065835b9ce517087a5216bd9f78816378c17fac432d7dce"
68
+ },
69
+ "index.d.ts": {
70
+ "size": 775,
71
+ "sha256": "69ade1c65e33274c5a4aef0ddddd02a2ad4b8436da40fdb918e352f17a038230"
72
+ },
73
+ "index.js": {
74
+ "size": 745,
75
+ "sha256": "b5bb2ade3e202388d2ea9606e279c849796227d8ca43c28082cde0feddad2f5a"
76
+ },
77
+ "server.d.ts": {
78
+ "size": 276,
79
+ "sha256": "3f9453082da4e8bdef61c904a4e6a8f2c5c24ef6ae8c785ec6671ee57739256c"
80
+ },
81
+ "server.js": {
82
+ "size": 3027,
83
+ "sha256": "843b4cb91e438003d474d1f145d30cdc0627f8e099fb90bfe02597eb469be192"
84
+ },
85
+ "types.d.ts": {
86
+ "size": 2348,
87
+ "sha256": "90ab4f7122923b219e51cd333688e90a1152def08b6e86891dd066c36f185157"
88
+ },
89
+ "types.js": {
90
+ "size": 11,
91
+ "sha256": "8e609bb71c20b858c77f0e9f90bb1319db8477b13f9f965f1a1e18524bf50881"
92
+ }
93
+ }
94
+ }
@@ -0,0 +1,2 @@
1
+ import type { RedisRpcClient, RedisRpcClientOptions } from './types';
2
+ export declare const createRedisRpcClient: (options?: RedisRpcClientOptions) => RedisRpcClient;