Files
travel-app/node_modules/bullmq/dist/esm/classes/valkey-glide-client.js
2026-08-09 21:56:00 +00:00

977 lines
33 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { createHash } from 'crypto';
import { EventEmitter } from 'events';
import { Readable } from 'stream';
import { ConnectionClosedError } from './errors/connection-closed-error';
const GLIDE_STRING_DECODER = 1;
function createCompatibilityBatch() {
return {
commands: [],
customCommand(args) {
this.commands.push(args);
},
};
}
function createGlideBatch(isAtomic) {
try {
const { Batch } = require('@valkey/valkey-glide');
return typeof Batch === 'function' ? new Batch(isAtomic) : null;
}
catch (_a) {
return null;
}
}
function isBuffer(value) {
return Buffer.isBuffer(value);
}
function toGlideArg(value) {
if (isBuffer(value)) {
return value;
}
if (value === undefined || value === null) {
return '';
}
return String(value);
}
function toStringValue(value) {
if (isBuffer(value)) {
return value.toString();
}
return String(value);
}
function isKeyValueArray(value) {
return (Array.isArray(value) &&
value.every(item => item && typeof item === 'object' && 'key' in item && 'value' in item));
}
function normalizeScriptArgs(args) {
return args.length === 1 && Array.isArray(args[0]) ? args[0] : args;
}
function normalizeHashReply(reply) {
if (!reply) {
return {};
}
if (isKeyValueArray(reply)) {
return reply.reduce((acc, item) => {
acc[toStringValue(item.key)] = toStringValue(item.value);
return acc;
}, {});
}
if (reply instanceof Map) {
const out = {};
for (const [key, value] of reply.entries()) {
out[toStringValue(key)] = toStringValue(value);
}
return out;
}
if (Array.isArray(reply)) {
const out = {};
for (let i = 0; i < reply.length; i += 2) {
out[toStringValue(reply[i])] = toStringValue(reply[i + 1]);
}
return out;
}
if (typeof reply === 'object') {
const out = {};
for (const [key, value] of Object.entries(reply)) {
out[key] = toStringValue(value);
}
return out;
}
return {};
}
function flattenFieldPairs(fields) {
if (!fields) {
return [];
}
if (Array.isArray(fields)) {
if (isKeyValueArray(fields)) {
return fields.flatMap(pair => [
toStringValue(pair.key),
toStringValue(pair.value),
]);
}
if (fields.every(item => Array.isArray(item) && item.length === 2)) {
return fields.flatMap(pair => {
const [field, value] = pair;
return [toStringValue(field), toStringValue(value)];
});
}
return fields.map(item => toStringValue(item));
}
if (typeof fields === 'object') {
return Object.entries(fields).flatMap(([field, value]) => [field, toStringValue(value)]);
}
return [];
}
function normalizeXReadReply(reply) {
if (!reply) {
return null;
}
if (Array.isArray(reply) &&
reply.every(stream => Array.isArray(stream) &&
stream.length === 2 &&
typeof stream[0] !== 'undefined' &&
Array.isArray(stream[1]))) {
return reply;
}
if (!isKeyValueArray(reply)) {
return reply;
}
return reply.map(streamEntry => {
const streamName = toStringValue(streamEntry.key);
const messages = isKeyValueArray(streamEntry.value)
? streamEntry.value.map(message => [
toStringValue(message.key),
flattenFieldPairs(message.value),
])
: [];
return [streamName, messages];
});
}
function normalizeScanReply(reply) {
if (Array.isArray(reply) && reply.length >= 2) {
const [cursor, keys] = reply;
const normalizedKeys = Array.isArray(keys)
? keys.map(key => toStringValue(key))
: [];
return [toStringValue(cursor), normalizedKeys];
}
if (reply && typeof reply === 'object') {
const result = reply;
if (result.cursor !== undefined && Array.isArray(result.keys)) {
return [
toStringValue(result.cursor),
result.keys.map(key => toStringValue(key)),
];
}
}
return ['0', []];
}
function normalizeXRangeReply(reply) {
if (!reply) {
return [];
}
if (Array.isArray(reply) &&
reply.every(entry => Array.isArray(entry) &&
entry.length === 2 &&
typeof entry[0] !== 'undefined' &&
Array.isArray(entry[1]))) {
return reply.map(([id, fields]) => [
toStringValue(id),
fields.map((field) => toStringValue(field)),
]);
}
if (!isKeyValueArray(reply)) {
return [];
}
return reply.map(entry => [
toStringValue(entry.key),
flattenFieldPairs(entry.value),
]);
}
function normalizeSortedSetReply(reply, withScores = false) {
if (!Array.isArray(reply)) {
return [];
}
if (!withScores) {
return reply.map(item => toStringValue(item));
}
if (isKeyValueArray(reply)) {
return reply.flatMap(entry => [
toStringValue(entry.key),
toStringValue(entry.value),
]);
}
if (reply.every(item => Array.isArray(item) && item.length >= 2 && item[0] !== undefined)) {
return reply.flatMap(item => [
toStringValue(item[0]),
toStringValue(item[1]),
]);
}
return reply.map(item => toStringValue(item));
}
function isBlockingCommand(args) {
const [command, ...rest] = args;
const name = toStringValue(command).toUpperCase();
if (name === 'BZPOPMIN') {
return true;
}
if (name !== 'XREAD') {
return false;
}
for (let i = 0; i < rest.length; i += 2) {
if (toStringValue(rest[i]).toUpperCase() === 'BLOCK') {
return true;
}
}
return false;
}
export function createValkeyGlideClient(client) {
return new ValkeyGlideAdapter(client);
}
class ValkeyGlideAdapter extends EventEmitter {
constructor(rawOrPromise, connectionName) {
super();
this.connectionName = connectionName;
this.scripts = new Map();
this.scriptsBySha = new Map();
this.scriptLoadPromises = new Map();
this.readyEmitted = false;
this.closed = false;
this.operationChain = Promise.resolve();
this.activeBlockingCommands = 0;
if (rawOrPromise instanceof Promise) {
this.rawPromise = rawOrPromise;
void this.connect().catch(error => this.emit('error', error));
}
else {
this.raw = rawOrPromise;
}
}
get status() {
if (this.statusOverride) {
return this.statusOverride;
}
if (this.closed) {
return 'end';
}
if (this.readyEmitted) {
return 'ready';
}
return 'wait';
}
set status(value) {
if (value === 'end') {
this.disconnect();
}
this.statusOverride = value;
}
get isCluster() {
var _a, _b, _c;
const name = (_c = (_b = (_a = this.raw) === null || _a === void 0 ? void 0 : _a.constructor) === null || _b === void 0 ? void 0 : _b.name) !== null && _c !== void 0 ? _c : '';
return name.includes('Cluster');
}
get options() {
var _a, _b, _c, _d;
return (_d = (_b = (_a = this.raw) === null || _a === void 0 ? void 0 : _a.config) !== null && _b !== void 0 ? _b : (_c = this.raw) === null || _c === void 0 ? void 0 : _c.options) !== null && _d !== void 0 ? _d : {};
}
set options(_value) {
// no-op
}
ensureOpen() {
if (this.closed) {
throw new ConnectionClosedError();
}
}
normalizeError(error) {
if (error instanceof ConnectionClosedError ||
(error instanceof Error && error.name === 'ClosingError')) {
throw new ConnectionClosedError(error.message, error);
}
throw error;
}
async ensureRaw() {
if (this.raw) {
return this.raw;
}
if (!this.rawPromise) {
throw new Error('BullMQ: Valkey Glide client not initialized - missing raw client ' +
'and client promise. Please report this as a bug.');
}
this.raw = await this.rawPromise;
return this.raw;
}
async runSerialized(fn) {
this.ensureOpen();
const previous = this.operationChain;
let release;
this.operationChain = new Promise(resolve => {
release = resolve;
});
await previous;
this.ensureOpen();
try {
const raw = await this.ensureRaw();
this.ensureOpen();
return await fn(raw);
}
catch (error) {
this.normalizeError(error);
}
finally {
release();
}
}
async runRawCommand(args, options) {
const blockingCommand = isBlockingCommand(args);
return this.runSerialized(async (raw) => {
if (blockingCommand) {
this.activeBlockingCommands++;
}
try {
return await raw.customCommand(args, options);
}
finally {
if (blockingCommand) {
this.activeBlockingCommands--;
}
}
});
}
ensureScriptLoaded(script) {
let pendingLoad = this.scriptLoadPromises.get(script.sha);
if (!pendingLoad) {
pendingLoad = this.runRawCommand(['SCRIPT', 'LOAD', script.lua])
.then(() => { })
.catch(() => {
// Ignore script preload errors here – runCommand has NOSCRIPT fallback
// for non-transactional usage, and transactional callers will surface
// the underlying Redis error if the script still is not available.
});
this.scriptLoadPromises.set(script.sha, pendingLoad);
}
return pendingLoad;
}
async applyConnectionNameIfNeeded() {
if (!this.connectionName) {
return;
}
await this.runRawCommand(['CLIENT', 'SETNAME', this.connectionName], {
decoder: GLIDE_STRING_DECODER,
});
}
async clearConnectionNameIfNeeded(raw) {
if (!this.connectionName) {
return;
}
try {
await raw.customCommand(['CLIENT', 'SETNAME', ''], {
decoder: GLIDE_STRING_DECODER,
});
}
catch (_a) {
// ignore
}
}
async recreateRaw() {
var _a, _b;
const raw = await this.ensureRaw();
const clientConstructor = raw.constructor;
const createClient = (_a = clientConstructor === null || clientConstructor === void 0 ? void 0 : clientConstructor.createClient) === null || _a === void 0 ? void 0 : _a.bind(clientConstructor);
const config = (_b = raw.config) !== null && _b !== void 0 ? _b : raw.options;
if (!createClient || !config) {
throw new Error('BullMQ: Cannot recreate Valkey Glide client: missing createClient() method or ' +
'config object. Ensure the client was created via GlideClient.createClient() ' +
'or GlideClusterClient.createClient().');
}
this.raw = await createClient(config);
// Glide closes clients permanently, so a recreated raw connection starts
// with an empty server-side script cache.
this.scriptLoadPromises.clear();
}
async connect() {
if (this.connecting) {
return this.connecting;
}
this.connecting = (async () => {
if (this.closed && this.raw) {
await this.recreateRaw();
}
this.closed = false;
this.statusOverride = undefined;
await this.ensureRaw();
try {
await this.applyConnectionNameIfNeeded();
}
catch (error) {
if (this.closed && error instanceof ConnectionClosedError) {
return;
}
throw error;
}
if (this.closed) {
return;
}
this.readyEmitted = true;
this.emit('ready');
})().finally(() => {
this.connecting = undefined;
});
return this.connecting;
}
closeRaw() {
if (!this.closingPromise) {
const closeRaw = () => {
if (!this.raw) {
return;
}
try {
this.raw.close();
}
catch (_a) {
// ignore
}
};
this.closingPromise = (this.activeBlockingCommands > 0
? Promise.resolve().then(closeRaw)
: this.operationChain
.catch(() => {
// ignore
})
.then(async () => {
if (!this.closed) {
return;
}
if (this.raw) {
await this.clearConnectionNameIfNeeded(this.raw);
}
closeRaw();
})).finally(() => {
this.closingPromise = undefined;
});
}
return this.closingPromise;
}
disconnect(reconnect = false) {
if (this.closed && !reconnect) {
return;
}
this.closed = true;
this.readyEmitted = false;
this.statusOverride = reconnect ? undefined : 'end';
this.emit('close');
if (reconnect) {
void this.closeRaw()
.then(() => {
this.emit('reconnecting');
return this.connect();
})
.catch(error => this.emit('error', error));
return;
}
void this.closeRaw();
this.emit('end');
}
async quit() {
if (this.closed) {
setImmediate(() => {
this.emit('end');
this.emit('close');
});
return 'OK';
}
this.closed = true;
this.readyEmitted = false;
this.statusOverride = 'end';
void this.closeRaw();
setImmediate(() => {
this.emit('end');
this.emit('close');
});
return 'OK';
}
duplicate(...args) {
var _a;
const options = ((_a = args[0]) !== null && _a !== void 0 ? _a : {});
const duplicated = (async () => {
var _a, _b;
const raw = await this.ensureRaw();
const clientConstructor = raw.constructor;
const createClient = (_a = clientConstructor === null || clientConstructor === void 0 ? void 0 : clientConstructor.createClient) === null || _a === void 0 ? void 0 : _a.bind(clientConstructor);
const config = (_b = raw.config) !== null && _b !== void 0 ? _b : raw.options;
if (!createClient || !config) {
throw new Error('BullMQ: Cannot duplicate Valkey Glide client: missing createClient() or ' +
'config. Ensure the client was created via GlideClient.createClient()/' +
'GlideClusterClient.createClient().');
}
return createClient(config);
})();
return new ValkeyGlideAdapter(duplicated, options.connectionName);
}
defineCommand(name, definition) {
const sha = createHash('sha1').update(definition.lua).digest('hex');
const script = {
sha,
lua: definition.lua,
numberOfKeys: definition.numberOfKeys,
};
this.scripts.set(name, script);
this.scriptsBySha.set(sha, script);
this[name] = (...args) => this.runCommand(name, args);
void this.ensureScriptLoaded(script);
}
async runCommand(name, args) {
const script = this.scripts.get(name);
if (!script) {
throw new Error(`BullMQ: command "${name}" is not defined. Use defineCommand() before runCommand().`);
}
const normalizedArgs = normalizeScriptArgs(args);
const keys = normalizedArgs.slice(0, script.numberOfKeys).map(toGlideArg);
const argv = normalizedArgs.slice(script.numberOfKeys).map(toGlideArg);
const evalShaArgs = [
'EVALSHA',
script.sha,
String(script.numberOfKeys),
...keys,
...argv,
];
try {
return await this.runRawCommand(evalShaArgs);
}
catch (err) {
if (typeof (err === null || err === void 0 ? void 0 : err.message) === 'string' &&
err.message.toLowerCase().includes('noscript')) {
return this.runRawCommand([
'EVAL',
script.lua,
String(script.numberOfKeys),
...keys,
...argv,
]);
}
throw err;
}
}
multi() {
return new ValkeyGlideTransaction(this, this.scripts);
}
pipeline() {
return this.multi();
}
async hgetall(key) {
return normalizeHashReply(await this.runRawCommand(['HGETALL', key]));
}
async hget(key, field) {
const value = await this.runRawCommand(['HGET', key, field]);
return value == null ? null : toStringValue(value);
}
async hmget(key, ...fields) {
const value = await this.runRawCommand(['HMGET', key, ...fields]);
return Array.isArray(value)
? value.map(item => (item == null ? null : toStringValue(item)))
: [];
}
async hset(key, data) {
const args = ['HSET', key];
for (const [field, value] of Object.entries(data)) {
args.push(field, toGlideArg(value));
}
return Number(await this.runRawCommand(args));
}
async hdel(key, ...fields) {
return Number(await this.runRawCommand(['HDEL', key, ...fields]));
}
async hexists(key, field) {
const result = await this.runRawCommand(['HEXISTS', key, field]);
if (typeof result === 'boolean') {
return result ? 1 : 0;
}
return Number(result);
}
async get(key) {
const value = await this.runRawCommand(['GET', key]);
return value == null ? null : toStringValue(value);
}
async set(key, value, options) {
const args = ['SET', key, toGlideArg(value)];
if ((options === null || options === void 0 ? void 0 : options.PX) != null) {
args.push('PX', String(options.PX));
}
else if ((options === null || options === void 0 ? void 0 : options.EX) != null) {
args.push('EX', String(options.EX));
}
const result = await this.runRawCommand(args);
return result == null ? null : toStringValue(result);
}
async del(...keys) {
if (keys.length === 0) {
return 0;
}
return Number(await this.runRawCommand(['DEL', ...keys]));
}
async zrange(key, start, end, options) {
const args = ['ZRANGE', key, String(start), String(end)];
if (options === null || options === void 0 ? void 0 : options.WITHSCORES) {
args.push('WITHSCORES');
}
const result = await this.runRawCommand(args);
return normalizeSortedSetReply(result, options === null || options === void 0 ? void 0 : options.WITHSCORES);
}
async zrevrange(key, start, end, options) {
const args = ['ZREVRANGE', key, String(start), String(end)];
if (options === null || options === void 0 ? void 0 : options.WITHSCORES) {
args.push('WITHSCORES');
}
const result = await this.runRawCommand(args);
return normalizeSortedSetReply(result, options === null || options === void 0 ? void 0 : options.WITHSCORES);
}
async zcard(key) {
return Number(await this.runRawCommand(['ZCARD', key]));
}
async zscore(key, member) {
const result = await this.runRawCommand(['ZSCORE', key, member]);
return result == null ? null : toStringValue(result);
}
async lrange(key, start, end) {
const result = await this.runRawCommand([
'LRANGE',
key,
String(start),
String(end),
]);
return Array.isArray(result) ? result.map(toStringValue) : [];
}
async llen(key) {
return Number(await this.runRawCommand(['LLEN', key]));
}
async ltrim(key, start, end) {
const result = await this.runRawCommand([
'LTRIM',
key,
String(start),
String(end),
]);
return result == null ? 'OK' : toStringValue(result);
}
async lpos(key, value) {
const result = await this.runRawCommand(['LPOS', key, value]);
return result == null ? null : Number(result);
}
async smembers(key) {
const result = await this.runRawCommand(['SMEMBERS', key]);
return Array.isArray(result) ? result.map(toStringValue) : [];
}
async xadd(key, id, fields, options) {
const args = ['XADD', key];
if ((options === null || options === void 0 ? void 0 : options.MAXLEN) != null) {
args.push('MAXLEN');
if (options.approximate !== false) {
args.push('~');
}
args.push(String(options.MAXLEN));
}
args.push(id);
for (const [field, value] of Object.entries(fields)) {
args.push(field, toGlideArg(value));
}
return toStringValue(await this.runRawCommand(args));
}
async xread(streams, options) {
const args = ['XREAD'];
if ((options === null || options === void 0 ? void 0 : options.BLOCK) != null) {
args.push('BLOCK', String(options.BLOCK));
}
if ((options === null || options === void 0 ? void 0 : options.COUNT) != null) {
args.push('COUNT', String(options.COUNT));
}
args.push('STREAMS');
for (const stream of streams) {
args.push(stream.key);
}
for (const stream of streams) {
args.push(stream.id);
}
return normalizeXReadReply(await this.runRawCommand(args));
}
async xtrim(key, strategy, threshold, options) {
const args = ['XTRIM', key, strategy];
if ((options === null || options === void 0 ? void 0 : options.approximate) !== false) {
args.push('~');
}
args.push(String(threshold));
return Number(await this.runRawCommand(args));
}
async bzpopmin(key, timeout) {
const result = await this.runRawCommand(['BZPOPMIN', key, String(timeout)]);
if (!result) {
return null;
}
if (Array.isArray(result) && result.length >= 3) {
return [
toStringValue(result[0]),
toStringValue(result[1]),
toStringValue(result[2]),
];
}
return null;
}
async info() {
return toStringValue(await this.runRawCommand(['INFO']));
}
async clientSetName(name) {
return this.runRawCommand(['CLIENT', 'SETNAME', name], {
decoder: GLIDE_STRING_DECODER,
});
}
async clientList() {
return toStringValue(await this.runRawCommand(['CLIENT', 'LIST'], {
decoder: GLIDE_STRING_DECODER,
}));
}
async scan(cursor, options) {
const args = ['SCAN', String(cursor)];
if (options === null || options === void 0 ? void 0 : options.MATCH) {
args.push('MATCH', options.MATCH);
}
if ((options === null || options === void 0 ? void 0 : options.COUNT) != null) {
args.push('COUNT', String(options.COUNT));
}
return normalizeScanReply(await this.runRawCommand(args));
}
scanStream(options) {
let cursor = '0';
let running = false;
const stream = new Readable({
objectMode: true,
read: () => {
if (running) {
return;
}
running = true;
(async () => {
do {
const [nextCursor, keys] = await this.scan(cursor, {
MATCH: options.match,
COUNT: options.count,
});
cursor = nextCursor;
if (keys.length > 0 && !stream.push(keys)) {
return;
}
} while (cursor !== '0');
stream.push(null);
})()
.catch(err => stream.destroy(err))
.finally(() => {
running = false;
});
},
});
return stream;
}
async keys(pattern) {
const result = await this.runRawCommand(['KEYS', pattern]);
return Array.isArray(result) ? result.map(toStringValue) : [];
}
async exists(...keys) {
if (keys.length === 0) {
return 0;
}
const result = await this.runRawCommand(['EXISTS', ...keys]);
if (typeof result === 'boolean') {
return result ? 1 : 0;
}
return Number(result);
}
async zadd(key, ...args) {
const commandArgs = ['ZADD', key];
for (let i = 0; i < args.length; i += 2) {
commandArgs.push(toGlideArg(args[i]), toGlideArg(args[i + 1]));
}
return Number(await this.runRawCommand(commandArgs));
}
async zrem(key, ...members) {
return Number(await this.runRawCommand(['ZREM', key, ...members]));
}
async xlen(key) {
return Number(await this.runRawCommand(['XLEN', key]));
}
async xrevrange(key, end, start, ...rest) {
const args = ['XREVRANGE', key, end, start];
if (rest[0] === 'COUNT') {
args.push('COUNT', toGlideArg(rest[1]));
}
return normalizeXRangeReply(await this.runRawCommand(args));
}
async sadd(key, ...members) {
return Number(await this.runRawCommand([
'SADD',
key,
...members.map(member => toGlideArg(member)),
]));
}
async scard(key) {
return Number(await this.runRawCommand(['SCARD', key]));
}
async lpush(key, ...values) {
return Number(await this.runRawCommand(['LPUSH', key, ...values]));
}
async rpop(key) {
const result = await this.runRawCommand(['RPOP', key]);
return result == null ? null : toStringValue(result);
}
async incr(key) {
return Number(await this.runRawCommand(['INCR', key]));
}
async incrby(key, increment) {
return Number(await this.runRawCommand(['INCRBY', key, String(increment)]));
}
async flushall() {
const result = await this.runRawCommand(['FLUSHALL']);
return result == null ? 'OK' : toStringValue(result);
}
async execQueuedCommands(commands) {
const requiredScripts = commands
.map(command => String(command.args[0]).toUpperCase() === 'EVALSHA'
? this.scriptsBySha.get(toStringValue(command.args[1]))
: undefined)
.filter((script) => Boolean(script));
if (requiredScripts.length > 0) {
await Promise.all(requiredScripts.map(script => this.ensureScriptLoaded(script)));
}
return this.runSerialized(async (raw) => {
if (typeof raw.exec === 'function') {
const nativeBatch = createGlideBatch(true);
const batch = nativeBatch !== null && nativeBatch !== void 0 ? nativeBatch : createCompatibilityBatch();
if (batch) {
for (const command of commands) {
batch.customCommand(command.args);
}
let execResult;
try {
execResult = await raw.exec(batch, false);
}
catch (err) {
if (nativeBatch) {
throw err;
}
execResult = null;
}
if (!execResult) {
if (nativeBatch) {
return null;
}
}
else {
return execResult.map((value, index) => {
var _a;
if (value instanceof Error) {
return [value, null];
}
const transform = (_a = commands[index]) === null || _a === void 0 ? void 0 : _a.transform;
return [null, transform ? transform(value) : value];
});
}
}
}
await raw.customCommand(['MULTI']);
try {
for (const command of commands) {
// During MULTI queueing Redis replies with "QUEUED". For commands such
// as HGETALL, Glide's default decoder expects a structured reply and
// throws if it receives this simple string, so force string decoding.
await raw.customCommand(command.args, {
decoder: GLIDE_STRING_DECODER,
});
}
const execResult = await raw.customCommand(['EXEC']);
if (!execResult) {
return null;
}
const results = Array.isArray(execResult) ? execResult : [execResult];
return results.map((value, index) => {
var _a;
if (value instanceof Error) {
return [value, null];
}
const transform = (_a = commands[index]) === null || _a === void 0 ? void 0 : _a.transform;
return [null, transform ? transform(value) : value];
});
}
catch (err) {
try {
await raw.customCommand(['DISCARD']);
}
catch (_a) {
// ignore
}
throw err;
}
});
}
}
class ValkeyGlideTransaction {
constructor(adapter, scripts) {
this.adapter = adapter;
this.scripts = scripts;
this.commands = [];
}
queueCommand(args, transform) {
this.commands.push({ args, transform });
return this;
}
hgetall(key) {
return this.queueCommand(['HGETALL', key], normalizeHashReply);
}
hset(key, data) {
const args = ['HSET', key];
for (const [field, value] of Object.entries(data)) {
args.push(field, toGlideArg(value));
}
return this.queueCommand(args);
}
hscan(key, cursor, options) {
const args = ['HSCAN', key, String(cursor)];
if ((options === null || options === void 0 ? void 0 : options.COUNT) != null) {
args.push('COUNT', String(options.COUNT));
}
return this.queueCommand(args, value => {
if (Array.isArray(value) && value.length >= 2) {
return [toStringValue(value[0]), flattenFieldPairs(value[1])];
}
return ['0', []];
});
}
smembers(key) {
return this.queueCommand(['SMEMBERS', key], value => Array.isArray(value) ? value.map(item => toStringValue(item)) : []);
}
sscan(key, cursor, options) {
const args = ['SSCAN', key, String(cursor)];
if ((options === null || options === void 0 ? void 0 : options.COUNT) != null) {
args.push('COUNT', String(options.COUNT));
}
return this.queueCommand(args, value => {
if (Array.isArray(value) && value.length >= 2) {
const members = Array.isArray(value[1])
? value[1].map(item => toStringValue(item))
: [];
return [toStringValue(value[0]), members];
}
return ['0', []];
});
}
zrange(key, start, end) {
return this.queueCommand(['ZRANGE', key, String(start), String(end)]);
}
lrange(key, start, end) {
return this.queueCommand(['LRANGE', key, String(start), String(end)]);
}
llen(key) {
return this.queueCommand(['LLEN', key]);
}
del(...keys) {
if (keys.length > 0) {
this.queueCommand(['DEL', ...keys]);
}
return this;
}
runCommand(name, args) {
const script = this.scripts.get(name);
if (!script) {
throw new Error(`BullMQ: command "${name}" is not defined. Use defineCommand() before adding it to transactions.`);
}
const normalizedArgs = normalizeScriptArgs(args);
const keys = normalizedArgs.slice(0, script.numberOfKeys).map(toGlideArg);
const argv = normalizedArgs.slice(script.numberOfKeys).map(toGlideArg);
return this.queueCommand([
'EVALSHA',
script.sha,
String(script.numberOfKeys),
...keys,
...argv,
]);
}
exec() {
return this.adapter.execQueuedCommands(this.commands);
}
}