Files
bun.sh/src/valkey/js_valkey_functions.zig
2025-04-17 12:32:47 -07:00

808 lines
31 KiB
Zig

pub fn jsSend(this: *JSValkeyClient, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue {
const command = try callframe.argument(0).toBunString(globalObject);
defer command.deref();
const args_array = callframe.argument(1);
if (!args_array.isObject() or !args_array.isArray()) {
return globalObject.throw("Arguments must be an array", .{});
}
var iter = args_array.arrayIterator(globalObject);
var args = try std.ArrayList(JSArgument).initCapacity(bun.default_allocator, iter.len);
defer {
for (args.items) |*item| {
item.deinit();
}
args.deinit();
}
while (iter.next()) |arg_js| {
args.appendAssumeCapacity(try fromJS(globalObject, arg_js) orelse {
return globalObject.throwInvalidArgumentType("sendCommand", "argument", "string or buffer");
});
}
const cmd_str = command.toUTF8WithoutRef(bun.default_allocator);
defer cmd_str.deinit();
var cmd: Command = .{
.command = cmd_str.slice(),
.args = .{ .args = args.items },
.meta = .{},
};
cmd.meta = cmd.meta.check(&cmd);
// Send command with slices directly
const promise = this.send(
globalObject,
callframe.this(),
&cmd,
) catch |err| {
return protocol.valkeyErrorToJS(globalObject, "Failed to send command", err);
};
return promise.asValue(globalObject);
}
pub fn get(this: *JSValkeyClient, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue {
const key = (try fromJS(globalObject, callframe.argument(0))) orelse {
return globalObject.throwInvalidArgumentType("get", "key", "string or buffer");
};
defer key.deinit();
// Send GET command
const promise = this.send(
globalObject,
callframe.this(),
&.{
.command = "GET",
.args = .{ .args = &.{key} },
},
) catch |err| {
return protocol.valkeyErrorToJS(globalObject, "Failed to send GET command", err);
};
return promise.asValue(globalObject);
}
pub fn set(this: *JSValkeyClient, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue {
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("set", "key", "string or buffer");
};
args.appendAssumeCapacity(key);
const value = (try fromJS(globalObject, callframe.argument(1))) orelse {
return globalObject.throwInvalidArgumentType("set", "value", "string or buffer or number");
};
args.appendAssumeCapacity(value);
if (args_view.len > 2) {
for (args_view[2..]) |arg| {
if (arg.isUndefinedOrNull()) {
break;
}
args.appendAssumeCapacity(try fromJS(globalObject, arg) orelse {
return globalObject.throwInvalidArgumentType("set", "arguments", "string or buffer");
});
}
}
// Send SET command
const promise = this.send(
globalObject,
callframe.this(),
&.{
.command = "SET",
.args = .{ .args = args.items },
},
) catch |err| {
return protocol.valkeyErrorToJS(globalObject, "Failed to send SET command", err);
};
return promise.asValue(globalObject);
}
pub fn incr(this: *JSValkeyClient, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue {
const key = (try fromJS(globalObject, callframe.argument(0))) orelse {
return globalObject.throwInvalidArgumentType("incr", "key", "string or buffer");
};
defer key.deinit();
// Send INCR command
const promise = this.send(
globalObject,
callframe.this(),
&.{
.command = "INCR",
.args = .{ .args = &.{key} },
},
) catch |err| {
return protocol.valkeyErrorToJS(globalObject, "Failed to send INCR command", err);
};
return promise.asValue(globalObject);
}
pub fn decr(this: *JSValkeyClient, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue {
const key = (try fromJS(globalObject, callframe.argument(0))) orelse {
return globalObject.throwInvalidArgumentType("decr", "key", "string or buffer");
};
defer key.deinit();
// Send DECR command
const promise = this.send(
globalObject,
callframe.this(),
&.{
.command = "DECR",
.args = .{ .args = &.{key} },
},
) catch |err| {
return protocol.valkeyErrorToJS(globalObject, "Failed to send DECR command", err);
};
return promise.asValue(globalObject);
}
pub fn exists(this: *JSValkeyClient, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue {
const key = (try fromJS(globalObject, callframe.argument(0))) orelse {
return globalObject.throwInvalidArgumentType("exists", "key", "string or buffer");
};
defer key.deinit();
// Send EXISTS command with special Exists type for boolean conversion
const promise = this.send(
globalObject,
callframe.this(),
&.{
.command = "EXISTS",
.args = .{ .args = &.{key} },
.meta = .{ .return_as_bool = true },
},
) catch |err| {
return protocol.valkeyErrorToJS(globalObject, "Failed to send EXISTS command", err);
};
return promise.asValue(globalObject);
}
pub fn expire(this: *JSValkeyClient, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue {
const key = (try fromJS(globalObject, callframe.argument(0))) orelse {
return globalObject.throwInvalidArgumentType("expire", "key", "string or buffer");
};
defer key.deinit();
const seconds = try globalObject.validateIntegerRange(callframe.argument(1), i32, 0, .{
.min = 0,
.max = 2147483647,
.field_name = "seconds",
});
// Convert seconds to a string
var int_buf: [64]u8 = undefined;
const seconds_len = std.fmt.formatIntBuf(&int_buf, seconds, 10, .lower, .{});
const seconds_slice = int_buf[0..seconds_len];
// Send EXPIRE command
const promise = this.send(
globalObject,
callframe.this(),
&.{
.command = "EXPIRE",
.args = .{ .raw = &.{ key.slice(), seconds_slice } },
},
) catch |err| {
return protocol.valkeyErrorToJS(globalObject, "Failed to send EXPIRE command", err);
};
return promise.asValue(globalObject);
}
pub fn ttl(this: *JSValkeyClient, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue {
const key = (try fromJS(globalObject, callframe.argument(0))) orelse {
return globalObject.throwInvalidArgumentType("ttl", "key", "string or buffer");
};
defer key.deinit();
// Send TTL command
const promise = this.send(
globalObject,
callframe.this(),
&.{
.command = "TTL",
.args = .{ .args = &.{key} },
},
) catch |err| {
return protocol.valkeyErrorToJS(globalObject, "Failed to send TTL command", err);
};
return promise.asValue(globalObject);
}
// Implement srem (remove value from a set)
pub fn srem(this: *JSValkeyClient, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue {
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();
// Send SREM command
const promise = this.send(
globalObject,
callframe.this(),
&.{
.command = "SREM",
.args = .{ .args = &.{ key, value } },
},
) catch |err| {
return protocol.valkeyErrorToJS(globalObject, "Failed to send SREM command", err);
};
return promise.asValue(globalObject);
}
// Implement srandmember (get random member from set)
pub fn srandmember(this: *JSValkeyClient, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue {
const key = (try fromJS(globalObject, callframe.argument(0))) orelse {
return globalObject.throwInvalidArgumentType("srandmember", "key", "string or buffer");
};
defer key.deinit();
// Send SRANDMEMBER command
const promise = this.send(
globalObject,
callframe.this(),
&.{
.command = "SRANDMEMBER",
.args = .{ .args = &.{key} },
},
) catch |err| {
return protocol.valkeyErrorToJS(globalObject, "Failed to send SRANDMEMBER command", err);
};
return promise.asValue(globalObject);
}
// Implement smembers (get all members of a set)
pub fn smembers(this: *JSValkeyClient, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue {
const key = (try fromJS(globalObject, callframe.argument(0))) orelse {
return globalObject.throwInvalidArgumentType("smembers", "key", "string or buffer");
};
defer key.deinit();
// Send SMEMBERS command
const promise = this.send(
globalObject,
callframe.this(),
&.{
.command = "SMEMBERS",
.args = .{ .args = &.{key} },
},
) catch |err| {
return protocol.valkeyErrorToJS(globalObject, "Failed to send SMEMBERS command", err);
};
return promise.asValue(globalObject);
}
// Implement spop (pop a random member from a set)
pub fn spop(this: *JSValkeyClient, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue {
const key = (try fromJS(globalObject, callframe.argument(0))) orelse {
return globalObject.throwInvalidArgumentType("spop", "key", "string or buffer");
};
defer key.deinit();
// Send SPOP command
const promise = this.send(
globalObject,
callframe.this(),
&.{
.command = "SPOP",
.args = .{ .args = &.{key} },
},
) catch |err| {
return protocol.valkeyErrorToJS(globalObject, "Failed to send SPOP command", err);
};
return promise.asValue(globalObject);
}
// Implement sadd (add member to a set)
pub fn sadd(this: *JSValkeyClient, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue {
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();
// Send SADD command
const promise = this.send(
globalObject,
callframe.this(),
&.{
.command = "SADD",
.args = .{ .args = &.{ key, value } },
},
) catch |err| {
return protocol.valkeyErrorToJS(globalObject, "Failed to send SADD command", err);
};
return promise.asValue(globalObject);
}
// Implement sismember (check if value is member of a set)
pub fn sismember(this: *JSValkeyClient, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue {
const key = (try fromJS(globalObject, callframe.argument(0))) orelse {
return globalObject.throwInvalidArgumentType("sismember", "key", "string or buffer");
};
defer key.deinit();
const value = (try fromJS(globalObject, callframe.argument(1))) orelse {
return globalObject.throwInvalidArgumentType("sismember", "value", "string or buffer");
};
defer value.deinit();
// Send SISMEMBER command
const promise = this.send(
globalObject,
callframe.this(),
&.{
.command = "SISMEMBER",
.args = .{ .args = &.{ key, value } },
.meta = .{ .return_as_bool = true },
},
) catch |err| {
return protocol.valkeyErrorToJS(globalObject, "Failed to send SISMEMBER command", err);
};
return promise.asValue(globalObject);
}
// Implement hmget (get multiple values from hash)
pub fn hmget(this: *JSValkeyClient, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue {
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", .{});
}
var iter = fields_array.arrayIterator(globalObject);
var args = try std.ArrayList(JSC.ZigString.Slice).initCapacity(bun.default_allocator, iter.len + 1);
defer {
for (args.items) |item| {
item.deinit();
}
args.deinit();
}
args.appendAssumeCapacity(JSC.ZigString.Slice.fromUTF8NeverFree(key.slice()));
// Add field names as arguments
while (iter.next()) |field_js| {
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);
}
// Send HMGET command
const promise = this.send(
globalObject,
callframe.this(),
&.{
.command = "HMGET",
.args = .{ .slices = args.items },
},
) catch |err| {
return protocol.valkeyErrorToJS(globalObject, "Failed to send HMGET command", err);
};
return promise.asValue(globalObject);
}
// Implement hincrby (increment hash field by integer value)
pub fn hincrby(this: *JSValkeyClient, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue {
const key = try callframe.argument(0).toBunString(globalObject);
defer key.deref();
const field = try callframe.argument(1).toBunString(globalObject);
defer field.deref();
const value = try callframe.argument(2).toBunString(globalObject);
defer value.deref();
const key_slice = key.toUTF8WithoutRef(bun.default_allocator);
defer key_slice.deinit();
const field_slice = field.toUTF8WithoutRef(bun.default_allocator);
defer field_slice.deinit();
const value_slice = value.toUTF8WithoutRef(bun.default_allocator);
defer value_slice.deinit();
// Send HINCRBY command
const promise = this.send(
globalObject,
callframe.this(),
&.{
.command = "HINCRBY",
.args = .{ .slices = &.{ key_slice, field_slice, value_slice } },
},
) catch |err| {
return protocol.valkeyErrorToJS(globalObject, "Failed to send HINCRBY command", err);
};
return promise.asValue(globalObject);
}
// Implement hincrbyfloat (increment hash field by float value)
pub fn hincrbyfloat(this: *JSValkeyClient, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue {
const key = try callframe.argument(0).toBunString(globalObject);
defer key.deref();
const field = try callframe.argument(1).toBunString(globalObject);
defer field.deref();
const value = try callframe.argument(2).toBunString(globalObject);
defer value.deref();
const key_slice = key.toUTF8WithoutRef(bun.default_allocator);
defer key_slice.deinit();
const field_slice = field.toUTF8WithoutRef(bun.default_allocator);
defer field_slice.deinit();
const value_slice = value.toUTF8WithoutRef(bun.default_allocator);
defer value_slice.deinit();
// Send HINCRBYFLOAT command
const promise = this.send(
globalObject,
callframe.this(),
&.{
.command = "HINCRBYFLOAT",
.args = .{ .slices = &.{ key_slice, field_slice, value_slice } },
},
) catch |err| {
return protocol.valkeyErrorToJS(globalObject, "Failed to send HINCRBYFLOAT command", err);
};
return promise.asValue(globalObject);
}
// Implement hmset (set multiple values in hash)
pub fn hmset(this: *JSValkeyClient, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue {
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", .{});
}
var iter = 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);
defer {
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);
// Add field-value pairs
while (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);
// Add value
if (iter.next()) |value_js| {
const value_str = try value_js.toBunString(globalObject);
defer value_str.deref();
const value_slice = value_str.toUTF8WithoutRef(bun.default_allocator);
args.appendAssumeCapacity(value_slice);
} else {
return globalObject.throw("Arguments must be an array of alternating field names and values", .{});
}
}
// Send HMSET command
const promise = this.send(
globalObject,
callframe.this(),
&.{
.command = "HMSET",
.args = .{ .slices = args.items },
},
) catch |err| {
return protocol.valkeyErrorToJS(globalObject, "Failed to send HMSET command", err);
};
return promise.asValue(globalObject);
}
pub const bitcount = compile.@"(key: RedisKey)"("bitcount", "BITCOUNT", "key").call;
pub const dump = compile.@"(key: RedisKey)"("dump", "DUMP", "key").call;
pub const expiretime = compile.@"(key: RedisKey)"("expiretime", "EXPIRETIME", "key").call;
pub const getdel = compile.@"(key: RedisKey)"("getdel", "GETDEL", "key").call;
pub const getex = compile.@"(key: RedisKey)"("getex", "GETEX", "key").call;
pub const hgetall = compile.@"(key: RedisKey)"("hgetall", "HGETALL", "key").call;
pub const hkeys = compile.@"(key: RedisKey)"("hkeys", "HKEYS", "key").call;
pub const hlen = compile.@"(key: RedisKey)"("hlen", "HLEN", "key").call;
pub const hvals = compile.@"(key: RedisKey)"("hvals", "HVALS", "key").call;
pub const keys = compile.@"(key: RedisKey)"("keys", "KEYS", "key").call;
pub const llen = compile.@"(key: RedisKey)"("llen", "LLEN", "key").call;
pub const lpop = compile.@"(key: RedisKey)"("lpop", "LPOP", "key").call;
pub const persist = compile.@"(key: RedisKey)"("persist", "PERSIST", "key").call;
pub const pexpiretime = compile.@"(key: RedisKey)"("pexpiretime", "PEXPIRETIME", "key").call;
pub const pttl = compile.@"(key: RedisKey)"("pttl", "PTTL", "key").call;
pub const rpop = compile.@"(key: RedisKey)"("rpop", "RPOP", "key").call;
pub const scard = compile.@"(key: RedisKey)"("scard", "SCARD", "key").call;
pub const strlen = compile.@"(key: RedisKey)"("strlen", "STRLEN", "key").call;
pub const @"type" = compile.@"(key: RedisKey)"("type", "TYPE", "key").call;
pub const zcard = compile.@"(key: RedisKey)"("zcard", "ZCARD", "key").call;
pub const zpopmax = compile.@"(key: RedisKey)"("zpopmax", "ZPOPMAX", "key").call;
pub const zpopmin = compile.@"(key: RedisKey)"("zpopmin", "ZPOPMIN", "key").call;
pub const zrandmember = compile.@"(key: RedisKey)"("zrandmember", "ZRANDMEMBER", "key").call;
pub const ping = compile.@"(key: RedisKey)"("ping", "PING", "message").call;
pub const append = compile.@"(key: RedisKey, value: RedisValue)"("append", "APPEND", "key", "value").call;
pub const getset = compile.@"(key: RedisKey, value: RedisValue)"("getset", "GETSET", "key", "value").call;
pub const lpush = compile.@"(key: RedisKey, value: RedisValue, ...args: RedisValue)"("lpush", "LPUSH").call;
pub const lpushx = compile.@"(key: RedisKey, value: RedisValue, ...args: RedisValue)"("lpushx", "LPUSHX").call;
pub const pfadd = compile.@"(key: RedisKey, value: RedisValue)"("pfadd", "PFADD", "key", "value").call;
pub const rpush = compile.@"(key: RedisKey, value: RedisValue, ...args: RedisValue)"("rpush", "RPUSH").call;
pub const rpushx = compile.@"(key: RedisKey, value: RedisValue, ...args: RedisValue)"("rpushx", "RPUSHX").call;
pub const setnx = compile.@"(key: RedisKey, value: RedisValue)"("setnx", "SETNX", "key", "value").call;
pub const zscore = compile.@"(key: RedisKey, value: RedisValue)"("zscore", "ZSCORE", "key", "value").call;
pub const del = compile.@"(key: RedisKey, ...args: RedisKey[])"("del", "DEL", "key").call;
pub const mget = compile.@"(key: RedisKey, ...args: RedisKey[])"("mget", "MGET", "key").call;
pub const publish = compile.@"(...strings: string[])"("publish", "PUBLISH").call;
pub const script = compile.@"(...strings: string[])"("script", "SCRIPT").call;
pub const select = compile.@"(...strings: string[])"("select", "SELECT").call;
pub const spublish = compile.@"(...strings: string[])"("spublish", "SPUBLISH").call;
pub const smove = compile.@"(...strings: string[])"("smove", "SMOVE").call;
pub const substr = compile.@"(...strings: string[])"("substr", "SUBSTR").call;
pub const hstrlen = compile.@"(...strings: string[])"("hstrlen", "HSTRLEN").call;
pub const zrank = compile.@"(...strings: string[])"("zrank", "ZRANK").call;
pub const zrevrank = compile.@"(...strings: string[])"("zrevrank", "ZREVRANK").call;
pub const subscribe = compile.@"(...strings: string[])"("subscribe", "SUBSCRIBE").call;
pub const psubscribe = compile.@"(...strings: string[])"("psubscribe", "PSUBSCRIBE").call;
pub const unsubscribe = compile.@"(...strings: string[])"("unsubscribe", "UNSUBSCRIBE").call;
pub const punsubscribe = compile.@"(...strings: string[])"("punsubscribe", "PUNSUBSCRIBE").call;
pub const pubsub = compile.@"(...strings: string[])"("pubsub", "PUBSUB").call;
// publish(channel: RedisValue, message: RedisValue)
// script(subcommand: "LOAD", script: RedisValue)
// select(index: number | string)
// spublish(shardchannel: RedisValue, message: RedisValue)
// smove(source: RedisKey, destination: RedisKey, member: RedisValue)
// substr(key: RedisKey, start: number, end: number)` // Deprecated alias for getrang
// hstrlen(key: RedisKey, field: RedisValue)
// zrank(key: RedisKey, member: RedisValue)
// zrevrank(key: RedisKey, member: RedisValue)
// zscore(key: RedisKey, member: RedisValue)
// cluster(subcommand: "KEYSLOT", key: RedisKey)
const JSFunction = fn (*JSValkeyClient, *JSC.JSGlobalObject, *JSC.CallFrame) bun.JSError!JSValue;
const compile = struct {
pub fn @"(key: RedisKey)"(
comptime name: []const u8,
comptime command: []const u8,
comptime arg0_name: []const u8,
) type {
return struct {
pub fn call(this: *JSValkeyClient, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue {
const key = (try fromJS(globalObject, callframe.argument(0))) orelse {
return globalObject.throwInvalidArgumentType(name, arg0_name, "string or buffer");
};
defer key.deinit();
const promise = this.send(
globalObject,
callframe.this(),
&.{
.command = command,
.args = .{ .args = &.{key} },
},
) catch |err| {
return protocol.valkeyErrorToJS(globalObject, "Failed to send " ++ command, err);
};
return promise.asValue(globalObject);
}
};
}
pub fn @"(key: RedisKey, ...args: RedisKey[])"(
comptime name: []const u8,
comptime command: []const u8,
comptime arg0_name: []const u8,
) type {
return struct {
pub fn call(this: *JSValkeyClient, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue {
if (callframe.argument(0).isUndefinedOrNull()) {
return globalObject.throwMissingArgumentsValue(&.{arg0_name});
}
const arguments = callframe.arguments();
var args = try std.ArrayList(JSArgument).initCapacity(bun.default_allocator, arguments.len);
defer {
for (args.items) |*item| {
item.deinit();
}
args.deinit();
}
for (arguments) |arg| {
if (arg.isUndefinedOrNull()) {
continue;
}
const another = (try fromJS(globalObject, arg)) orelse {
return globalObject.throwInvalidArgumentType(name, "additional arguments", "string or buffer");
};
try args.append(another);
}
const promise = this.send(
globalObject,
callframe.this(),
&.{
.command = command,
.args = .{ .args = args.items },
},
) catch |err| {
return protocol.valkeyErrorToJS(globalObject, "Failed to send " ++ command, err);
};
return promise.asValue(globalObject);
}
};
}
pub fn @"(key: RedisKey, value: RedisValue)"(
comptime name: []const u8,
comptime command: []const u8,
comptime arg0_name: []const u8,
comptime arg1_name: []const u8,
) type {
return struct {
pub fn call(this: *JSValkeyClient, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue {
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 promise = this.send(
globalObject,
callframe.this(),
&.{
.command = command,
.args = .{ .args = &.{ key, value } },
},
) catch |err| {
return protocol.valkeyErrorToJS(globalObject, "Failed to send " ++ command, err);
};
return promise.asValue(globalObject);
}
};
}
pub fn @"(...strings: string[])"(
comptime name: []const u8,
comptime command: []const u8,
) type {
return struct {
pub fn call(this: *JSValkeyClient, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue {
var args = try std.ArrayList(JSArgument).initCapacity(bun.default_allocator, callframe.arguments().len);
defer {
for (args.items) |*item| {
item.deinit();
}
args.deinit();
}
for (callframe.arguments()) |arg| {
const another = (try fromJS(globalObject, arg)) orelse {
return globalObject.throwInvalidArgumentType(name, "additional arguments", "string or buffer");
};
try args.append(another);
}
const promise = this.send(
globalObject,
callframe.this(),
&.{
.command = command,
.args = .{ .args = args.items },
},
) catch |err| {
return protocol.valkeyErrorToJS(globalObject, "Failed to send " ++ command, err);
};
return promise.asValue(globalObject);
}
};
}
pub fn @"(key: RedisKey, value: RedisValue, ...args: RedisValue)"(
comptime name: []const u8,
comptime command: []const u8,
) type {
return struct {
pub fn call(this: *JSValkeyClient, globalObject: *JSC.JSGlobalObject, callframe: *JSC.CallFrame) bun.JSError!JSValue {
var args = try std.ArrayList(JSArgument).initCapacity(bun.default_allocator, callframe.arguments().len);
defer {
for (args.items) |*item| {
item.deinit();
}
args.deinit();
}
for (callframe.arguments()) |arg| {
if (arg.isUndefinedOrNull()) {
continue;
}
const another = (try fromJS(globalObject, arg)) orelse {
return globalObject.throwInvalidArgumentType(name, "additional arguments", "string or buffer");
};
try args.append(another);
}
const promise = this.send(
globalObject,
callframe.this(),
&.{
.command = command,
.args = .{ .args = args.items },
},
) catch |err| {
return protocol.valkeyErrorToJS(globalObject, "Failed to send " ++ command, err);
};
return promise.asValue(globalObject);
}
};
}
};
const JSValkeyClient = @import("./js_valkey.zig").JSValkeyClient;
const bun = @import("bun");
const JSC = bun.JSC;
const valkey = bun.valkey;
const protocol = valkey.protocol;
const JSValue = JSC.JSValue;
const Command = valkey.Command;
const std = @import("std");
const Slice = JSC.ZigString.Slice;
const JSArgument = JSC.Node.BlobOrStringOrBuffer;
fn fromJS(globalObject: *JSC.JSGlobalObject, value: JSValue) !?JSArgument {
if (value == .undefined or value == .null) {
return null;
}
if (value.isNumber()) {
// Allow numbers to be passed as strings.
const str = value.toString(globalObject);
if (globalObject.hasException()) {
@branchHint(.unlikely);
return error.JSError;
}
return try JSArgument.fromJSMaybeFile(globalObject, bun.default_allocator, str.toJS(), true);
}
return try JSArgument.fromJSMaybeFile(globalObject, bun.default_allocator, value, false);
}