Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions docs/host-interface.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ export type RedisHost = {
redisCall: RedisCallHandler;
redisPcall: RedisCallHandler;
log: RedisLogHandler;
onSetResp?: (version: 2 | 3) => void;
};
```

Expand All @@ -38,6 +39,14 @@ sets, big numbers, and verbatim strings. Push replies are not representable.
- `level` is the numeric Redis log level.
- `message` is a binary-safe `Buffer`.

### onSetResp
- Optional. Invoked when the script calls `redis.setresp(n)` with the new
version (`2` or `3`).
- The WASM encoder flips its own RESP mode regardless; this hook only lets the
host mirror the protocol when choosing reply shapes for
`redisCall`/`redisPcall`.
- Fires after validation, so it only ever receives `2` or `3`.

## ReplyValue

```ts
Expand Down
7 changes: 7 additions & 0 deletions src/engine.ts
Original file line number Diff line number Diff line change
Expand Up @@ -452,6 +452,7 @@ type MutableHandlers = {
call: (...args: number[]) => bigint | void;
pcall: (...args: number[]) => bigint | void;
props: (...args: number[]) => bigint | void;
setresp: (version: number) => void;
};

/**
Expand Down Expand Up @@ -607,6 +608,10 @@ export class LuaWasmModule {
host.log(level, msg);
};

this.handlers.setresp = (version: number): void => {
host.onSetResp?.call(host, version as 2 | 3);
};

this.handlers.sha1hex = (...args: number[]): bigint | void => {
const abiArgs = parseAbiArgs(args);
const data = readBytes(exports.HEAPU8, abiArgs.ptr, abiArgs.len);
Expand Down Expand Up @@ -698,6 +703,7 @@ export async function load(options: LoadOptions = {}): Promise<LuaWasmModule> {
call: () => BigInt(0),
pcall: () => BigInt(0),
props: () => BigInt(0),
setresp: () => {},
};

// Create wrapper imports that delegate to mutable handlers
Expand All @@ -709,6 +715,7 @@ export async function load(options: LoadOptions = {}): Promise<LuaWasmModule> {
host_redis_call: (...args: number[]) => handlers.call(...args),
host_redis_pcall: (...args: number[]) => handlers.pcall(...args),
host_redis_props: (...args: number[]) => handlers.props(...args),
host_redis_setresp: (version: number) => handlers.setresp(version),
};

const { exports } = await loadModule(options, hostImports);
Expand Down
7 changes: 7 additions & 0 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -182,6 +182,13 @@ export type RedisHost = {

/** Handler for redis.log() messages. */
log: RedisLogHandler;

/**
* Optional: notified when the script calls `redis.setresp(n)`. The WASM
* encoder still flips its own RESP mode; this hook lets the host match the
* reply shapes it returns from `redisCall`/`redisPcall` to the new protocol.
*/
onSetResp?: (version: 2 | 3) => void;
};

/**
Expand Down
21 changes: 21 additions & 0 deletions test/engine.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -460,6 +460,27 @@ test("redis.setresp: rejects unsupported protocol versions", async () => {
assert.equal(result.meta?.line, 1);
});

test("redis.setresp: notifies the host of the new protocol version", async () => {
await resolveWasmPath();
const module = await load();
const seen: number[] = [];
const engine = module.create(createTestHost({
onSetResp: (version) => seen.push(version),
}));

engine.eval("redis.setresp(3); redis.setresp(2)");
assert.deepEqual(seen, [3, 2]);
});

test("redis.setresp: missing onSetResp host hook is a no-op", async () => {
await resolveWasmPath();
const module = await load();
const engine = module.create(createTestHost());

// No onSetResp provided; eval must not throw.
assert.equal(engine.eval("redis.setresp(3); return true"), true);
});

test("redis.setresp: decodes RESP3 host replies for redis.call", async () => {
await resolveWasmPath();
const module = await load();
Expand Down
1 change: 1 addition & 0 deletions wasm/include/abi.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,7 @@ typedef struct PtrLen {
PtrLen host_redis_call(uint32_t ptr, uint32_t len);
PtrLen host_redis_pcall(uint32_t ptr, uint32_t len);
void host_redis_log(uint32_t level, uint32_t ptr, uint32_t len);
void host_redis_setresp(uint32_t version);
PtrLen host_sha1hex(uint32_t ptr, uint32_t len);
PtrLen host_redis_props(void);

Expand Down
1 change: 1 addition & 0 deletions wasm/src/redis_api.c
Original file line number Diff line number Diff line change
Expand Up @@ -401,6 +401,7 @@ static int l_redis_setresp(lua_State *L) {
return luaL_error(L, "ERR RESP version must be 2 or 3.");
}
g_resp_version = next;
host_redis_setresp(next); /* notify host so it can match reply shapes */
return 0;
}

Expand Down
Loading