IO suspension driver: _driveAsync in platform, VmSuspended in value_to_js

- sx-platform.js: add _driveAsync to platform (was sandbox-only) for
  driving wait/fetch IO suspension chains in live site
- sx-platform.js: host-callback wrapper calls _driveAsync on callFn result
- sx_browser.ml: value_to_js callable wrapper catches VmSuspended, builds
  suspension object, and calls _driveAsync directly

Toggle and count clicks work fully. Bounce adds class but wait/remove
requires IO suspension in CEK context (eval-expr-cek doesn't support
perform — needs VM-path evaluation in hs-handler).

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-04-09 21:22:25 +00:00
parent de9ab4ca07
commit 0365ecb2b9
5 changed files with 111 additions and 11 deletions

View File

@@ -84,6 +84,31 @@
}
});
// IO suspension driver — resumes suspended callFn results (wait, fetch, etc.)
if (!window._driveAsync) {
window._driveAsync = function driveAsync(result) {
if (!result || !result.suspended) return;
var req = result.request;
var items = req && (req.items || req);
var op = items && items[0];
var opName = typeof op === "string" ? op : (op && op.name) || String(op);
var arg = items && items[1];
if (opName === "io-sleep" || opName === "wait") {
setTimeout(function() {
try { driveAsync(result.resume(null)); } catch(e) { console.error("[sx] driveAsync:", e.message); }
}, typeof arg === "number" ? arg : 0);
} else if (opName === "io-fetch") {
fetch(typeof arg === "string" ? arg : "").then(function(r) { return r.text(); }).then(function(t) {
try { driveAsync(result.resume({ok: true, text: t})); } catch(e) { console.error("[sx] driveAsync:", e.message); }
});
} else if (opName === "io-navigate") {
// navigation — don't resume
} else {
console.warn("[sx] unhandled IO:", opName);
}
};
}
K.registerNative("host-callback", function(args) {
var fn = args[0];
// Native JS function — pass through
@@ -92,7 +117,9 @@
if (fn && fn.__sx_handle !== undefined) {
return function() {
var a = Array.prototype.slice.call(arguments);
return K.callFn(fn, a);
var r = K.callFn(fn, a);
if (window._driveAsync) window._driveAsync(r);
return r;
};
}
return function() {};