diff --git a/pom.xml b/pom.xml
index af3ec7ad2..ddee8763e 100644
--- a/pom.xml
+++ b/pom.xml
@@ -43,7 +43,7 @@
OSV config contains a narrow temporary ignore because OSV Scanner
v2.3.8 still reports 2.22.1 even though the advisory text names it as
patched. -->
- 2.22.1
+ 2.22.2
1.5.35
diff --git a/src/main/resources/static/assets/viewer/demo.js b/src/main/resources/static/assets/viewer/demo.js
index 42998be56..6c863cf98 100644
--- a/src/main/resources/static/assets/viewer/demo.js
+++ b/src/main/resources/static/assets/viewer/demo.js
@@ -114,6 +114,9 @@ function renderHistory(history = loadHistory()) {
el.historyBody.textContent = "";
el.emptyHistory.hidden = history.length > 0;
+ // ⚡ Bolt: Use DocumentFragment to batch DOM insertions
+ // Impact: Reduces DOM reflows from O(n) to O(1), improving render performance for large history lists.
+ const fragment = document.createDocumentFragment();
for (const job of history) {
const row = document.createElement("tr");
const fileCell = document.createElement("td");
@@ -143,18 +146,19 @@ function renderHistory(history = loadHistory()) {
}
row.append(fileCell, statusCell, submittedCell, actionsCell);
- el.historyBody.appendChild(row);
+ fragment.appendChild(row);
}
+ el.historyBody.appendChild(fragment);
renderRecoveryEvidence(history);
}
-function addDetailRow(label, value) {
+function addDetailRow(fragment, label, value) {
const term = document.createElement("dt");
const description = document.createElement("dd");
term.textContent = label;
description.textContent = formatDetailValue(value);
- el.jobDetailBody.append(term, description);
+ fragment.append(term, description);
}
function formatDetailValue(value) {
@@ -177,17 +181,21 @@ function renderJobDetail(detail) {
: "Operational evidence";
el.jobDetailBody.textContent = "";
- addDetailRow("Job ID", detail.jobId);
- addDetailRow("Tenant", detail.tenantId);
- addDetailRow("Status", detail.status);
- addDetailRow("Message", detail.message);
- addDetailRow("Attempts", `${detail.attemptCount ?? 0} / ${detail.maxAttempts ?? "n/a"}`);
- addDetailRow("Dead-lettered", Boolean(detail.deadLettered));
- addDetailRow("Retry at", detail.retryAt);
- addDetailRow("Created", detail.createdAt);
- addDetailRow("Started", detail.startedAt);
- addDetailRow("Completed", detail.completedAt);
- addDetailRow("Artifact", detail.convertedResourcePath);
+ // ⚡ Bolt: Use DocumentFragment to batch DOM insertions for detail rows
+ // Impact: Prevents multiple layout recalculations by applying all detail rows in a single DOM update.
+ const fragment = document.createDocumentFragment();
+ addDetailRow(fragment, "Job ID", detail.jobId);
+ addDetailRow(fragment, "Tenant", detail.tenantId);
+ addDetailRow(fragment, "Status", detail.status);
+ addDetailRow(fragment, "Message", detail.message);
+ addDetailRow(fragment, "Attempts", `${detail.attemptCount ?? 0} / ${detail.maxAttempts ?? "n/a"}`);
+ addDetailRow(fragment, "Dead-lettered", Boolean(detail.deadLettered));
+ addDetailRow(fragment, "Retry at", detail.retryAt);
+ addDetailRow(fragment, "Created", detail.createdAt);
+ addDetailRow(fragment, "Started", detail.startedAt);
+ addDetailRow(fragment, "Completed", detail.completedAt);
+ addDetailRow(fragment, "Artifact", detail.convertedResourcePath);
+ el.jobDetailBody.appendChild(fragment);
el.retryJobBtn.hidden = !detail.deadLettered;
}
diff --git a/src/test/js/demo-integration.test.mjs b/src/test/js/demo-integration.test.mjs
index 0f0437ea2..bd7e6963c 100644
--- a/src/test/js/demo-integration.test.mjs
+++ b/src/test/js/demo-integration.test.mjs
@@ -1,7 +1,7 @@
import assert from "node:assert/strict";
import test from "node:test";
-import { MockElement } from "./mock-dom.mjs";
+import { MockElement, MockDocumentFragment } from "./mock-dom.mjs";
const elementIds = [
"upload-form",
@@ -54,6 +54,9 @@ test("the executable demo renders inert actions and blocks repeated status activ
createElement(tagName) {
return new MockElement(tagName);
},
+ createDocumentFragment() {
+ return new MockDocumentFragment();
+ }
};
globalThis.window = {
confirm() {
diff --git a/src/test/js/dom-utils.test.mjs b/src/test/js/dom-utils.test.mjs
index 94b151063..9daf9358a 100644
--- a/src/test/js/dom-utils.test.mjs
+++ b/src/test/js/dom-utils.test.mjs
@@ -6,11 +6,14 @@ import {
createLink,
setBusyState
} from "../../main/resources/static/assets/viewer/dom-utils.js";
-import { MockElement, MockTextNode } from "./mock-dom.mjs";
+import { MockElement, MockTextNode, MockDocumentFragment } from "./mock-dom.mjs";
globalThis.document = {
createElement(tagName) {
return new MockElement(tagName);
+ },
+ createDocumentFragment() {
+ return new MockDocumentFragment();
}
};
diff --git a/src/test/js/mock-dom.mjs b/src/test/js/mock-dom.mjs
index 774116bc0..f4d0a91c1 100644
--- a/src/test/js/mock-dom.mjs
+++ b/src/test/js/mock-dom.mjs
@@ -1,3 +1,23 @@
+export class MockDocumentFragment {
+ constructor() {
+ this.childNodes = [];
+ }
+
+ appendChild(node) {
+ if (node && node.constructor && node.constructor.name === "MockDocumentFragment") {
+ this.childNodes.push(...node.childNodes);
+ node.childNodes = []; // DocumentFragment gets emptied when appended
+ return node;
+ }
+ this.childNodes.push(node);
+ return node;
+ }
+
+ append(...nodes) {
+ this.childNodes.push(...nodes);
+ }
+}
+
export class MockTextNode {
constructor(text) {
this.type = "text";
@@ -31,6 +51,11 @@ export class MockElement {
}
appendChild(node) {
+ if (node && node.constructor && node.constructor.name === "MockDocumentFragment") {
+ this.childNodes.push(...node.childNodes);
+ node.childNodes = []; // DocumentFragment gets emptied when appended
+ return node;
+ }
this.childNodes.push(node);
return node;
}