import { Buffer } from "node:buffer";
import { createConnection as __createNetConnection, isIP as __netIsIp } from "node:net";
import { connect as __createTlsConnection } from "node:tls";
const __REDIS_COMMAND_SURFACE = Object.freeze([
"AUTH",
"DEL",
"EVAL",
"EXPIRE",
"GET",
"HELLO",
"INCR",
"INCRBY",
"PING",
"PUBLISH",
"SCAN",
"SET",
"SUBSCRIBE",
"TTL",
"UNSUBSCRIBE",
]);
const __REDIS_ALLOWED_COMMANDS = new Set(__REDIS_COMMAND_SURFACE);
const __REDIS_MAX_REPLY_BYTES = 16 * 1024 * 1024;
const __REDIS_MAX_NESTING = 64;
const __REDIS_COMMAND_TIMEOUT_MS = 5_000;
const __REDIS_STORAGE_PREFIX = "noxid:storage:v1:";
const __REDIS_RATE_PREFIX = "noxid:endpoint-rate:v1:";
const __REDIS_IDEMPOTENCY_PREFIX = "noxid:endpoint-idempotency:v1:";
const __REDIS_IDEMPOTENCY_CLAIM = "claim";
const __REDIS_IDEMPOTENCY_STORED = "stored";
const __redisTextDecoder = new TextDecoder("utf-8", { fatal: true });
let __redisConfigurationValue;
const __redisConnections = new Map();
function __redisError(code, message, cause) {
return Object.assign(new Error(message, cause === undefined ? undefined : { cause }), { code });
}
function __redisUnavailable(message, cause) {
return __redisError(
"SERVER_STORAGE_REDIS_UNAVAILABLE",
`Redis server storage is unavailable: ${message}`,
cause,
);
}
function __redisIsUnavailable(error) {
return error?.code === "SERVER_STORAGE_REDIS_UNAVAILABLE";
}
function __redisEnvironmentUrl() {
const node = globalThis.process?.env?.REDIS_URL;
if (typeof node === "string" && node.length > 0) return node;
try {
const deno = globalThis.Deno?.env?.get?.("REDIS_URL");
if (typeof deno === "string" && deno.length > 0) return deno;
} catch {}
return null;
}
function __redisCredential(value, label) {
try { return decodeURIComponent(value); }
catch {
throw __redisError(
"SERVER_STORAGE_REDIS_URL_INVALID",
`REDIS_URL ${label} must use valid percent encoding; provide one redis:// or rediss:// single-instance endpoint`,
);
}
}
function __redisConfiguration() {
if (__redisConfigurationValue !== undefined) return __redisConfigurationValue;
const source = __redisEnvironmentUrl();
if (source === null) {
throw __redisError(
"SERVER_STORAGE_REDIS_URL_REQUIRED",
"REDIS_URL is required for Redis server storage; declare it under [server] secrets and provide a redis:// or rediss:// single-instance endpoint",
);
}
const scheme = source.slice(0, Math.max(0, source.indexOf(":"))).toLowerCase();
if (scheme.includes("cluster") || scheme.includes("sentinel")) {
throw __redisError(
"SERVER_STORAGE_REDIS_CLUSTER_UNSUPPORTED",
"Redis Cluster and Sentinel URL forms are not supported in v1; provide one redis:// or rediss:// single-instance or managed endpoint",
);
}
let url;
try { url = new URL(source); }
catch {
throw __redisError(
"SERVER_STORAGE_REDIS_URL_INVALID",
"REDIS_URL must be one valid redis:// or rediss:// single-instance endpoint",
);
}
const clustered = url.hostname.includes(",")
|| url.hostname.includes(";")
|| [...url.searchParams.keys()].some((key) => /cluster|sentinel/i.test(key))
|| [...url.searchParams.values()].some((value) => /cluster|sentinel/i.test(value));
if (clustered) {
throw __redisError(
"SERVER_STORAGE_REDIS_CLUSTER_UNSUPPORTED",
"Redis Cluster and Sentinel endpoints are not supported in v1; provide one redis:// or rediss:// single-instance or managed endpoint",
);
}
if (url.protocol !== "redis:" && url.protocol !== "rediss:") {
throw __redisError(
"SERVER_STORAGE_REDIS_URL_INVALID",
`REDIS_URL uses unsupported scheme ${url.protocol || "(missing)"}; use redis:// for TCP or rediss:// for TLS`,
);
}
if (url.hostname.length === 0 || url.hash.length !== 0 || (url.pathname !== "" && url.pathname !== "/" && url.pathname !== "/0")) {
throw __redisError(
"SERVER_STORAGE_REDIS_URL_INVALID",
"REDIS_URL must name one host and database 0; cluster, sentinel, fragments, and alternate logical databases are not supported in v1",
);
}
const port = url.port.length === 0 ? 6379 : Number(url.port);
if (!Number.isSafeInteger(port) || port <= 0 || port > 65535) {
throw __redisError("SERVER_STORAGE_REDIS_URL_INVALID", "REDIS_URL port must be an integer from 1 through 65535");
}
const username = __redisCredential(url.username, "username");
const password = __redisCredential(url.password, "password");
if (username.length > 0 && password.length === 0) {
throw __redisError(
"SERVER_STORAGE_REDIS_URL_INVALID",
"REDIS_URL cannot declare a username without a password; provide credentials accepted by Redis AUTH",
);
}
__redisConfigurationValue = Object.freeze({
host: url.hostname,
port,
tls: url.protocol === "rediss:",
username,
password,
});
return __redisConfigurationValue;
}
function __redisUtf8(buffer) {
try { return __redisTextDecoder.decode(buffer); }
catch { throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", "Redis returned text that is not valid UTF-8"); }
}
function __redisLine(buffer, offset) {
const end = buffer.indexOf("\r\n", offset);
return end < 0 ? null : { bytes: buffer.subarray(offset, end), offset: end + 2 };
}
function __redisLength(line, label, allowNull = false) {
const text = __redisUtf8(line);
if (!/^-?(0|[1-9][0-9]*)$/.test(text) || text === "-0") {
throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", `Redis returned an invalid RESP ${label}`);
}
const value = Number(text);
if (!Number.isSafeInteger(value) || value < (allowNull ? -1 : 0) || value > __REDIS_MAX_REPLY_BYTES) {
throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", `Redis RESP ${label} exceeds the admitted bounds`);
}
return value;
}
function __redisParse(buffer, offset = 0, depth = 0) {
if (offset >= buffer.length) return null;
if (depth > __REDIS_MAX_NESTING) throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", "Redis RESP nesting exceeds 64 levels");
const type = String.fromCharCode(buffer[offset]);
const start = offset + 1;
if (type === "+" || type === "-" || type === ":" || type === "," || type === "(" || type === "#" || type === "_") {
const line = __redisLine(buffer, start);
if (line === null) return null;
const text = __redisUtf8(line.bytes);
if (type === "+") return { value: text, offset: line.offset, push: false };
if (type === "-") return { value: __redisError("SERVER_STORAGE_REDIS_REPLY", `Redis refused a command: ${text}`), offset: line.offset, push: false, replyError: true };
if (type === "_") {
if (text.length !== 0) throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", "Redis returned an invalid RESP null");
return { value: null, offset: line.offset, push: false };
}
if (type === "#") {
if (text !== "t" && text !== "f") throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", "Redis returned an invalid RESP boolean");
return { value: text === "t", offset: line.offset, push: false };
}
if (type === ",") {
if (text.length === 0) throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", "Redis returned an invalid RESP number");
const value = Number(text);
if (!Number.isFinite(value)) throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", "Redis returned a non-finite RESP number");
return { value, offset: line.offset, push: false };
}
if (!/^-?(0|[1-9][0-9]*)$/.test(text) || text === "-0") {
throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", "Redis returned an invalid RESP integer");
}
let integer;
try { integer = BigInt(text); }
catch { throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", "Redis returned an invalid RESP integer"); }
const value = integer <= BigInt(Number.MAX_SAFE_INTEGER) && integer >= BigInt(Number.MIN_SAFE_INTEGER) ? Number(integer) : integer;
return { value, offset: line.offset, push: false };
}
if (type === "$" || type === "!" || type === "=") {
const line = __redisLine(buffer, start);
if (line === null) return null;
const length = __redisLength(line.bytes, "bulk length", true);
if (length === -1) return { value: null, offset: line.offset, push: false };
if (buffer.length < line.offset + length + 2) return null;
if (buffer[line.offset + length] !== 13 || buffer[line.offset + length + 1] !== 10) {
throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", "Redis bulk reply is missing its CRLF terminator");
}
const text = __redisUtf8(buffer.subarray(line.offset, line.offset + length));
const next = line.offset + length + 2;
if (type === "!") return { value: __redisError("SERVER_STORAGE_REDIS_REPLY", `Redis refused a command: ${text}`), offset: next, push: false, replyError: true };
return { value: type === "=" && text.length >= 4 && text[3] === ":" ? text.slice(4) : text, offset: next, push: false };
}
if (type === "*" || type === "~" || type === ">" || type === "%") {
const line = __redisLine(buffer, start);
if (line === null) return null;
const length = __redisLength(line.bytes, "aggregate length", true);
if (length === -1) return { value: null, offset: line.offset, push: type === ">" };
const itemCount = type === "%" ? length * 2 : length;
if (!Number.isSafeInteger(itemCount) || itemCount > __REDIS_MAX_REPLY_BYTES) {
throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", "Redis aggregate reply exceeds the admitted bounds");
}
const values = [];
let next = line.offset;
for (let index = 0; index < itemCount; index += 1) {
const parsed = __redisParse(buffer, next, depth + 1);
if (parsed === null) return null;
if (parsed.replyError) throw parsed.value;
values.push(parsed.value);
next = parsed.offset;
}
if (type === "%") {
const map = new Map();
for (let index = 0; index < values.length; index += 2) map.set(values[index], values[index + 1]);
return { value: map, offset: next, push: false };
}
return { value: values, offset: next, push: type === ">" };
}
throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", `Redis returned unsupported RESP type byte ${JSON.stringify(type)}`);
}
function __redisAssertCommand(command) {
if (typeof command !== "string" || command !== command.toUpperCase() || !__REDIS_ALLOWED_COMMANDS.has(command)) {
throw __redisError(
"SERVER_STORAGE_REDIS_COMMAND_UNSUPPORTED",
`Redis command ${JSON.stringify(command)} is not admitted; extend the declared first-party RESP command surface before using it`,
);
}
}
function __redisArgument(value) {
if (typeof value === "string") return Buffer.from(value, "utf8");
if (typeof value === "number" && Number.isSafeInteger(value)) return Buffer.from(String(value), "ascii");
throw __redisError("SERVER_STORAGE_REDIS_COMMAND_INVALID", "Redis command arguments must be strings or safe integers");
}
function __redisRequest(command, arguments_) {
__redisAssertCommand(command);
const values = [Buffer.from(command, "ascii"), ...arguments_.map(__redisArgument)];
const chunks = [Buffer.from(`*${values.length}\r\n`, "ascii")];
for (const value of values) chunks.push(Buffer.from(`$${value.length}\r\n`, "ascii"), value, Buffer.from("\r\n", "ascii"));
return Buffer.concat(chunks);
}
function __redisFailSocket(state, socket, cause) {
if (state.socket !== socket) return;
const error = typeof cause?.code === "string" && cause.code.startsWith("SERVER_STORAGE_REDIS_")
? cause
: __redisUnavailable(cause?.message || "the RESP connection closed", cause);
state.socket = null;
state.ready = false;
state.buffer = Buffer.alloc(0);
const rejectOpen = state.openReject;
state.openReject = null;
if (rejectOpen) rejectOpen(error);
for (const pending of state.pending.splice(0)) {
clearTimeout(pending.timer);
pending.reject(error);
}
state.retryAt = Date.now() + state.backoffMs;
state.backoffMs = Math.min(1_000, state.backoffMs * 2);
if (!socket.destroyed) socket.destroy();
if (state.reconnectTimer !== null) clearTimeout(state.reconnectTimer);
let reconnect = false;
try { reconnect = state.shouldReconnect?.() === true; } catch {}
if (reconnect) {
const delay = Math.max(1, state.retryAt - Date.now());
state.reconnectTimer = setTimeout(() => {
state.reconnectTimer = null;
__redisEnsureOpen(state).catch(() => {});
}, delay);
}
}
function __redisDrain(state, socket) {
try {
while (state.buffer.length > 0) {
const parsed = __redisParse(state.buffer);
if (parsed === null) return;
state.buffer = state.buffer.subarray(parsed.offset);
if (parsed.push) {
if (typeof state.onPush === "function") state.onPush(parsed.value);
const pending = state.pending[0];
if (pending?.acceptPush === true && Array.isArray(parsed.value) && typeof parsed.value[0] === "string" && parsed.value[0].toUpperCase() === pending.command) {
state.pending.shift();
clearTimeout(pending.timer);
pending.resolve(parsed.value);
}
continue;
}
const pending = state.pending.shift();
if (pending === undefined) throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", "Redis returned an unsolicited ordinary reply");
clearTimeout(pending.timer);
if (parsed.replyError) pending.reject(parsed.value);
else pending.resolve(parsed.value);
}
} catch (error) {
__redisFailSocket(state, socket, error);
}
}
function __redisAttachSocket(state, socket) {
socket.on("data", (chunk) => {
if (state.socket !== socket) return;
state.buffer = state.buffer.length === 0 ? chunk : Buffer.concat([state.buffer, chunk]);
if (state.buffer.length > __REDIS_MAX_REPLY_BYTES) {
__redisFailSocket(state, socket, __redisError("SERVER_STORAGE_REDIS_PROTOCOL", "Redis reply buffering exceeds 16 MiB"));
return;
}
__redisDrain(state, socket);
});
socket.on("error", (error) => __redisFailSocket(state, socket, error));
socket.on("end", () => __redisFailSocket(state, socket, __redisUnavailable("the RESP connection ended")));
socket.on("close", () => __redisFailSocket(state, socket, __redisUnavailable("the RESP connection closed")));
}
function __redisSendConnected(state, command, arguments_, acceptPush = false) {
__redisAssertCommand(command);
const socket = state.socket;
if (socket === null || socket.destroyed) return Promise.reject(__redisUnavailable("no RESP connection is established"));
return new Promise((resolve, reject) => {
const pending = { resolve, reject, timer: null, acceptPush, command };
pending.timer = setTimeout(
() => __redisFailSocket(state, socket, __redisUnavailable(`command ${command} timed out`)),
__REDIS_COMMAND_TIMEOUT_MS,
);
state.pending.push(pending);
try { socket.write(__redisRequest(command, arguments_)); }
catch (error) { __redisFailSocket(state, socket, error); }
});
}
async function __redisOpen(state) {
const config = __redisConfiguration();
if (Date.now() < state.retryAt) throw __redisUnavailable("reconnect backoff is active");
let socket;
const connected = new Promise((resolve, reject) => {
state.openReject = reject;
if (config.tls) {
socket = __createTlsConnection({
host: config.host,
port: config.port,
...( __netIsIp(config.host) === 0 ? { servername: config.host } : {}),
});
} else {
socket = __createNetConnection({ host: config.host, port: config.port });
}
state.socket = socket;
__redisAttachSocket(state, socket);
socket.once(config.tls ? "secureConnect" : "connect", resolve);
});
try {
await connected;
state.openReject = null;
if (config.password.length > 0) {
const auth = config.username.length > 0
? await __redisSendConnected(state, "AUTH", [config.username, config.password])
: await __redisSendConnected(state, "AUTH", [config.password]);
if (auth !== "OK") throw __redisError("SERVER_STORAGE_REDIS_AUTH_FAILED", "Redis AUTH did not return OK");
}
await __redisSendConnected(state, "HELLO", [3]);
const pong = await __redisSendConnected(state, "PING", []);
if (pong !== "PONG") throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", "Redis PING did not return PONG");
state.ready = true;
state.backoffMs = 25;
state.retryAt = 0;
if (typeof state.onReady === "function") {
await state.onReady((command, arguments_) => __redisSendConnected(state, command, arguments_, true));
}
} catch (error) {
__redisFailSocket(state, socket, error);
throw error;
}
}
async function __redisEnsureOpen(state) {
if (state.ready) return;
if (state.opening === null) {
const opening = __redisOpen(state);
state.opening = opening;
opening.finally(() => { if (state.opening === opening) state.opening = null; }).catch(() => {});
}
await state.opening;
}
function __redisConnection(role, options = {}) {
if (typeof role !== "string" || role.length === 0) throw new TypeError("Redis connection role must be a non-empty string");
let state = __redisConnections.get(role);
if (state === undefined) {
state = {
role,
socket: null,
ready: false,
opening: null,
openReject: null,
pending: [],
buffer: Buffer.alloc(0),
retryAt: 0,
backoffMs: 25,
onPush: options.onPush,
onReady: options.onReady,
shouldReconnect: options.shouldReconnect,
reconnectTimer: null,
};
__redisConnections.set(role, state);
}
return Object.freeze({
async command(command, ...arguments_) {
__redisAssertCommand(command);
await __redisEnsureOpen(state);
return __redisSendConnected(state, command, arguments_);
},
async subscription(command, ...arguments_) {
if (command !== "SUBSCRIBE" && command !== "UNSUBSCRIBE") throw __redisError("SERVER_STORAGE_REDIS_COMMAND_UNSUPPORTED", "the RESP subscription role accepts only SUBSCRIBE and UNSUBSCRIBE");
await __redisEnsureOpen(state);
return __redisSendConnected(state, command, arguments_, true);
},
});
}
const __redisOrdinaryConnection = __redisConnection("commands");
function __redisCommand(command, ...arguments_) {
return __redisOrdinaryConnection.command(command, ...arguments_);
}
let __redisPubSubState;
function __redisPubSub() {
if (__redisPubSubState !== undefined) return __redisPubSubState;
const listeners = new Map();
const connection = __redisConnection("pubsub", {
onPush(value) {
if (!Array.isArray(value) || value[0] !== "message" || typeof value[1] !== "string" || typeof value[2] !== "string") return;
for (const receive of [...(listeners.get(value[1]) ?? [])]) receive(value[2]);
},
shouldReconnect: () => listeners.size > 0,
async onReady(sendSubscription) {
for (const channel of listeners.keys()) {
const acknowledged = await sendSubscription("SUBSCRIBE", [channel]);
if (acknowledged[0] !== "subscribe" || acknowledged[1] !== channel) throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", "Redis SUBSCRIBE acknowledgement drifted during reconnect");
}
},
});
__redisPubSubState = Object.freeze({ listeners, connection });
return __redisPubSubState;
}
export async function __noxidRedisPubSubPublish(channel, encoded) {
__assertName(channel, "pub/sub channel");
if (typeof encoded !== "string") throw __redisError("SERVER_STORAGE_REDIS_COMMAND_INVALID", "Redis pub/sub events must be encoded strings");
const delivered = await __redisCommand("PUBLISH", channel, encoded);
if (!Number.isSafeInteger(delivered) || delivered < 0) throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", "Redis PUBLISH returned an invalid subscriber count");
return delivered;
}
export async function __noxidRedisPubSubSubscribe(channel, receive) {
__assertName(channel, "pub/sub channel");
if (typeof receive !== "function") throw new TypeError("Redis pub/sub delivery must be a function");
const state = __redisPubSub();
let topic = state.listeners.get(channel);
if (topic === undefined) {
const acknowledged = await state.connection.subscription("SUBSCRIBE", channel);
if (acknowledged[0] !== "subscribe" || acknowledged[1] !== channel) throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", "Redis SUBSCRIBE acknowledgement drifted");
state.listeners.set(channel, topic = new Set());
}
topic.add(receive);
let stopped = false;
return async () => {
if (stopped) return;
stopped = true;
topic.delete(receive);
if (topic.size === 0) {
state.listeners.delete(channel);
const acknowledged = await state.connection.subscription("UNSUBSCRIBE", channel);
if (acknowledged[0] !== "unsubscribe" || acknowledged[1] !== channel) throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", "Redis UNSUBSCRIBE acknowledgement drifted");
}
};
}
function __redisHex(value) {
let encoded = "";
for (let index = 0; index < value.length; index += 1) encoded += value.charCodeAt(index).toString(16).padStart(4, "0");
return encoded;
}
function __redisUnhex(value) {
if (value.length % 4 !== 0 || !/^[0-9a-f]*$/.test(value)) {
throw __redisError("SERVER_STORAGE_NAME_DRIFT", "Redis storage contains a malformed escaped name");
}
let decoded = "";
for (let index = 0; index < value.length; index += 4) decoded += String.fromCharCode(Number.parseInt(value.slice(index, index + 4), 16));
return decoded;
}
async function __redisScan(pattern) {
const keys = [];
const cursors = new Set();
let cursor = "0";
do {
if (cursors.has(cursor)) throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", "Redis SCAN repeated a cursor before completion");
cursors.add(cursor);
const reply = await __redisCommand("SCAN", cursor, "MATCH", pattern, "COUNT", 1000);
if (!Array.isArray(reply) || reply.length !== 2 || typeof reply[0] !== "string" || !Array.isArray(reply[1]) || !reply[1].every((key) => typeof key === "string")) {
throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", "Redis SCAN returned an invalid cursor/key reply");
}
cursor = reply[0];
keys.push(...reply[1]);
} while (cursor !== "0");
return keys;
}
function __redisStoredJson(raw, label) {
let value;
try { value = JSON.parse(raw); }
catch { throw __redisError("SERVER_STORAGE_REDIS_RECORD_INVALID", `Redis storage ${label} is not valid JSON`); }
return __cloneJson(value);
}
function __redisStorageKey(namespace, key) {
return `${__REDIS_STORAGE_PREFIX}${__redisHex(namespace)}:${__redisHex(key)}`;
}
export async function __noxidEndpointRateLimit(key, requests, windowMs) {
__assertName(key, "key");
if (!Number.isSafeInteger(requests) || requests <= 0 || !Number.isSafeInteger(windowMs) || windowMs <= 0) {
throw Object.assign(new TypeError("compiler-owned endpoint rate policy is invalid"), { code: "ENDPOINT_RATE_POLICY_INVALID" });
}
const redisKey = `${__REDIS_RATE_PREFIX}${__redisHex(key)}`;
try {
const count = await __redisCommand("INCR", redisKey);
if (!Number.isSafeInteger(count) || count <= 0) throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", "Redis INCR returned an invalid endpoint rate count");
let ttl;
if (count === 1) {
await __redisCommand("EXPIRE", redisKey, Math.max(1, Math.ceil(windowMs / 1000)));
ttl = Math.max(1, Math.ceil(windowMs / 1000));
} else {
ttl = await __redisCommand("TTL", redisKey);
if (!Number.isSafeInteger(ttl)) throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", "Redis TTL returned an invalid endpoint rate lifetime");
if (ttl < 0) {
ttl = Math.max(1, Math.ceil(windowMs / 1000));
await __redisCommand("EXPIRE", redisKey, ttl);
}
}
return count > requests ? Math.max(1, ttl) : null;
} catch (error) {
if (__redisIsUnavailable(error)) return 1;
throw error;
}
}
function __redisIdempotencyKey(key) {
return `${__REDIS_IDEMPOTENCY_PREFIX}${__redisHex(key)}`;
}
function __redisIdempotencyEnvelope(raw) {
const envelope = __redisStoredJson(raw, "idempotency record");
if (envelope === null || typeof envelope !== "object" || Array.isArray(envelope) || typeof envelope.kind !== "string") {
throw __redisError("SERVER_STORAGE_REDIS_RECORD_INVALID", "Redis idempotency record has an unsupported envelope");
}
return envelope;
}
export async function __noxidEndpointIdempotencyPrepare(key, claim, leaseMs) {
__assertName(key, "key");
__assertName(claim, "idempotency claim");
if (!Number.isSafeInteger(leaseMs) || leaseMs <= 0) throw new TypeError("compiler-owned idempotency lease must be a positive safe integer");
const redisKey = __redisIdempotencyKey(key);
const existing = await __redisCommand("GET", redisKey);
if (existing !== null) {
const envelope = __redisIdempotencyEnvelope(existing);
if (envelope.kind === __REDIS_IDEMPOTENCY_STORED && Object.hasOwn(envelope, "value")) return Object.freeze({ state: "stored", value: __cloneJson(envelope.value) });
if (envelope.kind === __REDIS_IDEMPOTENCY_CLAIM && typeof envelope.claim === "string") {
if (envelope.claim !== claim) return Object.freeze({ state: "pending" });
const renewed = await __redisCommand("EVAL", "local v=redis.call('GET',KEYS[1]); if not v then return 0 end; local o=cjson.decode(v); if o.kind=='claim' and o.claim==ARGV[1] then return redis.call('PEXPIRE',KEYS[1],ARGV[2]) end; return 0", 1, redisKey, claim, leaseMs);
return Object.freeze({ state: renewed === 1 ? "owner" : "pending" });
}
throw __redisError("SERVER_STORAGE_REDIS_RECORD_INVALID", "Redis idempotency record has an unsupported envelope");
}
const marker = __serializeJson({ kind: __REDIS_IDEMPOTENCY_CLAIM, claim });
const acquired = await __redisCommand("SET", redisKey, marker, "PX", leaseMs, "NX");
if (acquired === "OK") return Object.freeze({ state: "owner" });
if (acquired !== null) throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", "Redis SET NX returned an invalid idempotency reply");
const raced = await __redisCommand("GET", redisKey);
if (raced === null) return Object.freeze({ state: "pending" });
const envelope = __redisIdempotencyEnvelope(raced);
if (envelope.kind === __REDIS_IDEMPOTENCY_STORED && Object.hasOwn(envelope, "value")) return Object.freeze({ state: "stored", value: __cloneJson(envelope.value) });
if (envelope.kind === __REDIS_IDEMPOTENCY_CLAIM && typeof envelope.claim === "string") return Object.freeze({ state: envelope.claim === claim ? "owner" : "pending" });
throw __redisError("SERVER_STORAGE_REDIS_RECORD_INVALID", "Redis idempotency record has an unsupported envelope");
}
export async function __noxidEndpointIdempotencyComplete(key, claim, value, ttlSeconds) {
__assertName(key, "key");
__assertName(claim, "idempotency claim");
const copied = __cloneJson(value);
const expiresAt = __expiresAt({ ttl: ttlSeconds });
const redisKey = __redisIdempotencyKey(key);
const existing = await __redisCommand("GET", redisKey);
const envelope = existing === null ? null : __redisIdempotencyEnvelope(existing);
if (envelope?.kind !== __REDIS_IDEMPOTENCY_CLAIM || envelope.claim !== claim) {
throw Object.assign(new Error("idempotency claim ownership was lost before completion"), { code: "ENDPOINT_IDEMPOTENCY_CLAIM_LOST" });
}
const ttlMs = Math.max(1, Math.ceil(expiresAt - Date.now()));
const stored = await __redisCommand("SET", redisKey, __serializeJson({ kind: __REDIS_IDEMPOTENCY_STORED, value: copied }), "PX", ttlMs);
if (stored !== "OK") throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", "Redis SET did not complete the idempotency snapshot");
}
export async function __noxidEndpointIdempotencyRelease(key, claim) {
__assertName(key, "key");
__assertName(claim, "idempotency claim");
const redisKey = __redisIdempotencyKey(key);
await __redisCommand("EVAL", "local v=redis.call('GET',KEYS[1]); if not v then return 0 end; local o=cjson.decode(v); if o.kind=='claim' and o.claim==ARGV[1] then return redis.call('DEL',KEYS[1]) end; return 0", 1, redisKey, claim);
}
export function storage(namespace) {
__assertName(namespace, "namespace");
const namespacePrefix = `${__REDIS_STORAGE_PREFIX}${__redisHex(namespace)}:`;
return Object.freeze({
async get(key) {
__assertName(key, "key");
try {
const raw = await __redisCommand("GET", __redisStorageKey(namespace, key));
return raw === null ? null : __redisStoredJson(raw, `record for namespace ${JSON.stringify(namespace)}, key ${JSON.stringify(key)}`);
} catch (error) {
if (__redisIsUnavailable(error)) return null;
throw error;
}
},
async set(key, value, options) {
__assertName(key, "key");
const serialized = __serializeJson(value);
const expiresAt = __expiresAt(options);
const redisKey = __redisStorageKey(namespace, key);
if (expiresAt !== null && expiresAt <= Date.now()) {
await __redisCommand("DEL", redisKey);
return;
}
const result = expiresAt === null
? await __redisCommand("SET", redisKey, serialized)
: await __redisCommand("SET", redisKey, serialized, "PX", Math.max(1, Math.ceil(expiresAt - Date.now())));
if (result !== "OK") throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", "Redis SET did not return OK");
},
async compareAndSet(key, expected, value, options) {
__assertName(key, "key");
__assertExpected(expected);
const serialized = __serializeJson(value);
const guard = __serializeJson(expected);
const expiresAt = __expiresAt(options);
const lifetimeMs = expiresAt === null ? 0 : Math.max(1, Math.ceil(expiresAt - Date.now()));
if (expiresAt !== null && expiresAt <= Date.now()) return false;
const swapped = await __redisCommand(
"EVAL",
"local v=redis.call('GET',KEYS[1]); if not v then return 0 end; local c=cjson.decode(v); local e=cjson.decode(ARGV[2]); if type(c)~='table' then return 0 end; for k,x in pairs(e) do if c[k]~=x then return 0 end end; local px=tonumber(ARGV[3]); if px>0 then redis.call('SET',KEYS[1],ARGV[1],'PX',px) else redis.call('SET',KEYS[1],ARGV[1]) end; return 1",
1,
__redisStorageKey(namespace, key),
serialized,
guard,
lifetimeMs,
);
if (swapped !== 0 && swapped !== 1) throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", "Redis compare-and-swap returned an invalid reply");
return swapped === 1;
},
async delete(key) {
__assertName(key, "key");
const deleted = await __redisCommand("DEL", __redisStorageKey(namespace, key));
if (!Number.isSafeInteger(deleted) || deleted < 0) throw __redisError("SERVER_STORAGE_REDIS_PROTOCOL", "Redis DEL returned an invalid count");
return deleted > 0;
},
async list(prefix = "") {
__assertName(prefix, "list prefix", true);
const physical = await __redisScan(`${namespacePrefix}*`);
const keys = physical.map((key) => {
if (!key.startsWith(namespacePrefix)) throw __redisError("SERVER_STORAGE_NAME_DRIFT", "Redis SCAN returned a key outside the requested namespace");
return __redisUnhex(key.slice(namespacePrefix.length));
}).filter((key) => key.startsWith(prefix));
return Object.freeze([...new Set(keys)].sort());
},
});
}