openclaw
Version:
Multi-channel AI gateway with extensible messaging integrations
185 lines • 8.11 kB
JavaScript
/**
* @license
* Copyright 2026 Google LLC
* SPDX-License-Identifier: Apache-2.0
*/
import { DevTools } from '../third_party/index.js';
import { stableIdSymbol } from '../utils/id.js';
const { formatBytesToKb } = DevTools.I18n.ByteUtilities;
export function isNodeLike(item) {
return (typeof item === 'object' && item !== null && 'id' in item && 'name' in item);
}
export function isEdgeLike(item) {
return (typeof item === 'object' &&
item !== null &&
'name' in item &&
'node' in item &&
'type' in item &&
typeof item.node === 'object' &&
item.node !== null &&
'id' in item.node &&
'name' in item.node);
}
export class HeapSnapshotFormatter {
#aggregates;
constructor(aggregates) {
this.#aggregates = aggregates;
}
static formatNodes(items) {
const lines = [];
if (items.length > 0) {
const firstItem = items[0];
if (isNodeLike(firstItem)) {
lines.push('nodeId,nodeName,type,distance,selfSize,retainedSize');
}
else if (isEdgeLike(firstItem)) {
lines.push('name,type,nodeId,nodeName,selfSize,retainedSize');
}
}
for (const item of items) {
if (isNodeLike(item)) {
lines.push(`${item.id},${item.name},${item.type},${item.distance},${formatBytesToKb(item.selfSize)},${formatBytesToKb(item.retainedSize)}`);
}
else if (isEdgeLike(item)) {
lines.push(`${item.name},${item.type},${item.node.id},${item.node.name},${formatBytesToKb(item.node.selfSize)},${formatBytesToKb(item.node.retainedSize)}`);
}
}
return lines.join('\n');
}
static formatRetainingPaths(retainingPaths) {
const lines = [];
function formatEdge(edge, depth) {
const indent = ' '.repeat(depth);
lines.push(`${indent}<- @${edge.nodeId} ${edge.nodeName} via ${edge.edgeType} ${edge.edgeName} (distance: ${edge.distance})`);
for (const child of edge.children) {
formatEdge(child, depth + 1);
}
}
for (const path of retainingPaths) {
formatEdge(path, 0);
}
return lines.join('\n');
}
static formatDominators(dominators) {
const lines = [];
lines.push('nodeId,nodeName,selfSize,retainedSize');
for (const node of dominators) {
lines.push(`${node.nodeId},${node.nodeName},${formatBytesToKb(node.selfSize)},${formatBytesToKb(node.retainedSize)}`);
}
return lines.join('\n');
}
static formatDuplicateStrings(groups) {
const lines = [];
lines.push('value,count,totalSelfSize,totalRetainedSize,truncated,nodeIds');
for (const group of groups) {
const nodeIds = group.nodes.map(n => `@${n.id}`).join(' ');
const truncated = group.truncated ?? false;
lines.push(`${JSON.stringify(group.value)},${group.count},${formatBytesToKb(group.totalSelfSize)},${formatBytesToKb(group.totalRetainedSize)},${truncated},${nodeIds}`);
}
return lines.join('\n');
}
static formatNativeContextSizes(sizes) {
const lines = [];
lines.push('nodeId,nodeName,selfSize,retainedSize,attributedSize');
const sortedContexts = [...sizes.nativeContexts].sort((a, b) => b.attributedSize - a.attributedSize);
for (const nc of sortedContexts) {
lines.push(`${nc.nodeId},${nc.nodeName},${formatBytesToKb(nc.selfSize)},${formatBytesToKb(nc.retainedSize)},${formatBytesToKb(nc.attributedSize)}`);
}
lines.push(`Shared Size: ${formatBytesToKb(sizes.sharedSize)}`);
lines.push(`Unattributed Size: ${formatBytesToKb(sizes.noAttributionSize)}`);
return lines.join('\n');
}
static formatRetainedByContextSummary(summary) {
const lines = [];
lines.push(`Context count: ${summary.contextCount}`);
lines.push(`Retained by context size: ${formatBytesToKb(summary.retainedByContextSize)} (${summary.retainedByContextCount} objects)`);
lines.push(`Not retained by context size: ${formatBytesToKb(summary.notRetainedByContextSize)} (${summary.notRetainedByContextCount} objects)`);
lines.push(`Total size: ${formatBytesToKb(summary.totalSize)}`);
return lines.join('\n');
}
#getSortedAggregates() {
return Object.values(this.#aggregates).sort((a, b) => b.maxRet - a.maxRet);
}
toString() {
const sorted = this.#getSortedAggregates();
const lines = [];
lines.push('id,name,count,selfSize,retainedSize');
for (const info of sorted) {
const id = info[stableIdSymbol] ?? '';
lines.push(`${id},${info.name},${info.count},${formatBytesToKb(info.self)},${formatBytesToKb(info.maxRet)}`);
}
return lines.join('\n');
}
toJSON() {
const sorted = this.#getSortedAggregates();
return sorted.map(info => ({
id: info[stableIdSymbol],
className: info.name,
count: info.count,
selfSize: formatBytesToKb(info.self),
retainedSize: formatBytesToKb(info.maxRet),
}));
}
static sort(aggregates) {
return Object.entries(aggregates).sort((a, b) => b[1].maxRet - a[1].maxRet);
}
static formatDiffSummary(diffs) {
const lines = [];
lines.push('index,className,addedCount,removedCount,countDelta,addedSize,removedSize,sizeDelta');
let index = 0;
for (const diff of diffs) {
lines.push(`${index},${diff.className},${diff.addedCount},${diff.removedCount},${diff.countDelta},${formatBytesToKb(diff.addedSize)},${formatBytesToKb(diff.removedSize)},${formatBytesToKb(diff.sizeDelta)}`);
index++;
}
return lines.join('\n');
}
static formatDiffDetails(diff) {
const lines = [];
lines.push(`${diff.className}: # new: ${diff.addedCount}, # deleted: ${diff.removedCount}, # delta: ${formatSignedCount(diff.countDelta)}, alloc size: ${formatSignedSize(diff.addedSize)}, freed size: ${formatSignedSize(diff.removedSize)}, size delta: ${formatSignedSize(diff.sizeDelta)}`);
const addedIds = diff.addedIds;
const addedSelfSizes = diff.addedSelfSizes;
const deletedIds = diff.deletedIds;
const deletedSelfSizes = diff.deletedSelfSizes;
lines.push(`Objects:`);
for (let i = 0; i < addedIds.length; i++) {
lines.push(` + @${addedIds[i]} (self_size: ${formatBytesToKb(addedSelfSizes[i])})`);
}
for (let i = 0; i < deletedIds.length; i++) {
lines.push(` - @${deletedIds[i]} (self_size: ${formatBytesToKb(deletedSelfSizes[i])})`);
}
return lines.join('\n');
}
static formatObjectInfo(info) {
const lines = [
`id: @${info.id}`,
`name: ${info.name}`,
`type: ${info.type}`,
`detachedness: ${formatDOMLinkState(info.detachedness)}`,
`selfSize: ${formatBytesToKb(info.selfSize)}`,
`retainedSize: ${formatBytesToKb(info.retainedSize)}`,
`distance: ${info.distance}`,
`edgeCount: ${info.edgeCount}`,
`retainerCount: ${info.retainerCount}`,
];
return lines.join('\n');
}
}
function formatDOMLinkState(state) {
switch (state) {
case 1 /* DevTools.HeapSnapshotModel.HeapSnapshotModel.DOMLinkState.ATTACHED */:
return 'attached';
case 2 /* DevTools.HeapSnapshotModel.HeapSnapshotModel.DOMLinkState.DETACHED */:
return 'detached';
case 0 /* DevTools.HeapSnapshotModel.HeapSnapshotModel.DOMLinkState.UNKNOWN */:
default:
return 'unknown';
}
}
function formatSignedCount(n) {
return n > 0 ? `+${n}` : `${n}`;
}
function formatSignedSize(bytes) {
const formatted = formatBytesToKb(bytes);
return bytes > 0 ? `+${formatted}` : formatted;
}
//# sourceMappingURL=HeapSnapshotFormatter.js.map