Bootstrap stores + event bridge, add island hydration to boot.sx
- signals.sx: fix has? → has-key?, add def-store/use-store/clear-stores (L3 named stores), emit-event/on-event/bridge-event (event bridge) - boot.sx: add sx-hydrate-islands, hydrate-island, dispose-island for client-side island hydration from SSR output - bootstrap_js.py: add RENAMES, platform fns (domListen, eventDetail, domGetData, jsonParse), public API exports for all new functions - bootstrap_py.py: add RENAMES, server-side no-op stubs for DOM events - Regenerate sx-ref.js (with boot adapter) and sx_ref.py - Update reactive-islands status: hydration, stores, bridge all spec'd 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-08T10:13:40Z";
|
||||
var SX_VERSION = "2026-03-08T11:12:31Z";
|
||||
|
||||
function isNil(x) { return x === NIL || x === null || x === undefined; }
|
||||
function isSxTruthy(x) { return x !== false && !isNil(x); }
|
||||
@@ -572,6 +572,92 @@
|
||||
return makeThunk(componentBody(comp), local);
|
||||
};
|
||||
|
||||
// =========================================================================
|
||||
// Platform: deps module — component dependency analysis
|
||||
// =========================================================================
|
||||
|
||||
function componentDeps(c) {
|
||||
return c.deps ? c.deps.slice() : [];
|
||||
}
|
||||
|
||||
function componentSetDeps(c, deps) {
|
||||
c.deps = deps;
|
||||
}
|
||||
|
||||
function componentCssClasses(c) {
|
||||
return c.cssClasses ? c.cssClasses.slice() : [];
|
||||
}
|
||||
|
||||
function envComponents(env) {
|
||||
var names = [];
|
||||
for (var k in env) {
|
||||
var v = env[k];
|
||||
if (v && (v._component || v._macro)) names.push(k);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
function regexFindAll(pattern, source) {
|
||||
var re = new RegExp(pattern, "g");
|
||||
var results = [];
|
||||
var m;
|
||||
while ((m = re.exec(source)) !== null) {
|
||||
if (m[1] !== undefined) results.push(m[1]);
|
||||
else results.push(m[0]);
|
||||
}
|
||||
return results;
|
||||
}
|
||||
|
||||
function scanCssClasses(source) {
|
||||
var classes = {};
|
||||
var result = [];
|
||||
var m;
|
||||
var re1 = /:class\s+"([^"]*)"/g;
|
||||
while ((m = re1.exec(source)) !== null) {
|
||||
var parts = m[1].split(/\s+/);
|
||||
for (var i = 0; i < parts.length; i++) {
|
||||
if (parts[i] && !classes[parts[i]]) {
|
||||
classes[parts[i]] = true;
|
||||
result.push(parts[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
var re2 = /:class\s+\(str\s+((?:"[^"]*"\s*)+)\)/g;
|
||||
while ((m = re2.exec(source)) !== null) {
|
||||
var re3 = /"([^"]*)"/g;
|
||||
var m2;
|
||||
while ((m2 = re3.exec(m[1])) !== null) {
|
||||
var parts2 = m2[1].split(/\s+/);
|
||||
for (var j = 0; j < parts2.length; j++) {
|
||||
if (parts2[j] && !classes[parts2[j]]) {
|
||||
classes[parts2[j]] = true;
|
||||
result.push(parts2[j]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
var re4 = /;;\s*@css\s+(.+)/g;
|
||||
while ((m = re4.exec(source)) !== null) {
|
||||
var parts3 = m[1].split(/\s+/);
|
||||
for (var k = 0; k < parts3.length; k++) {
|
||||
if (parts3[k] && !classes[parts3[k]]) {
|
||||
classes[parts3[k]] = true;
|
||||
result.push(parts3[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
function componentIoRefs(c) {
|
||||
return c.ioRefs ? c.ioRefs.slice() : [];
|
||||
}
|
||||
|
||||
function componentSetIoRefs(c, refs) {
|
||||
c.ioRefs = refs;
|
||||
}
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// Platform interface — Parser
|
||||
// =========================================================================
|
||||
@@ -2179,6 +2265,93 @@ return (function() {
|
||||
})() : NIL);
|
||||
})(); };
|
||||
|
||||
// _optimistic-snapshots
|
||||
var _optimisticSnapshots = {};
|
||||
|
||||
// optimistic-cache-update
|
||||
var optimisticCacheUpdate = function(cacheKey, mutator) { return (function() {
|
||||
var cached = pageDataCacheGet(cacheKey);
|
||||
return (isSxTruthy(cached) ? (function() {
|
||||
var predicted = mutator(cached);
|
||||
_optimisticSnapshots[cacheKey] = cached;
|
||||
pageDataCacheSet(cacheKey, predicted);
|
||||
return predicted;
|
||||
})() : NIL);
|
||||
})(); };
|
||||
|
||||
// optimistic-cache-revert
|
||||
var optimisticCacheRevert = function(cacheKey) { return (function() {
|
||||
var snapshot = get(_optimisticSnapshots, cacheKey);
|
||||
return (isSxTruthy(snapshot) ? (pageDataCacheSet(cacheKey, snapshot), dictDelete(_optimisticSnapshots, cacheKey), snapshot) : NIL);
|
||||
})(); };
|
||||
|
||||
// optimistic-cache-confirm
|
||||
var optimisticCacheConfirm = function(cacheKey) { return dictDelete(_optimisticSnapshots, cacheKey); };
|
||||
|
||||
// submit-mutation
|
||||
var submitMutation = function(pageName, params, actionName, payload, mutatorFn, onComplete) { return (function() {
|
||||
var cacheKey = pageDataCacheKey(pageName, params);
|
||||
var predicted = optimisticCacheUpdate(cacheKey, mutatorFn);
|
||||
if (isSxTruthy(predicted)) {
|
||||
tryRerenderPage(pageName, params, predicted);
|
||||
}
|
||||
return executeAction(actionName, payload, function(result) { if (isSxTruthy(result)) {
|
||||
pageDataCacheSet(cacheKey, result);
|
||||
}
|
||||
optimisticCacheConfirm(cacheKey);
|
||||
if (isSxTruthy(result)) {
|
||||
tryRerenderPage(pageName, params, result);
|
||||
}
|
||||
logInfo((String("sx:optimistic confirmed ") + String(pageName)));
|
||||
return (isSxTruthy(onComplete) ? onComplete("confirmed") : NIL); }, function(error) { return (function() {
|
||||
var reverted = optimisticCacheRevert(cacheKey);
|
||||
if (isSxTruthy(reverted)) {
|
||||
tryRerenderPage(pageName, params, reverted);
|
||||
}
|
||||
logWarn((String("sx:optimistic reverted ") + String(pageName) + String(": ") + String(error)));
|
||||
return (isSxTruthy(onComplete) ? onComplete("reverted") : NIL);
|
||||
})(); });
|
||||
})(); };
|
||||
|
||||
// _is-online
|
||||
var _isOnline = true;
|
||||
|
||||
// _offline-queue
|
||||
var _offlineQueue = [];
|
||||
|
||||
// offline-is-online?
|
||||
var offlineIsOnline_p = function() { return _isOnline; };
|
||||
|
||||
// offline-set-online!
|
||||
var offlineSetOnline_b = function(val) { return (_isOnline = val); };
|
||||
|
||||
// offline-queue-mutation
|
||||
var offlineQueueMutation = function(actionName, payload, pageName, params, mutatorFn) { return (function() {
|
||||
var cacheKey = pageDataCacheKey(pageName, params);
|
||||
var entry = {["action"]: actionName, ["payload"]: payload, ["page"]: pageName, ["params"]: params, ["timestamp"]: nowMs(), ["status"]: "pending"};
|
||||
_offlineQueue.push(entry);
|
||||
(function() {
|
||||
var predicted = optimisticCacheUpdate(cacheKey, mutatorFn);
|
||||
return (isSxTruthy(predicted) ? tryRerenderPage(pageName, params, predicted) : NIL);
|
||||
})();
|
||||
logInfo((String("sx:offline queued ") + String(actionName) + String(" (") + String(len(_offlineQueue)) + String(" pending)")));
|
||||
return entry;
|
||||
})(); };
|
||||
|
||||
// offline-sync
|
||||
var offlineSync = function() { return (function() {
|
||||
var pending = filter(function(e) { return (get(e, "status") == "pending"); }, _offlineQueue);
|
||||
return (isSxTruthy(!isSxTruthy(isEmpty(pending))) ? (logInfo((String("sx:offline syncing ") + String(len(pending)) + String(" mutations"))), forEach(function(entry) { return executeAction(get(entry, "action"), get(entry, "payload"), function(result) { entry["status"] = "synced";
|
||||
return logInfo((String("sx:offline synced ") + String(get(entry, "action")))); }, function(error) { entry["status"] = "failed";
|
||||
return logWarn((String("sx:offline sync failed ") + String(get(entry, "action")) + String(": ") + String(error))); }); }, pending)) : NIL);
|
||||
})(); };
|
||||
|
||||
// offline-pending-count
|
||||
var offlinePendingCount = function() { return len(filter(function(e) { return (get(e, "status") == "pending"); }, _offlineQueue)); };
|
||||
|
||||
// offline-aware-mutation
|
||||
var offlineAwareMutation = function(pageName, params, actionName, payload, mutatorFn, onComplete) { return (isSxTruthy(_isOnline) ? submitMutation(pageName, params, actionName, payload, mutatorFn, onComplete) : (offlineQueueMutation(actionName, payload, pageName, params, mutatorFn), (isSxTruthy(onComplete) ? onComplete("queued") : NIL))); };
|
||||
|
||||
// current-page-layout
|
||||
var currentPageLayout = function() { return (function() {
|
||||
var pathname = urlPathname(browserLocationHref());
|
||||
@@ -2382,7 +2555,8 @@ return bindInlineHandlers(root); };
|
||||
domAppend(el, node);
|
||||
hoistHeadElementsFull(el);
|
||||
processElements(el);
|
||||
return sxHydrateElements(el);
|
||||
sxHydrateElements(el);
|
||||
return sxHydrateIslands(el);
|
||||
})() : NIL);
|
||||
})(); };
|
||||
|
||||
@@ -2397,6 +2571,7 @@ return (function() {
|
||||
{ var _c = exprs; for (var _i = 0; _i < _c.length; _i++) { var expr = _c[_i]; domAppend(el, renderToDom(expr, env, NIL)); } }
|
||||
processElements(el);
|
||||
sxHydrateElements(el);
|
||||
sxHydrateIslands(el);
|
||||
return domDispatch(el, "sx:resolved", {"id": id});
|
||||
})() : logWarn((String("resolveSuspense: no element for id=") + String(id))));
|
||||
})(); };
|
||||
@@ -2491,8 +2666,186 @@ callExpr.push(dictGet(kwargs, k)); } }
|
||||
return logInfo((String("pages: ") + String(len(_pageRoutes)) + String(" routes loaded")));
|
||||
})(); };
|
||||
|
||||
// sx-hydrate-islands
|
||||
var sxHydrateIslands = function(root) { return (function() {
|
||||
var els = domQueryAll(sxOr(root, domBody()), "[data-sx-island]");
|
||||
return forEach(function(el) { return (isSxTruthy(!isSxTruthy(isProcessed(el, "island-hydrated"))) ? (markProcessed(el, "island-hydrated"), hydrateIsland(el)) : NIL); }, els);
|
||||
})(); };
|
||||
|
||||
// hydrate-island
|
||||
var hydrateIsland = function(el) { return (function() {
|
||||
var name = domGetAttr(el, "data-sx-island");
|
||||
var stateJson = sxOr(domGetAttr(el, "data-sx-state"), "{}");
|
||||
return (function() {
|
||||
var compName = (String("~") + String(name));
|
||||
var env = getRenderEnv(NIL);
|
||||
return (function() {
|
||||
var comp = envGet(env, compName);
|
||||
return (isSxTruthy(!isSxTruthy(sxOr(isComponent(comp), isIsland(comp)))) ? logWarn((String("hydrate-island: unknown island ") + String(compName))) : (function() {
|
||||
var kwargs = jsonParse(stateJson);
|
||||
var disposers = [];
|
||||
var local = envMerge(componentClosure(comp), env);
|
||||
{ var _c = componentParams(comp); for (var _i = 0; _i < _c.length; _i++) { var p = _c[_i]; local[p] = (isSxTruthy(dictHas(kwargs, p)) ? dictGet(kwargs, p) : NIL); } }
|
||||
return (function() {
|
||||
var bodyDom = withIslandScope(function(disposable) { return append_b(disposers, disposable); }, function() { return renderToDom(componentBody(comp), local, NIL); });
|
||||
morphChildren(el, bodyDom);
|
||||
domSetData(el, "sx-disposers", disposers);
|
||||
processElements(el);
|
||||
return logInfo((String("hydrated island: ") + String(compName) + String(" (") + String(len(disposers)) + String(" disposers)")));
|
||||
})();
|
||||
})());
|
||||
})();
|
||||
})();
|
||||
})(); };
|
||||
|
||||
// dispose-island
|
||||
var disposeIsland = function(el) { return (function() {
|
||||
var disposers = domGetData(el, "sx-disposers");
|
||||
return (isSxTruthy(disposers) ? (forEach(function(d) { return (isSxTruthy(isCallable(d)) ? d() : NIL); }, disposers), domSetData(el, "sx-disposers", NIL)) : NIL);
|
||||
})(); };
|
||||
|
||||
// boot-init
|
||||
var bootInit = function() { return (logInfo((String("sx-browser ") + String(SX_VERSION))), initCssTracking(), processPageScripts(), processSxScripts(NIL), sxHydrateElements(NIL), processElements(NIL)); };
|
||||
var bootInit = function() { return (logInfo((String("sx-browser ") + String(SX_VERSION))), initCssTracking(), processPageScripts(), processSxScripts(NIL), sxHydrateElements(NIL), sxHydrateIslands(NIL), processElements(NIL)); };
|
||||
|
||||
|
||||
// === Transpiled from deps (component dependency analysis) ===
|
||||
|
||||
// scan-refs
|
||||
var scanRefs = function(node) { return (function() {
|
||||
var refs = [];
|
||||
scanRefsWalk(node, refs);
|
||||
return refs;
|
||||
})(); };
|
||||
|
||||
// scan-refs-walk
|
||||
var scanRefsWalk = function(node, refs) { return (isSxTruthy((typeOf(node) == "symbol")) ? (function() {
|
||||
var name = symbolName(node);
|
||||
return (isSxTruthy(startsWith(name, "~")) ? (isSxTruthy(!isSxTruthy(contains(refs, name))) ? append_b(refs, name) : NIL) : NIL);
|
||||
})() : (isSxTruthy((typeOf(node) == "list")) ? forEach(function(item) { return scanRefsWalk(item, refs); }, node) : (isSxTruthy((typeOf(node) == "dict")) ? forEach(function(key) { return scanRefsWalk(dictGet(node, key), refs); }, keys(node)) : NIL))); };
|
||||
|
||||
// transitive-deps-walk
|
||||
var transitiveDepsWalk = function(n, seen, env) { return (isSxTruthy(!isSxTruthy(contains(seen, n))) ? (append_b(seen, n), (function() {
|
||||
var val = envGet(env, n);
|
||||
return (isSxTruthy((typeOf(val) == "component")) ? forEach(function(ref) { return transitiveDepsWalk(ref, seen, env); }, scanRefs(componentBody(val))) : (isSxTruthy((typeOf(val) == "macro")) ? forEach(function(ref) { return transitiveDepsWalk(ref, seen, env); }, scanRefs(macroBody(val))) : NIL));
|
||||
})()) : NIL); };
|
||||
|
||||
// transitive-deps
|
||||
var transitiveDeps = function(name, env) { return (function() {
|
||||
var seen = [];
|
||||
var key = (isSxTruthy(startsWith(name, "~")) ? name : (String("~") + String(name)));
|
||||
transitiveDepsWalk(key, seen, env);
|
||||
return filter(function(x) { return !isSxTruthy((x == key)); }, seen);
|
||||
})(); };
|
||||
|
||||
// compute-all-deps
|
||||
var computeAllDeps = function(env) { return forEach(function(name) { return (function() {
|
||||
var val = envGet(env, name);
|
||||
return (isSxTruthy((typeOf(val) == "component")) ? componentSetDeps(val, transitiveDeps(name, env)) : NIL);
|
||||
})(); }, envComponents(env)); };
|
||||
|
||||
// scan-components-from-source
|
||||
var scanComponentsFromSource = function(source) { return (function() {
|
||||
var matches = regexFindAll("\\(~([a-zA-Z_][a-zA-Z0-9_\\-]*)", source);
|
||||
return map(function(m) { return (String("~") + String(m)); }, matches);
|
||||
})(); };
|
||||
|
||||
// components-needed
|
||||
var componentsNeeded = function(pageSource, env) { return (function() {
|
||||
var direct = scanComponentsFromSource(pageSource);
|
||||
var allNeeded = [];
|
||||
{ var _c = direct; for (var _i = 0; _i < _c.length; _i++) { var name = _c[_i]; if (isSxTruthy(!isSxTruthy(contains(allNeeded, name)))) {
|
||||
allNeeded.push(name);
|
||||
}
|
||||
(function() {
|
||||
var val = envGet(env, name);
|
||||
return (function() {
|
||||
var deps = (isSxTruthy((isSxTruthy((typeOf(val) == "component")) && !isSxTruthy(isEmpty(componentDeps(val))))) ? componentDeps(val) : transitiveDeps(name, env));
|
||||
return forEach(function(dep) { return (isSxTruthy(!isSxTruthy(contains(allNeeded, dep))) ? append_b(allNeeded, dep) : NIL); }, deps);
|
||||
})();
|
||||
})(); } }
|
||||
return allNeeded;
|
||||
})(); };
|
||||
|
||||
// page-component-bundle
|
||||
var pageComponentBundle = function(pageSource, env) { return componentsNeeded(pageSource, env); };
|
||||
|
||||
// page-css-classes
|
||||
var pageCssClasses = function(pageSource, env) { return (function() {
|
||||
var needed = componentsNeeded(pageSource, env);
|
||||
var classes = [];
|
||||
{ var _c = needed; for (var _i = 0; _i < _c.length; _i++) { var name = _c[_i]; (function() {
|
||||
var val = envGet(env, name);
|
||||
return (isSxTruthy((typeOf(val) == "component")) ? forEach(function(cls) { return (isSxTruthy(!isSxTruthy(contains(classes, cls))) ? append_b(classes, cls) : NIL); }, componentCssClasses(val)) : NIL);
|
||||
})(); } }
|
||||
{ var _c = scanCssClasses(pageSource); for (var _i = 0; _i < _c.length; _i++) { var cls = _c[_i]; if (isSxTruthy(!isSxTruthy(contains(classes, cls)))) {
|
||||
classes.push(cls);
|
||||
} } }
|
||||
return classes;
|
||||
})(); };
|
||||
|
||||
// scan-io-refs-walk
|
||||
var scanIoRefsWalk = function(node, ioNames, refs) { return (isSxTruthy((typeOf(node) == "symbol")) ? (function() {
|
||||
var name = symbolName(node);
|
||||
return (isSxTruthy(contains(ioNames, name)) ? (isSxTruthy(!isSxTruthy(contains(refs, name))) ? append_b(refs, name) : NIL) : NIL);
|
||||
})() : (isSxTruthy((typeOf(node) == "list")) ? forEach(function(item) { return scanIoRefsWalk(item, ioNames, refs); }, node) : (isSxTruthy((typeOf(node) == "dict")) ? forEach(function(key) { return scanIoRefsWalk(dictGet(node, key), ioNames, refs); }, keys(node)) : NIL))); };
|
||||
|
||||
// scan-io-refs
|
||||
var scanIoRefs = function(node, ioNames) { return (function() {
|
||||
var refs = [];
|
||||
scanIoRefsWalk(node, ioNames, refs);
|
||||
return refs;
|
||||
})(); };
|
||||
|
||||
// transitive-io-refs-walk
|
||||
var transitiveIoRefsWalk = function(n, seen, allRefs, env, ioNames) { return (isSxTruthy(!isSxTruthy(contains(seen, n))) ? (append_b(seen, n), (function() {
|
||||
var val = envGet(env, n);
|
||||
return (isSxTruthy((typeOf(val) == "component")) ? (forEach(function(ref) { return (isSxTruthy(!isSxTruthy(contains(allRefs, ref))) ? append_b(allRefs, ref) : NIL); }, scanIoRefs(componentBody(val), ioNames)), forEach(function(dep) { return transitiveIoRefsWalk(dep, seen, allRefs, env, ioNames); }, scanRefs(componentBody(val)))) : (isSxTruthy((typeOf(val) == "macro")) ? (forEach(function(ref) { return (isSxTruthy(!isSxTruthy(contains(allRefs, ref))) ? append_b(allRefs, ref) : NIL); }, scanIoRefs(macroBody(val), ioNames)), forEach(function(dep) { return transitiveIoRefsWalk(dep, seen, allRefs, env, ioNames); }, scanRefs(macroBody(val)))) : NIL));
|
||||
})()) : NIL); };
|
||||
|
||||
// transitive-io-refs
|
||||
var transitiveIoRefs = function(name, env, ioNames) { return (function() {
|
||||
var allRefs = [];
|
||||
var seen = [];
|
||||
var key = (isSxTruthy(startsWith(name, "~")) ? name : (String("~") + String(name)));
|
||||
transitiveIoRefsWalk(key, seen, allRefs, env, ioNames);
|
||||
return allRefs;
|
||||
})(); };
|
||||
|
||||
// compute-all-io-refs
|
||||
var computeAllIoRefs = function(env, ioNames) { return forEach(function(name) { return (function() {
|
||||
var val = envGet(env, name);
|
||||
return (isSxTruthy((typeOf(val) == "component")) ? componentSetIoRefs(val, transitiveIoRefs(name, env, ioNames)) : NIL);
|
||||
})(); }, envComponents(env)); };
|
||||
|
||||
// component-pure?
|
||||
var componentPure_p = function(name, env, ioNames) { return isEmpty(transitiveIoRefs(name, env, ioNames)); };
|
||||
|
||||
// render-target
|
||||
var renderTarget = function(name, env, ioNames) { return (function() {
|
||||
var key = (isSxTruthy(startsWith(name, "~")) ? name : (String("~") + String(name)));
|
||||
return (function() {
|
||||
var val = envGet(env, key);
|
||||
return (isSxTruthy(!isSxTruthy((typeOf(val) == "component"))) ? "server" : (function() {
|
||||
var affinity = componentAffinity(val);
|
||||
return (isSxTruthy((affinity == "server")) ? "server" : (isSxTruthy((affinity == "client")) ? "client" : (isSxTruthy(!isSxTruthy(componentPure_p(name, env, ioNames))) ? "server" : "client")));
|
||||
})());
|
||||
})();
|
||||
})(); };
|
||||
|
||||
// page-render-plan
|
||||
var pageRenderPlan = function(pageSource, env, ioNames) { return (function() {
|
||||
var needed = componentsNeeded(pageSource, env);
|
||||
var compTargets = {};
|
||||
var serverList = [];
|
||||
var clientList = [];
|
||||
var ioDeps = [];
|
||||
{ var _c = needed; for (var _i = 0; _i < _c.length; _i++) { var name = _c[_i]; (function() {
|
||||
var target = renderTarget(name, env, ioNames);
|
||||
compTargets[name] = target;
|
||||
return (isSxTruthy((target == "server")) ? (append_b(serverList, name), forEach(function(ioRef) { return (isSxTruthy(!isSxTruthy(contains(ioDeps, ioRef))) ? append_b(ioDeps, ioRef) : NIL); }, transitiveIoRefs(name, env, ioNames))) : append_b(clientList, name));
|
||||
})(); } }
|
||||
return {"components": compTargets, "server": serverList, "client": clientList, "io-deps": ioDeps};
|
||||
})(); };
|
||||
|
||||
|
||||
// === Transpiled from router (client-side route matching) ===
|
||||
@@ -2702,6 +3055,40 @@ return (isSxTruthy((_batchDepth == 0)) ? (function() {
|
||||
// register-in-scope
|
||||
var registerInScope = function(disposable) { return (isSxTruthy(_islandScope) ? _islandScope(disposable) : NIL); };
|
||||
|
||||
// *store-registry*
|
||||
var _storeRegistry = {};
|
||||
|
||||
// def-store
|
||||
var defStore = function(name, initFn) { return (function() {
|
||||
var registry = _storeRegistry;
|
||||
if (isSxTruthy(!isSxTruthy(hasKey_p(registry, name)))) {
|
||||
_storeRegistry = assoc(registry, name, initFn());
|
||||
}
|
||||
return get(_storeRegistry, name);
|
||||
})(); };
|
||||
|
||||
// use-store
|
||||
var useStore = function(name) { return (isSxTruthy(hasKey_p(_storeRegistry, name)) ? get(_storeRegistry, name) : error((String("Store not found: ") + String(name) + String(". Call (def-store ...) before (use-store ...).")))); };
|
||||
|
||||
// clear-stores
|
||||
var clearStores = function() { return (_storeRegistry = {}); };
|
||||
|
||||
// emit-event
|
||||
var emitEvent = function(el, eventName, detail) { return domDispatch(el, eventName, detail); };
|
||||
|
||||
// on-event
|
||||
var onEvent = function(el, eventName, handler) { return domListen(el, eventName, handler); };
|
||||
|
||||
// bridge-event
|
||||
var bridgeEvent = function(el, eventName, targetSignal, transformFn) { return effect(function() { return (function() {
|
||||
var remove = domListen(el, eventName, function(e) { return (function() {
|
||||
var detail = eventDetail(e);
|
||||
var newVal = (isSxTruthy(transformFn) ? transformFn(detail) : detail);
|
||||
return reset_b(targetSignal, newVal);
|
||||
})(); });
|
||||
return remove;
|
||||
})(); }); };
|
||||
|
||||
|
||||
// =========================================================================
|
||||
// Platform interface — DOM adapter (browser-only)
|
||||
@@ -2852,6 +3239,16 @@ return (isSxTruthy((_batchDepth == 0)) ? (function() {
|
||||
return el.dispatchEvent(evt);
|
||||
}
|
||||
|
||||
function domListen(el, name, handler) {
|
||||
if (!_hasDom || !el) return function() {};
|
||||
el.addEventListener(name, handler);
|
||||
return function() { el.removeEventListener(name, handler); };
|
||||
}
|
||||
|
||||
function eventDetail(e) {
|
||||
return (e && e.detail != null) ? e.detail : nil;
|
||||
}
|
||||
|
||||
function domQuery(sel) {
|
||||
return _hasDom ? document.querySelector(sel) : null;
|
||||
}
|
||||
@@ -2879,6 +3276,12 @@ return (isSxTruthy((_batchDepth == 0)) ? (function() {
|
||||
function domSetData(el, key, val) {
|
||||
if (el) { if (!el._sxData) el._sxData = {}; el._sxData[key] = val; }
|
||||
}
|
||||
function domGetData(el, key) {
|
||||
return (el && el._sxData) ? (el._sxData[key] != null ? el._sxData[key] : nil) : nil;
|
||||
}
|
||||
function jsonParse(s) {
|
||||
try { return JSON.parse(s); } catch(e) { return {}; }
|
||||
}
|
||||
|
||||
// =========================================================================
|
||||
// Performance overrides — replace transpiled spec with imperative JS
|
||||
@@ -4706,7 +5109,20 @@ return (isSxTruthy((_batchDepth == 0)) ? (function() {
|
||||
renderComponent: typeof sxRenderComponent === "function" ? sxRenderComponent : null,
|
||||
getEnv: function() { return componentEnv; },
|
||||
resolveSuspense: typeof resolveSuspense === "function" ? resolveSuspense : null,
|
||||
hydrateIslands: typeof sxHydrateIslands === "function" ? sxHydrateIslands : null,
|
||||
disposeIsland: typeof disposeIsland === "function" ? disposeIsland : null,
|
||||
init: typeof bootInit === "function" ? bootInit : null,
|
||||
scanRefs: scanRefs,
|
||||
scanComponentsFromSource: scanComponentsFromSource,
|
||||
transitiveDeps: transitiveDeps,
|
||||
computeAllDeps: computeAllDeps,
|
||||
componentsNeeded: componentsNeeded,
|
||||
pageComponentBundle: pageComponentBundle,
|
||||
pageCssClasses: pageCssClasses,
|
||||
scanIoRefs: scanIoRefs,
|
||||
transitiveIoRefs: transitiveIoRefs,
|
||||
computeAllIoRefs: computeAllIoRefs,
|
||||
componentPure_p: componentPure_p,
|
||||
splitPathSegments: splitPathSegments,
|
||||
parseRoutePattern: parseRoutePattern,
|
||||
matchRoute: matchRoute,
|
||||
@@ -4724,6 +5140,12 @@ return (isSxTruthy((_batchDepth == 0)) ? (function() {
|
||||
batch: batch,
|
||||
isSignal: isSignal,
|
||||
makeSignal: makeSignal,
|
||||
defStore: defStore,
|
||||
useStore: useStore,
|
||||
clearStores: clearStores,
|
||||
emitEvent: emitEvent,
|
||||
onEvent: onEvent,
|
||||
bridgeEvent: bridgeEvent,
|
||||
_version: "ref-2.0 (boot+dom+engine+html+orchestration+parser+sx, bootstrap-compiled)"
|
||||
};
|
||||
|
||||
@@ -4766,4 +5188,4 @@ return (isSxTruthy((_batchDepth == 0)) ? (function() {
|
||||
if (typeof module !== "undefined" && module.exports) module.exports = Sx;
|
||||
else global.Sx = Sx;
|
||||
|
||||
})(typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : this);
|
||||
})(typeof globalThis !== "undefined" ? globalThis : typeof window !== "undefined" ? window : this);
|
||||
Reference in New Issue
Block a user