Dynamic IO proxy: derive proxied primitives from component io_refs

Replace hardcoded IO primitive lists on both client and server with
data-driven registration. Page registry entries carry :io-deps (list
of IO primitive names) instead of :has-io boolean. Client registers
proxied IO on demand per page via registerIoDeps(). Server builds
allowlist from component analysis.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-07 09:13:53 +00:00
parent c2a85ed026
commit cb0990feb3
7 changed files with 61 additions and 38 deletions

View File

@@ -14,7 +14,7 @@
// =========================================================================
var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
var SX_VERSION = "2026-03-07T02:32:34Z";
var SX_VERSION = "2026-03-07T09:03:03Z";
function isNil(x) { return x === NIL || x === null || x === undefined; }
function isSxTruthy(x) { return x !== false && !isNil(x); }
@@ -2031,7 +2031,11 @@ return domAppendToHead(link); }, domQueryAll(container, "link[rel=\"stylesheet\"
return (isSxTruthy(sxOr(isNil(contentSrc), isEmpty(contentSrc))) ? (logWarn((String("sx:route no content for ") + String(pathname))), false) : (function() {
var target = resolveRouteTarget(targetSel);
return (isSxTruthy(isNil(target)) ? (logWarn((String("sx:route target not found: ") + String(targetSel))), false) : (isSxTruthy(!isSxTruthy(depsSatisfied_p(match))) ? (logInfo((String("sx:route deps miss for ") + String(pageName))), false) : (function() {
var hasIo = get(match, "has-io");
var ioDeps = get(match, "io-deps");
var hasIo = (isSxTruthy(ioDeps) && !isSxTruthy(isEmpty(ioDeps)));
if (isSxTruthy(hasIo)) {
registerIoDeps(ioDeps);
}
return (isSxTruthy(get(match, "has-data")) ? (function() {
var cacheKey = pageDataCacheKey(pageName, params);
var cached = pageDataCacheGet(cacheKey);
@@ -2476,7 +2480,7 @@ callExpr.push(dictGet(kwargs, k)); } }
})(); };
// boot-init
var bootInit = function() { return (logInfo((String("sx-browser ") + String(SX_VERSION))), initCssTracking(), initStyleDict(), initIoPrimitives(), processPageScripts(), processSxScripts(NIL), sxHydrateElements(NIL), processElements(NIL)); };
var bootInit = function() { return (logInfo((String("sx-browser ") + String(SX_VERSION))), initCssTracking(), initStyleDict(), processPageScripts(), processSxScripts(NIL), sxHydrateElements(NIL), processElements(NIL)); };
// === Transpiled from router (client-side route matching) ===
@@ -4359,16 +4363,20 @@ callExpr.push(dictGet(kwargs, k)); } }
});
}
// Register default proxied IO primitives
function initIoPrimitives() {
var defaults = [
"highlight", "current-user", "request-arg", "request-path",
"app-url", "asset-url", "config"
];
for (var i = 0; i < defaults.length; i++) {
registerProxiedIo(defaults[i]);
// Register IO deps as proxied primitives (idempotent, called per-page)
function registerIoDeps(names) {
if (!names || !names.length) return;
var registered = 0;
for (var i = 0; i < names.length; i++) {
var name = names[i];
if (!IO_PRIMITIVES[name]) {
registerProxiedIo(name);
registered++;
}
}
if (registered > 0) {
logInfo("sx:io registered " + registered + " proxied primitives: " + names.join(", "));
}
logInfo("sx:io registered " + defaults.length + " proxied primitives");
}
@@ -4445,6 +4453,7 @@ callExpr.push(dictGet(kwargs, k)); } }
matchRoute: matchRoute,
findMatchingRoute: findMatchingRoute,
registerIo: typeof registerIoPrimitive === "function" ? registerIoPrimitive : null,
registerIoDeps: typeof registerIoDeps === "function" ? registerIoDeps : null,
asyncRender: typeof asyncSxRenderWithEnv === "function" ? asyncSxRenderWithEnv : null,
asyncRenderToDom: typeof asyncRenderToDom === "function" ? asyncRenderToDom : null,
_version: "ref-2.0 (boot+cssx+dom+engine+html+orchestration+parser+sx, bootstrap-compiled)"