IO proxy: client-side cache with 5min TTL, server Cache-Control

Client caches IO results by (name + args) in memory. In-flight
promises are cached too (dedup concurrent calls for same args).
Server adds Cache-Control: public, max-age=300 for HTTP caching.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-07 09:51:51 +00:00
parent 0d6b959045
commit 84ea5d4c16
3 changed files with 44 additions and 5 deletions

View File

@@ -14,7 +14,7 @@
// =========================================================================
var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
var SX_VERSION = "2026-03-07T09:40:17Z";
var SX_VERSION = "2026-03-07T09:51:42Z";
function isNil(x) { return x === NIL || x === null || x === undefined; }
function isSxTruthy(x) { return x !== false && !isNil(x); }
@@ -4328,10 +4328,24 @@ callExpr.push(dictGet(kwargs, k)); } }
return asyncRenderChildren(exprs, env, null);
}
// IO proxy cache: key → { value, expires }
var _ioCache = {};
var IO_CACHE_TTL = 300000; // 5 minutes
// Register a server-proxied IO primitive: fetches from /sx/io/<name>
// Uses GET for short args, POST for long payloads (URL length safety).
// Results are cached client-side by (name + args) with a TTL.
function registerProxiedIo(name) {
registerIoPrimitive(name, function(args, kwargs) {
// Cache key: name + serialized args
var cacheKey = name;
for (var ci = 0; ci < args.length; ci++) cacheKey += "" + String(args[ci]);
for (var ck in kwargs) {
if (kwargs.hasOwnProperty(ck)) cacheKey += "" + ck + "=" + String(kwargs[ck]);
}
var cached = _ioCache[cacheKey];
if (cached && cached.expires > Date.now()) return cached.value;
var url = "/sx/io/" + encodeURIComponent(name);
var qs = [];
for (var i = 0; i < args.length; i++) {
@@ -4364,7 +4378,7 @@ callExpr.push(dictGet(kwargs, k)); } }
if (queryStr) url += "?" + queryStr;
fetchOpts = { headers: { "SX-Request": "true" } };
}
return fetch(url, fetchOpts)
var result = fetch(url, fetchOpts)
.then(function(resp) {
if (!resp.ok) {
logWarn("sx:io " + name + " failed " + resp.status);
@@ -4376,7 +4390,9 @@ callExpr.push(dictGet(kwargs, k)); } }
if (!text || text === "nil") return NIL;
try {
var exprs = parse(text);
return exprs.length === 1 ? exprs[0] : exprs;
var val = exprs.length === 1 ? exprs[0] : exprs;
_ioCache[cacheKey] = { value: val, expires: Date.now() + IO_CACHE_TTL };
return val;
} catch (e) {
logWarn("sx:io " + name + " parse error: " + (e && e.message ? e.message : e));
return NIL;
@@ -4386,6 +4402,9 @@ callExpr.push(dictGet(kwargs, k)); } }
logWarn("sx:io " + name + " network error: " + (e && e.message ? e.message : e));
return NIL;
});
// Cache the in-flight promise too (dedup concurrent calls for same args)
_ioCache[cacheKey] = { value: result, expires: Date.now() + IO_CACHE_TTL };
return result;
});
}