release: WRNexusJS 0.2.31

This commit is contained in:
2026-07-14 21:58:19 +05:30
parent 3cfdc747fa
commit 8b4b7ca5ac
58 changed files with 1152 additions and 196 deletions
+375 -81
View File
@@ -4,127 +4,421 @@
* Progressive enhancement over normal links: intercepts same-origin `<a>`
* clicks, fetches the target page's HTML, swaps the `#app` container in place,
* updates history/title/scroll, ensures any framework runtimes the new page
* needs are present, and re-hydrates. Anything unexpected (cross-origin,
* modified click, non-HTML response, missing `#app`) falls back to a full
* browser navigation, so behaviour degrades safely.
* needs are present, and re-hydrates.
*
* Data "loaders": pages load their data on the server (SSR `api` bindings), so
* the fetched HTML already contains fresh data — no separate client loader is
* needed. Client-side (`csr`) bindings and reactive scopes re-hydrate after the
* swap. Programmatic navigation is exposed as `window.__wrnexusNavigate(url)`.
* Before replacing the current page, component lifecycle behaviors are
* explicitly disposed. This ensures `unmount` hooks and watcher cleanups run
* before the old DOM is removed.
*
* Programmatic navigation is exposed as:
*
* window.__wrnexusNavigate(url)
*/
export const NAV_RUNTIME = String.raw`
(function () {
if (!window.history || !history.pushState || !window.fetch || !window.DOMParser) return;
if (window.__wrnexusNavInstalled) return;
if (
!window.history ||
!history.pushState ||
!window.fetch ||
!window.DOMParser
) {
return;
}
if (window.__wrnexusNavInstalled) {
return;
}
window.__wrnexusNavInstalled = true;
var APP_ID = "app";
var inFlight = null;
function pathOf(src) { return String(src).split("?")[0]; }
function pathOf(src) {
return String(src).split("?")[0];
}
function isLocalLink(anchor) {
if (
!anchor ||
anchor.hasAttribute("download") ||
anchor.hasAttribute("data-no-nav")
) {
return false;
}
if (anchor.target && anchor.target !== "_self") {
return false;
}
if (anchor.origin !== location.origin) {
return false;
}
var href = anchor.getAttribute("href");
if (!href || href.charAt(0) === "#") {
return false;
}
var rel = (
anchor.getAttribute("rel") || ""
).toLowerCase();
function isLocalLink(a) {
if (!a || a.hasAttribute("download") || a.hasAttribute("data-no-nav")) return false;
if (a.target && a.target !== "_self") return false;
if (a.origin !== location.origin) return false;
var href = a.getAttribute("href");
if (!href || href.charAt(0) === "#") return false;
var rel = (a.getAttribute("rel") || "").toLowerCase();
return rel.indexOf("external") === -1;
}
function loadedScriptPaths() {
var set = {};
document.querySelectorAll("script[src]").forEach(function (s) {
var src = s.getAttribute("src");
if (src) set[pathOf(src)] = true;
});
return set;
var loaded = {};
document
.querySelectorAll("script[src]")
.forEach(function (script) {
var src = script.getAttribute("src");
if (src) {
loaded[pathOf(src)] = true;
}
});
return loaded;
}
// Append any /__wrnexus/* runtime the incoming page declares but the current
// document has not loaded yet. Fresh scripts self-initialise on load.
/**
* Append framework runtimes declared by the incoming document but not
* currently loaded.
*/
function ensureScripts(doc) {
var loaded = loadedScriptPaths();
doc.querySelectorAll("script[src]").forEach(function (s) {
var src = s.getAttribute("src");
if (!src || loaded[pathOf(src)]) return;
loaded[pathOf(src)] = true;
var el = document.createElement("script");
el.src = src;
el.async = false; // preserve execution order (e.g. schemas.js before validate.js)
document.body.appendChild(el);
});
doc
.querySelectorAll("script[src]")
.forEach(function (script) {
var src = script.getAttribute("src");
if (!src || loaded[pathOf(src)]) {
return;
}
loaded[pathOf(src)] = true;
var element =
document.createElement("script");
element.src = src;
element.async = false;
document.body.appendChild(element);
});
}
// Re-hydrate already-loaded runtimes against the swapped-in DOM. Every
// entrypoint is idempotent, so this is safe even when a fresh script also
// self-initialises the same nodes.
function rehydrate() {
try { if (window.__wrnexusHydrateScopes) window.__wrnexusHydrateScopes(document); } catch (e) {}
try { if (window.__wrnexusHydrateCsrFetches) window.__wrnexusHydrateCsrFetches(document); } catch (e) {}
try { if (window.__wireValidate) window.__wireValidate.init(document); } catch (e) {}
try { if (window.wireTheme) window.wireTheme.bind(document); } catch (e) {}
/**
* Run explicit component cleanup before removing the existing page.
*
* The reactive runtime also observes removed DOM nodes, but explicit
* disposal here guarantees that unmount hooks run before replacement.
*/
function dispose(root) {
try {
if (
typeof window.__wrnexusDisposeBehaviors ===
"function"
) {
window.__wrnexusDisposeBehaviors(root);
}
} catch (error) {
console.error(
"[wrnexus] failed to dispose page behaviors",
error,
);
}
}
/**
* Re-hydrate already-loaded runtimes against newly inserted DOM.
*
* All framework hydration functions must remain idempotent.
*/
function rehydrate(root) {
try {
if (
typeof window.__wrnexusHydrateScopes ===
"function"
) {
window.__wrnexusHydrateScopes(root);
}
} catch (error) {
console.error(
"[wrnexus] failed to hydrate reactive scopes",
error,
);
}
try {
if (
typeof window.__wrnexusHydrateCsrFetches ===
"function"
) {
window.__wrnexusHydrateCsrFetches(root);
}
} catch (error) {
console.error(
"[wrnexus] failed to hydrate CSR bindings",
error,
);
}
try {
if (
window.__wireValidate &&
typeof window.__wireValidate.init ===
"function"
) {
window.__wireValidate.init(document);
}
} catch (error) {
console.error(
"[wrnexus] failed to hydrate validation",
error,
);
}
try {
if (
window.wireTheme &&
typeof window.wireTheme.bind ===
"function"
) {
window.wireTheme.bind(document);
}
} catch (error) {
console.error(
"[wrnexus] failed to hydrate theme bindings",
error,
);
}
}
function dispatchNavigationEvent(url) {
try {
window.dispatchEvent(
new CustomEvent("wrnexus:navigated", {
detail: {
url: url,
},
}),
);
} catch (_) {
// CustomEvent may not be available in very old browsers.
}
}
function hardNavigate(url) {
location.href = url;
}
function render(html, url, isPop) {
var doc = new DOMParser().parseFromString(html, "text/html");
var to = doc.getElementById(APP_ID);
var from = document.getElementById(APP_ID);
if (!to || !from) { location.href = url; return; } // structure mismatch → hard nav
if (doc.title) document.title = doc.title;
// Swap #app by importing nodes — NOT innerHTML — so it works under a strict
// Trusted-Types CSP (require-trusted-types-for 'script').
var imported = [];
for (var i = 0; i < to.childNodes.length; i++) imported.push(document.importNode(to.childNodes[i], true));
from.replaceChildren.apply(from, imported);
ensureScripts(doc);
rehydrate();
if (!isPop) { history.pushState({ wrnexusNav: true }, "", url); window.scrollTo(0, 0); }
try {
window.dispatchEvent(new CustomEvent("wrnexus:navigated", { detail: { url: url } }));
} catch (e) {}
}
var doc;
var inFlight = null;
try {
doc = new DOMParser().parseFromString(
html,
"text/html",
);
} catch (_) {
hardNavigate(url);
return;
}
var incomingApp =
doc.getElementById(APP_ID);
var currentApp =
document.getElementById(APP_ID);
if (!incomingApp || !currentApp) {
hardNavigate(url);
return;
}
if (doc.title) {
document.title = doc.title;
}
var importedNodes = [];
for (
var index = 0;
index < incomingApp.childNodes.length;
index++
) {
importedNodes.push(
document.importNode(
incomingApp.childNodes[index],
true,
),
);
}
/*
* Run unmount hooks and watcher cleanup while the current DOM is still
* connected. This is important for components that remove window or
* document listeners during lifecycle.unmount.
*/
dispose(currentApp);
try {
/*
* Avoid innerHTML so navigation works with strict Trusted Types and CSP
* configurations.
*/
currentApp.replaceChildren.apply(
currentApp,
importedNodes,
);
} catch (_) {
hardNavigate(url);
return;
}
/*
* Load any runtime required only by the incoming page. Fresh scripts
* initialise themselves when loaded.
*/
ensureScripts(doc);
/*
* Hydrate runtimes that are already available. Hydration is scoped to
* #app instead of the entire document for less DOM traversal.
*/
rehydrate(currentApp);
if (!isPop) {
history.pushState(
{
wrnexusNav: true,
},
"",
url,
);
window.scrollTo(0, 0);
}
dispatchNavigationEvent(url);
}
function navigate(url, isPop) {
var token = {};
inFlight = token;
fetch(url, { headers: { "x-wrnexus-nav": "1", accept: "text/html" }, credentials: "same-origin" })
.then(function (r) {
if (inFlight !== token) return null; // superseded by a newer navigation
if (r.redirected && r.url) url = r.url; // follow server redirects (e.g. auth)
var ct = r.headers.get("content-type") || "";
if (ct.indexOf("text/html") === -1) { location.href = url; return null; }
return r.text().then(function (t) {
if (inFlight === token) render(t, url, isPop);
fetch(url, {
headers: {
"x-wrnexus-nav": "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;
}
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 () { location.href = url; });
.catch(function () {
if (inFlight === token) {
hardNavigate(url);
}
});
}
document.addEventListener(
"click",
function (e) {
if (e.defaultPrevented || e.button !== 0 || e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return;
var a = e.target && e.target.closest ? e.target.closest("a") : null;
if (!isLocalLink(a)) return;
if (a.href === location.href) { e.preventDefault(); return; }
e.preventDefault();
navigate(a.href, false);
function (event) {
if (
event.defaultPrevented ||
event.button !== 0 ||
event.metaKey ||
event.ctrlKey ||
event.shiftKey ||
event.altKey
) {
return;
}
var anchor =
event.target &&
event.target.closest
? event.target.closest("a")
: null;
if (!isLocalLink(anchor)) {
return;
}
if (anchor.href === location.href) {
event.preventDefault();
return;
}
event.preventDefault();
navigate(anchor.href, false);
},
false,
);
window.addEventListener("popstate", function () {
navigate(location.href, true);
});
window.addEventListener(
"popstate",
function () {
navigate(location.href, true);
},
);
// Programmatic navigation for forms/actions and app code.
/**
* Programmatic navigation for forms, actions and application code.
*/
window.__wrnexusNavigate = function (url) {
navigate(new URL(url, location.href).href, false);
navigate(
new URL(url, location.href).href,
false,
);
};
})();
`.trim();