contents/ui/UsageDetails.js
.pragma library
.import "SafeText.js" as SafeText
var maximumSectionsPerSnapshot = 8
var maximumRowsPerSection = 24
var maximumPointsPerChart = 120
var maximumStringLength = 120
var maximumStringCodeUnitsForSafety = maximumStringLength * 32
function optionalText(value) {
if (typeof value !== "string") {
return ""
}
var trimmed = value.trim()
if (trimmed.length > maximumStringCodeUnitsForSafety) {
return ""
}
// QML JavaScript has no grapheme segmenter. The official CLI already
// validates the 120-character Swift String contract; re-counting UTF-16
// here would corrupt valid combining and emoji sequences. Keep an exact
// ASCII fast path plus a separate storage bound for untrusted payloads.
var isAscii = /^[\x00-\x7F]*$/.test(trimmed)
var redacted = isAscii
? SafeText.redactCredentialsWithinSourceLimit(trimmed, maximumStringLength)
: SafeText.redactCredentials(trimmed, maximumStringCodeUnitsForSafety)
if (redacted.length > maximumStringCodeUnitsForSafety) {
return ""
}
return isAscii
? redacted.slice(0, maximumStringLength)
: redacted
}
function normalizeSections(rawSections) {
if (!Array.isArray(rawSections)) {
return []
}
var sections = []
var sectionLimit = Math.min(rawSections.length, maximumSectionsPerSnapshot)
for (var i = 0; i < sectionLimit; i++) {
var rawSection = rawSections[i]
if (!rawSection || typeof rawSection !== "object" || Array.isArray(rawSection)) {
continue
}
var rows = []
var rawRows = Array.isArray(rawSection.rows) ? rawSection.rows : []
var rowLimit = Math.min(rawRows.length, maximumRowsPerSection)
for (var rowIndex = 0; rowIndex < rowLimit; rowIndex++) {
var rawRow = rawRows[rowIndex]
if (!rawRow || typeof rawRow !== "object" || Array.isArray(rawRow)) {
continue
}
var label = optionalText(rawRow.label)
var value = optionalText(rawRow.value)
if (label.length === 0 || value.length === 0) {
continue
}
rows.push({
label: label,
value: value,
secondaryValue: optionalText(rawRow.secondaryValue)
})
}
var rawChart = rawSection.chart
var chart = null
if (rawChart
&& typeof rawChart === "object"
&& !Array.isArray(rawChart)
&& (rawChart.kind === "bars" || rawChart.kind === "line")) {
var points = []
var rawPoints = Array.isArray(rawChart.points) ? rawChart.points : []
var pointLimit = Math.min(rawPoints.length, maximumPointsPerChart)
for (var pointIndex = 0; pointIndex < pointLimit; pointIndex++) {
var rawPoint = rawPoints[pointIndex]
if (!rawPoint || typeof rawPoint !== "object" || Array.isArray(rawPoint)) {
continue
}
var pointLabel = optionalText(rawPoint.label)
if (pointLabel.length === 0
|| typeof rawPoint.value !== "number"
|| !isFinite(rawPoint.value)) {
continue
}
points.push({
label: pointLabel,
value: rawPoint.value
})
}
chart = {
kind: rawChart.kind,
title: optionalText(rawChart.title),
unit: optionalText(rawChart.unit),
points: points
}
}
var title = optionalText(rawSection.title)
if (title.length === 0 && rows.length === 0 && chart === null) {
continue
}
sections.push({
title: title,
rows: rows,
chart: chart
})
}
return sections
}