release: WRNexusJS 0.8.0
Quality / quality (ubuntu-latest) (push) Failing after 21s
Quality / quality (windows-latest) (push) Canceled after 0s

This commit is contained in:
2026-08-02 23:18:51 +05:30
parent 87507edf59
commit 586a6db8ff
625 changed files with 243608 additions and 11210 deletions
+58
View File
@@ -0,0 +1,58 @@
export const ACTION_RUNTIME = String.raw`
(function () {
if (window.__wrnexusActionsInstalled) return;
window.__wrnexusActionsInstalled = true;
function csrf() {
var match = document.cookie.match(/(?:^|;\s*)wire-csrf=([^;]+)/);
return match ? decodeURIComponent(match[1]) : "";
}
function detail(form, name, extra) {
return Object.assign({ form: form, name: name }, extra || {});
}
function emit(form, phase, name, extra, cancelable) {
return form.dispatchEvent(new CustomEvent("wrnexus:action:" + phase, {
bubbles: true,
cancelable: !!cancelable,
detail: detail(form, name, extra),
}));
}
document.addEventListener("submit", function (event) {
var form = event.target && event.target.closest && event.target.closest("form[data-wrn-action]");
if (!form || event.defaultPrevented) return;
var name = form.getAttribute("data-wrn-action");
if (!name) return;
event.preventDefault();
var data = new FormData(form);
data.set("_wrnexus_action", name);
data.set("_csrf", csrf());
emit(form, "optimistic", name, { input: data }, true);
form.setAttribute("aria-busy", "true");
form.setAttribute("data-wrn-action-state", "pending");
emit(form, "pending", name, { input: data });
fetch(form.action || location.href, {
method: "POST",
body: data,
credentials: "same-origin",
headers: { accept: "application/json", "x-wrnexus-action": name, "x-csrf-token": csrf() },
}).then(async function (response) {
var payload;
try { payload = await response.json(); } catch (_) { payload = { error: await response.text() }; }
if (!response.ok) throw Object.assign(new Error(payload.error || "Action failed"), { response: response, payload: payload });
form.setAttribute("data-wrn-action-state", "success");
emit(form, "success", name, { data: payload.data, invalidated: payload.invalidated || [] });
if (payload.invalidated && payload.invalidated.length) {
window.dispatchEvent(new CustomEvent("wrnexus:cache:invalidate", { detail: { tags: payload.invalidated } }));
}
}).catch(function (error) {
form.setAttribute("data-wrn-action-state", "error");
emit(form, "error", name, { error: error, errors: error.payload && error.payload.errors });
}).finally(function () {
form.removeAttribute("aria-busy");
});
});
})();
`;
+46
View File
@@ -0,0 +1,46 @@
export interface ActionClientOptions<I> {
signal?: AbortSignal;
csrfToken?: string;
headers?: HeadersInit;
serialize?: (input: I) => BodyInit;
}
export interface ActionResult<O> {
data: O;
invalidated: string[];
}
export class ActionClientError extends Error {
constructor(
public readonly status: number,
public readonly errors?: Record<string, string>,
) {
super(`Server action failed with status ${status}.`);
this.name = "ActionClientError";
}
}
export function createActionClient<I, O>(route: string, name: string) {
return async (input: I, options: ActionClientOptions<I> = {}): Promise<ActionResult<O>> => {
const headers = new Headers(options.headers);
headers.set("accept", "application/json");
headers.set("x-wrnexus-action", name);
if (options.csrfToken) headers.set("x-csrf-token", options.csrfToken);
const body = options.serialize ? options.serialize(input) : JSON.stringify(input);
if (!options.serialize) headers.set("content-type", "application/json");
const response = await fetch(route, {
method: "POST",
credentials: "same-origin",
signal: options.signal,
headers,
body,
});
const payload = (await response.json()) as {
data?: O;
invalidated?: string[];
errors?: Record<string, string>;
};
if (!response.ok) throw new ActionClientError(response.status, payload.errors);
return { data: payload.data as O, invalidated: payload.invalidated ?? [] };
};
}
+7
View File
@@ -10,10 +10,12 @@
import { REACTIVE_RUNTIME } from "./reactive-runtime.ts";
import { NAV_RUNTIME } from "./nav-runtime.ts";
import { REALTIME_RUNTIME } from "./realtime-runtime.ts";
import { ACTION_RUNTIME } from "./action-runtime.ts";
export { REACTIVE_RUNTIME } from "./reactive-runtime.ts";
export { NAV_RUNTIME } from "./nav-runtime.ts";
export { REALTIME_RUNTIME } from "./realtime-runtime.ts";
export { ACTION_RUNTIME } from "./action-runtime.ts";
/** The reactive runtime served at `/__wrnexus/reactive.js` (plain browser JS). */
export function getReactiveRuntime(): string {
@@ -30,8 +32,13 @@ export function getRealtimeRuntime(): string {
return REALTIME_RUNTIME;
}
export function getActionRuntime(): string {
return ACTION_RUNTIME;
}
export * from "./outputs.ts";
export * from "./server-client.ts";
export * from "./refs.ts";
export * from "./client-functions.ts";
export * from "./actions.ts";
export type * from "./types.ts";
+189 -35
View File
@@ -34,6 +34,138 @@ export const NAV_RUNTIME = String.raw`
var APP_ID = "app";
var inFlight = null;
var memory = Object.create(null);
var keepAlive = Object.create(null);
var keepAliveOrder = [];
function keepAliveKey(node) {
return String(node && node.getAttribute("data-wrn-keepalive") || "");
}
function retainKeepAlive(root) {
Array.prototype.forEach.call(root.querySelectorAll("[data-wrn-keepalive]"), function (node) {
var key = keepAliveKey(node);
if (!key) return;
if (!keepAlive[key]) keepAliveOrder.push(key);
keepAlive[key] = node;
if (node.parentNode) node.parentNode.removeChild(node);
});
while (keepAliveOrder.length > 32) {
var expired = keepAliveOrder.shift();
if (expired && keepAlive[expired]) {
dispose(keepAlive[expired]);
delete keepAlive[expired];
}
}
}
function restoreKeepAlive(root) {
var placeholders = [];
if (root.matches && root.matches("[data-wrn-keepalive]")) placeholders.push(root);
Array.prototype.forEach.call(root.querySelectorAll("[data-wrn-keepalive]"), function (node) { placeholders.push(node); });
placeholders.forEach(function (placeholder) {
var saved = keepAlive[keepAliveKey(placeholder)];
if (saved && placeholder.parentNode) placeholder.parentNode.replaceChild(saved, placeholder);
});
}
function preservationPolicy(doc) {
var meta = doc.querySelector('meta[name="wrnexus-preserve"]');
var out = Object.create(null);
String(meta && meta.getAttribute("content") || "").split(",").forEach(function (name) {
if (name) out[name] = true;
});
return out;
}
function syncPreservationPolicy(nextDocument) {
var selector = 'meta[name="wrnexus-preserve"]';
var current = document.querySelector(selector);
var next = nextDocument.querySelector(selector);
if (!next) {
if (current) current.remove();
return;
}
if (!current) {
current = document.createElement("meta");
current.setAttribute("name", "wrnexus-preserve");
document.head.appendChild(current);
}
current.setAttribute("content", next.getAttribute("content") || "");
}
function stateKey(url) {
try {
var parsed = new URL(url, location.href);
return "wrnexus:navigation:" + parsed.pathname + parsed.search;
} catch (_) {
return "wrnexus:navigation:" + String(url);
}
}
function safeField(field) {
var type = String(field.type || "").toLowerCase();
var name = String(field.name || field.id || "").toLowerCase();
return type !== "password" && type !== "file" && type !== "hidden" &&
!field.hasAttribute("data-no-preserve") &&
!/(?:csrf|token|secret|password|credential)/.test(name);
}
function capturePage(url) {
var policy = preservationPolicy(document);
var state = { fields: Object.create(null), elements: Object.create(null) };
if (policy.scroll) state.scroll = [window.scrollX || 0, window.scrollY || 0];
if (policy.forms || policy.filters || policy.pagination || policy.workflow) {
Array.prototype.forEach.call(document.querySelectorAll("#app input,#app select,#app textarea"), function (field, index) {
if (!safeField(field)) return;
var key = field.name || field.id || String(index);
state.fields[key] = { value: field.value, checked: !!field.checked, selectedIndex: field.selectedIndex };
});
}
if (policy.tabs || policy.expanded || policy.component) {
Array.prototype.forEach.call(document.querySelectorAll("#app [data-wrn-preserve]"), function (node, index) {
var key = node.getAttribute("data-wrn-preserve") || node.id || String(index);
state.elements[key] = {
selected: node.getAttribute("aria-selected"),
expanded: node.getAttribute("aria-expanded"),
value: "value" in node ? node.value : null,
};
});
}
memory[stateKey(url)] = state;
try { sessionStorage.setItem(stateKey(url), JSON.stringify(state)); } catch (_) {}
}
function restorePage(url, isPop) {
var policy = preservationPolicy(document);
var state = memory[stateKey(url)];
if (!state) {
try { state = JSON.parse(sessionStorage.getItem(stateKey(url)) || "null"); } catch (_) {}
}
if (state && (policy.forms || policy.filters || policy.pagination || policy.workflow)) {
Array.prototype.forEach.call(document.querySelectorAll("#app input,#app select,#app textarea"), function (field, index) {
if (!safeField(field)) return;
var saved = state.fields && state.fields[field.name || field.id || String(index)];
if (!saved) return;
if (field.type === "checkbox" || field.type === "radio") field.checked = !!saved.checked;
else field.value = saved.value;
if (field.tagName === "SELECT") field.selectedIndex = saved.selectedIndex;
field.dispatchEvent(new Event("input", { bubbles: true }));
});
}
if (state && (policy.tabs || policy.expanded || policy.component)) {
Array.prototype.forEach.call(document.querySelectorAll("#app [data-wrn-preserve]"), function (node, index) {
var key = node.getAttribute("data-wrn-preserve") || node.id || String(index);
var saved = state.elements && state.elements[key];
if (!saved) return;
if (saved.selected != null) node.setAttribute("aria-selected", saved.selected);
if (saved.expanded != null) node.setAttribute("aria-expanded", saved.expanded);
if (saved.value != null && "value" in node) node.value = saved.value;
});
}
if (state && policy.scroll && state.scroll) window.scrollTo(state.scroll[0], state.scroll[1]);
else if (!isPop) window.scrollTo(0, 0);
}
function pathOf(src) {
return String(src).split("?")[0];
@@ -365,6 +497,8 @@ export const NAV_RUNTIME = String.raw`
return;
}
capturePage(location.href);
var incomingApp =
doc.getElementById(APP_ID);
@@ -380,6 +514,8 @@ export const NAV_RUNTIME = String.raw`
document.title = doc.title;
}
syncPreservationPolicy(doc);
syncWrnStyles(doc);
var importedNodes = [];
@@ -402,6 +538,7 @@ export const NAV_RUNTIME = String.raw`
* connected. This is important for components that remove window or
* document listeners during lifecycle.unmount.
*/
retainKeepAlive(currentApp);
dispose(currentApp);
try {
@@ -413,6 +550,7 @@ export const NAV_RUNTIME = String.raw`
currentApp,
importedNodes,
);
restoreKeepAlive(currentApp);
} catch (_) {
hardNavigate(url);
return;
@@ -439,9 +577,10 @@ export const NAV_RUNTIME = String.raw`
url,
);
window.scrollTo(0, 0);
}
restorePage(url, isPop);
dispatchNavigationEvent(url);
}
@@ -449,50 +588,65 @@ export const NAV_RUNTIME = String.raw`
var token = {};
inFlight = token;
window.dispatchEvent(new CustomEvent("wrnexus:navigation-start", { detail: { url: url } }));
fetch(url, {
var pending = prefetched.get(url) || requestDocument(url, false);
prefetched.delete(url);
pending
.then(function (result) {
if (inFlight !== token) return null;
if (result.redirectedUrl) url = result.redirectedUrl;
if (result.contentType.indexOf("text/html") === -1) {
hardNavigate(url);
return null;
}
render(result.text, url, isPop);
return null;
})
.catch(function () {
if (inFlight === token) hardNavigate(url);
});
}
var prefetched = new Map();
function requestDocument(url, isPrefetch) {
return fetch(url, {
headers: {
"x-wrnexus-nav": "1",
...(isPrefetch ? { "x-wrnexus-prefetch": "1" } : {}),
accept: "text/html",
},
credentials: "same-origin",
})
.then(function (response) {
if (inFlight !== token) {
return null;
}
if (response.redirected && response.url) {
url = response.url;
}
var contentType =
response.headers.get("content-type") ||
"";
if (
contentType.indexOf("text/html") === -1
) {
hardNavigate(url);
return null;
}
return response.text().then(function (text) {
if (inFlight !== token) {
return;
}
render(text, url, isPop);
});
})
.catch(function () {
if (inFlight === token) {
hardNavigate(url);
}
}).then(function (response) {
var contentType = response.headers.get("content-type") || "";
return response.text().then(function (text) {
return {
text: text,
contentType: contentType,
redirectedUrl: response.redirected && response.url ? response.url : "",
};
});
});
}
function prefetch(anchor) {
if (!isLocalLink(anchor) || anchor.hasAttribute("data-no-prefetch")) return;
var url = anchor.href;
if (url === location.href || prefetched.has(url)) return;
prefetched.set(url, requestDocument(url, true));
while (prefetched.size > 20) prefetched.delete(prefetched.keys().next().value);
}
document.addEventListener("pointerover", function (event) {
var anchor = event.target && event.target.closest ? event.target.closest("a") : null;
prefetch(anchor);
}, { passive: true });
document.addEventListener("focusin", function (event) {
var anchor = event.target && event.target.closest ? event.target.closest("a") : null;
prefetch(anchor);
});
document.addEventListener(
"click",
function (event) {
+216 -1
View File
@@ -199,6 +199,17 @@ export const REACTIVE_RUNTIME = String.raw`
? input[nextIndex]
: "";
var currentLine = input
.slice(start, previousIndex + 1)
.trim();
// Postfix updates are complete statements. Treating their trailing + or
// - as a multiline operator merges the following assignment into the
// same statement (for example count++ followed by message = ...).
if (/\+\+$|--$/.test(currentLine)) {
return false;
}
// Newlines are formatting whitespace while either side is an
// incomplete expression. This covers multiline assignments and
// ternaries emitted by formatted .wrn component functions.
@@ -1650,7 +1661,10 @@ export const REACTIVE_RUNTIME = String.raw`
});
});
// data-show="expr" — toggle visibility on truthiness.
// data-show="expr" — toggle visibility on truthiness. This directive is
// intentionally non-destructive because popovers, selects and remote data
// controls keep event wiring and state while closed. Use a compiled {#if}
// block when the inactive branch must not be rendered.
el.querySelectorAll("[data-show]").forEach(function (node) {
if (!owns(node)) return;
@@ -3651,6 +3665,56 @@ export const REACTIVE_RUNTIME = String.raw`
return special.value;
}
// Template literals are evaluated without eval so they remain compatible
// with a strict CSP. Each interpolation uses the same bounded expression
// evaluator as every other reactive binding.
if (
expr.length >= 2 &&
expr[0] === "\`" &&
expr[expr.length - 1] === "\`"
) {
var template = expr.slice(1, -1);
var rendered = "";
var cursor = 0;
while (cursor < template.length) {
if (template[cursor] === "\\") {
cursor++;
var escaped = template[cursor++];
rendered += escaped === "n" ? "\n" : escaped === "t" ? "\t" : escaped || "";
continue;
}
if (template[cursor] === "$" && template[cursor + 1] === "{") {
var expressionStart = cursor + 2;
var expressionEnd = expressionStart;
var depth = 1;
var quote = "";
for (; expressionEnd < template.length; expressionEnd++) {
var character = template[expressionEnd];
if (quote) {
if (character === "\\") expressionEnd++;
else if (character === quote) quote = "";
continue;
}
if (character === '"' || character === "'") quote = character;
else if (character === "{") depth++;
else if (character === "}" && --depth === 0) break;
}
if (depth !== 0) throw new Error("Unclosed template interpolation in '" + expr + "'");
rendered += String(evaluateExpression(template.slice(expressionStart, expressionEnd), read));
cursor = expressionEnd + 1;
continue;
}
rendered += template[cursor++];
}
return rendered;
}
var tokens =
tokenizeExpression(expr);
@@ -3959,18 +4023,169 @@ export const REACTIVE_RUNTIME = String.raw`
}
}
var asyncLoads = new Map();
function asyncValueAt(value, path) {
var current = value;
var parts = String(path || "").split(".").filter(Boolean);
for (var i = 0; i < parts.length; i++) {
if (current == null || typeof current !== "object") return "";
current = current[parts[i]];
}
return current == null ? "" : current;
}
function interpolateAsync(root, model, prefix) {
function visit(node) {
if (node.nodeType === 3) {
node.textContent = String(node.textContent || "").replace(/\{\s*([A-Za-z_$][\w$]*(?:\.[A-Za-z_$][\w$]*)*)\s*\}/g, function (whole, path) {
var parts = path.split(".");
if (parts[0] === prefix) parts.shift();
if (prefix === "error" && parts[0] !== "message") return whole;
var value = asyncValueAt(model, parts.join("."));
return typeof value === "object" ? JSON.stringify(value) : String(value);
});
}
Array.from(node.childNodes || []).forEach(visit);
}
visit(root);
root.querySelectorAll("[data-wrn-async-value]").forEach(function (node) {
var path = node.getAttribute("data-wrn-async-value") || "";
var value = asyncValueAt(model, path);
node.textContent = typeof value === "object" ? JSON.stringify(value, null, 2) : String(value);
});
}
function showAsync(boundary, state, model) {
var template = boundary.querySelector("template[data-wrn-async-" + state + "]");
var content = boundary.querySelector("[data-wrn-async-content]");
if (!template || !content) return;
var fragment = template.content.cloneNode(true);
interpolateAsync(fragment, model, state === "error" ? "error" : boundary.getAttribute("data-wrn-async"));
content.replaceChildren(fragment);
boundary.setAttribute("aria-busy", state === "loading" ? "true" : "false");
boundary.setAttribute("data-wrn-async-state", state);
}
function setupAsyncBoundary(boundary) {
if (boundary.__wrnexusAsync) return;
boundary.__wrnexusAsync = true;
if (boundary.getAttribute("data-wrn-async-resolved") === "true") {
boundary.setAttribute("data-wrn-async-state", "success");
boundary.setAttribute("aria-busy", "false");
return;
}
var name = boundary.getAttribute("data-wrn-async") || "";
if (!/^[A-Za-z_$][\w$]{0,63}$/.test(name)) return showAsync(boundary, "error", { message: "Invalid data source" });
var url = "/__wrnexus/client-load?route=" + encodeURIComponent(location.pathname) + "&name=" + encodeURIComponent(name);
var attempts = Math.min(5, Math.max(0, Number(boundary.getAttribute("data-wrn-async-retries")) || 0));
var controller = new AbortController();
boundary.__wrnexusAsyncAbort = controller;
window.addEventListener("wrnexus:navigation-start", function () { controller.abort(); }, { once: true });
function request(remaining) {
var pending = asyncLoads.get(url);
if (!pending) {
pending = fetch(url, { headers: { accept: "application/json" }, signal: controller.signal }).then(function (response) {
if (!response.ok) throw new Error("Client load returned " + response.status);
return response.json();
}).finally(function () { asyncLoads.delete(url); });
asyncLoads.set(url, pending);
}
pending.then(function (result) {
showAsync(boundary, "success", result.data);
}).catch(function (error) {
if (controller.signal.aborted) return;
if (remaining > 0) return setTimeout(function () { request(remaining - 1); }, 100 * (attempts - remaining + 1));
showAsync(boundary, "error", { message: error && error.message ? error.message : "Loading failed" });
});
}
showAsync(boundary, "loading", {});
request(attempts);
}
function hydrateAsyncBoundaries(root) {
(root || document).querySelectorAll("[data-wrn-async]").forEach(setupAsyncBoundary);
}
function mountClientRoots(root) {
(root || document).querySelectorAll("template[data-wrn-client-template]").forEach(function (template) {
var id = template.getAttribute("data-wrn-client-template");
var mount = null;
(root || document).querySelectorAll("[data-wrn-client-root]").forEach(function (candidate) {
if (!mount && candidate.getAttribute("data-wrn-client-root") === id) mount = candidate;
});
if (!mount || !template.content) return;
var fragment = template.content.cloneNode(true);
mount.replaceWith(fragment);
template.remove();
window.dispatchEvent(new window.CustomEvent("wrnexus:client-mounted", { detail: { id: id } }));
});
}
function hydrateDeclarativeUi(root) {
var host = root || document;
host.querySelectorAll("[data-wrn-portal]").forEach(function (portal) {
if (portal.__wrnPortalMounted) return;
var selector = portal.getAttribute("data-wrn-portal") || "body";
var target = null; try { target = document.querySelector(selector); } catch (_) {}
if (!target || target === portal || portal.contains(target)) return;
portal.__wrnPortalMounted = true;
var marker = document.createComment("wrnexus-portal");
portal.parentNode && portal.parentNode.insertBefore(marker, portal);
target.appendChild(portal);
});
host.querySelectorAll("[data-wrn-transition]").forEach(function (element) {
if (element.__wrnTransitionMounted) return;
element.__wrnTransitionMounted = true;
var name = element.getAttribute("data-wrn-transition") || "wrn-transition";
element.classList.add(name + "-enter", name + "-enter-active");
requestAnimationFrame(function () { element.classList.remove(name + "-enter"); element.classList.add(name + "-enter-to"); });
element.addEventListener("transitionend", function () { element.classList.remove(name + "-enter-active", name + "-enter-to"); }, { once: true });
});
host.querySelectorAll("[data-wrn-dynamic-component]").forEach(function (element) {
if (element.__wrnDynamicMounted) return;
element.__wrnDynamicMounted = true;
var cases = Array.prototype.slice.call(element.children).filter(function (candidate) {
return candidate.hasAttribute("data-component-case");
}).map(function (candidate) {
var marker = document.createComment("wrnexus-component-case:" + (candidate.getAttribute("data-component-case") || ""));
element.insertBefore(marker, candidate);
return { candidate: candidate, marker: marker };
});
var update = function () {
var selected = element.getAttribute("data-wrn-dynamic-component") || "";
cases.forEach(function (record) {
var candidate = record.candidate;
var active = candidate.getAttribute("data-component-case") === selected;
candidate.hidden = false;
if (active && !candidate.isConnected && record.marker.parentNode) {
record.marker.parentNode.insertBefore(candidate, record.marker.nextSibling);
} else if (!active && candidate.parentNode) {
candidate.parentNode.removeChild(candidate);
}
});
window.dispatchEvent(new window.CustomEvent("wrnexus:dynamic-component", { detail: { component: selected } }));
};
update();
new MutationObserver(update).observe(element, { attributes: true, attributeFilter: ["data-wrn-dynamic-component"] });
});
}
window.__wrnexusMountClientRoots = mountClientRoots;
window.__wrnexusHydrateAsyncBoundaries = hydrateAsyncBoundaries;
window.__wrnexusHydrateScopes = hydrateScopes;
window.__wrnexusInvalidateClientModule = function (url) { clientModuleCache.delete(url); };
window.__wrnexusHydrateCsrFetches = hydrateCsrFetches;
window.__wrnexusDisposeBehaviors = disposeBehaviors;
if (document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", function () {
mountClientRoots(document);
hydrateScopes(document);
hydrateCsrFetches(document);
hydrateAsyncBoundaries(document);
hydrateDeclarativeUi(document);
});
} else {
mountClientRoots(document);
hydrateScopes(document);
hydrateCsrFetches(document);
hydrateAsyncBoundaries(document);
hydrateDeclarativeUi(document);
}
})();
`.trim();