Review funkt

This commit is contained in:
2026-08-09 21:56:00 +00:00
parent f522ae9953
commit fe9dfa4519
11366 changed files with 2335077 additions and 97 deletions

View File

@@ -0,0 +1,979 @@
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.createValkeyGlideClient = createValkeyGlideClient;
const crypto_1 = require("crypto");
const events_1 = require("events");
const stream_1 = require("stream");
const connection_closed_error_1 = require("./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;
}
function createValkeyGlideClient(client) {
return new ValkeyGlideAdapter(client);
}
class ValkeyGlideAdapter extends events_1.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 connection_closed_error_1.ConnectionClosedError();
}
}
normalizeError(error) {
if (error instanceof connection_closed_error_1.ConnectionClosedError ||
(error instanceof Error && error.name === 'ClosingError')) {
throw new connection_closed_error_1.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 connection_closed_error_1.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 = (0, crypto_1.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 stream_1.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);
}
}