IO proxy: POST for long payloads, network error resilience
- Switch to POST with JSON body when query string exceeds 1500 chars (highlight calls with large component sources hit URL length limits) - Include CSRF token header on POST requests - Add .catch() on fetch to gracefully handle network errors (return NIL) - Upgrade async eval miss logs from logInfo to logWarn for visibility Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -14,7 +14,7 @@
|
||||
// =========================================================================
|
||||
|
||||
var NIL = Object.freeze({ _nil: true, toString: function() { return "nil"; } });
|
||||
var SX_VERSION = "2026-03-07T09:03:03Z";
|
||||
var SX_VERSION = "2026-03-07T09:23:03Z";
|
||||
|
||||
function isNil(x) { return x === NIL || x === null || x === undefined; }
|
||||
function isSxTruthy(x) { return x !== false && !isNil(x); }
|
||||
@@ -3336,7 +3336,7 @@ callExpr.push(dictGet(kwargs, k)); } }
|
||||
result.then(function(rendered) {
|
||||
callback(rendered);
|
||||
}).catch(function(e) {
|
||||
logInfo("sx:async eval miss: " + (e && e.message ? e.message : e));
|
||||
logWarn("sx:async eval miss: " + (e && e.message ? e.message : e));
|
||||
callback(null);
|
||||
});
|
||||
} else {
|
||||
@@ -4329,6 +4329,7 @@ callExpr.push(dictGet(kwargs, k)); } }
|
||||
}
|
||||
|
||||
// Register a server-proxied IO primitive: fetches from /sx/io/<name>
|
||||
// Uses GET for short args, POST for long payloads (URL length safety).
|
||||
function registerProxiedIo(name) {
|
||||
registerIoPrimitive(name, function(args, kwargs) {
|
||||
var url = "/sx/io/" + encodeURIComponent(name);
|
||||
@@ -4341,8 +4342,29 @@ callExpr.push(dictGet(kwargs, k)); } }
|
||||
qs.push(encodeURIComponent(k) + "=" + encodeURIComponent(String(kwargs[k])));
|
||||
}
|
||||
}
|
||||
if (qs.length) url += "?" + qs.join("&");
|
||||
return fetch(url, { headers: { "SX-Request": "true" } })
|
||||
var queryStr = qs.join("&");
|
||||
var fetchOpts;
|
||||
if (queryStr.length > 1500) {
|
||||
// POST with JSON body for long payloads
|
||||
var sArgs = [];
|
||||
for (var j = 0; j < args.length; j++) sArgs.push(String(args[j]));
|
||||
var sKwargs = {};
|
||||
for (var kk in kwargs) {
|
||||
if (kwargs.hasOwnProperty(kk)) sKwargs[kk] = String(kwargs[kk]);
|
||||
}
|
||||
var postHeaders = { "SX-Request": "true", "Content-Type": "application/json" };
|
||||
var csrf = csrfToken();
|
||||
if (csrf && csrf !== NIL) postHeaders["X-CSRFToken"] = csrf;
|
||||
fetchOpts = {
|
||||
method: "POST",
|
||||
headers: postHeaders,
|
||||
body: JSON.stringify({ args: sArgs, kwargs: sKwargs })
|
||||
};
|
||||
} else {
|
||||
if (queryStr) url += "?" + queryStr;
|
||||
fetchOpts = { headers: { "SX-Request": "true" } };
|
||||
}
|
||||
return fetch(url, fetchOpts)
|
||||
.then(function(resp) {
|
||||
if (!resp.ok) {
|
||||
logWarn("sx:io " + name + " failed " + resp.status);
|
||||
@@ -4359,6 +4381,10 @@ callExpr.push(dictGet(kwargs, k)); } }
|
||||
logWarn("sx:io " + name + " parse error: " + (e && e.message ? e.message : e));
|
||||
return NIL;
|
||||
}
|
||||
})
|
||||
.catch(function(e) {
|
||||
logWarn("sx:io " + name + " network error: " + (e && e.message ? e.message : e));
|
||||
return NIL;
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1699,6 +1699,7 @@ ASYNC_IO_JS = '''
|
||||
}
|
||||
|
||||
// Register a server-proxied IO primitive: fetches from /sx/io/<name>
|
||||
// Uses GET for short args, POST for long payloads (URL length safety).
|
||||
function registerProxiedIo(name) {
|
||||
registerIoPrimitive(name, function(args, kwargs) {
|
||||
var url = "/sx/io/" + encodeURIComponent(name);
|
||||
@@ -1711,8 +1712,29 @@ ASYNC_IO_JS = '''
|
||||
qs.push(encodeURIComponent(k) + "=" + encodeURIComponent(String(kwargs[k])));
|
||||
}
|
||||
}
|
||||
if (qs.length) url += "?" + qs.join("&");
|
||||
return fetch(url, { headers: { "SX-Request": "true" } })
|
||||
var queryStr = qs.join("&");
|
||||
var fetchOpts;
|
||||
if (queryStr.length > 1500) {
|
||||
// POST with JSON body for long payloads
|
||||
var sArgs = [];
|
||||
for (var j = 0; j < args.length; j++) sArgs.push(String(args[j]));
|
||||
var sKwargs = {};
|
||||
for (var kk in kwargs) {
|
||||
if (kwargs.hasOwnProperty(kk)) sKwargs[kk] = String(kwargs[kk]);
|
||||
}
|
||||
var postHeaders = { "SX-Request": "true", "Content-Type": "application/json" };
|
||||
var csrf = csrfToken();
|
||||
if (csrf && csrf !== NIL) postHeaders["X-CSRFToken"] = csrf;
|
||||
fetchOpts = {
|
||||
method: "POST",
|
||||
headers: postHeaders,
|
||||
body: JSON.stringify({ args: sArgs, kwargs: sKwargs })
|
||||
};
|
||||
} else {
|
||||
if (queryStr) url += "?" + queryStr;
|
||||
fetchOpts = { headers: { "SX-Request": "true" } };
|
||||
}
|
||||
return fetch(url, fetchOpts)
|
||||
.then(function(resp) {
|
||||
if (!resp.ok) {
|
||||
logWarn("sx:io " + name + " failed " + resp.status);
|
||||
@@ -1729,6 +1751,10 @@ ASYNC_IO_JS = '''
|
||||
logWarn("sx:io " + name + " parse error: " + (e && e.message ? e.message : e));
|
||||
return NIL;
|
||||
}
|
||||
})
|
||||
.catch(function(e) {
|
||||
logWarn("sx:io " + name + " network error: " + (e && e.message ? e.message : e));
|
||||
return NIL;
|
||||
});
|
||||
});
|
||||
}
|
||||
@@ -3406,7 +3432,7 @@ PLATFORM_ORCHESTRATION_JS = """
|
||||
result.then(function(rendered) {
|
||||
callback(rendered);
|
||||
}).catch(function(e) {
|
||||
logInfo("sx:async eval miss: " + (e && e.message ? e.message : e));
|
||||
logWarn("sx:async eval miss: " + (e && e.message ? e.message : e));
|
||||
callback(null);
|
||||
});
|
||||
} else {
|
||||
|
||||
Reference in New Issue
Block a user