chore: add pre-blazor crash diagnostics

This commit is contained in:
2026-05-04 22:53:14 +02:00
parent e60b4b5867
commit f86ac43153
4 changed files with 141 additions and 2 deletions

View File

@@ -25,11 +25,16 @@
}
else
{
<Routes @rendermode="@(new InteractiveServerRenderMode(prerender: false))"/>
<div id="rr-interactive-host" data-request-path="@RequestPath">
<Routes @rendermode="@(new InteractiveServerRenderMode(prerender: false))"/>
</div>
}
<script src="js/rpgroller-api.js"></script>
@if (UseInteractiveApp)
{
<script>
window.rpgRollerApi.bootstrapPreBlazorDiagnostics("@RequestPath");
</script>
<script src="_framework/blazor.web.js"></script>
}
</body>
@@ -55,6 +60,8 @@ else
private bool AuthStatusIsError => string.Equals(ReadAuthQueryValue("kind"), "error", StringComparison.OrdinalIgnoreCase);
private string RequestPath => HttpContext?.Request.Path.Value ?? "/";
private string BaseHref
{
get

View File

@@ -13,6 +13,7 @@ window.rpgRollerApi = (() => {
observer: null,
route: null,
globalHandlersInstalled: false,
domOperationDiagnosticsInstalled: false,
mutationBatchCount: 0
};
@@ -24,6 +25,14 @@ window.rpgRollerApi = (() => {
console.warn(debugPrefix, new Date().toISOString(), ...args);
}
function summarizeChildren(element) {
if (!element || !element.childNodes) {
return [];
}
return Array.from(element.childNodes).slice(0, 20).map(summarizeNode);
}
function summarizeNode(node) {
if (!node) {
return "<null>";
@@ -117,6 +126,53 @@ window.rpgRollerApi = (() => {
});
}
function installDomOperationDiagnostics() {
if (workspaceDiagnostics.domOperationDiagnosticsInstalled) {
return;
}
workspaceDiagnostics.domOperationDiagnosticsInstalled = true;
const originalInsertBefore = Node.prototype.insertBefore;
const originalAppendChild = Node.prototype.appendChild;
const originalRemoveChild = Node.prototype.removeChild;
const originalReplaceChild = Node.prototype.replaceChild;
Node.prototype.insertBefore = function (newNode, referenceNode) {
debug("dom insertBefore", {
parent: summarizeNode(this),
newNode: summarizeNode(newNode),
referenceNode: summarizeNode(referenceNode),
referenceParent: referenceNode ? summarizeNode(referenceNode.parentNode) : null
});
return originalInsertBefore.call(this, newNode, referenceNode);
};
Node.prototype.appendChild = function (child) {
debug("dom appendChild", {
parent: summarizeNode(this),
child: summarizeNode(child)
});
return originalAppendChild.call(this, child);
};
Node.prototype.removeChild = function (child) {
debug("dom removeChild", {
parent: summarizeNode(this),
child: summarizeNode(child)
});
return originalRemoveChild.call(this, child);
};
Node.prototype.replaceChild = function (newChild, oldChild) {
debug("dom replaceChild", {
parent: summarizeNode(this),
newChild: summarizeNode(newChild),
oldChild: summarizeNode(oldChild)
});
return originalReplaceChild.call(this, newChild, oldChild);
};
}
function summarizeMutation(mutation) {
return {
type: mutation.type,
@@ -181,6 +237,79 @@ window.rpgRollerApi = (() => {
logWorkspaceSnapshot(`phase:${label}`);
}
function bootstrapPreBlazorDiagnostics(requestPath) {
installGlobalDiagnostics();
installDomOperationDiagnostics();
const host = document.getElementById("rr-interactive-host");
workspaceDiagnostics.route = requestPath;
debug("bootstrapPreBlazorDiagnostics", {
requestPath,
readyState: document.readyState,
bodyChildren: summarizeChildren(document.body),
host: summarizeNode(host),
hostChildren: summarizeChildren(host)
});
if (!host) {
warn("bootstrapPreBlazorDiagnostics missing host", { requestPath });
return;
}
const preconnectObserver = new MutationObserver((mutations) => {
debug("preblazor host mutations", {
requestPath,
mutations: mutations.slice(0, 20).map(summarizeMutation),
bodyChildren: summarizeChildren(document.body),
hostChildren: summarizeChildren(host)
});
});
preconnectObserver.observe(host, {
subtree: true,
childList: true,
attributes: true,
characterData: false
});
const bodyObserver = new MutationObserver((mutations) => {
debug("preblazor body mutations", {
requestPath,
mutations: mutations.slice(0, 20).map(summarizeMutation),
bodyChildren: summarizeChildren(document.body),
hostChildren: summarizeChildren(host)
});
});
bodyObserver.observe(document.body, {
subtree: false,
childList: true,
attributes: false
});
queueMicrotask(() => {
debug("preblazor microtask snapshot", {
requestPath,
bodyChildren: summarizeChildren(document.body),
hostChildren: summarizeChildren(host)
});
});
requestAnimationFrame(() => {
debug("preblazor raf snapshot", {
requestPath,
bodyChildren: summarizeChildren(document.body),
hostChildren: summarizeChildren(host)
});
});
setTimeout(() => {
debug("preblazor timeout50 snapshot", {
requestPath,
bodyChildren: summarizeChildren(document.body),
hostChildren: summarizeChildren(host)
});
}, 50);
}
function toAppUrl(url) {
if (!url || typeof url !== "string") {
return url;
@@ -588,6 +717,7 @@ window.rpgRollerApi = (() => {
scrollElementToBottom,
clearInputValue,
installWorkspaceDiagnostics,
markWorkspacePhase
markWorkspacePhase,
bootstrapPreBlazorDiagnostics
};
})();