mirror of
https://github.com/oven-sh/bun
synced 2026-02-02 15:08:46 +00:00
Many more Redis commands (#23116)
This commit is contained in:
2593
packages/bun-types/redis.d.ts
vendored
2593
packages/bun-types/redis.d.ts
vendored
File diff suppressed because it is too large
Load Diff
@@ -48,10 +48,22 @@ export default [
|
||||
fn: "incr",
|
||||
length: 1,
|
||||
},
|
||||
incrby: {
|
||||
fn: "incrby",
|
||||
length: 2,
|
||||
},
|
||||
incrbyfloat: {
|
||||
fn: "incrbyfloat",
|
||||
length: 2,
|
||||
},
|
||||
decr: {
|
||||
fn: "decr",
|
||||
length: 1,
|
||||
},
|
||||
decrby: {
|
||||
fn: "decrby",
|
||||
length: 2,
|
||||
},
|
||||
exists: {
|
||||
fn: "exists",
|
||||
length: 1,
|
||||
@@ -60,6 +72,14 @@ export default [
|
||||
fn: "expire",
|
||||
length: 2,
|
||||
},
|
||||
expireat: {
|
||||
fn: "expireat",
|
||||
length: 2,
|
||||
},
|
||||
pexpire: {
|
||||
fn: "pexpire",
|
||||
length: 2,
|
||||
},
|
||||
connect: {
|
||||
fn: "jsConnect",
|
||||
length: 0,
|
||||
@@ -78,6 +98,14 @@ export default [
|
||||
},
|
||||
hmset: {
|
||||
fn: "hmset",
|
||||
length: 2,
|
||||
},
|
||||
hset: {
|
||||
fn: "hset",
|
||||
length: 2,
|
||||
},
|
||||
hsetnx: {
|
||||
fn: "hsetnx",
|
||||
length: 3,
|
||||
},
|
||||
hget: {
|
||||
@@ -88,6 +116,70 @@ export default [
|
||||
fn: "hmget",
|
||||
length: 2,
|
||||
},
|
||||
hdel: {
|
||||
fn: "hdel",
|
||||
length: 2,
|
||||
},
|
||||
hexists: {
|
||||
fn: "hexists",
|
||||
length: 2,
|
||||
},
|
||||
hrandfield: {
|
||||
fn: "hrandfield",
|
||||
length: 1,
|
||||
},
|
||||
hscan: {
|
||||
fn: "hscan",
|
||||
length: 2,
|
||||
},
|
||||
hgetdel: {
|
||||
fn: "hgetdel",
|
||||
length: 2,
|
||||
},
|
||||
hgetex: {
|
||||
fn: "hgetex",
|
||||
length: 2,
|
||||
},
|
||||
hsetex: {
|
||||
fn: "hsetex",
|
||||
length: 3,
|
||||
},
|
||||
hexpire: {
|
||||
fn: "hexpire",
|
||||
length: 3,
|
||||
},
|
||||
hexpireat: {
|
||||
fn: "hexpireat",
|
||||
length: 3,
|
||||
},
|
||||
hexpiretime: {
|
||||
fn: "hexpiretime",
|
||||
length: 2,
|
||||
},
|
||||
hpersist: {
|
||||
fn: "hpersist",
|
||||
length: 2,
|
||||
},
|
||||
hpexpire: {
|
||||
fn: "hpexpire",
|
||||
length: 3,
|
||||
},
|
||||
hpexpireat: {
|
||||
fn: "hpexpireat",
|
||||
length: 3,
|
||||
},
|
||||
hpexpiretime: {
|
||||
fn: "hpexpiretime",
|
||||
length: 2,
|
||||
},
|
||||
hpttl: {
|
||||
fn: "hpttl",
|
||||
length: 2,
|
||||
},
|
||||
httl: {
|
||||
fn: "httl",
|
||||
length: 2,
|
||||
},
|
||||
sismember: {
|
||||
fn: "sismember",
|
||||
length: 2,
|
||||
@@ -123,6 +215,42 @@ export default [
|
||||
bitcount: {
|
||||
fn: "bitcount",
|
||||
},
|
||||
blmove: {
|
||||
fn: "blmove",
|
||||
length: 5,
|
||||
},
|
||||
blmpop: {
|
||||
fn: "blmpop",
|
||||
length: 3,
|
||||
},
|
||||
blpop: {
|
||||
fn: "blpop",
|
||||
length: 2,
|
||||
},
|
||||
brpop: {
|
||||
fn: "brpop",
|
||||
length: 2,
|
||||
},
|
||||
brpoplpush: {
|
||||
fn: "brpoplpush",
|
||||
length: 3,
|
||||
},
|
||||
getbit: {
|
||||
fn: "getbit",
|
||||
length: 2,
|
||||
},
|
||||
setbit: {
|
||||
fn: "setbit",
|
||||
length: 3,
|
||||
},
|
||||
getrange: {
|
||||
fn: "getrange",
|
||||
length: 3,
|
||||
},
|
||||
setrange: {
|
||||
fn: "setrange",
|
||||
length: 3,
|
||||
},
|
||||
dump: {
|
||||
fn: "dump",
|
||||
},
|
||||
@@ -150,33 +278,132 @@ export default [
|
||||
keys: {
|
||||
fn: "keys",
|
||||
},
|
||||
lindex: {
|
||||
fn: "lindex",
|
||||
length: 2,
|
||||
},
|
||||
linsert: {
|
||||
fn: "linsert",
|
||||
length: 4,
|
||||
},
|
||||
llen: {
|
||||
fn: "llen",
|
||||
},
|
||||
lmove: {
|
||||
fn: "lmove",
|
||||
length: 4,
|
||||
},
|
||||
lmpop: {
|
||||
fn: "lmpop",
|
||||
length: 2,
|
||||
},
|
||||
lpop: {
|
||||
fn: "lpop",
|
||||
},
|
||||
lpos: {
|
||||
fn: "lpos",
|
||||
length: 2,
|
||||
},
|
||||
lrange: {
|
||||
fn: "lrange",
|
||||
length: 3,
|
||||
},
|
||||
lrem: {
|
||||
fn: "lrem",
|
||||
length: 3,
|
||||
},
|
||||
lset: {
|
||||
fn: "lset",
|
||||
length: 3,
|
||||
},
|
||||
ltrim: {
|
||||
fn: "ltrim",
|
||||
length: 3,
|
||||
},
|
||||
persist: {
|
||||
fn: "persist",
|
||||
},
|
||||
pexpireat: {
|
||||
fn: "pexpireat",
|
||||
length: 2,
|
||||
},
|
||||
pexpiretime: {
|
||||
fn: "pexpiretime",
|
||||
},
|
||||
pttl: {
|
||||
fn: "pttl",
|
||||
},
|
||||
randomkey: {
|
||||
fn: "randomkey",
|
||||
length: 0,
|
||||
},
|
||||
rpop: {
|
||||
fn: "rpop",
|
||||
},
|
||||
rpoplpush: {
|
||||
fn: "rpoplpush",
|
||||
length: 2,
|
||||
},
|
||||
scan: {
|
||||
fn: "scan",
|
||||
},
|
||||
scard: {
|
||||
fn: "scard",
|
||||
},
|
||||
sdiff: {
|
||||
fn: "sdiff",
|
||||
length: 1,
|
||||
},
|
||||
sdiffstore: {
|
||||
fn: "sdiffstore",
|
||||
length: 2,
|
||||
},
|
||||
sinter: {
|
||||
fn: "sinter",
|
||||
length: 1,
|
||||
},
|
||||
sintercard: {
|
||||
fn: "sintercard",
|
||||
length: 1,
|
||||
},
|
||||
sinterstore: {
|
||||
fn: "sinterstore",
|
||||
length: 2,
|
||||
},
|
||||
smismember: {
|
||||
fn: "smismember",
|
||||
length: 2,
|
||||
},
|
||||
sscan: {
|
||||
fn: "sscan",
|
||||
length: 2,
|
||||
},
|
||||
strlen: {
|
||||
fn: "strlen",
|
||||
},
|
||||
sunion: {
|
||||
fn: "sunion",
|
||||
length: 1,
|
||||
},
|
||||
sunionstore: {
|
||||
fn: "sunionstore",
|
||||
length: 2,
|
||||
},
|
||||
type: {
|
||||
fn: "type",
|
||||
length: 1,
|
||||
},
|
||||
zcard: {
|
||||
fn: "zcard",
|
||||
},
|
||||
zcount: {
|
||||
fn: "zcount",
|
||||
length: 3,
|
||||
},
|
||||
zlexcount: {
|
||||
fn: "zlexcount",
|
||||
length: 3,
|
||||
},
|
||||
zpopmax: {
|
||||
fn: "zpopmax",
|
||||
},
|
||||
@@ -186,6 +413,50 @@ export default [
|
||||
zrandmember: {
|
||||
fn: "zrandmember",
|
||||
},
|
||||
zrange: {
|
||||
fn: "zrange",
|
||||
length: 3,
|
||||
},
|
||||
zrangebylex: {
|
||||
fn: "zrangebylex",
|
||||
length: 3,
|
||||
},
|
||||
zrangebyscore: {
|
||||
fn: "zrangebyscore",
|
||||
length: 3,
|
||||
},
|
||||
zrangestore: {
|
||||
fn: "zrangestore",
|
||||
length: 4,
|
||||
},
|
||||
zrem: {
|
||||
fn: "zrem",
|
||||
length: 2,
|
||||
},
|
||||
zremrangebylex: {
|
||||
fn: "zremrangebylex",
|
||||
length: 3,
|
||||
},
|
||||
zremrangebyrank: {
|
||||
fn: "zremrangebyrank",
|
||||
length: 3,
|
||||
},
|
||||
zremrangebyscore: {
|
||||
fn: "zremrangebyscore",
|
||||
length: 3,
|
||||
},
|
||||
zrevrange: {
|
||||
fn: "zrevrange",
|
||||
length: 3,
|
||||
},
|
||||
zrevrangebylex: {
|
||||
fn: "zrevrangebylex",
|
||||
length: 3,
|
||||
},
|
||||
zrevrangebyscore: {
|
||||
fn: "zrevrangebyscore",
|
||||
length: 3,
|
||||
},
|
||||
append: {
|
||||
fn: "append",
|
||||
},
|
||||
@@ -210,12 +481,85 @@ export default [
|
||||
setnx: {
|
||||
fn: "setnx",
|
||||
},
|
||||
setex: {
|
||||
fn: "setex",
|
||||
length: 3,
|
||||
},
|
||||
psetex: {
|
||||
fn: "psetex",
|
||||
length: 3,
|
||||
},
|
||||
zscore: {
|
||||
fn: "zscore",
|
||||
},
|
||||
zincrby: {
|
||||
fn: "zincrby",
|
||||
length: 3,
|
||||
},
|
||||
zmscore: {
|
||||
fn: "zmscore",
|
||||
},
|
||||
zadd: {
|
||||
fn: "zadd",
|
||||
length: 3,
|
||||
},
|
||||
zscan: {
|
||||
fn: "zscan",
|
||||
length: 2,
|
||||
},
|
||||
zdiff: {
|
||||
fn: "zdiff",
|
||||
length: 1,
|
||||
},
|
||||
zdiffstore: {
|
||||
fn: "zdiffstore",
|
||||
length: 2,
|
||||
},
|
||||
zinter: {
|
||||
fn: "zinter",
|
||||
length: 2,
|
||||
},
|
||||
zintercard: {
|
||||
fn: "zintercard",
|
||||
length: 1,
|
||||
},
|
||||
zinterstore: {
|
||||
fn: "zinterstore",
|
||||
length: 3,
|
||||
},
|
||||
zunion: {
|
||||
fn: "zunion",
|
||||
length: 2,
|
||||
},
|
||||
zunionstore: {
|
||||
fn: "zunionstore",
|
||||
length: 3,
|
||||
},
|
||||
zmpop: {
|
||||
fn: "zmpop",
|
||||
length: 2,
|
||||
},
|
||||
bzmpop: {
|
||||
fn: "bzmpop",
|
||||
length: 3,
|
||||
},
|
||||
bzpopmin: {
|
||||
fn: "bzpopmin",
|
||||
length: 2,
|
||||
},
|
||||
bzpopmax: {
|
||||
fn: "bzpopmax",
|
||||
length: 2,
|
||||
},
|
||||
mget: {
|
||||
fn: "mget",
|
||||
},
|
||||
mset: {
|
||||
fn: "mset",
|
||||
},
|
||||
msetnx: {
|
||||
fn: "msetnx",
|
||||
},
|
||||
ping: { fn: "ping" },
|
||||
publish: { fn: "publish" },
|
||||
script: { fn: "script" },
|
||||
@@ -232,6 +576,11 @@ export default [
|
||||
unsubscribe: { fn: "unsubscribe" },
|
||||
punsubscribe: { fn: "punsubscribe" },
|
||||
pubsub: { fn: "pubsub" },
|
||||
copy: { fn: "copy" },
|
||||
unlink: { fn: "unlink" },
|
||||
touch: { fn: "touch" },
|
||||
rename: { fn: "rename", length: 2 },
|
||||
renamenx: { fn: "renamenx", length: 2 },
|
||||
},
|
||||
values: ["onconnect", "onclose", "connectionPromise", "hello", "subscriptionCallbackMap"],
|
||||
}),
|
||||
|
||||
@@ -1243,72 +1243,163 @@ pub const JSValkeyClient = struct {
|
||||
pub const @"type" = fns.type;
|
||||
pub const append = fns.append;
|
||||
pub const bitcount = fns.bitcount;
|
||||
pub const blpop = fns.blpop;
|
||||
pub const brpop = fns.brpop;
|
||||
pub const copy = fns.copy;
|
||||
pub const decr = fns.decr;
|
||||
pub const decrby = fns.decrby;
|
||||
pub const del = fns.del;
|
||||
pub const dump = fns.dump;
|
||||
pub const duplicate = fns.duplicate;
|
||||
pub const exists = fns.exists;
|
||||
pub const expire = fns.expire;
|
||||
pub const expireat = fns.expireat;
|
||||
pub const expiretime = fns.expiretime;
|
||||
pub const get = fns.get;
|
||||
pub const getBuffer = fns.getBuffer;
|
||||
pub const getbit = fns.getbit;
|
||||
pub const getdel = fns.getdel;
|
||||
pub const getex = fns.getex;
|
||||
pub const getrange = fns.getrange;
|
||||
pub const getset = fns.getset;
|
||||
pub const hgetall = fns.hgetall;
|
||||
pub const hget = fns.hget;
|
||||
pub const hincrby = fns.hincrby;
|
||||
pub const hincrbyfloat = fns.hincrbyfloat;
|
||||
pub const hkeys = fns.hkeys;
|
||||
pub const hdel = fns.hdel;
|
||||
pub const hexists = fns.hexists;
|
||||
pub const hgetdel = fns.hgetdel;
|
||||
pub const hgetex = fns.hgetex;
|
||||
pub const hlen = fns.hlen;
|
||||
pub const hmget = fns.hmget;
|
||||
pub const hmset = fns.hmset;
|
||||
pub const hrandfield = fns.hrandfield;
|
||||
pub const hscan = fns.hscan;
|
||||
pub const hset = fns.hset;
|
||||
pub const hsetex = fns.hsetex;
|
||||
pub const hsetnx = fns.hsetnx;
|
||||
pub const hstrlen = fns.hstrlen;
|
||||
pub const hvals = fns.hvals;
|
||||
pub const hexpire = fns.hexpire;
|
||||
pub const hexpireat = fns.hexpireat;
|
||||
pub const hexpiretime = fns.hexpiretime;
|
||||
pub const hpersist = fns.hpersist;
|
||||
pub const hpexpire = fns.hpexpire;
|
||||
pub const hpexpireat = fns.hpexpireat;
|
||||
pub const hpexpiretime = fns.hpexpiretime;
|
||||
pub const hpttl = fns.hpttl;
|
||||
pub const httl = fns.httl;
|
||||
pub const incr = fns.incr;
|
||||
pub const incrby = fns.incrby;
|
||||
pub const incrbyfloat = fns.incrbyfloat;
|
||||
pub const keys = fns.keys;
|
||||
pub const lindex = fns.lindex;
|
||||
pub const linsert = fns.linsert;
|
||||
pub const llen = fns.llen;
|
||||
pub const lmove = fns.lmove;
|
||||
pub const lmpop = fns.lmpop;
|
||||
pub const lpop = fns.lpop;
|
||||
pub const lpos = fns.lpos;
|
||||
pub const lpush = fns.lpush;
|
||||
pub const lpushx = fns.lpushx;
|
||||
pub const lrange = fns.lrange;
|
||||
pub const lrem = fns.lrem;
|
||||
pub const lset = fns.lset;
|
||||
pub const ltrim = fns.ltrim;
|
||||
pub const mget = fns.mget;
|
||||
pub const mset = fns.mset;
|
||||
pub const msetnx = fns.msetnx;
|
||||
pub const persist = fns.persist;
|
||||
pub const pexpire = fns.pexpire;
|
||||
pub const pexpireat = fns.pexpireat;
|
||||
pub const pexpiretime = fns.pexpiretime;
|
||||
pub const pfadd = fns.pfadd;
|
||||
pub const ping = fns.ping;
|
||||
pub const psetex = fns.psetex;
|
||||
pub const psubscribe = fns.psubscribe;
|
||||
pub const pttl = fns.pttl;
|
||||
pub const publish = fns.publish;
|
||||
pub const pubsub = fns.pubsub;
|
||||
pub const punsubscribe = fns.punsubscribe;
|
||||
pub const randomkey = fns.randomkey;
|
||||
pub const rename = fns.rename;
|
||||
pub const renamenx = fns.renamenx;
|
||||
pub const rpop = fns.rpop;
|
||||
pub const rpoplpush = fns.rpoplpush;
|
||||
pub const rpush = fns.rpush;
|
||||
pub const rpushx = fns.rpushx;
|
||||
pub const sadd = fns.sadd;
|
||||
pub const scan = fns.scan;
|
||||
pub const scard = fns.scard;
|
||||
pub const script = fns.script;
|
||||
pub const sdiff = fns.sdiff;
|
||||
pub const sdiffstore = fns.sdiffstore;
|
||||
pub const sinter = fns.sinter;
|
||||
pub const sintercard = fns.sintercard;
|
||||
pub const sinterstore = fns.sinterstore;
|
||||
pub const select = fns.select;
|
||||
pub const set = fns.set;
|
||||
pub const setbit = fns.setbit;
|
||||
pub const setex = fns.setex;
|
||||
pub const setnx = fns.setnx;
|
||||
pub const setrange = fns.setrange;
|
||||
pub const sismember = fns.sismember;
|
||||
pub const smembers = fns.smembers;
|
||||
pub const smismember = fns.smismember;
|
||||
pub const smove = fns.smove;
|
||||
pub const spop = fns.spop;
|
||||
pub const spublish = fns.spublish;
|
||||
pub const srandmember = fns.srandmember;
|
||||
pub const srem = fns.srem;
|
||||
pub const sscan = fns.sscan;
|
||||
pub const strlen = fns.strlen;
|
||||
pub const subscribe = fns.subscribe;
|
||||
pub const substr = fns.substr;
|
||||
pub const sunion = fns.sunion;
|
||||
pub const sunionstore = fns.sunionstore;
|
||||
pub const touch = fns.touch;
|
||||
pub const ttl = fns.ttl;
|
||||
pub const unlink = fns.unlink;
|
||||
pub const unsubscribe = fns.unsubscribe;
|
||||
pub const zcard = fns.zcard;
|
||||
pub const zcount = fns.zcount;
|
||||
pub const zlexcount = fns.zlexcount;
|
||||
pub const zpopmax = fns.zpopmax;
|
||||
pub const zpopmin = fns.zpopmin;
|
||||
pub const zrandmember = fns.zrandmember;
|
||||
pub const zrange = fns.zrange;
|
||||
pub const zrangebylex = fns.zrangebylex;
|
||||
pub const zrangebyscore = fns.zrangebyscore;
|
||||
pub const zrangestore = fns.zrangestore;
|
||||
pub const zrank = fns.zrank;
|
||||
pub const zrem = fns.zrem;
|
||||
pub const zremrangebylex = fns.zremrangebylex;
|
||||
pub const zremrangebyrank = fns.zremrangebyrank;
|
||||
pub const zremrangebyscore = fns.zremrangebyscore;
|
||||
pub const zrevrange = fns.zrevrange;
|
||||
pub const zrevrangebylex = fns.zrevrangebylex;
|
||||
pub const zrevrangebyscore = fns.zrevrangebyscore;
|
||||
pub const zrevrank = fns.zrevrank;
|
||||
pub const zscore = fns.zscore;
|
||||
pub const zincrby = fns.zincrby;
|
||||
pub const zmscore = fns.zmscore;
|
||||
pub const zadd = fns.zadd;
|
||||
pub const zscan = fns.zscan;
|
||||
pub const zdiff = fns.zdiff;
|
||||
pub const zdiffstore = fns.zdiffstore;
|
||||
pub const zinter = fns.zinter;
|
||||
pub const zintercard = fns.zintercard;
|
||||
pub const zinterstore = fns.zinterstore;
|
||||
pub const zunion = fns.zunion;
|
||||
pub const zunionstore = fns.zunionstore;
|
||||
pub const zmpop = fns.zmpop;
|
||||
pub const bzmpop = fns.bzmpop;
|
||||
pub const bzpopmin = fns.bzpopmin;
|
||||
pub const bzpopmax = fns.bzpopmax;
|
||||
pub const blmove = fns.blmove;
|
||||
pub const blmpop = fns.blmpop;
|
||||
pub const brpoplpush = fns.brpoplpush;
|
||||
|
||||
const fns = @import("./js_valkey_functions.zig");
|
||||
};
|
||||
|
||||
@@ -274,14 +274,34 @@ pub fn ttl(this: *JSValkeyClient, globalObject: *jsc.JSGlobalObject, callframe:
|
||||
pub fn srem(this: *JSValkeyClient, globalObject: *jsc.JSGlobalObject, callframe: *jsc.CallFrame) bun.JSError!JSValue {
|
||||
try requireNotSubscriber(this, @src().fn_name);
|
||||
|
||||
const args_view = callframe.arguments();
|
||||
if (args_view.len < 2) {
|
||||
return globalObject.throw("SREM requires at least a key and one member", .{});
|
||||
}
|
||||
|
||||
var stack_fallback = std.heap.stackFallback(512, bun.default_allocator);
|
||||
var args = try std.ArrayList(JSArgument).initCapacity(stack_fallback.get(), args_view.len);
|
||||
defer {
|
||||
for (args.items) |*item| {
|
||||
item.deinit();
|
||||
}
|
||||
args.deinit();
|
||||
}
|
||||
|
||||
const key = (try fromJS(globalObject, callframe.argument(0))) orelse {
|
||||
return globalObject.throwInvalidArgumentType("srem", "key", "string or buffer");
|
||||
};
|
||||
defer key.deinit();
|
||||
const value = (try fromJS(globalObject, callframe.argument(1))) orelse {
|
||||
return globalObject.throwInvalidArgumentType("srem", "value", "string or buffer");
|
||||
};
|
||||
defer value.deinit();
|
||||
args.appendAssumeCapacity(key);
|
||||
|
||||
for (args_view[1..]) |arg| {
|
||||
if (arg.isUndefinedOrNull()) {
|
||||
break;
|
||||
}
|
||||
const value = (try fromJS(globalObject, arg)) orelse {
|
||||
return globalObject.throwInvalidArgumentType("srem", "member", "string or buffer");
|
||||
};
|
||||
args.appendAssumeCapacity(value);
|
||||
}
|
||||
|
||||
// Send SREM command
|
||||
const promise = this.send(
|
||||
@@ -289,7 +309,7 @@ pub fn srem(this: *JSValkeyClient, globalObject: *jsc.JSGlobalObject, callframe:
|
||||
callframe.this(),
|
||||
&.{
|
||||
.command = "SREM",
|
||||
.args = .{ .args = &.{ key, value } },
|
||||
.args = .{ .args = args.items },
|
||||
},
|
||||
) catch |err| {
|
||||
return protocol.valkeyErrorToJS(globalObject, "Failed to send SREM command", err);
|
||||
@@ -301,10 +321,28 @@ pub fn srem(this: *JSValkeyClient, globalObject: *jsc.JSGlobalObject, callframe:
|
||||
pub fn srandmember(this: *JSValkeyClient, globalObject: *jsc.JSGlobalObject, callframe: *jsc.CallFrame) bun.JSError!JSValue {
|
||||
try requireNotSubscriber(this, @src().fn_name);
|
||||
|
||||
const args_view = callframe.arguments();
|
||||
var stack_fallback = std.heap.stackFallback(512, bun.default_allocator);
|
||||
var args = try std.ArrayList(JSArgument).initCapacity(stack_fallback.get(), args_view.len);
|
||||
defer {
|
||||
for (args.items) |*item| {
|
||||
item.deinit();
|
||||
}
|
||||
args.deinit();
|
||||
}
|
||||
|
||||
const key = (try fromJS(globalObject, callframe.argument(0))) orelse {
|
||||
return globalObject.throwInvalidArgumentType("srandmember", "key", "string or buffer");
|
||||
};
|
||||
defer key.deinit();
|
||||
args.appendAssumeCapacity(key);
|
||||
|
||||
// Optional count argument
|
||||
if (args_view.len > 1 and !callframe.argument(1).isUndefinedOrNull()) {
|
||||
const count_arg = try fromJS(globalObject, callframe.argument(1)) orelse {
|
||||
return globalObject.throwInvalidArgumentType("srandmember", "count", "number or string");
|
||||
};
|
||||
args.appendAssumeCapacity(count_arg);
|
||||
}
|
||||
|
||||
// Send SRANDMEMBER command
|
||||
const promise = this.send(
|
||||
@@ -312,7 +350,7 @@ pub fn srandmember(this: *JSValkeyClient, globalObject: *jsc.JSGlobalObject, cal
|
||||
callframe.this(),
|
||||
&.{
|
||||
.command = "SRANDMEMBER",
|
||||
.args = .{ .args = &.{key} },
|
||||
.args = .{ .args = args.items },
|
||||
},
|
||||
) catch |err| {
|
||||
return protocol.valkeyErrorToJS(globalObject, "Failed to send SRANDMEMBER command", err);
|
||||
@@ -347,10 +385,28 @@ pub fn smembers(this: *JSValkeyClient, globalObject: *jsc.JSGlobalObject, callfr
|
||||
pub fn spop(this: *JSValkeyClient, globalObject: *jsc.JSGlobalObject, callframe: *jsc.CallFrame) bun.JSError!JSValue {
|
||||
try requireNotSubscriber(this, @src().fn_name);
|
||||
|
||||
const args_view = callframe.arguments();
|
||||
var stack_fallback = std.heap.stackFallback(512, bun.default_allocator);
|
||||
var args = try std.ArrayList(JSArgument).initCapacity(stack_fallback.get(), args_view.len);
|
||||
defer {
|
||||
for (args.items) |*item| {
|
||||
item.deinit();
|
||||
}
|
||||
args.deinit();
|
||||
}
|
||||
|
||||
const key = (try fromJS(globalObject, callframe.argument(0))) orelse {
|
||||
return globalObject.throwInvalidArgumentType("spop", "key", "string or buffer");
|
||||
};
|
||||
defer key.deinit();
|
||||
args.appendAssumeCapacity(key);
|
||||
|
||||
// Optional count argument
|
||||
if (args_view.len > 1 and !callframe.argument(1).isUndefinedOrNull()) {
|
||||
const count_arg = try fromJS(globalObject, callframe.argument(1)) orelse {
|
||||
return globalObject.throwInvalidArgumentType("spop", "count", "number or string");
|
||||
};
|
||||
args.appendAssumeCapacity(count_arg);
|
||||
}
|
||||
|
||||
// Send SPOP command
|
||||
const promise = this.send(
|
||||
@@ -358,7 +414,7 @@ pub fn spop(this: *JSValkeyClient, globalObject: *jsc.JSGlobalObject, callframe:
|
||||
callframe.this(),
|
||||
&.{
|
||||
.command = "SPOP",
|
||||
.args = .{ .args = &.{key} },
|
||||
.args = .{ .args = args.items },
|
||||
},
|
||||
) catch |err| {
|
||||
return protocol.valkeyErrorToJS(globalObject, "Failed to send SPOP command", err);
|
||||
@@ -370,14 +426,34 @@ pub fn spop(this: *JSValkeyClient, globalObject: *jsc.JSGlobalObject, callframe:
|
||||
pub fn sadd(this: *JSValkeyClient, globalObject: *jsc.JSGlobalObject, callframe: *jsc.CallFrame) bun.JSError!JSValue {
|
||||
try requireNotSubscriber(this, @src().fn_name);
|
||||
|
||||
const args_view = callframe.arguments();
|
||||
if (args_view.len < 2) {
|
||||
return globalObject.throw("SADD requires at least a key and one member", .{});
|
||||
}
|
||||
|
||||
var stack_fallback = std.heap.stackFallback(512, bun.default_allocator);
|
||||
var args = try std.ArrayList(JSArgument).initCapacity(stack_fallback.get(), args_view.len);
|
||||
defer {
|
||||
for (args.items) |*item| {
|
||||
item.deinit();
|
||||
}
|
||||
args.deinit();
|
||||
}
|
||||
|
||||
const key = (try fromJS(globalObject, callframe.argument(0))) orelse {
|
||||
return globalObject.throwInvalidArgumentType("sadd", "key", "string or buffer");
|
||||
};
|
||||
defer key.deinit();
|
||||
const value = (try fromJS(globalObject, callframe.argument(1))) orelse {
|
||||
return globalObject.throwInvalidArgumentType("sadd", "value", "string or buffer");
|
||||
};
|
||||
defer value.deinit();
|
||||
args.appendAssumeCapacity(key);
|
||||
|
||||
for (args_view[1..]) |arg| {
|
||||
if (arg.isUndefinedOrNull()) {
|
||||
break;
|
||||
}
|
||||
const value = (try fromJS(globalObject, arg)) orelse {
|
||||
return globalObject.throwInvalidArgumentType("sadd", "member", "string or buffer");
|
||||
};
|
||||
args.appendAssumeCapacity(value);
|
||||
}
|
||||
|
||||
// Send SADD command
|
||||
const promise = this.send(
|
||||
@@ -385,7 +461,7 @@ pub fn sadd(this: *JSValkeyClient, globalObject: *jsc.JSGlobalObject, callframe:
|
||||
callframe.this(),
|
||||
&.{
|
||||
.command = "SADD",
|
||||
.args = .{ .args = &.{ key, value } },
|
||||
.args = .{ .args = args.items },
|
||||
},
|
||||
) catch |err| {
|
||||
return protocol.valkeyErrorToJS(globalObject, "Failed to send SADD command", err);
|
||||
@@ -425,35 +501,49 @@ pub fn sismember(this: *JSValkeyClient, globalObject: *jsc.JSGlobalObject, callf
|
||||
pub fn hmget(this: *JSValkeyClient, globalObject: *jsc.JSGlobalObject, callframe: *jsc.CallFrame) bun.JSError!JSValue {
|
||||
try requireNotSubscriber(this, @src().fn_name);
|
||||
|
||||
const key = (try fromJS(globalObject, callframe.argument(0))) orelse {
|
||||
return globalObject.throwInvalidArgumentType("hmget", "key", "string or buffer");
|
||||
};
|
||||
defer key.deinit();
|
||||
|
||||
// Get field array argument
|
||||
const fields_array = callframe.argument(1);
|
||||
if (!fields_array.isObject() or !fields_array.isArray()) {
|
||||
return globalObject.throw("Fields must be an array", .{});
|
||||
const args_view = callframe.arguments();
|
||||
if (args_view.len < 2) {
|
||||
return globalObject.throw("HMGET requires at least a key and one field", .{});
|
||||
}
|
||||
|
||||
var iter = try fields_array.arrayIterator(globalObject);
|
||||
var args = try std.ArrayList(jsc.ZigString.Slice).initCapacity(bun.default_allocator, iter.len + 1);
|
||||
var stack_fallback = std.heap.stackFallback(512, bun.default_allocator);
|
||||
var args = try std.ArrayList(JSArgument).initCapacity(stack_fallback.get(), args_view.len);
|
||||
defer {
|
||||
for (args.items) |item| {
|
||||
for (args.items) |*item| {
|
||||
item.deinit();
|
||||
}
|
||||
args.deinit();
|
||||
}
|
||||
|
||||
args.appendAssumeCapacity(jsc.ZigString.Slice.fromUTF8NeverFree(key.slice()));
|
||||
const key = (try fromJS(globalObject, callframe.argument(0))) orelse {
|
||||
return globalObject.throwInvalidArgumentType("hmget", "key", "string or buffer");
|
||||
};
|
||||
args.appendAssumeCapacity(key);
|
||||
|
||||
// Add field names as arguments
|
||||
while (try iter.next()) |field_js| {
|
||||
const field_str = try field_js.toBunString(globalObject);
|
||||
defer field_str.deref();
|
||||
const second_arg = callframe.argument(1);
|
||||
if (second_arg.isArray()) {
|
||||
const array_len = try second_arg.getLength(globalObject);
|
||||
if (array_len == 0) {
|
||||
return globalObject.throw("HMGET requires at least one field", .{});
|
||||
}
|
||||
|
||||
const field_slice = field_str.toUTF8WithoutRef(bun.default_allocator);
|
||||
args.appendAssumeCapacity(field_slice);
|
||||
var array_iter = try second_arg.arrayIterator(globalObject);
|
||||
while (try array_iter.next()) |element| {
|
||||
const field = (try fromJS(globalObject, element)) orelse {
|
||||
return globalObject.throwInvalidArgumentType("hmget", "field", "string or buffer");
|
||||
};
|
||||
try args.append(field);
|
||||
}
|
||||
} else {
|
||||
for (args_view[1..]) |arg| {
|
||||
if (arg.isUndefinedOrNull()) {
|
||||
break;
|
||||
}
|
||||
const field = (try fromJS(globalObject, arg)) orelse {
|
||||
return globalObject.throwInvalidArgumentType("hmget", "field", "string or buffer");
|
||||
};
|
||||
try args.append(field);
|
||||
}
|
||||
}
|
||||
|
||||
// Send HMGET command
|
||||
@@ -462,7 +552,7 @@ pub fn hmget(this: *JSValkeyClient, globalObject: *jsc.JSGlobalObject, callframe
|
||||
callframe.this(),
|
||||
&.{
|
||||
.command = "HMGET",
|
||||
.args = .{ .slices = args.items },
|
||||
.args = .{ .args = args.items },
|
||||
},
|
||||
) catch |err| {
|
||||
return protocol.valkeyErrorToJS(globalObject, "Failed to send HMGET command", err);
|
||||
@@ -534,66 +624,183 @@ pub fn hincrbyfloat(this: *JSValkeyClient, globalObject: *jsc.JSGlobalObject, ca
|
||||
return promise.toJS();
|
||||
}
|
||||
|
||||
// Implement hmset (set multiple values in hash)
|
||||
pub fn hmset(this: *JSValkeyClient, globalObject: *jsc.JSGlobalObject, callframe: *jsc.CallFrame) bun.JSError!JSValue {
|
||||
try requireNotSubscriber(this, @src().fn_name);
|
||||
fn hsetImpl(this: *JSValkeyClient, globalObject: *jsc.JSGlobalObject, callframe: *jsc.CallFrame, comptime command: []const u8) bun.JSError!JSValue {
|
||||
try requireNotSubscriber(this, command);
|
||||
|
||||
const key = try callframe.argument(0).toBunString(globalObject);
|
||||
defer key.deref();
|
||||
|
||||
// For simplicity, let's accept a list of alternating keys and values
|
||||
const array_arg = callframe.argument(1);
|
||||
if (!array_arg.isObject() or !array_arg.isArray()) {
|
||||
return globalObject.throw("Arguments must be an array of alternating field names and values", .{});
|
||||
}
|
||||
const second_arg = callframe.argument(1);
|
||||
|
||||
var iter = try array_arg.arrayIterator(globalObject);
|
||||
if (iter.len % 2 != 0) {
|
||||
return globalObject.throw("Arguments must be an array of alternating field names and values", .{});
|
||||
}
|
||||
|
||||
var args = try std.ArrayList(jsc.ZigString.Slice).initCapacity(bun.default_allocator, iter.len + 1);
|
||||
var args = std.ArrayList(jsc.ZigString.Slice).init(bun.default_allocator);
|
||||
defer {
|
||||
for (args.items) |item| {
|
||||
item.deinit();
|
||||
}
|
||||
for (args.items) |item| item.deinit();
|
||||
args.deinit();
|
||||
}
|
||||
|
||||
// Add key as first argument
|
||||
const key_slice = key.toUTF8WithoutRef(bun.default_allocator);
|
||||
defer key_slice.deinit();
|
||||
args.appendAssumeCapacity(key_slice);
|
||||
try args.append(key.toUTF8(bun.default_allocator));
|
||||
|
||||
// Add field-value pairs
|
||||
while (try iter.next()) |field_js| {
|
||||
// Add field name
|
||||
const field_str = try field_js.toBunString(globalObject);
|
||||
defer field_str.deref();
|
||||
const field_slice = field_str.toUTF8WithoutRef(bun.default_allocator);
|
||||
args.appendAssumeCapacity(field_slice);
|
||||
if (second_arg.isObject() and !second_arg.isArray()) {
|
||||
// Pattern 1: Object/Record - hset(key, {field: value, ...})
|
||||
const obj = second_arg.getObject() orelse {
|
||||
return globalObject.throwInvalidArgumentType(command, "fields", "object");
|
||||
};
|
||||
|
||||
// Add value
|
||||
if (try iter.next()) |value_js| {
|
||||
const value_str = try value_js.toBunString(globalObject);
|
||||
var object_iter = try jsc.JSPropertyIterator(.{
|
||||
.skip_empty_name = false,
|
||||
.include_value = true,
|
||||
}).init(globalObject, obj);
|
||||
defer object_iter.deinit();
|
||||
|
||||
try args.ensureTotalCapacity(1 + object_iter.len * 2);
|
||||
|
||||
while (try object_iter.next()) |field_name| {
|
||||
const field_slice = field_name.toUTF8(bun.default_allocator);
|
||||
args.appendAssumeCapacity(field_slice);
|
||||
|
||||
const value_str = try object_iter.value.toBunString(globalObject);
|
||||
defer value_str.deref();
|
||||
const value_slice = value_str.toUTF8WithoutRef(bun.default_allocator);
|
||||
|
||||
const value_slice = value_str.toUTF8(bun.default_allocator);
|
||||
args.appendAssumeCapacity(value_slice);
|
||||
} else {
|
||||
return globalObject.throw("Arguments must be an array of alternating field names and values", .{});
|
||||
}
|
||||
} else if (second_arg.isArray()) {
|
||||
// Pattern 3: Array - hmset(key, [field, value, ...])
|
||||
var iter = try second_arg.arrayIterator(globalObject);
|
||||
if (iter.len % 2 != 0) {
|
||||
return globalObject.throw("Array must have an even number of elements (field-value pairs)", .{});
|
||||
}
|
||||
|
||||
try args.ensureTotalCapacity(1 + iter.len);
|
||||
|
||||
while (try iter.next()) |field_js| {
|
||||
const field_str = try field_js.toBunString(globalObject);
|
||||
args.appendAssumeCapacity(field_str.toUTF8(bun.default_allocator));
|
||||
field_str.deref();
|
||||
|
||||
const value_js = try iter.next() orelse {
|
||||
return globalObject.throw("Array must have an even number of elements (field-value pairs)", .{});
|
||||
};
|
||||
const value_str = try value_js.toBunString(globalObject);
|
||||
args.appendAssumeCapacity(value_str.toUTF8(bun.default_allocator));
|
||||
value_str.deref();
|
||||
}
|
||||
} else {
|
||||
// Pattern 2: Variadic - hset(key, field, value, ...)
|
||||
const args_count = callframe.argumentsCount();
|
||||
if (args_count < 3) {
|
||||
return globalObject.throw("HSET requires at least key, field, and value arguments", .{});
|
||||
}
|
||||
|
||||
const field_value_count = args_count - 1; // Exclude key
|
||||
if (field_value_count % 2 != 0) {
|
||||
return globalObject.throw("HSET requires field-value pairs (even number of arguments after key)", .{});
|
||||
}
|
||||
|
||||
try args.ensureTotalCapacity(args_count);
|
||||
|
||||
var i: u32 = 1;
|
||||
while (i < args_count) : (i += 1) {
|
||||
const arg_str = try callframe.argument(i).toBunString(globalObject);
|
||||
args.appendAssumeCapacity(arg_str.toUTF8(bun.default_allocator));
|
||||
arg_str.deref();
|
||||
}
|
||||
}
|
||||
|
||||
// Send HMSET command
|
||||
if (args.items.len == 1) {
|
||||
return globalObject.throw("HSET requires at least one field-value pair", .{});
|
||||
}
|
||||
|
||||
const promise = this.send(
|
||||
globalObject,
|
||||
callframe.this(),
|
||||
&.{
|
||||
.command = "HMSET",
|
||||
.command = command,
|
||||
.args = .{ .slices = args.items },
|
||||
},
|
||||
) catch |err| {
|
||||
return protocol.valkeyErrorToJS(globalObject, "Failed to send HMSET command", err);
|
||||
const msg = if (bun.strings.eqlComptime(command, "HSET")) "Failed to send HSET command" else "Failed to send HMSET command";
|
||||
return protocol.valkeyErrorToJS(globalObject, msg, err);
|
||||
};
|
||||
|
||||
return promise.toJS();
|
||||
}
|
||||
|
||||
pub fn hset(this: *JSValkeyClient, globalObject: *jsc.JSGlobalObject, callframe: *jsc.CallFrame) bun.JSError!JSValue {
|
||||
return hsetImpl(this, globalObject, callframe, "HSET");
|
||||
}
|
||||
|
||||
pub fn hmset(this: *JSValkeyClient, globalObject: *jsc.JSGlobalObject, callframe: *jsc.CallFrame) bun.JSError!JSValue {
|
||||
return hsetImpl(this, globalObject, callframe, "HMSET");
|
||||
}
|
||||
|
||||
pub const hdel = compile.@"(key: RedisKey, ...args: RedisKey[])"("hdel", "HDEL", "key", .not_subscriber).call;
|
||||
pub const hrandfield = compile.@"(key: RedisKey, ...args: RedisKey[])"("hrandfield", "HRANDFIELD", "key", .not_subscriber).call;
|
||||
pub const hscan = compile.@"(key: RedisKey, ...args: RedisKey[])"("hscan", "HSCAN", "key", .not_subscriber).call;
|
||||
pub const hgetdel = compile.@"(...strings: string[])"("hgetdel", "HGETDEL", .not_subscriber).call;
|
||||
pub const hgetex = compile.@"(...strings: string[])"("hgetex", "HGETEX", .not_subscriber).call;
|
||||
pub const hsetex = compile.@"(...strings: string[])"("hsetex", "HSETEX", .not_subscriber).call;
|
||||
pub const hexpire = compile.@"(...strings: string[])"("hexpire", "HEXPIRE", .not_subscriber).call;
|
||||
pub const hexpireat = compile.@"(...strings: string[])"("hexpireat", "HEXPIREAT", .not_subscriber).call;
|
||||
pub const hexpiretime = compile.@"(...strings: string[])"("hexpiretime", "HEXPIRETIME", .not_subscriber).call;
|
||||
pub const hpersist = compile.@"(...strings: string[])"("hpersist", "HPERSIST", .not_subscriber).call;
|
||||
pub const hpexpire = compile.@"(...strings: string[])"("hpexpire", "HPEXPIRE", .not_subscriber).call;
|
||||
pub const hpexpireat = compile.@"(...strings: string[])"("hpexpireat", "HPEXPIREAT", .not_subscriber).call;
|
||||
pub const hpexpiretime = compile.@"(...strings: string[])"("hpexpiretime", "HPEXPIRETIME", .not_subscriber).call;
|
||||
pub const hpttl = compile.@"(...strings: string[])"("hpttl", "HPTTL", .not_subscriber).call;
|
||||
pub const httl = compile.@"(...strings: string[])"("httl", "HTTL", .not_subscriber).call;
|
||||
|
||||
pub fn hsetnx(this: *JSValkeyClient, globalObject: *jsc.JSGlobalObject, callframe: *jsc.CallFrame) bun.JSError!JSValue {
|
||||
try requireNotSubscriber(this, "hsetnx");
|
||||
|
||||
const key = (try fromJS(globalObject, callframe.argument(0))) orelse {
|
||||
return globalObject.throwInvalidArgumentType("hsetnx", "key", "string or buffer");
|
||||
};
|
||||
defer key.deinit();
|
||||
const field = (try fromJS(globalObject, callframe.argument(1))) orelse {
|
||||
return globalObject.throwInvalidArgumentType("hsetnx", "field", "string or buffer");
|
||||
};
|
||||
defer field.deinit();
|
||||
const value = (try fromJS(globalObject, callframe.argument(2))) orelse {
|
||||
return globalObject.throwInvalidArgumentType("hsetnx", "value", "string or buffer");
|
||||
};
|
||||
defer value.deinit();
|
||||
|
||||
const promise = this.send(
|
||||
globalObject,
|
||||
callframe.this(),
|
||||
&.{
|
||||
.command = "HSETNX",
|
||||
.args = .{ .args = &.{ key, field, value } },
|
||||
.meta = .{ .return_as_bool = true },
|
||||
},
|
||||
) catch |err| {
|
||||
return protocol.valkeyErrorToJS(globalObject, "Failed to send HSETNX command", err);
|
||||
};
|
||||
return promise.toJS();
|
||||
}
|
||||
|
||||
pub fn hexists(this: *JSValkeyClient, globalObject: *jsc.JSGlobalObject, callframe: *jsc.CallFrame) bun.JSError!JSValue {
|
||||
try requireNotSubscriber(this, "hexists");
|
||||
|
||||
const key = (try fromJS(globalObject, callframe.argument(0))) orelse
|
||||
return globalObject.throwInvalidArgumentType("hexists", "key", "string or buffer");
|
||||
defer key.deinit();
|
||||
|
||||
const field = (try fromJS(globalObject, callframe.argument(1))) orelse
|
||||
return globalObject.throwInvalidArgumentType("hexists", "field", "string or buffer");
|
||||
defer field.deinit();
|
||||
|
||||
const promise = this.send(
|
||||
globalObject,
|
||||
callframe.this(),
|
||||
&.{
|
||||
.command = "HEXISTS",
|
||||
.args = .{ .args = &.{ key, field } },
|
||||
.meta = .{ .return_as_bool = true },
|
||||
},
|
||||
) catch |err| {
|
||||
return protocol.valkeyErrorToJS(globalObject, "Failed to send HEXISTS command", err);
|
||||
};
|
||||
return promise.toJS();
|
||||
}
|
||||
@@ -631,7 +838,17 @@ pub fn ping(this: *JSValkeyClient, globalObject: *jsc.JSGlobalObject, callframe:
|
||||
}
|
||||
|
||||
pub const bitcount = compile.@"(key: RedisKey)"("bitcount", "BITCOUNT", "key", .not_subscriber).call;
|
||||
pub const blmove = compile.@"(...strings: string[])"("blmove", "BLMOVE", .not_subscriber).call;
|
||||
pub const blmpop = compile.@"(...strings: string[])"("blmpop", "BLMPOP", .not_subscriber).call;
|
||||
pub const blpop = compile.@"(...strings: string[])"("blpop", "BLPOP", .not_subscriber).call;
|
||||
pub const brpop = compile.@"(...strings: string[])"("brpop", "BRPOP", .not_subscriber).call;
|
||||
pub const brpoplpush = compile.@"(key: RedisKey, value: RedisValue, value2: RedisValue)"("brpoplpush", "BRPOPLPUSH", "source", "destination", "timeout", .not_subscriber).call;
|
||||
pub const getbit = compile.@"(key: RedisKey, value: RedisValue)"("getbit", "GETBIT", "key", "offset", .not_subscriber).call;
|
||||
pub const setbit = compile.@"(key: RedisKey, value: RedisValue, value2: RedisValue)"("setbit", "SETBIT", "key", "offset", "value", .not_subscriber).call;
|
||||
pub const getrange = compile.@"(key: RedisKey, value: RedisValue, value2: RedisValue)"("getrange", "GETRANGE", "key", "start", "end", .not_subscriber).call;
|
||||
pub const setrange = compile.@"(key: RedisKey, value: RedisValue, value2: RedisValue)"("setrange", "SETRANGE", "key", "offset", "value", .not_subscriber).call;
|
||||
pub const dump = compile.@"(key: RedisKey)"("dump", "DUMP", "key", .not_subscriber).call;
|
||||
pub const expireat = compile.@"(key: RedisKey, value: RedisValue)"("expireat", "EXPIREAT", "key", "timestamp", .not_subscriber).call;
|
||||
pub const expiretime = compile.@"(key: RedisKey)"("expiretime", "EXPIRETIME", "key", .not_subscriber).call;
|
||||
pub const getdel = compile.@"(key: RedisKey)"("getdel", "GETDEL", "key", .not_subscriber).call;
|
||||
pub const getex = compile.@"(...strings: string[])"("getex", "GETEX", .not_subscriber).call;
|
||||
@@ -640,45 +857,133 @@ pub const hkeys = compile.@"(key: RedisKey)"("hkeys", "HKEYS", "key", .not_subsc
|
||||
pub const hlen = compile.@"(key: RedisKey)"("hlen", "HLEN", "key", .not_subscriber).call;
|
||||
pub const hvals = compile.@"(key: RedisKey)"("hvals", "HVALS", "key", .not_subscriber).call;
|
||||
pub const keys = compile.@"(key: RedisKey)"("keys", "KEYS", "key", .not_subscriber).call;
|
||||
pub const lindex = compile.@"(key: RedisKey, value: RedisValue)"("lindex", "LINDEX", "key", "index", .not_subscriber).call;
|
||||
pub const linsert = compile.@"(...strings: string[])"("linsert", "LINSERT", .not_subscriber).call;
|
||||
pub const llen = compile.@"(key: RedisKey)"("llen", "LLEN", "key", .not_subscriber).call;
|
||||
pub const lpop = compile.@"(key: RedisKey)"("lpop", "LPOP", "key", .not_subscriber).call;
|
||||
pub const lmove = compile.@"(...strings: string[])"("lmove", "LMOVE", .not_subscriber).call;
|
||||
pub const lmpop = compile.@"(...strings: string[])"("lmpop", "LMPOP", .not_subscriber).call;
|
||||
pub const lpop = compile.@"(key: RedisKey, ...args: RedisKey[])"("lpop", "LPOP", "key", .not_subscriber).call;
|
||||
pub const lpos = compile.@"(...strings: string[])"("lpos", "LPOS", .not_subscriber).call;
|
||||
pub const lrange = compile.@"(key: RedisKey, value: RedisValue, value2: RedisValue)"("lrange", "LRANGE", "key", "start", "stop", .not_subscriber).call;
|
||||
pub const lrem = compile.@"(key: RedisKey, value: RedisValue, value2: RedisValue)"("lrem", "LREM", "key", "count", "element", .not_subscriber).call;
|
||||
pub const lset = compile.@"(key: RedisKey, value: RedisValue, value2: RedisValue)"("lset", "LSET", "key", "index", "element", .not_subscriber).call;
|
||||
pub const ltrim = compile.@"(key: RedisKey, value: RedisValue, value2: RedisValue)"("ltrim", "LTRIM", "key", "start", "stop", .not_subscriber).call;
|
||||
pub const persist = compile.@"(key: RedisKey)"("persist", "PERSIST", "key", .not_subscriber).call;
|
||||
pub const pexpire = compile.@"(key: RedisKey, value: RedisValue)"("pexpire", "PEXPIRE", "key", "milliseconds", .not_subscriber).call;
|
||||
pub const pexpireat = compile.@"(key: RedisKey, value: RedisValue)"("pexpireat", "PEXPIREAT", "key", "milliseconds-timestamp", .not_subscriber).call;
|
||||
pub const pexpiretime = compile.@"(key: RedisKey)"("pexpiretime", "PEXPIRETIME", "key", .not_subscriber).call;
|
||||
pub const pttl = compile.@"(key: RedisKey)"("pttl", "PTTL", "key", .not_subscriber).call;
|
||||
pub const rpop = compile.@"(key: RedisKey)"("rpop", "RPOP", "key", .not_subscriber).call;
|
||||
pub const randomkey = compile.@"()"("randomkey", "RANDOMKEY", .not_subscriber).call;
|
||||
pub const rpop = compile.@"(key: RedisKey, ...args: RedisKey[])"("rpop", "RPOP", "key", .not_subscriber).call;
|
||||
pub const rpoplpush = compile.@"(key: RedisKey, value: RedisValue)"("rpoplpush", "RPOPLPUSH", "source", "destination", .not_subscriber).call;
|
||||
pub const scan = compile.@"(...strings: string[])"("scan", "SCAN", .not_subscriber).call;
|
||||
pub const scard = compile.@"(key: RedisKey)"("scard", "SCARD", "key", .not_subscriber).call;
|
||||
pub const sdiff = compile.@"(...strings: string[])"("sdiff", "SDIFF", .not_subscriber).call;
|
||||
pub const sdiffstore = compile.@"(...strings: string[])"("sdiffstore", "SDIFFSTORE", .not_subscriber).call;
|
||||
pub const sinter = compile.@"(...strings: string[])"("sinter", "SINTER", .not_subscriber).call;
|
||||
pub const sintercard = compile.@"(...strings: string[])"("sintercard", "SINTERCARD", .not_subscriber).call;
|
||||
pub const sinterstore = compile.@"(...strings: string[])"("sinterstore", "SINTERSTORE", .not_subscriber).call;
|
||||
pub const smismember = compile.@"(...strings: string[])"("smismember", "SMISMEMBER", .not_subscriber).call;
|
||||
pub const sscan = compile.@"(...strings: string[])"("sscan", "SSCAN", .not_subscriber).call;
|
||||
pub const strlen = compile.@"(key: RedisKey)"("strlen", "STRLEN", "key", .not_subscriber).call;
|
||||
pub const sunion = compile.@"(...strings: string[])"("sunion", "SUNION", .not_subscriber).call;
|
||||
pub const sunionstore = compile.@"(...strings: string[])"("sunionstore", "SUNIONSTORE", .not_subscriber).call;
|
||||
pub const @"type" = compile.@"(key: RedisKey)"("type", "TYPE", "key", .not_subscriber).call;
|
||||
pub const zcard = compile.@"(key: RedisKey)"("zcard", "ZCARD", "key", .not_subscriber).call;
|
||||
pub const zpopmax = compile.@"(key: RedisKey)"("zpopmax", "ZPOPMAX", "key", .not_subscriber).call;
|
||||
pub const zpopmin = compile.@"(key: RedisKey)"("zpopmin", "ZPOPMIN", "key", .not_subscriber).call;
|
||||
pub const zrandmember = compile.@"(key: RedisKey)"("zrandmember", "ZRANDMEMBER", "key", .not_subscriber).call;
|
||||
|
||||
pub const zcount = compile.@"(key: RedisKey, value: RedisValue, value2: RedisValue)"("zcount", "ZCOUNT", "key", "min", "max", .not_subscriber).call;
|
||||
pub const zlexcount = compile.@"(key: RedisKey, value: RedisValue, value2: RedisValue)"("zlexcount", "ZLEXCOUNT", "key", "min", "max", .not_subscriber).call;
|
||||
pub const zpopmax = compile.@"(key: RedisKey, ...args: RedisKey[])"("zpopmax", "ZPOPMAX", "key", .not_subscriber).call;
|
||||
pub const zpopmin = compile.@"(key: RedisKey, ...args: RedisKey[])"("zpopmin", "ZPOPMIN", "key", .not_subscriber).call;
|
||||
pub const zrandmember = compile.@"(key: RedisKey, ...args: RedisKey[])"("zrandmember", "ZRANDMEMBER", "key", .not_subscriber).call;
|
||||
pub const zrange = compile.@"(...strings: string[])"("zrange", "ZRANGE", .not_subscriber).call;
|
||||
pub const zrevrange = compile.@"(...strings: string[])"("zrevrange", "ZREVRANGE", .not_subscriber).call;
|
||||
pub const zrangebyscore = compile.@"(...strings: string[])"("zrangebyscore", "ZRANGEBYSCORE", .not_subscriber).call;
|
||||
pub const zrevrangebyscore = compile.@"(...strings: string[])"("zrevrangebyscore", "ZREVRANGEBYSCORE", .not_subscriber).call;
|
||||
pub const zrangebylex = compile.@"(key: RedisKey, ...args: RedisKey[])"("zrangebylex", "ZRANGEBYLEX", "key", .not_subscriber).call;
|
||||
pub const zrevrangebylex = compile.@"(key: RedisKey, ...args: RedisKey[])"("zrevrangebylex", "ZREVRANGEBYLEX", "key", .not_subscriber).call;
|
||||
pub const append = compile.@"(key: RedisKey, value: RedisValue)"("append", "APPEND", "key", "value", .not_subscriber).call;
|
||||
pub const getset = compile.@"(key: RedisKey, value: RedisValue)"("getset", "GETSET", "key", "value", .not_subscriber).call;
|
||||
pub const hget = compile.@"(key: RedisKey, value: RedisValue)"("hget", "HGET", "key", "field", .not_subscriber).call;
|
||||
pub const incrby = compile.@"(key: RedisKey, value: RedisValue)"("incrby", "INCRBY", "key", "increment", .not_subscriber).call;
|
||||
pub const incrbyfloat = compile.@"(key: RedisKey, value: RedisValue)"("incrbyfloat", "INCRBYFLOAT", "key", "increment", .not_subscriber).call;
|
||||
pub const decrby = compile.@"(key: RedisKey, value: RedisValue)"("decrby", "DECRBY", "key", "decrement", .not_subscriber).call;
|
||||
pub const lpush = compile.@"(key: RedisKey, value: RedisValue, ...args: RedisValue)"("lpush", "LPUSH", .not_subscriber).call;
|
||||
pub const lpushx = compile.@"(key: RedisKey, value: RedisValue, ...args: RedisValue)"("lpushx", "LPUSHX", .not_subscriber).call;
|
||||
pub const pfadd = compile.@"(key: RedisKey, value: RedisValue)"("pfadd", "PFADD", "key", "value", .not_subscriber).call;
|
||||
pub const rpush = compile.@"(key: RedisKey, value: RedisValue, ...args: RedisValue)"("rpush", "RPUSH", .not_subscriber).call;
|
||||
pub const rpushx = compile.@"(key: RedisKey, value: RedisValue, ...args: RedisValue)"("rpushx", "RPUSHX", .not_subscriber).call;
|
||||
pub const setnx = compile.@"(key: RedisKey, value: RedisValue)"("setnx", "SETNX", "key", "value", .not_subscriber).call;
|
||||
pub const setex = compile.@"(key: RedisKey, value: RedisValue, value2: RedisValue)"("setex", "SETEX", "key", "seconds", "value", .not_subscriber).call;
|
||||
pub const psetex = compile.@"(key: RedisKey, value: RedisValue, value2: RedisValue)"("psetex", "PSETEX", "key", "milliseconds", "value", .not_subscriber).call;
|
||||
pub const zscore = compile.@"(key: RedisKey, value: RedisValue)"("zscore", "ZSCORE", "key", "value", .not_subscriber).call;
|
||||
|
||||
pub const zincrby = compile.@"(key: RedisKey, value: RedisValue, value2: RedisValue)"("zincrby", "ZINCRBY", "key", "increment", "member", .not_subscriber).call;
|
||||
pub const zmscore = compile.@"(key: RedisKey, value: RedisValue, ...args: RedisValue)"("zmscore", "ZMSCORE", .not_subscriber).call;
|
||||
pub const zadd = compile.@"(...strings: string[])"("zadd", "ZADD", .not_subscriber).call;
|
||||
pub const zscan = compile.@"(...strings: string[])"("zscan", "ZSCAN", .not_subscriber).call;
|
||||
pub const zdiff = compile.@"(...strings: string[])"("zdiff", "ZDIFF", .not_subscriber).call;
|
||||
pub const zdiffstore = compile.@"(...strings: string[])"("zdiffstore", "ZDIFFSTORE", .not_subscriber).call;
|
||||
pub const zinter = compile.@"(...strings: string[])"("zinter", "ZINTER", .not_subscriber).call;
|
||||
pub const zintercard = compile.@"(...strings: string[])"("zintercard", "ZINTERCARD", .not_subscriber).call;
|
||||
pub const zinterstore = compile.@"(...strings: string[])"("zinterstore", "ZINTERSTORE", .not_subscriber).call;
|
||||
pub const zunion = compile.@"(...strings: string[])"("zunion", "ZUNION", .not_subscriber).call;
|
||||
pub const zunionstore = compile.@"(...strings: string[])"("zunionstore", "ZUNIONSTORE", .not_subscriber).call;
|
||||
pub const zmpop = compile.@"(...strings: string[])"("zmpop", "ZMPOP", .not_subscriber).call;
|
||||
pub const bzmpop = compile.@"(...strings: string[])"("bzmpop", "BZMPOP", .not_subscriber).call;
|
||||
pub const bzpopmin = compile.@"(...strings: string[])"("bzpopmin", "BZPOPMIN", .not_subscriber).call;
|
||||
pub const bzpopmax = compile.@"(...strings: string[])"("bzpopmax", "BZPOPMAX", .not_subscriber).call;
|
||||
pub const del = compile.@"(key: RedisKey, ...args: RedisKey[])"("del", "DEL", "key", .not_subscriber).call;
|
||||
pub const mget = compile.@"(key: RedisKey, ...args: RedisKey[])"("mget", "MGET", "key", .not_subscriber).call;
|
||||
|
||||
pub const mset = compile.@"(...strings: string[])"("mset", "MSET", .not_subscriber).call;
|
||||
pub const msetnx = compile.@"(...strings: string[])"("msetnx", "MSETNX", .not_subscriber).call;
|
||||
pub const script = compile.@"(...strings: string[])"("script", "SCRIPT", .not_subscriber).call;
|
||||
pub const select = compile.@"(...strings: string[])"("select", "SELECT", .not_subscriber).call;
|
||||
pub const spublish = compile.@"(...strings: string[])"("spublish", "SPUBLISH", .not_subscriber).call;
|
||||
pub const smove = compile.@"(...strings: string[])"("smove", "SMOVE", .not_subscriber).call;
|
||||
pub const substr = compile.@"(...strings: string[])"("substr", "SUBSTR", .not_subscriber).call;
|
||||
pub const hstrlen = compile.@"(...strings: string[])"("hstrlen", "HSTRLEN", .not_subscriber).call;
|
||||
pub const zrank = compile.@"(...strings: string[])"("zrank", "ZRANK", .not_subscriber).call;
|
||||
pub const zrevrank = compile.@"(...strings: string[])"("zrevrank", "ZREVRANK", .not_subscriber).call;
|
||||
pub const spublish = compile.@"(key: RedisKey, value: RedisValue)"("spublish", "SPUBLISH", "channel", "message", .not_subscriber).call;
|
||||
pub fn smove(this: *JSValkeyClient, globalObject: *jsc.JSGlobalObject, callframe: *jsc.CallFrame) bun.JSError!JSValue {
|
||||
try requireNotSubscriber(this, @src().fn_name);
|
||||
|
||||
const source = (try fromJS(globalObject, callframe.argument(0))) orelse {
|
||||
return globalObject.throwInvalidArgumentType("smove", "source", "string or buffer");
|
||||
};
|
||||
defer source.deinit();
|
||||
const destination = (try fromJS(globalObject, callframe.argument(1))) orelse {
|
||||
return globalObject.throwInvalidArgumentType("smove", "destination", "string or buffer");
|
||||
};
|
||||
defer destination.deinit();
|
||||
const member = (try fromJS(globalObject, callframe.argument(2))) orelse {
|
||||
return globalObject.throwInvalidArgumentType("smove", "member", "string or buffer");
|
||||
};
|
||||
defer member.deinit();
|
||||
|
||||
const promise = this.send(
|
||||
globalObject,
|
||||
callframe.this(),
|
||||
&.{
|
||||
.command = "SMOVE",
|
||||
.args = .{ .args = &.{ source, destination, member } },
|
||||
.meta = .{ .return_as_bool = true },
|
||||
},
|
||||
) catch |err| {
|
||||
return protocol.valkeyErrorToJS(globalObject, "Failed to send SMOVE command", err);
|
||||
};
|
||||
return promise.toJS();
|
||||
}
|
||||
pub const substr = compile.@"(key: RedisKey, value: RedisValue, value2: RedisValue)"("substr", "SUBSTR", "key", "start", "end", .not_subscriber).call;
|
||||
pub const hstrlen = compile.@"(key: RedisKey, value: RedisValue)"("hstrlen", "HSTRLEN", "key", "field", .not_subscriber).call;
|
||||
pub const zrank = compile.@"(key: RedisKey, ...args: RedisKey[])"("zrank", "ZRANK", "key", .not_subscriber).call;
|
||||
pub const zrangestore = compile.@"(...strings: string[])"("zrangestore", "ZRANGESTORE", .not_subscriber).call;
|
||||
pub const zrem = compile.@"(key: RedisKey, ...args: RedisKey[])"("zrem", "ZREM", "key", .not_subscriber).call;
|
||||
pub const zremrangebylex = compile.@"(key: RedisKey, value: RedisValue, value2: RedisValue)"("zremrangebylex", "ZREMRANGEBYLEX", "key", "min", "max", .not_subscriber).call;
|
||||
pub const zremrangebyrank = compile.@"(key: RedisKey, value: RedisValue, value2: RedisValue)"("zremrangebyrank", "ZREMRANGEBYRANK", "key", "start", "stop", .not_subscriber).call;
|
||||
pub const zremrangebyscore = compile.@"(key: RedisKey, value: RedisValue, value2: RedisValue)"("zremrangebyscore", "ZREMRANGEBYSCORE", "key", "min", "max", .not_subscriber).call;
|
||||
pub const zrevrank = compile.@"(key: RedisKey, ...args: RedisKey[])"("zrevrank", "ZREVRANK", "key", .not_subscriber).call;
|
||||
pub const psubscribe = compile.@"(...strings: string[])"("psubscribe", "PSUBSCRIBE", .dont_care).call;
|
||||
pub const punsubscribe = compile.@"(...strings: string[])"("punsubscribe", "PUNSUBSCRIBE", .dont_care).call;
|
||||
pub const pubsub = compile.@"(...strings: string[])"("pubsub", "PUBSUB", .dont_care).call;
|
||||
pub const copy = compile.@"(...strings: string[])"("copy", "COPY", .not_subscriber).call;
|
||||
pub const unlink = compile.@"(key: RedisKey, ...args: RedisKey[])"("unlink", "UNLINK", "key", .not_subscriber).call;
|
||||
pub const touch = compile.@"(key: RedisKey, ...args: RedisKey[])"("touch", "TOUCH", "key", .not_subscriber).call;
|
||||
pub const rename = compile.@"(key: RedisKey, value: RedisValue)"("rename", "RENAME", "key", "newkey", .not_subscriber).call;
|
||||
pub const renamenx = compile.@"(key: RedisKey, value: RedisValue)"("renamenx", "RENAMENX", "key", "newkey", .not_subscriber).call;
|
||||
|
||||
pub fn publish(
|
||||
this: *JSValkeyClient,
|
||||
@@ -1004,6 +1309,30 @@ const compile = struct {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn @"()"(
|
||||
comptime name: []const u8,
|
||||
comptime command: []const u8,
|
||||
comptime client_state_requirement: ClientStateRequirement,
|
||||
) type {
|
||||
return struct {
|
||||
pub fn call(this: *JSValkeyClient, globalObject: *jsc.JSGlobalObject, callframe: *jsc.CallFrame) bun.JSError!JSValue {
|
||||
try testCorrectState(this, name, client_state_requirement);
|
||||
|
||||
const promise = this.send(
|
||||
globalObject,
|
||||
callframe.this(),
|
||||
&.{
|
||||
.command = command,
|
||||
.args = .{ .args = &.{} },
|
||||
},
|
||||
) catch |err| {
|
||||
return protocol.valkeyErrorToJS(globalObject, "Failed to send " ++ command, err);
|
||||
};
|
||||
return promise.toJS();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn @"(key: RedisKey)"(
|
||||
comptime name: []const u8,
|
||||
comptime command: []const u8,
|
||||
@@ -1117,6 +1446,46 @@ const compile = struct {
|
||||
};
|
||||
}
|
||||
|
||||
pub fn @"(key: RedisKey, value: RedisValue, value2: RedisValue)"(
|
||||
comptime name: []const u8,
|
||||
comptime command: []const u8,
|
||||
comptime arg0_name: []const u8,
|
||||
comptime arg1_name: []const u8,
|
||||
comptime arg2_name: []const u8,
|
||||
comptime client_state_requirement: ClientStateRequirement,
|
||||
) type {
|
||||
return struct {
|
||||
pub fn call(this: *JSValkeyClient, globalObject: *jsc.JSGlobalObject, callframe: *jsc.CallFrame) bun.JSError!JSValue {
|
||||
try testCorrectState(this, name, client_state_requirement);
|
||||
|
||||
const key = (try fromJS(globalObject, callframe.argument(0))) orelse {
|
||||
return globalObject.throwInvalidArgumentType(name, arg0_name, "string or buffer");
|
||||
};
|
||||
defer key.deinit();
|
||||
const value = (try fromJS(globalObject, callframe.argument(1))) orelse {
|
||||
return globalObject.throwInvalidArgumentType(name, arg1_name, "string or buffer");
|
||||
};
|
||||
defer value.deinit();
|
||||
const value2 = (try fromJS(globalObject, callframe.argument(2))) orelse {
|
||||
return globalObject.throwInvalidArgumentType(name, arg2_name, "string or buffer");
|
||||
};
|
||||
defer value2.deinit();
|
||||
|
||||
const promise = this.send(
|
||||
globalObject,
|
||||
callframe.this(),
|
||||
&.{
|
||||
.command = command,
|
||||
.args = .{ .args = &.{ key, value, value2 } },
|
||||
},
|
||||
) catch |err| {
|
||||
return protocol.valkeyErrorToJS(globalObject, "Failed to send " ++ command, err);
|
||||
};
|
||||
return promise.toJS();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
pub fn @"(...strings: string[])"(
|
||||
comptime name: []const u8,
|
||||
comptime command: []const u8,
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
# Unified Dockerfile for Valkey/Redis with TCP, TLS, Unix socket, and authentication support
|
||||
FROM redis:7-alpine
|
||||
FROM redis:8-alpine
|
||||
|
||||
# Set user to root
|
||||
USER root
|
||||
@@ -10,6 +10,7 @@ RUN apk add --no-cache bash
|
||||
# Create directories
|
||||
RUN mkdir -p /etc/redis/certs
|
||||
RUN mkdir -p /docker-entrypoint-initdb.d
|
||||
RUN mkdir -p /data/appendonlydir && chown -R redis:redis /data
|
||||
|
||||
# Copy certificates
|
||||
COPY server.key /etc/redis/certs/
|
||||
@@ -25,7 +26,9 @@ RUN chmod +x /docker-entrypoint-initdb.d/init-redis.sh
|
||||
|
||||
# Expose ports
|
||||
EXPOSE 6379 6380
|
||||
WORKDIR /docker-entrypoint-initdb.d
|
||||
WORKDIR /data
|
||||
|
||||
USER redis
|
||||
|
||||
# Use custom entrypoint to run initialization script
|
||||
CMD ["redis-server", "/etc/redis/redis.conf"]
|
||||
@@ -1,349 +0,0 @@
|
||||
import { beforeEach, describe, expect, test } from "bun:test";
|
||||
import { ConnectionType, createClient, ctx, expectType, isEnabled } from "../test-utils";
|
||||
|
||||
/**
|
||||
* Test suite covering Redis set operations
|
||||
* - Basic operations (SADD, SREM, SISMEMBER)
|
||||
* - Set retrieval (SMEMBERS, SCARD)
|
||||
* - Set manipulation (SPOP, SRANDMEMBER)
|
||||
* - Set operations (SUNION, SINTER, SDIFF)
|
||||
*/
|
||||
describe.skipIf(!isEnabled)("Valkey: Set Data Type Operations", () => {
|
||||
beforeEach(() => {
|
||||
if (ctx.redis?.connected) {
|
||||
ctx.redis.close?.();
|
||||
}
|
||||
ctx.redis = createClient(ConnectionType.TCP);
|
||||
});
|
||||
|
||||
describe("Basic Set Operations", () => {
|
||||
test("SADD and SISMEMBER commands", async () => {
|
||||
const key = ctx.generateKey("set-test");
|
||||
|
||||
// Add a single member
|
||||
const singleAddResult = await ctx.redis.sadd(key, "member1");
|
||||
console.log("singleAddResult", singleAddResult);
|
||||
expectType<number>(singleAddResult, "number");
|
||||
expect(singleAddResult).toBe(1); // 1 new member added
|
||||
|
||||
// Add multiple members using sendCommand
|
||||
const multiAddResult = await ctx.redis.send("SADD", [key, "member2", "member3", "member1"]);
|
||||
expectType<number>(multiAddResult, "number");
|
||||
expect(multiAddResult).toBe(2); // 2 new members added, 1 duplicate ignored
|
||||
|
||||
// Check if member exists
|
||||
const isFirstMember = await ctx.redis.sismember(key, "member1");
|
||||
expect(isFirstMember).toBe(true);
|
||||
|
||||
// Check if non-existent member exists
|
||||
const isNonMember = await ctx.redis.sismember(key, "nonexistent");
|
||||
expect(isNonMember).toBe(false);
|
||||
});
|
||||
|
||||
test("SREM command", async () => {
|
||||
const key = ctx.generateKey("srem-test");
|
||||
|
||||
// Add multiple members
|
||||
await ctx.redis.send("SADD", [key, "member1", "member2", "member3", "member4"]);
|
||||
|
||||
// Remove a single member
|
||||
const singleRemResult = await ctx.redis.srem(key, "member1");
|
||||
expectType<number>(singleRemResult, "number");
|
||||
expect(singleRemResult).toBe(1); // 1 member removed
|
||||
|
||||
// Remove multiple members using sendCommand
|
||||
const multiRemResult = await ctx.redis.send("SREM", [key, "member2", "member3", "nonexistent"]);
|
||||
expectType<number>(multiRemResult, "number");
|
||||
expect(multiRemResult).toBe(2); // 2 members removed, non-existent member ignored
|
||||
|
||||
// Verify only member4 remains
|
||||
const members = await ctx.redis.smembers(key);
|
||||
expect(Array.isArray(members)).toBe(true);
|
||||
expect(members.length).toBe(1);
|
||||
expect(members[0]).toBe("member4");
|
||||
});
|
||||
|
||||
test("SMEMBERS command", async () => {
|
||||
const key = ctx.generateKey("smembers-test");
|
||||
|
||||
// Add members one at a time using direct sadd method
|
||||
await ctx.redis.sadd(key, "apple");
|
||||
await ctx.redis.sadd(key, "banana");
|
||||
await ctx.redis.sadd(key, "cherry");
|
||||
|
||||
// Get all members using direct smembers method
|
||||
const members = await ctx.redis.smembers(key);
|
||||
expect(Array.isArray(members)).toBe(true);
|
||||
|
||||
// Sort for consistent snapshot since set members can come in any order
|
||||
const sortedMembers = [...members].sort();
|
||||
expect(sortedMembers).toMatchInlineSnapshot(`
|
||||
[
|
||||
"apple",
|
||||
"banana",
|
||||
"cherry",
|
||||
]
|
||||
`);
|
||||
});
|
||||
|
||||
test("SCARD command", async () => {
|
||||
const key = ctx.generateKey("scard-test");
|
||||
|
||||
// Add members - using direct sadd method for first item, then send for multiple
|
||||
await ctx.redis.sadd(key, "item1");
|
||||
await ctx.redis.send("SADD", [key, "item2", "item3", "item4"]);
|
||||
|
||||
// Get set cardinality (size)
|
||||
// TODO: When a direct scard method is implemented, use that instead
|
||||
const size = await ctx.redis.send("SCARD", [key]);
|
||||
expectType<number>(size, "number");
|
||||
expect(size).toMatchInlineSnapshot(`4`);
|
||||
|
||||
// Remove some members - using direct srem method for first item, then send for second
|
||||
await ctx.redis.srem(key, "item1");
|
||||
await ctx.redis.send("SREM", [key, "item2"]);
|
||||
|
||||
// Check size again
|
||||
const updatedSize = await ctx.redis.send("SCARD", [key]);
|
||||
expectType<number>(updatedSize, "number");
|
||||
expect(updatedSize).toMatchInlineSnapshot(`2`);
|
||||
});
|
||||
});
|
||||
|
||||
describe("Set Manipulation", () => {
|
||||
test("SPOP command", async () => {
|
||||
const key = ctx.generateKey("spop-test");
|
||||
|
||||
// Add members - using send for multiple values
|
||||
// TODO: When a SADD method that supports multiple values is added, use that instead
|
||||
await ctx.redis.send("SADD", [key, "red", "green", "blue", "yellow", "purple"]);
|
||||
|
||||
// Pop a single member - using direct spop method
|
||||
const popResult = await ctx.redis.spop(key);
|
||||
expect(popResult).toBeDefined();
|
||||
expect(typeof popResult).toBe("string");
|
||||
|
||||
// Pop multiple members
|
||||
// TODO: When SPOP method that supports count parameter is added, use that instead
|
||||
const multiPopResult = await ctx.redis.send("SPOP", [key, "2"]);
|
||||
expect(Array.isArray(multiPopResult)).toBe(true);
|
||||
expect(multiPopResult.length).toMatchInlineSnapshot(`2`);
|
||||
|
||||
// Verify remaining members
|
||||
// TODO: When a direct scard method is added, use that instead
|
||||
const remainingCount = await ctx.redis.send("SCARD", [key]);
|
||||
expectType<number>(remainingCount, "number");
|
||||
expect(remainingCount).toMatchInlineSnapshot(`2`); // 5 original - 1 - 2 = 2 remaining
|
||||
});
|
||||
|
||||
test("SRANDMEMBER command", async () => {
|
||||
const key = ctx.generateKey("srandmember-test");
|
||||
|
||||
// Add members - first with direct sadd, then with send for remaining
|
||||
await ctx.redis.sadd(key, "one");
|
||||
await ctx.redis.send("SADD", [key, "two", "three", "four", "five"]);
|
||||
|
||||
// Get a random member - using direct srandmember method
|
||||
const randResult = await ctx.redis.srandmember(key);
|
||||
expect(randResult).toBeDefined();
|
||||
expect(typeof randResult).toBe("string");
|
||||
|
||||
// Get multiple random members
|
||||
// TODO: When srandmember method with count parameter is added, use that instead
|
||||
const multiRandResult = await ctx.redis.send("SRANDMEMBER", [key, "3"]);
|
||||
expect(Array.isArray(multiRandResult)).toBe(true);
|
||||
expect(multiRandResult.length).toMatchInlineSnapshot(`3`);
|
||||
|
||||
// Verify set is unchanged
|
||||
const count = await ctx.redis.send("SCARD", [key]);
|
||||
expectType<number>(count, "number");
|
||||
expect(count).toMatchInlineSnapshot(`5`); // All members still present unlike SPOP
|
||||
});
|
||||
|
||||
test("SMOVE command", async () => {
|
||||
const sourceKey = ctx.generateKey("smove-source");
|
||||
const destinationKey = ctx.generateKey("smove-dest");
|
||||
|
||||
// Set up source and destination sets
|
||||
await ctx.redis.send("SADD", [sourceKey, "a", "b", "c"]);
|
||||
await ctx.redis.send("SADD", [destinationKey, "c", "d", "e"]);
|
||||
|
||||
// Move a member from source to destination
|
||||
const moveResult = await ctx.redis.send("SMOVE", [sourceKey, destinationKey, "b"]);
|
||||
expectType<number>(moveResult, "number");
|
||||
expect(moveResult).toBe(1); // 1 indicates success
|
||||
|
||||
// Try to move a non-existent member
|
||||
const failedMoveResult = await ctx.redis.send("SMOVE", [sourceKey, destinationKey, "z"]);
|
||||
expectType<number>(failedMoveResult, "number");
|
||||
expect(failedMoveResult).toBe(0); // 0 indicates failure
|
||||
|
||||
// Verify source set (should have "a" and "c" left)
|
||||
const sourceMembers = await ctx.redis.smembers(sourceKey);
|
||||
expect(Array.isArray(sourceMembers)).toBe(true);
|
||||
expect(sourceMembers.length).toBe(2);
|
||||
expect(sourceMembers).toContain("a");
|
||||
expect(sourceMembers).toContain("c");
|
||||
expect(sourceMembers).not.toContain("b");
|
||||
|
||||
// Verify destination set (should have "b", "c", "d", "e")
|
||||
const destMembers = await ctx.redis.smembers(destinationKey);
|
||||
expect(Array.isArray(destMembers)).toBe(true);
|
||||
expect(destMembers.length).toBe(4);
|
||||
expect(destMembers).toContain("b");
|
||||
expect(destMembers).toContain("c");
|
||||
expect(destMembers).toContain("d");
|
||||
expect(destMembers).toContain("e");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Set Operations", () => {
|
||||
test("SUNION and SUNIONSTORE commands", async () => {
|
||||
const set1 = ctx.generateKey("sunion-1");
|
||||
const set2 = ctx.generateKey("sunion-2");
|
||||
const set3 = ctx.generateKey("sunion-3");
|
||||
const destSet = ctx.generateKey("sunion-dest");
|
||||
|
||||
// Set up test sets
|
||||
await ctx.redis.send("SADD", [set1, "a", "b", "c"]);
|
||||
await ctx.redis.send("SADD", [set2, "c", "d", "e"]);
|
||||
await ctx.redis.send("SADD", [set3, "e", "f", "g"]);
|
||||
|
||||
// Get union of two sets
|
||||
const unionResult = await ctx.redis.send("SUNION", [set1, set2]);
|
||||
expect(Array.isArray(unionResult)).toBe(true);
|
||||
expect(unionResult.length).toBe(5);
|
||||
expect(unionResult).toContain("a");
|
||||
expect(unionResult).toContain("b");
|
||||
expect(unionResult).toContain("c");
|
||||
expect(unionResult).toContain("d");
|
||||
expect(unionResult).toContain("e");
|
||||
|
||||
// Store union of three sets
|
||||
const storeResult = await ctx.redis.send("SUNIONSTORE", [destSet, set1, set2, set3]);
|
||||
expectType<number>(storeResult, "number");
|
||||
expect(storeResult).toBe(7); // 7 unique members across all sets
|
||||
|
||||
// Verify destination set
|
||||
const destMembers = await ctx.redis.smembers(destSet);
|
||||
expect(Array.isArray(destMembers)).toBe(true);
|
||||
expect(destMembers.length).toBe(7);
|
||||
expect(destMembers).toContain("a");
|
||||
expect(destMembers).toContain("b");
|
||||
expect(destMembers).toContain("c");
|
||||
expect(destMembers).toContain("d");
|
||||
expect(destMembers).toContain("e");
|
||||
expect(destMembers).toContain("f");
|
||||
expect(destMembers).toContain("g");
|
||||
});
|
||||
|
||||
test("SINTER and SINTERSTORE commands", async () => {
|
||||
const set1 = ctx.generateKey("sinter-1");
|
||||
const set2 = ctx.generateKey("sinter-2");
|
||||
const set3 = ctx.generateKey("sinter-3");
|
||||
const destSet = ctx.generateKey("sinter-dest");
|
||||
|
||||
// Set up test sets
|
||||
await ctx.redis.send("SADD", [set1, "a", "b", "c", "d"]);
|
||||
await ctx.redis.send("SADD", [set2, "c", "d", "e"]);
|
||||
await ctx.redis.send("SADD", [set3, "a", "c", "e"]);
|
||||
|
||||
// Get intersection of two sets
|
||||
const interResult = await ctx.redis.send("SINTER", [set1, set2]);
|
||||
expect(Array.isArray(interResult)).toBe(true);
|
||||
expect(interResult.length).toBe(2);
|
||||
expect(interResult).toContain("c");
|
||||
expect(interResult).toContain("d");
|
||||
|
||||
// Store intersection of three sets
|
||||
const storeResult = await ctx.redis.send("SINTERSTORE", [destSet, set1, set2, set3]);
|
||||
expectType<number>(storeResult, "number");
|
||||
expect(storeResult).toBe(1); // Only "c" is in all three sets
|
||||
|
||||
// Verify destination set
|
||||
const destMembers = await ctx.redis.smembers(destSet);
|
||||
expect(Array.isArray(destMembers)).toBe(true);
|
||||
expect(destMembers.length).toBe(1);
|
||||
expect(destMembers[0]).toBe("c");
|
||||
});
|
||||
|
||||
test("SDIFF and SDIFFSTORE commands", async () => {
|
||||
const set1 = ctx.generateKey("sdiff-1");
|
||||
const set2 = ctx.generateKey("sdiff-2");
|
||||
const destSet = ctx.generateKey("sdiff-dest");
|
||||
|
||||
// Set up test sets
|
||||
await ctx.redis.send("SADD", [set1, "a", "b", "c", "d"]);
|
||||
await ctx.redis.send("SADD", [set2, "c", "d", "e"]);
|
||||
|
||||
// Get difference (elements in set1 that aren't in set2)
|
||||
const diffResult = await ctx.redis.send("SDIFF", [set1, set2]);
|
||||
expect(Array.isArray(diffResult)).toBe(true);
|
||||
expect(diffResult.length).toBe(2);
|
||||
expect(diffResult).toContain("a");
|
||||
expect(diffResult).toContain("b");
|
||||
|
||||
// Store difference
|
||||
const storeResult = await ctx.redis.send("SDIFFSTORE", [destSet, set1, set2]);
|
||||
expectType<number>(storeResult, "number");
|
||||
expect(storeResult).toBe(2); // "a" and "b" are only in set1
|
||||
|
||||
// Verify destination set
|
||||
const destMembers = await ctx.redis.smembers(destSet);
|
||||
expect(Array.isArray(destMembers)).toBe(true);
|
||||
expect(destMembers.length).toBe(2);
|
||||
expect(destMembers).toContain("a");
|
||||
expect(destMembers).toContain("b");
|
||||
});
|
||||
});
|
||||
|
||||
describe("Scanning Operations", () => {
|
||||
test("SSCAN command", async () => {
|
||||
const key = ctx.generateKey("sscan-test");
|
||||
|
||||
// Create a set with many members
|
||||
const memberCount = 100;
|
||||
const members = [];
|
||||
for (let i = 0; i < memberCount; i++) {
|
||||
members.push(`member:${i}`);
|
||||
}
|
||||
|
||||
await ctx.redis.send("SADD", [key, ...members]);
|
||||
|
||||
// Use SSCAN to iterate through members
|
||||
const scanResult = await ctx.redis.send("SSCAN", [key, "0", "COUNT", "20"]);
|
||||
expect(Array.isArray(scanResult)).toBe(true);
|
||||
expect(scanResult.length).toBe(2);
|
||||
|
||||
const cursor = scanResult[0];
|
||||
const items = scanResult[1];
|
||||
|
||||
// Cursor should be either "0" (done) or a string number
|
||||
expect(typeof cursor).toBe("string");
|
||||
|
||||
// Items should be an array of members
|
||||
expect(Array.isArray(items)).toBe(true);
|
||||
|
||||
// All results should match our expected pattern
|
||||
for (const item of items) {
|
||||
expect(item.startsWith("member:")).toBe(true);
|
||||
}
|
||||
|
||||
// Verify MATCH pattern works
|
||||
const patternResult = await ctx.redis.send("SSCAN", [key, "0", "MATCH", "member:1*", "COUNT", "1000"]);
|
||||
expect(Array.isArray(patternResult)).toBe(true);
|
||||
expect(patternResult.length).toBe(2);
|
||||
|
||||
const patternItems = patternResult[1];
|
||||
expect(Array.isArray(patternItems)).toBe(true);
|
||||
|
||||
// Should return only members that match the pattern (member:1, member:10-19, etc)
|
||||
// There should be at least "member:1" and "member:10" through "member:19"
|
||||
expect(patternItems.length).toBeGreaterThan(0);
|
||||
|
||||
for (const item of patternItems) {
|
||||
expect(item.startsWith("member:1")).toBe(true);
|
||||
}
|
||||
});
|
||||
});
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user