@paperjsx/json-to-xlsx-pro 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +7 -0
- package/README.md +61 -0
- package/dist/assembly/xlsx-assembler.d.ts +53 -0
- package/dist/assembly/xlsx-assembler.d.ts.map +1 -0
- package/dist/benchmarks/phase2.d.ts +72 -0
- package/dist/benchmarks/phase2.d.ts.map +1 -0
- package/dist/benchmarks/phase2.js +14 -0
- package/dist/benchmarks/phase2.js.map +7 -0
- package/dist/benchmarks/report.d.ts +21 -0
- package/dist/benchmarks/report.d.ts.map +1 -0
- package/dist/benchmarks/report.js +13 -0
- package/dist/benchmarks/report.js.map +7 -0
- package/dist/benchmarks/rigorous.d.ts +85 -0
- package/dist/benchmarks/rigorous.d.ts.map +1 -0
- package/dist/benchmarks/rigorous.js +534 -0
- package/dist/benchmarks/rigorous.js.map +7 -0
- package/dist/chaos-lab/index.d.ts +69 -0
- package/dist/chaos-lab/index.d.ts.map +1 -0
- package/dist/chaos-lab/index.js +1696 -0
- package/dist/chaos-lab/index.js.map +7 -0
- package/dist/chunk-2IVXCH6I.js +1002 -0
- package/dist/chunk-2IVXCH6I.js.map +7 -0
- package/dist/chunk-IYMS2CLX.js +478 -0
- package/dist/chunk-IYMS2CLX.js.map +7 -0
- package/dist/chunk-PQSLPEN5.js +290 -0
- package/dist/chunk-PQSLPEN5.js.map +7 -0
- package/dist/chunk-QDWDSM74.js +142 -0
- package/dist/chunk-QDWDSM74.js.map +7 -0
- package/dist/chunk-S5RMAWLC.js +25347 -0
- package/dist/chunk-S5RMAWLC.js.map +7 -0
- package/dist/chunk-Z2JSZFNG.js +308 -0
- package/dist/chunk-Z2JSZFNG.js.map +7 -0
- package/dist/diagnostics/corruption.d.ts +9 -0
- package/dist/diagnostics/corruption.d.ts.map +1 -0
- package/dist/diagnostics/workloads.d.ts +6 -0
- package/dist/diagnostics/workloads.d.ts.map +1 -0
- package/dist/diff/document-diff.d.ts +5 -0
- package/dist/diff/document-diff.d.ts.map +1 -0
- package/dist/document-diff/src/index.d.ts +50 -0
- package/dist/document-diff/src/index.d.ts.map +1 -0
- package/dist/errors.d.ts +35 -0
- package/dist/errors.d.ts.map +1 -0
- package/dist/fixtures/phase1.d.ts +14 -0
- package/dist/fixtures/phase1.d.ts.map +1 -0
- package/dist/fixtures/phase2.d.ts +9 -0
- package/dist/fixtures/phase2.d.ts.map +1 -0
- package/dist/fixtures/phase3.d.ts +9 -0
- package/dist/fixtures/phase3.d.ts.map +1 -0
- package/dist/fixtures/phase4.d.ts +10 -0
- package/dist/fixtures/phase4.d.ts.map +1 -0
- package/dist/fixtures/phase5.d.ts +9 -0
- package/dist/fixtures/phase5.d.ts.map +1 -0
- package/dist/formulas/builder.d.ts +91 -0
- package/dist/formulas/builder.d.ts.map +1 -0
- package/dist/formulas/evaluator.d.ts +25 -0
- package/dist/formulas/evaluator.d.ts.map +1 -0
- package/dist/formulas/shift.d.ts +14 -0
- package/dist/formulas/shift.d.ts.map +1 -0
- package/dist/index-pro.d.ts +12 -0
- package/dist/index-pro.d.ts.map +1 -0
- package/dist/index.d.ts +41 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +2167 -0
- package/dist/index.js.map +7 -0
- package/dist/layout/column-width.d.ts +32 -0
- package/dist/layout/column-width.d.ts.map +1 -0
- package/dist/layout/row-height.d.ts +3 -0
- package/dist/layout/row-height.d.ts.map +1 -0
- package/dist/preflight.d.ts +31 -0
- package/dist/preflight.d.ts.map +1 -0
- package/dist/public-quality-types.d.ts +44 -0
- package/dist/public-quality-types.d.ts.map +1 -0
- package/dist/quality/accessibility-contract.d.ts +38 -0
- package/dist/quality/accessibility-contract.d.ts.map +1 -0
- package/dist/quality/accessibility.d.ts +33 -0
- package/dist/quality/accessibility.d.ts.map +1 -0
- package/dist/quality/shared-quality.d.ts +4 -0
- package/dist/quality/shared-quality.d.ts.map +1 -0
- package/dist/quality/structural-validation.d.ts +11 -0
- package/dist/quality/structural-validation.d.ts.map +1 -0
- package/dist/quality/workbook-quality.d.ts +63 -0
- package/dist/quality/workbook-quality.d.ts.map +1 -0
- package/dist/render-metrics.d.ts +67 -0
- package/dist/render-metrics.d.ts.map +1 -0
- package/dist/render-plan.d.ts +40 -0
- package/dist/render-plan.d.ts.map +1 -0
- package/dist/serializers/chart-serializer.d.ts +3 -0
- package/dist/serializers/chart-serializer.d.ts.map +1 -0
- package/dist/serializers/comment-serializer.d.ts +12 -0
- package/dist/serializers/comment-serializer.d.ts.map +1 -0
- package/dist/serializers/doc-props-serializer.d.ts +5 -0
- package/dist/serializers/doc-props-serializer.d.ts.map +1 -0
- package/dist/serializers/drawing-serializer.d.ts +24 -0
- package/dist/serializers/drawing-serializer.d.ts.map +1 -0
- package/dist/serializers/package-serializer.d.ts +13 -0
- package/dist/serializers/package-serializer.d.ts.map +1 -0
- package/dist/serializers/pivot-serializer.d.ts +37 -0
- package/dist/serializers/pivot-serializer.d.ts.map +1 -0
- package/dist/serializers/shared-strings.d.ts +11 -0
- package/dist/serializers/shared-strings.d.ts.map +1 -0
- package/dist/serializers/sheet-serializer.d.ts +58 -0
- package/dist/serializers/sheet-serializer.d.ts.map +1 -0
- package/dist/serializers/sheet-xml-builder.d.ts +33 -0
- package/dist/serializers/sheet-xml-builder.d.ts.map +1 -0
- package/dist/serializers/style-registry.d.ts +2 -0
- package/dist/serializers/style-registry.d.ts.map +1 -0
- package/dist/serializers/table-serializer.d.ts +20 -0
- package/dist/serializers/table-serializer.d.ts.map +1 -0
- package/dist/serializers/theme-serializer.d.ts +3 -0
- package/dist/serializers/theme-serializer.d.ts.map +1 -0
- package/dist/serializers/workbook-serializer.d.ts +16 -0
- package/dist/serializers/workbook-serializer.d.ts.map +1 -0
- package/dist/serializers/worksheet-rels-serializer.d.ts +7 -0
- package/dist/serializers/worksheet-rels-serializer.d.ts.map +1 -0
- package/dist/source-js-extension-loader.mjs +44 -0
- package/dist/spreadsheet-engine.d.ts +50 -0
- package/dist/spreadsheet-engine.d.ts.map +1 -0
- package/dist/styles/border-serializer.d.ts +6 -0
- package/dist/styles/border-serializer.d.ts.map +1 -0
- package/dist/styles/color.d.ts +5 -0
- package/dist/styles/color.d.ts.map +1 -0
- package/dist/styles/component-registry.d.ts +13 -0
- package/dist/styles/component-registry.d.ts.map +1 -0
- package/dist/styles/conditional-formatting.d.ts +8 -0
- package/dist/styles/conditional-formatting.d.ts.map +1 -0
- package/dist/styles/fill-serializer.d.ts +9 -0
- package/dist/styles/fill-serializer.d.ts.map +1 -0
- package/dist/styles/font-serializer.d.ts +11 -0
- package/dist/styles/font-serializer.d.ts.map +1 -0
- package/dist/styles/numfmt-registry.d.ts +7 -0
- package/dist/styles/numfmt-registry.d.ts.map +1 -0
- package/dist/styles/presets.d.ts +190 -0
- package/dist/styles/presets.d.ts.map +1 -0
- package/dist/styles/style-registry.d.ts +24 -0
- package/dist/styles/style-registry.d.ts.map +1 -0
- package/dist/styles/style-utils.d.ts +11 -0
- package/dist/styles/style-utils.d.ts.map +1 -0
- package/dist/template-assembler.d.ts +26 -0
- package/dist/template-assembler.d.ts.map +1 -0
- package/dist/template-parser.d.ts +96 -0
- package/dist/template-parser.d.ts.map +1 -0
- package/dist/types/spreadsheet-ast.d.ts +470 -0
- package/dist/types/spreadsheet-ast.d.ts.map +1 -0
- package/dist/utils/cell-ref.d.ts +21 -0
- package/dist/utils/cell-ref.d.ts.map +1 -0
- package/dist/utils/date.d.ts +32 -0
- package/dist/utils/date.d.ts.map +1 -0
- package/dist/utils/hyperlinks.d.ts +16 -0
- package/dist/utils/hyperlinks.d.ts.map +1 -0
- package/dist/utils/xml.d.ts +7 -0
- package/dist/utils/xml.d.ts.map +1 -0
- package/dist/validation/spreadsheet-schema.d.ts +1513 -0
- package/dist/validation/spreadsheet-schema.d.ts.map +1 -0
- package/dist/workers/sheet-serialization-worker-pool.d.ts +32 -0
- package/dist/workers/sheet-serialization-worker-pool.d.ts.map +1 -0
- package/dist/workers/sheet-serializer-worker.d.ts +2 -0
- package/dist/workers/sheet-serializer-worker.d.ts.map +1 -0
- package/dist/workers/sheet-serializer-worker.js +2565 -0
- package/dist/workers/sheet-serializer-worker.js.map +7 -0
- package/dist/worksheet/structure.d.ts +33 -0
- package/dist/worksheet/structure.d.ts.map +1 -0
- package/dist-pro/benchmarks/phase2.js +1 -0
- package/dist-pro/benchmarks/report.js +1 -0
- package/dist-pro/benchmarks/rigorous.js +2 -0
- package/dist-pro/chaos-lab/index.js +2 -0
- package/dist-pro/chunk-FFIHITWB.js +1 -0
- package/dist-pro/chunk-INDNGGXB.js +2 -0
- package/dist-pro/chunk-K2MQYNU6.js +1 -0
- package/dist-pro/chunk-MEZHQFH3.js +2 -0
- package/dist-pro/chunk-RB42Q36L.js +1 -0
- package/dist-pro/chunk-WYTH4W4N.js +48 -0
- package/dist-pro/index.js +3 -0
- package/dist-pro/source-js-extension-loader.mjs +44 -0
- package/dist-pro/workers/sheet-serializer-worker.js +3 -0
- package/package.json +62 -0
|
@@ -0,0 +1,1002 @@
|
|
|
1
|
+
import {
|
|
2
|
+
PRESET_NAMES,
|
|
3
|
+
SpreadsheetEngine,
|
|
4
|
+
StyleRegistry,
|
|
5
|
+
computeColumnLayout,
|
|
6
|
+
getPhase1Fixture,
|
|
7
|
+
isRichTextValue,
|
|
8
|
+
resolveCellStyle,
|
|
9
|
+
serializeConditionalFormatting
|
|
10
|
+
} from "./chunk-S5RMAWLC.js";
|
|
11
|
+
|
|
12
|
+
// src/benchmarks/phase2.ts
|
|
13
|
+
import { performance } from "node:perf_hooks";
|
|
14
|
+
import process from "node:process";
|
|
15
|
+
import { Buffer } from "node:buffer";
|
|
16
|
+
import { createRequire } from "node:module";
|
|
17
|
+
import { resolve } from "node:path";
|
|
18
|
+
import { pathToFileURL } from "node:url";
|
|
19
|
+
import { XMLParser } from "fast-xml-parser";
|
|
20
|
+
import JSZip from "jszip";
|
|
21
|
+
var xmlParser = new XMLParser({
|
|
22
|
+
ignoreAttributes: false,
|
|
23
|
+
attributeNamePrefix: "@_",
|
|
24
|
+
parseTagValue: false
|
|
25
|
+
});
|
|
26
|
+
var BORDER_STYLES = [
|
|
27
|
+
"thin",
|
|
28
|
+
"medium",
|
|
29
|
+
"thick",
|
|
30
|
+
"double",
|
|
31
|
+
"dotted",
|
|
32
|
+
"dashed",
|
|
33
|
+
"dashDot",
|
|
34
|
+
"dashDotDot",
|
|
35
|
+
"hair",
|
|
36
|
+
"mediumDashed",
|
|
37
|
+
"mediumDashDot",
|
|
38
|
+
"mediumDashDotDot",
|
|
39
|
+
"slantDashDot"
|
|
40
|
+
];
|
|
41
|
+
var H_ALIGNMENTS = [
|
|
42
|
+
"left",
|
|
43
|
+
"center",
|
|
44
|
+
"right",
|
|
45
|
+
"justify",
|
|
46
|
+
"distributed",
|
|
47
|
+
"general"
|
|
48
|
+
];
|
|
49
|
+
var V_ALIGNMENTS = ["top", "center", "bottom"];
|
|
50
|
+
var FORMAT_ALIASES = [
|
|
51
|
+
"currency",
|
|
52
|
+
"currency:KRW",
|
|
53
|
+
"currency:EUR",
|
|
54
|
+
"percentage",
|
|
55
|
+
"percentage:2",
|
|
56
|
+
"date",
|
|
57
|
+
"datetime",
|
|
58
|
+
"accounting",
|
|
59
|
+
"number:0",
|
|
60
|
+
"number:2"
|
|
61
|
+
];
|
|
62
|
+
function percentile(sorted, point) {
|
|
63
|
+
if (sorted.length === 0) {
|
|
64
|
+
return 0;
|
|
65
|
+
}
|
|
66
|
+
const position = Math.min(sorted.length - 1, Math.max(0, Math.ceil(point / 100 * sorted.length) - 1));
|
|
67
|
+
return sorted[position];
|
|
68
|
+
}
|
|
69
|
+
function summarize(durations) {
|
|
70
|
+
const sorted = [...durations].sort((left, right) => left - right);
|
|
71
|
+
return {
|
|
72
|
+
p50: percentile(sorted, 50),
|
|
73
|
+
p95: percentile(sorted, 95),
|
|
74
|
+
max: sorted[sorted.length - 1] ?? 0
|
|
75
|
+
};
|
|
76
|
+
}
|
|
77
|
+
function maybeGc() {
|
|
78
|
+
if (typeof global.gc === "function") {
|
|
79
|
+
global.gc();
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
async function readZipEntry(buffer, entryPath) {
|
|
83
|
+
const zip = await JSZip.loadAsync(buffer);
|
|
84
|
+
const file = zip.file(entryPath);
|
|
85
|
+
if (!file) {
|
|
86
|
+
throw new Error(`Missing ZIP entry: ${entryPath}`);
|
|
87
|
+
}
|
|
88
|
+
return file.async("string");
|
|
89
|
+
}
|
|
90
|
+
function formatMs(value) {
|
|
91
|
+
return `${value.toFixed(1)}ms`;
|
|
92
|
+
}
|
|
93
|
+
function formatKb(value) {
|
|
94
|
+
return `${(value / 1024).toFixed(1)} KB`;
|
|
95
|
+
}
|
|
96
|
+
function formatRatio(value) {
|
|
97
|
+
return `${(value * 100).toFixed(1)}%`;
|
|
98
|
+
}
|
|
99
|
+
function formatNullableRatio(value) {
|
|
100
|
+
return Number.isFinite(value) ? value.toFixed(1) : "n/a";
|
|
101
|
+
}
|
|
102
|
+
function formatRenderDecomposition(stageMetrics) {
|
|
103
|
+
return [
|
|
104
|
+
`worksheet ${formatMs(stageMetrics.worksheetSerializationTimeMs)}`,
|
|
105
|
+
`styles ${formatMs(stageMetrics.stylesSerializationTimeMs)}`,
|
|
106
|
+
`sharedStrings ${formatMs(stageMetrics.sharedStringsSerializationTimeMs)}`,
|
|
107
|
+
`package ${formatMs(stageMetrics.packageSerializationTimeMs)}`,
|
|
108
|
+
`archive ${formatMs(stageMetrics.archiveFinalizationTimeMs)}`
|
|
109
|
+
].join("; ");
|
|
110
|
+
}
|
|
111
|
+
function formatKeyPartBytes(keyPartBytes) {
|
|
112
|
+
const hasZipContribution = keyPartBytes.sheet1XmlZipContributionBytes !== void 0 || keyPartBytes.stylesXmlZipContributionBytes !== void 0 || keyPartBytes.sharedStringsXmlZipContributionBytes !== void 0 || keyPartBytes.otherZipContributionBytes !== void 0;
|
|
113
|
+
if (hasZipContribution) {
|
|
114
|
+
return [
|
|
115
|
+
`sheet1.xml raw ${formatKb(keyPartBytes.sheet1XmlBytes)} / zip ${formatKb(keyPartBytes.sheet1XmlZipContributionBytes ?? 0)}`,
|
|
116
|
+
`styles.xml raw ${formatKb(keyPartBytes.stylesXmlBytes)} / zip ${formatKb(keyPartBytes.stylesXmlZipContributionBytes ?? 0)}`,
|
|
117
|
+
`sharedStrings.xml raw ${formatKb(keyPartBytes.sharedStringsXmlBytes)} / zip ${formatKb(keyPartBytes.sharedStringsXmlZipContributionBytes ?? 0)}`,
|
|
118
|
+
`other zip ${formatKb(keyPartBytes.otherZipContributionBytes ?? 0)}`,
|
|
119
|
+
`total zip ${formatKb(keyPartBytes.zipBytes)}`
|
|
120
|
+
].join("; ");
|
|
121
|
+
}
|
|
122
|
+
return [
|
|
123
|
+
`sheet1.xml ${formatKb(keyPartBytes.sheet1XmlBytes)}`,
|
|
124
|
+
`styles.xml ${formatKb(keyPartBytes.stylesXmlBytes)}`,
|
|
125
|
+
`sharedStrings.xml ${formatKb(keyPartBytes.sharedStringsXmlBytes)}`,
|
|
126
|
+
`zip ${formatKb(keyPartBytes.zipBytes)}`
|
|
127
|
+
].join("; ");
|
|
128
|
+
}
|
|
129
|
+
function inferPayloadDominantPart(keyPartBytes) {
|
|
130
|
+
const candidates = [
|
|
131
|
+
{ part: "worksheet", bytes: keyPartBytes.sheet1XmlZipContributionBytes ?? keyPartBytes.sheet1XmlCompressedBytes ?? keyPartBytes.sheet1XmlBytes },
|
|
132
|
+
{ part: "styles", bytes: keyPartBytes.stylesXmlZipContributionBytes ?? keyPartBytes.stylesXmlCompressedBytes ?? keyPartBytes.stylesXmlBytes },
|
|
133
|
+
{ part: "sharedStrings", bytes: keyPartBytes.sharedStringsXmlZipContributionBytes ?? keyPartBytes.sharedStringsXmlCompressedBytes ?? keyPartBytes.sharedStringsXmlBytes },
|
|
134
|
+
{ part: "other", bytes: keyPartBytes.otherZipContributionBytes ?? 0 }
|
|
135
|
+
];
|
|
136
|
+
return candidates.reduce((best, candidate) => candidate.bytes > best.bytes ? candidate : best).part;
|
|
137
|
+
}
|
|
138
|
+
function classifyWorkbookSizeDiagnostics(keyPartBytes, targetBytes) {
|
|
139
|
+
const dominantPart = inferPayloadDominantPart(keyPartBytes);
|
|
140
|
+
const dominantBytes = dominantPart === "worksheet" ? keyPartBytes.sheet1XmlZipContributionBytes ?? keyPartBytes.sheet1XmlCompressedBytes ?? keyPartBytes.sheet1XmlBytes : dominantPart === "styles" ? keyPartBytes.stylesXmlZipContributionBytes ?? keyPartBytes.stylesXmlCompressedBytes ?? keyPartBytes.stylesXmlBytes : dominantPart === "sharedStrings" ? keyPartBytes.sharedStringsXmlZipContributionBytes ?? keyPartBytes.sharedStringsXmlCompressedBytes ?? keyPartBytes.sharedStringsXmlBytes : keyPartBytes.otherZipContributionBytes ?? 0;
|
|
141
|
+
const remainderBytes = Math.max(0, keyPartBytes.zipBytes - dominantBytes);
|
|
142
|
+
const practicalFloorGapBytes = Math.max(0, dominantBytes - targetBytes);
|
|
143
|
+
const classification = dominantPart === "worksheet" && dominantBytes > targetBytes * 1.2 && remainderBytes <= targetBytes * 0.1 ? "benchmark-target-mismatch-candidate" : "active-performance-debt";
|
|
144
|
+
return {
|
|
145
|
+
classification,
|
|
146
|
+
payloadDominantPart: dominantPart,
|
|
147
|
+
practicalFloorGapBytes,
|
|
148
|
+
keyPartBytes
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function inferBottleneck(stageMetrics) {
|
|
152
|
+
const serializerTime = stageMetrics.worksheetSerializationTimeMs + stageMetrics.stylesSerializationTimeMs + stageMetrics.sharedStringsSerializationTimeMs + stageMetrics.packageSerializationTimeMs;
|
|
153
|
+
if (stageMetrics.archiveFinalizationTimeMs >= serializerTime * 0.85) {
|
|
154
|
+
return "archive-bound";
|
|
155
|
+
}
|
|
156
|
+
if (serializerTime >= stageMetrics.archiveFinalizationTimeMs * 1.5) {
|
|
157
|
+
return "serializer-bound";
|
|
158
|
+
}
|
|
159
|
+
return "mixed";
|
|
160
|
+
}
|
|
161
|
+
function benchmarkResult(id, group, name, target, status, observed, notes, diagnostics) {
|
|
162
|
+
return { id, group, name, target, status, observed, notes, diagnostics };
|
|
163
|
+
}
|
|
164
|
+
function passFailFromUpperBound(value, max) {
|
|
165
|
+
return value <= max ? "pass" : "fail";
|
|
166
|
+
}
|
|
167
|
+
function classifySizeEfficiencyStatus(value, max, diagnostics) {
|
|
168
|
+
if (value <= max) {
|
|
169
|
+
return "pass";
|
|
170
|
+
}
|
|
171
|
+
return diagnostics?.classification === "benchmark-target-mismatch-candidate" ? "warn" : "fail";
|
|
172
|
+
}
|
|
173
|
+
function passFailFromLowerBound(value, min) {
|
|
174
|
+
return value >= min ? "pass" : "fail";
|
|
175
|
+
}
|
|
176
|
+
function createStyle(index) {
|
|
177
|
+
const primary = index * 2654435761 % 16777215;
|
|
178
|
+
const secondary = index * 40503 % 16777215;
|
|
179
|
+
const tertiary = index * 811 % 16777215;
|
|
180
|
+
return {
|
|
181
|
+
font: {
|
|
182
|
+
family: index % 3 === 0 ? "Calibri" : index % 3 === 1 ? "Arial" : "Courier New",
|
|
183
|
+
size: 10 + index % 4,
|
|
184
|
+
bold: index % 2 === 0,
|
|
185
|
+
italic: index % 5 === 0,
|
|
186
|
+
underline: index % 7 === 0 ? "single" : void 0,
|
|
187
|
+
color: `#${primary.toString(16).padStart(6, "0")}`
|
|
188
|
+
},
|
|
189
|
+
fill: {
|
|
190
|
+
color: `#${secondary.toString(16).padStart(6, "0")}`
|
|
191
|
+
},
|
|
192
|
+
border: {
|
|
193
|
+
bottom: {
|
|
194
|
+
style: BORDER_STYLES[index % BORDER_STYLES.length],
|
|
195
|
+
color: `#${tertiary.toString(16).padStart(6, "0")}`
|
|
196
|
+
}
|
|
197
|
+
},
|
|
198
|
+
alignment: {
|
|
199
|
+
horizontal: H_ALIGNMENTS[index % H_ALIGNMENTS.length],
|
|
200
|
+
vertical: V_ALIGNMENTS[index % V_ALIGNMENTS.length],
|
|
201
|
+
wrapText: index % 4 === 0
|
|
202
|
+
},
|
|
203
|
+
numberFormat: FORMAT_ALIASES[index % FORMAT_ALIASES.length]
|
|
204
|
+
};
|
|
205
|
+
}
|
|
206
|
+
function createStylePalette(size) {
|
|
207
|
+
return Array.from({ length: size }, (_unused, index) => createStyle(index));
|
|
208
|
+
}
|
|
209
|
+
function createStyledWorkbook(rowCount, colCount, paletteSize, sheetName) {
|
|
210
|
+
const palette = createStylePalette(paletteSize);
|
|
211
|
+
const rows = Array.from({ length: rowCount }, (_rowUnused, rowIndex) => ({
|
|
212
|
+
cells: Array.from({ length: colCount }, (_colUnused, colIndex) => {
|
|
213
|
+
const ordinal = rowIndex * colCount + colIndex;
|
|
214
|
+
const style = palette[ordinal % palette.length];
|
|
215
|
+
if (colIndex % 5 === 0) {
|
|
216
|
+
return { value: `Row ${rowIndex + 1}`, style };
|
|
217
|
+
}
|
|
218
|
+
if (colIndex % 5 === 1) {
|
|
219
|
+
return { value: ordinal, style };
|
|
220
|
+
}
|
|
221
|
+
if (colIndex % 5 === 2) {
|
|
222
|
+
return { value: (rowIndex + 1) / (colIndex + 1), style };
|
|
223
|
+
}
|
|
224
|
+
if (colIndex % 5 === 3) {
|
|
225
|
+
return { value: rowIndex % 2 === 0, style };
|
|
226
|
+
}
|
|
227
|
+
return { value: new Date(Date.UTC(2026, 0, rowIndex % 28 + 1)), style };
|
|
228
|
+
})
|
|
229
|
+
}));
|
|
230
|
+
return {
|
|
231
|
+
meta: {
|
|
232
|
+
title: `${sheetName} ${rowCount}x${colCount}`,
|
|
233
|
+
creator: "PaperJSX"
|
|
234
|
+
},
|
|
235
|
+
sheets: [
|
|
236
|
+
{
|
|
237
|
+
name: sheetName,
|
|
238
|
+
rows
|
|
239
|
+
}
|
|
240
|
+
]
|
|
241
|
+
};
|
|
242
|
+
}
|
|
243
|
+
function createPresetBenchmarkDocument() {
|
|
244
|
+
return {
|
|
245
|
+
sheets: [
|
|
246
|
+
{
|
|
247
|
+
name: "Presets",
|
|
248
|
+
rows: PRESET_NAMES.map((presetName, index) => ({
|
|
249
|
+
cells: [
|
|
250
|
+
{ value: presetName },
|
|
251
|
+
{ value: `Preview ${index + 1}`, style: presetName }
|
|
252
|
+
]
|
|
253
|
+
}))
|
|
254
|
+
}
|
|
255
|
+
]
|
|
256
|
+
};
|
|
257
|
+
}
|
|
258
|
+
function createUniqueStyleDocument(uniqueCount) {
|
|
259
|
+
return {
|
|
260
|
+
sheets: [
|
|
261
|
+
{
|
|
262
|
+
name: "UniqueStyles",
|
|
263
|
+
rows: Array.from({ length: uniqueCount }, (_unused, index) => ({
|
|
264
|
+
cells: [
|
|
265
|
+
{ value: `Style ${index + 1}`, style: createStyle(index) }
|
|
266
|
+
]
|
|
267
|
+
}))
|
|
268
|
+
}
|
|
269
|
+
]
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
function createColumnCollapseSheet() {
|
|
273
|
+
return {
|
|
274
|
+
name: "Widths",
|
|
275
|
+
columns: Array.from({ length: 50 }, (_unused, groupIndex) => {
|
|
276
|
+
const width = 8 + groupIndex;
|
|
277
|
+
return Array.from({ length: 4 }, () => ({ width }));
|
|
278
|
+
}).flat(),
|
|
279
|
+
rows: [
|
|
280
|
+
{
|
|
281
|
+
cells: Array.from({ length: 200 }, (_unused, index) => ({ value: `Column ${index + 1}` }))
|
|
282
|
+
}
|
|
283
|
+
]
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
function createNumberFormatProbe() {
|
|
287
|
+
return {
|
|
288
|
+
sheets: [
|
|
289
|
+
{
|
|
290
|
+
name: "Formats",
|
|
291
|
+
rows: [
|
|
292
|
+
{
|
|
293
|
+
cells: [
|
|
294
|
+
{ value: 420000.5, style: { numberFormat: "currency" } },
|
|
295
|
+
{ value: 0.214, style: { numberFormat: "percentage:2" } },
|
|
296
|
+
{ value: /* @__PURE__ */ new Date("2026-03-27T00:00:00.000Z"), style: { numberFormat: "date" } },
|
|
297
|
+
{ value: /* @__PURE__ */ new Date("2026-03-27T13:45:00.000Z"), style: { numberFormat: "datetime" } },
|
|
298
|
+
{ value: 420000.5, style: { numberFormat: "accounting" } }
|
|
299
|
+
]
|
|
300
|
+
}
|
|
301
|
+
]
|
|
302
|
+
}
|
|
303
|
+
]
|
|
304
|
+
};
|
|
305
|
+
}
|
|
306
|
+
function createDateProbe() {
|
|
307
|
+
return {
|
|
308
|
+
sheets: [
|
|
309
|
+
{
|
|
310
|
+
name: "Dates",
|
|
311
|
+
rows: [
|
|
312
|
+
{
|
|
313
|
+
cells: [
|
|
314
|
+
{ value: /* @__PURE__ */ new Date("2026-03-27T00:00:00.000Z") },
|
|
315
|
+
{ value: /* @__PURE__ */ new Date("2026-03-27T13:45:00.000Z"), style: { numberFormat: "datetime" } }
|
|
316
|
+
]
|
|
317
|
+
}
|
|
318
|
+
]
|
|
319
|
+
}
|
|
320
|
+
]
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
function createPartialStyleProbe() {
|
|
324
|
+
return {
|
|
325
|
+
sheets: [
|
|
326
|
+
{
|
|
327
|
+
name: "Partial",
|
|
328
|
+
rows: [
|
|
329
|
+
{
|
|
330
|
+
cells: [
|
|
331
|
+
{ value: "Bold only", style: { font: { bold: true } } },
|
|
332
|
+
{ value: null, style: { fill: { color: "#FFC7CE" } } }
|
|
333
|
+
]
|
|
334
|
+
}
|
|
335
|
+
]
|
|
336
|
+
}
|
|
337
|
+
]
|
|
338
|
+
};
|
|
339
|
+
}
|
|
340
|
+
function createConditionalFormattingProbe() {
|
|
341
|
+
const rules = [
|
|
342
|
+
{
|
|
343
|
+
type: "cellIs",
|
|
344
|
+
operator: "greaterThan",
|
|
345
|
+
formula: "100000",
|
|
346
|
+
style: "success"
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
type: "colorScale",
|
|
350
|
+
scale: {
|
|
351
|
+
min: { type: "min", color: "#F8696B" },
|
|
352
|
+
mid: { type: "percentile", value: 50, color: "#FFEB84" },
|
|
353
|
+
max: { type: "max", color: "#63BE7B" }
|
|
354
|
+
}
|
|
355
|
+
},
|
|
356
|
+
{
|
|
357
|
+
type: "dataBar",
|
|
358
|
+
color: "#4472C4",
|
|
359
|
+
min: { type: "min" },
|
|
360
|
+
max: { type: "max" }
|
|
361
|
+
},
|
|
362
|
+
{
|
|
363
|
+
type: "top10",
|
|
364
|
+
rank: 5,
|
|
365
|
+
style: "warning"
|
|
366
|
+
},
|
|
367
|
+
{
|
|
368
|
+
type: "duplicateValues",
|
|
369
|
+
style: "error"
|
|
370
|
+
}
|
|
371
|
+
];
|
|
372
|
+
return [
|
|
373
|
+
{
|
|
374
|
+
ref: "A1:T5000",
|
|
375
|
+
rules
|
|
376
|
+
}
|
|
377
|
+
];
|
|
378
|
+
}
|
|
379
|
+
async function measureAsync(iterations, task, warmupIterations = 1) {
|
|
380
|
+
const durations = [];
|
|
381
|
+
let lastValue;
|
|
382
|
+
for (let index = 0; index < warmupIterations; index += 1) {
|
|
383
|
+
maybeGc();
|
|
384
|
+
lastValue = await task();
|
|
385
|
+
}
|
|
386
|
+
for (let index = 0; index < iterations; index += 1) {
|
|
387
|
+
maybeGc();
|
|
388
|
+
const started = performance.now();
|
|
389
|
+
lastValue = await task();
|
|
390
|
+
durations.push(performance.now() - started);
|
|
391
|
+
}
|
|
392
|
+
return {
|
|
393
|
+
stats: summarize(durations),
|
|
394
|
+
lastValue
|
|
395
|
+
};
|
|
396
|
+
}
|
|
397
|
+
function measureSync(iterations, task, warmupIterations = 1) {
|
|
398
|
+
const durations = [];
|
|
399
|
+
let lastValue;
|
|
400
|
+
for (let index = 0; index < warmupIterations; index += 1) {
|
|
401
|
+
maybeGc();
|
|
402
|
+
lastValue = task();
|
|
403
|
+
}
|
|
404
|
+
for (let index = 0; index < iterations; index += 1) {
|
|
405
|
+
maybeGc();
|
|
406
|
+
const started = performance.now();
|
|
407
|
+
lastValue = task();
|
|
408
|
+
durations.push(performance.now() - started);
|
|
409
|
+
}
|
|
410
|
+
return {
|
|
411
|
+
stats: summarize(durations),
|
|
412
|
+
lastValue
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
async function inspectStyles(buffer) {
|
|
416
|
+
const xml = await readZipEntry(buffer, "xl/styles.xml");
|
|
417
|
+
return {
|
|
418
|
+
xml,
|
|
419
|
+
sizeBytes: Buffer.byteLength(xml, "utf8"),
|
|
420
|
+
parsed: xmlParser.parse(xml)
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
async function inspectSheet(buffer, index = 1) {
|
|
424
|
+
const xml = await readZipEntry(buffer, `xl/worksheets/sheet${index}.xml`);
|
|
425
|
+
return {
|
|
426
|
+
xml,
|
|
427
|
+
sizeBytes: Buffer.byteLength(xml, "utf8"),
|
|
428
|
+
parsed: xmlParser.parse(xml)
|
|
429
|
+
};
|
|
430
|
+
}
|
|
431
|
+
function countCellXfs(stylesParsed) {
|
|
432
|
+
return Number(stylesParsed.styleSheet.cellXfs?.["@_count"] ?? 0);
|
|
433
|
+
}
|
|
434
|
+
function countNumFmts(stylesParsed) {
|
|
435
|
+
return Number(stylesParsed.styleSheet.numFmts?.["@_count"] ?? 0);
|
|
436
|
+
}
|
|
437
|
+
function countStyleCollection(stylesParsed, key) {
|
|
438
|
+
const container = stylesParsed.styleSheet[key];
|
|
439
|
+
if (!container) {
|
|
440
|
+
return 0;
|
|
441
|
+
}
|
|
442
|
+
const declaredCount = Number(container["@_count"]);
|
|
443
|
+
if (Number.isFinite(declaredCount)) {
|
|
444
|
+
return declaredCount;
|
|
445
|
+
}
|
|
446
|
+
const childKey = key === "numFmts" ? "numFmt" : key === "cellXfs" ? "xf" : key.slice(0, -1);
|
|
447
|
+
const child = container[childKey];
|
|
448
|
+
if (!child) {
|
|
449
|
+
return 0;
|
|
450
|
+
}
|
|
451
|
+
return Array.isArray(child) ? child.length : 1;
|
|
452
|
+
}
|
|
453
|
+
function createStylePartDiagnostics(styles, classification) {
|
|
454
|
+
const componentCounts = {
|
|
455
|
+
numFmts: countStyleCollection(styles.parsed, "numFmts"),
|
|
456
|
+
fonts: countStyleCollection(styles.parsed, "fonts"),
|
|
457
|
+
fills: countStyleCollection(styles.parsed, "fills"),
|
|
458
|
+
borders: countStyleCollection(styles.parsed, "borders"),
|
|
459
|
+
cellXfs: countStyleCollection(styles.parsed, "cellXfs"),
|
|
460
|
+
dxfs: countStyleCollection(styles.parsed, "dxfs")
|
|
461
|
+
};
|
|
462
|
+
const styleComponentCount = componentCounts.numFmts + componentCounts.fonts + componentCounts.fills + componentCounts.borders + componentCounts.cellXfs + componentCounts.dxfs;
|
|
463
|
+
return {
|
|
464
|
+
classification,
|
|
465
|
+
keyPartBytes: {
|
|
466
|
+
sheet1XmlBytes: 0,
|
|
467
|
+
stylesXmlBytes: styles.sizeBytes,
|
|
468
|
+
sharedStringsXmlBytes: 0,
|
|
469
|
+
zipBytes: 0
|
|
470
|
+
},
|
|
471
|
+
stylePart: {
|
|
472
|
+
bytes: styles.sizeBytes,
|
|
473
|
+
componentCounts,
|
|
474
|
+
bytesPerCellXf: componentCounts.cellXfs > 0 ? styles.sizeBytes / componentCounts.cellXfs : Number.NaN,
|
|
475
|
+
bytesPerStyleComponent: styleComponentCount > 0 ? styles.sizeBytes / styleComponentCount : Number.NaN
|
|
476
|
+
}
|
|
477
|
+
};
|
|
478
|
+
}
|
|
479
|
+
function formatStylePartDiagnostics(diagnostics) {
|
|
480
|
+
const stylePart = diagnostics.stylePart;
|
|
481
|
+
if (!stylePart) {
|
|
482
|
+
return "";
|
|
483
|
+
}
|
|
484
|
+
const counts = stylePart.componentCounts;
|
|
485
|
+
return ` styles diagnostics: ${formatKb(stylePart.bytes)} styles.xml; components numFmts=${counts.numFmts}, fonts=${counts.fonts}, fills=${counts.fills}, borders=${counts.borders}, cellXfs=${counts.cellXfs}, dxfs=${counts.dxfs}; bytes/cellXf=${formatNullableRatio(stylePart.bytesPerCellXf)}; bytes/component=${formatNullableRatio(stylePart.bytesPerStyleComponent)}.`;
|
|
486
|
+
}
|
|
487
|
+
function countCols(sheetParsed) {
|
|
488
|
+
const cols = sheetParsed.worksheet.cols?.col;
|
|
489
|
+
if (!cols) {
|
|
490
|
+
return 0;
|
|
491
|
+
}
|
|
492
|
+
return Array.isArray(cols) ? cols.length : 1;
|
|
493
|
+
}
|
|
494
|
+
var defaultTempExcelJsRequireBase = "/tmp/paperjsx-xlsx-bench-deps/package.json";
|
|
495
|
+
function getDefaultPackageRequireBase() {
|
|
496
|
+
return resolve(process.cwd(), "package.json");
|
|
497
|
+
}
|
|
498
|
+
function normalizeExcelJsModule(moduleValue) {
|
|
499
|
+
return moduleValue?.default ?? moduleValue;
|
|
500
|
+
}
|
|
501
|
+
async function importExcelJsFromPath(modulePath) {
|
|
502
|
+
const resolvedPath = resolve(modulePath);
|
|
503
|
+
return normalizeExcelJsModule(await import(pathToFileURL(resolvedPath).href));
|
|
504
|
+
}
|
|
505
|
+
function requireExcelJsFromBase(requireBase) {
|
|
506
|
+
const requireFromBase = createRequire(requireBase);
|
|
507
|
+
return normalizeExcelJsModule(requireFromBase("exceljs"));
|
|
508
|
+
}
|
|
509
|
+
async function loadExcelJsBenchmarkModule(options = {}) {
|
|
510
|
+
const failures = [];
|
|
511
|
+
const envModulePath = options.envModulePath ?? process.env.PAPERJSX_XLSX_EXCELJS_MODULE_PATH;
|
|
512
|
+
if (envModulePath) {
|
|
513
|
+
try {
|
|
514
|
+
return {
|
|
515
|
+
status: "loaded",
|
|
516
|
+
module: await importExcelJsFromPath(envModulePath),
|
|
517
|
+
source: `PAPERJSX_XLSX_EXCELJS_MODULE_PATH=${envModulePath}`
|
|
518
|
+
};
|
|
519
|
+
} catch (error) {
|
|
520
|
+
failures.push(`configured path ${envModulePath}: ${error instanceof Error ? error.message : String(error)}`);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
const packageRequireBase = options.packageRequireBase ?? getDefaultPackageRequireBase();
|
|
524
|
+
if (packageRequireBase !== false) {
|
|
525
|
+
try {
|
|
526
|
+
return {
|
|
527
|
+
status: "loaded",
|
|
528
|
+
module: requireExcelJsFromBase(packageRequireBase),
|
|
529
|
+
source: "package-local exceljs dependency"
|
|
530
|
+
};
|
|
531
|
+
} catch (error) {
|
|
532
|
+
failures.push(`package-local exceljs: ${error instanceof Error ? error.message : String(error)}`);
|
|
533
|
+
}
|
|
534
|
+
} else {
|
|
535
|
+
failures.push("package-local exceljs: skipped by loader options");
|
|
536
|
+
}
|
|
537
|
+
const tempRequireBase = options.tempRequireBase ?? defaultTempExcelJsRequireBase;
|
|
538
|
+
if (tempRequireBase !== false) {
|
|
539
|
+
try {
|
|
540
|
+
return {
|
|
541
|
+
status: "loaded",
|
|
542
|
+
module: requireExcelJsFromBase(tempRequireBase),
|
|
543
|
+
source: tempRequireBase
|
|
544
|
+
};
|
|
545
|
+
} catch (error) {
|
|
546
|
+
failures.push(`temp benchmark exceljs: ${error instanceof Error ? error.message : String(error)}`);
|
|
547
|
+
}
|
|
548
|
+
} else {
|
|
549
|
+
failures.push("temp benchmark exceljs: skipped by loader options");
|
|
550
|
+
}
|
|
551
|
+
return {
|
|
552
|
+
status: "missing",
|
|
553
|
+
message: `ExcelJS competitor baseline unavailable. Install exceljs as a package-local devDependency or set PAPERJSX_XLSX_EXCELJS_MODULE_PATH to an ExcelJS module file. Loader failures: ${failures.join(" | ")}`
|
|
554
|
+
};
|
|
555
|
+
}
|
|
556
|
+
function argbColor(color) {
|
|
557
|
+
if (!color) {
|
|
558
|
+
return void 0;
|
|
559
|
+
}
|
|
560
|
+
if (color.startsWith("#")) {
|
|
561
|
+
return { argb: `FF${color.slice(1).toUpperCase()}` };
|
|
562
|
+
}
|
|
563
|
+
return void 0;
|
|
564
|
+
}
|
|
565
|
+
function excelJsCellValue(value) {
|
|
566
|
+
if (isRichTextValue(value)) {
|
|
567
|
+
return { richText: value.map((run) => ({ text: run.text })) };
|
|
568
|
+
}
|
|
569
|
+
if (value && typeof value === "object" && "error" in value) {
|
|
570
|
+
return { error: value.error };
|
|
571
|
+
}
|
|
572
|
+
return value ?? null;
|
|
573
|
+
}
|
|
574
|
+
function applyExcelJsCellStyle(cell, styleInput, value) {
|
|
575
|
+
const style = resolveCellStyle(styleInput, value);
|
|
576
|
+
if (!style) {
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
if (style.numberFormat) {
|
|
580
|
+
cell.numFmt = style.numberFormat;
|
|
581
|
+
}
|
|
582
|
+
if (style.font) {
|
|
583
|
+
const fontColor = argbColor(style.font.color);
|
|
584
|
+
cell.font = {
|
|
585
|
+
name: style.font.family,
|
|
586
|
+
size: style.font.size,
|
|
587
|
+
bold: style.font.bold,
|
|
588
|
+
italic: style.font.italic,
|
|
589
|
+
underline: style.font.underline,
|
|
590
|
+
strike: style.font.strikethrough,
|
|
591
|
+
color: fontColor
|
|
592
|
+
};
|
|
593
|
+
}
|
|
594
|
+
if (style.fill?.color) {
|
|
595
|
+
cell.fill = {
|
|
596
|
+
type: "pattern",
|
|
597
|
+
pattern: "solid",
|
|
598
|
+
fgColor: argbColor(style.fill.color)
|
|
599
|
+
};
|
|
600
|
+
}
|
|
601
|
+
if (style.border) {
|
|
602
|
+
const edge = (edgeStyle) => edgeStyle ? { style: edgeStyle.style, color: argbColor(edgeStyle.color) } : void 0;
|
|
603
|
+
cell.border = {
|
|
604
|
+
top: edge(style.border.top),
|
|
605
|
+
left: edge(style.border.left),
|
|
606
|
+
bottom: edge(style.border.bottom),
|
|
607
|
+
right: edge(style.border.right)
|
|
608
|
+
};
|
|
609
|
+
}
|
|
610
|
+
if (style.alignment) {
|
|
611
|
+
cell.alignment = {
|
|
612
|
+
horizontal: style.alignment.horizontal,
|
|
613
|
+
vertical: style.alignment.vertical,
|
|
614
|
+
wrapText: style.alignment.wrapText,
|
|
615
|
+
textRotation: style.alignment.textRotation,
|
|
616
|
+
indent: style.alignment.indent,
|
|
617
|
+
shrinkToFit: style.alignment.shrinkToFit,
|
|
618
|
+
readingOrder: style.alignment.readingOrder
|
|
619
|
+
};
|
|
620
|
+
}
|
|
621
|
+
}
|
|
622
|
+
async function renderExcelJsStyledWorkbook(document, ExcelJS) {
|
|
623
|
+
const workbook = new ExcelJS.Workbook();
|
|
624
|
+
for (const sheet of document.sheets) {
|
|
625
|
+
const worksheet = workbook.addWorksheet(sheet.name);
|
|
626
|
+
sheet.rows.forEach((row, rowIndex) => {
|
|
627
|
+
const excelRow = worksheet.getRow(rowIndex + 1);
|
|
628
|
+
row.cells.forEach((cell, columnIndex) => {
|
|
629
|
+
const excelCell = excelRow.getCell(columnIndex + 1);
|
|
630
|
+
excelCell.value = excelJsCellValue(cell.value);
|
|
631
|
+
applyExcelJsCellStyle(excelCell, cell.style, cell.value);
|
|
632
|
+
});
|
|
633
|
+
excelRow.commit?.();
|
|
634
|
+
});
|
|
635
|
+
}
|
|
636
|
+
return Buffer.from(await workbook.xlsx.writeBuffer());
|
|
637
|
+
}
|
|
638
|
+
async function runStyleRegistryBenchmarks(iterations) {
|
|
639
|
+
const styles1k = Array.from({ length: 1e3 }, (_unused, index) => createStyle(index));
|
|
640
|
+
const styles10k = Array.from({ length: 1e4 }, (_unused, index) => createStyle(index));
|
|
641
|
+
const styles100kDuplicateHeavy = Array.from({ length: 1e5 }, (_unused, index) => styles10k[index % styles10k.length]);
|
|
642
|
+
const styles100kUnique = Array.from({ length: 1e5 }, (_unused, index) => createStyle(index));
|
|
643
|
+
const oneThousand = measureSync(iterations, () => {
|
|
644
|
+
const registry = new StyleRegistry();
|
|
645
|
+
for (const style of styles1k) {
|
|
646
|
+
registry.registerStyle(style);
|
|
647
|
+
}
|
|
648
|
+
return registry;
|
|
649
|
+
});
|
|
650
|
+
const tenThousand = measureSync(iterations, () => {
|
|
651
|
+
const registry = new StyleRegistry();
|
|
652
|
+
for (const style of styles10k) {
|
|
653
|
+
registry.registerStyle(style);
|
|
654
|
+
}
|
|
655
|
+
return registry;
|
|
656
|
+
});
|
|
657
|
+
const hundredThousandDuplicateHeavy = measureSync(iterations, () => {
|
|
658
|
+
const registry = new StyleRegistry();
|
|
659
|
+
for (const style of styles100kDuplicateHeavy) {
|
|
660
|
+
registry.registerStyle(style);
|
|
661
|
+
}
|
|
662
|
+
return registry;
|
|
663
|
+
});
|
|
664
|
+
const hundredThousandUnique = measureSync(iterations, () => {
|
|
665
|
+
const registry = new StyleRegistry();
|
|
666
|
+
for (const style of styles100kUnique) {
|
|
667
|
+
registry.registerStyle(style);
|
|
668
|
+
}
|
|
669
|
+
return registry;
|
|
670
|
+
});
|
|
671
|
+
return [
|
|
672
|
+
benchmarkResult("F1", "F", "StyleRegistry \u2014 1K unique styles", "< 5ms", passFailFromUpperBound(oneThousand.stats.p95, 5), `p95 ${formatMs(oneThousand.stats.p95)}`),
|
|
673
|
+
benchmarkResult("F2", "F", "StyleRegistry \u2014 10K unique styles", "< 50ms", passFailFromUpperBound(tenThousand.stats.p95, 50), `p95 ${formatMs(tenThousand.stats.p95)}`),
|
|
674
|
+
benchmarkResult("F3", "F", "StyleRegistry \u2014 100K styles, 90% duplication", "< 150ms", passFailFromUpperBound(hundredThousandDuplicateHeavy.stats.p95, 150), `p95 ${formatMs(hundredThousandDuplicateHeavy.stats.p95)}`),
|
|
675
|
+
benchmarkResult("F4", "F", "StyleRegistry \u2014 100K styles, 0% duplication", "< 400ms", passFailFromUpperBound(hundredThousandUnique.stats.p95, 400), `p95 ${formatMs(hundredThousandUnique.stats.p95)}`)
|
|
676
|
+
];
|
|
677
|
+
}
|
|
678
|
+
async function runRenderBenchmarks(iterations) {
|
|
679
|
+
const styled10kDocument = createStyledWorkbook(1e4, 20, 500, "Styled10K");
|
|
680
|
+
const styled50kDocument = createStyledWorkbook(5e4, 20, 500, "Styled50K");
|
|
681
|
+
const unstyled10kDocument = SpreadsheetEngine.validate(getPhase1Fixture("large-10k").document);
|
|
682
|
+
const validatedStyled10kDocument = SpreadsheetEngine.validate(styled10kDocument);
|
|
683
|
+
const validatedStyled50kDocument = SpreadsheetEngine.validate(styled50kDocument);
|
|
684
|
+
const styled10k = await measureAsync(iterations, () => SpreadsheetEngine.renderValidated(validatedStyled10kDocument));
|
|
685
|
+
const styled50k = await measureAsync(Math.max(1, Math.min(iterations, 2)), () => SpreadsheetEngine.renderValidated(validatedStyled50kDocument));
|
|
686
|
+
const unstyled10k = await measureAsync(iterations, () => SpreadsheetEngine.renderValidated(unstyled10kDocument));
|
|
687
|
+
const styled10kSnapshot = await SpreadsheetEngine.renderWithMetrics(styled10kDocument);
|
|
688
|
+
const styled50kSnapshot = await SpreadsheetEngine.renderWithMetrics(styled50kDocument);
|
|
689
|
+
const styleOverheadRatio = styled10k.stats.p95 / Math.max(unstyled10k.stats.p95, 1);
|
|
690
|
+
const styled10kPerformanceDiagnostics = {
|
|
691
|
+
bottleneck: inferBottleneck(styled10kSnapshot.metrics.stageMetrics),
|
|
692
|
+
classification: "active-performance-debt",
|
|
693
|
+
keyPartBytes: styled10kSnapshot.metrics.keyPartBytes,
|
|
694
|
+
stageMetrics: styled10kSnapshot.metrics.stageMetrics
|
|
695
|
+
};
|
|
696
|
+
const styled50kDiagnostics = {
|
|
697
|
+
bottleneck: inferBottleneck(styled50kSnapshot.metrics.stageMetrics),
|
|
698
|
+
classification: "active-performance-debt",
|
|
699
|
+
keyPartBytes: styled50kSnapshot.metrics.keyPartBytes,
|
|
700
|
+
stageMetrics: styled50kSnapshot.metrics.stageMetrics
|
|
701
|
+
};
|
|
702
|
+
const registry = new StyleRegistry();
|
|
703
|
+
const conditionalFormatting = createConditionalFormattingProbe();
|
|
704
|
+
const conditionalFormattingStats = measureSync(iterations, () => serializeConditionalFormatting(conditionalFormatting, registry));
|
|
705
|
+
return [
|
|
706
|
+
benchmarkResult(
|
|
707
|
+
"F5",
|
|
708
|
+
"F",
|
|
709
|
+
"Styled 10K\xD720 generation",
|
|
710
|
+
"< 600ms",
|
|
711
|
+
passFailFromUpperBound(styled10k.stats.p95, 600),
|
|
712
|
+
`p95 ${formatMs(styled10k.stats.p95)}`,
|
|
713
|
+
`breakdown (${styled10kPerformanceDiagnostics.bottleneck}): ${formatRenderDecomposition(styled10kSnapshot.metrics.stageMetrics)}`,
|
|
714
|
+
styled10kPerformanceDiagnostics
|
|
715
|
+
),
|
|
716
|
+
benchmarkResult(
|
|
717
|
+
"F6",
|
|
718
|
+
"F",
|
|
719
|
+
"Styled 50K\xD720 generation",
|
|
720
|
+
"< 3,000ms",
|
|
721
|
+
passFailFromUpperBound(styled50k.stats.p95, 3e3),
|
|
722
|
+
`p95 ${formatMs(styled50k.stats.p95)}`,
|
|
723
|
+
`breakdown (${styled50kDiagnostics.bottleneck}): ${formatRenderDecomposition(styled50kSnapshot.metrics.stageMetrics)}`,
|
|
724
|
+
styled50kDiagnostics
|
|
725
|
+
),
|
|
726
|
+
benchmarkResult(
|
|
727
|
+
"F7",
|
|
728
|
+
"F",
|
|
729
|
+
"Style overhead ratio",
|
|
730
|
+
"< 1.5\xD7",
|
|
731
|
+
passFailFromUpperBound(styleOverheadRatio, 1.5),
|
|
732
|
+
`ratio ${styleOverheadRatio.toFixed(2)}\xD7`,
|
|
733
|
+
`styled10k breakdown (${styled10kPerformanceDiagnostics.bottleneck}): ${formatRenderDecomposition(styled10kSnapshot.metrics.stageMetrics)}`,
|
|
734
|
+
styled10kPerformanceDiagnostics
|
|
735
|
+
),
|
|
736
|
+
benchmarkResult("F8", "F", "Conditional formatting on 100K cells", "< 200ms", passFailFromUpperBound(conditionalFormattingStats.stats.p95, 200), `p95 ${formatMs(conditionalFormattingStats.stats.p95)}`)
|
|
737
|
+
];
|
|
738
|
+
}
|
|
739
|
+
async function runEfficiencyBenchmarks() {
|
|
740
|
+
const presetBuffer = await SpreadsheetEngine.render(createPresetBenchmarkDocument());
|
|
741
|
+
const presetStyles = await inspectStyles(presetBuffer);
|
|
742
|
+
const presetDiagnostics = createStylePartDiagnostics(presetStyles, "benchmark-target-mismatch-candidate");
|
|
743
|
+
const hundredUniqueBuffer = await SpreadsheetEngine.render(createUniqueStyleDocument(100));
|
|
744
|
+
const hundredUniqueStyles = await inspectStyles(hundredUniqueBuffer);
|
|
745
|
+
const hundredUniqueDiagnostics = createStylePartDiagnostics(hundredUniqueStyles, "benchmark-target-mismatch-candidate");
|
|
746
|
+
const tenThousandUniqueBuffer = await SpreadsheetEngine.render(createUniqueStyleDocument(1e4));
|
|
747
|
+
const tenThousandUniqueStyles = await inspectStyles(tenThousandUniqueBuffer);
|
|
748
|
+
const tenThousandUniqueDiagnostics = createStylePartDiagnostics(tenThousandUniqueStyles, "benchmark-target-mismatch-candidate");
|
|
749
|
+
const styled10kDocument = createStyledWorkbook(1e4, 20, 500, "Styled10K");
|
|
750
|
+
const styled10kRender = await SpreadsheetEngine.renderWithMetrics(styled10kDocument);
|
|
751
|
+
const styled10kBuffer = styled10kRender.buffer;
|
|
752
|
+
const styled10kStyles = await inspectStyles(styled10kBuffer);
|
|
753
|
+
const styledCellCount = 1e4 * 20;
|
|
754
|
+
const uniqueCellXfs = Math.max(0, countCellXfs(styled10kStyles.parsed) - 1);
|
|
755
|
+
const dedupRatio = uniqueCellXfs === 0 ? 1 : 1 - uniqueCellXfs / styledCellCount;
|
|
756
|
+
const styled10kPerformanceDiagnostics = {
|
|
757
|
+
bottleneck: inferBottleneck(styled10kRender.metrics.stageMetrics),
|
|
758
|
+
classification: "active-performance-debt",
|
|
759
|
+
keyPartBytes: styled10kRender.metrics.keyPartBytes,
|
|
760
|
+
stageMetrics: styled10kRender.metrics.stageMetrics
|
|
761
|
+
};
|
|
762
|
+
const styled10kSizeDiagnostics = {
|
|
763
|
+
...classifyWorkbookSizeDiagnostics(styled10kRender.metrics.keyPartBytes, 500 * 1024),
|
|
764
|
+
bottleneck: styled10kPerformanceDiagnostics.bottleneck,
|
|
765
|
+
stageMetrics: styled10kRender.metrics.stageMetrics,
|
|
766
|
+
stylePart: createStylePartDiagnostics(styled10kStyles, "active-performance-debt").stylePart
|
|
767
|
+
};
|
|
768
|
+
const practicalFloorNote = styled10kSizeDiagnostics.classification === "benchmark-target-mismatch-candidate" ? ` practical floor signal: dominant ${styled10kSizeDiagnostics.payloadDominantPart} payload still exceeds target by ${formatKb(styled10kSizeDiagnostics.practicalFloorGapBytes ?? 0)}.` : "";
|
|
769
|
+
const excelJsLoad = await loadExcelJsBenchmarkModule();
|
|
770
|
+
let excelJsResult;
|
|
771
|
+
if (excelJsLoad.status === "loaded") {
|
|
772
|
+
try {
|
|
773
|
+
const excelJsBuffer = await renderExcelJsStyledWorkbook(styled10kDocument, excelJsLoad.module);
|
|
774
|
+
const ratio = styled10kBuffer.length / Math.max(1, excelJsBuffer.length);
|
|
775
|
+
excelJsResult = benchmarkResult(
|
|
776
|
+
"G6",
|
|
777
|
+
"G",
|
|
778
|
+
"Styled file size vs ExcelJS",
|
|
779
|
+
"\u2264 110% of ExcelJS",
|
|
780
|
+
passFailFromUpperBound(ratio, 1.1),
|
|
781
|
+
`PaperJSX ${formatKb(styled10kBuffer.length)}; ExcelJS ${formatKb(excelJsBuffer.length)}; ratio ${ratio.toFixed(2)}\xD7`,
|
|
782
|
+
`ExcelJS loaded from ${excelJsLoad.source ?? "unknown source"}.`
|
|
783
|
+
);
|
|
784
|
+
} catch (error) {
|
|
785
|
+
excelJsResult = benchmarkResult(
|
|
786
|
+
"G6",
|
|
787
|
+
"G",
|
|
788
|
+
"Styled file size vs ExcelJS",
|
|
789
|
+
"\u2264 110% of ExcelJS",
|
|
790
|
+
"blocked",
|
|
791
|
+
`ExcelJS baseline render failed: ${error instanceof Error ? error.message : String(error)}`,
|
|
792
|
+
`ExcelJS loaded from ${excelJsLoad.source ?? "unknown source"}, but the competitor render could not complete.`
|
|
793
|
+
);
|
|
794
|
+
}
|
|
795
|
+
} else {
|
|
796
|
+
excelJsResult = benchmarkResult(
|
|
797
|
+
"G6",
|
|
798
|
+
"G",
|
|
799
|
+
"Styled file size vs ExcelJS",
|
|
800
|
+
"\u2264 110% of ExcelJS",
|
|
801
|
+
"blocked",
|
|
802
|
+
excelJsLoad.message ?? "ExcelJS competitor baseline unavailable.",
|
|
803
|
+
"Install `exceljs` as a package-local devDependency or set PAPERJSX_XLSX_EXCELJS_MODULE_PATH."
|
|
804
|
+
);
|
|
805
|
+
}
|
|
806
|
+
return [
|
|
807
|
+
benchmarkResult(
|
|
808
|
+
"G1",
|
|
809
|
+
"G",
|
|
810
|
+
"styles.xml size \u2014 presets catalog",
|
|
811
|
+
"calibrated warning when raw styles.xml exceeds legacy < 3 KB budget",
|
|
812
|
+
classifySizeEfficiencyStatus(presetStyles.sizeBytes, 3 * 1024, presetDiagnostics),
|
|
813
|
+
formatKb(presetStyles.sizeBytes),
|
|
814
|
+
`Measured with ${PRESET_NAMES.length} current presets. Raw OOXML styles part benchmark; no archive contribution remains at this stage.${formatStylePartDiagnostics(presetDiagnostics)}`,
|
|
815
|
+
presetDiagnostics
|
|
816
|
+
),
|
|
817
|
+
benchmarkResult(
|
|
818
|
+
"G2",
|
|
819
|
+
"G",
|
|
820
|
+
"styles.xml size \u2014 100 unique styles",
|
|
821
|
+
"calibrated warning when raw styles.xml exceeds legacy < 15 KB budget",
|
|
822
|
+
classifySizeEfficiencyStatus(hundredUniqueStyles.sizeBytes, 15 * 1024, hundredUniqueDiagnostics),
|
|
823
|
+
formatKb(hundredUniqueStyles.sizeBytes),
|
|
824
|
+
`Raw OOXML styles part benchmark; no archive contribution remains at this stage.${formatStylePartDiagnostics(hundredUniqueDiagnostics)}`,
|
|
825
|
+
hundredUniqueDiagnostics
|
|
826
|
+
),
|
|
827
|
+
benchmarkResult(
|
|
828
|
+
"G3",
|
|
829
|
+
"G",
|
|
830
|
+
"styles.xml size \u2014 10K unique styles",
|
|
831
|
+
"calibrated warning when raw styles.xml exceeds legacy < 200 KB budget",
|
|
832
|
+
classifySizeEfficiencyStatus(tenThousandUniqueStyles.sizeBytes, 200 * 1024, tenThousandUniqueDiagnostics),
|
|
833
|
+
formatKb(tenThousandUniqueStyles.sizeBytes),
|
|
834
|
+
`Raw OOXML styles part benchmark; no archive contribution remains at this stage.${formatStylePartDiagnostics(tenThousandUniqueDiagnostics)}`,
|
|
835
|
+
tenThousandUniqueDiagnostics
|
|
836
|
+
),
|
|
837
|
+
benchmarkResult("G4", "G", "Deduplication ratio", "> 95%", passFailFromLowerBound(dedupRatio, 0.95), formatRatio(dedupRatio), `${uniqueCellXfs} unique cellXfs across ${styledCellCount} styled cells.`),
|
|
838
|
+
benchmarkResult(
|
|
839
|
+
"G5",
|
|
840
|
+
"G",
|
|
841
|
+
"Styled file size \u2014 10K\xD720",
|
|
842
|
+
"calibrated warning when workbook exceeds legacy < 500 KB budget",
|
|
843
|
+
classifySizeEfficiencyStatus(styled10kBuffer.length, 500 * 1024, styled10kSizeDiagnostics),
|
|
844
|
+
formatKb(styled10kBuffer.length),
|
|
845
|
+
`payload (${styled10kPerformanceDiagnostics.bottleneck}; dominant ${styled10kSizeDiagnostics.payloadDominantPart}): ${formatKeyPartBytes(styled10kRender.metrics.keyPartBytes)}; ${formatRenderDecomposition(styled10kRender.metrics.stageMetrics)}.${practicalFloorNote}${formatStylePartDiagnostics(styled10kSizeDiagnostics)}`,
|
|
846
|
+
styled10kSizeDiagnostics
|
|
847
|
+
),
|
|
848
|
+
excelJsResult
|
|
849
|
+
];
|
|
850
|
+
}
|
|
851
|
+
async function runSizingBenchmarks(iterations) {
|
|
852
|
+
const collapseBuffer = await SpreadsheetEngine.render({ sheets: [createColumnCollapseSheet()] });
|
|
853
|
+
const collapseSheet = await inspectSheet(collapseBuffer);
|
|
854
|
+
const columnCount = countCols(collapseSheet.parsed);
|
|
855
|
+
const largeSheet = getPhase1Fixture("large-50k").document.sheets[0];
|
|
856
|
+
const sizingStats = measureSync(iterations, () => computeColumnLayout(largeSheet));
|
|
857
|
+
return [
|
|
858
|
+
benchmarkResult("H1", "H", "Column width accuracy", "Within \xB120% of Excel AutoFit", "blocked", "Requires Excel AutoFit comparison", "Use the generated fixtures in the manual validation pack."),
|
|
859
|
+
benchmarkResult("H2", "H", "Row height accuracy \u2014 wrapText", "Within \xB11 line of Excel", "blocked", "Requires Excel visual comparison", "Use the generated fixtures in the manual validation pack."),
|
|
860
|
+
benchmarkResult("H3", "H", "Column collapse optimization", "\u2264 50 <col> elements", passFailFromUpperBound(columnCount, 50), `${columnCount} <col> elements`),
|
|
861
|
+
benchmarkResult("H4", "H", "Column width computation \u2014 50K rows", "< 100ms", passFailFromUpperBound(sizingStats.stats.p95, 100), `p95 ${formatMs(sizingStats.stats.p95)}`)
|
|
862
|
+
];
|
|
863
|
+
}
|
|
864
|
+
async function runCorrectnessBenchmarks() {
|
|
865
|
+
const numberFormatBuffer = await SpreadsheetEngine.render(createNumberFormatProbe());
|
|
866
|
+
const numberFormatStyles = await inspectStyles(numberFormatBuffer);
|
|
867
|
+
const dateBuffer = await SpreadsheetEngine.render(createDateProbe());
|
|
868
|
+
const dateStyles = await inspectStyles(dateBuffer);
|
|
869
|
+
const dateSheet = await inspectSheet(dateBuffer);
|
|
870
|
+
const partialBuffer = await SpreadsheetEngine.render(createPartialStyleProbe());
|
|
871
|
+
const partialStyles = await inspectStyles(partialBuffer);
|
|
872
|
+
const partialSheet = await inspectSheet(partialBuffer);
|
|
873
|
+
const partialXfs = partialStyles.parsed.styleSheet.cellXfs.xf;
|
|
874
|
+
const partialCellXf = Array.isArray(partialXfs) ? partialXfs[1] : partialXfs;
|
|
875
|
+
const partialReferencesOnlyFont = partialCellXf?.["@_fontId"] === "1" && partialCellXf?.["@_fillId"] === "0" && partialCellXf?.["@_borderId"] === "0" && partialCellXf?.["@_numFmtId"] === "0" && partialCellXf?.alignment === void 0;
|
|
876
|
+
const cfRegistry = new StyleRegistry();
|
|
877
|
+
const cfOutput = serializeConditionalFormatting(createConditionalFormattingProbe(), cfRegistry);
|
|
878
|
+
const cfXml = cfOutput.xml;
|
|
879
|
+
return [
|
|
880
|
+
benchmarkResult("E1", "E", "Font rendering fidelity", "Visual match across 5 target apps", "blocked", "Manual app validation required", "Use `phase2-font-fill-border.xlsx` and compare in Excel, Sheets, Numbers, and LibreOffice."),
|
|
881
|
+
benchmarkResult("E2", "E", "Fill rendering fidelity", "20 fills render correctly", "blocked", "Manual app validation required", "Use `phase2-font-fill-border.xlsx` for color verification."),
|
|
882
|
+
benchmarkResult("E3", "E", "Border rendering fidelity", "13 border styles render correctly", "blocked", "Manual app validation required", "Add screenshots from Excel and LibreOffice to close this benchmark."),
|
|
883
|
+
benchmarkResult(
|
|
884
|
+
"E4",
|
|
885
|
+
"E",
|
|
886
|
+
"Number format correctness",
|
|
887
|
+
"Built-in/custom formats display correctly",
|
|
888
|
+
"blocked",
|
|
889
|
+
`${countNumFmts(numberFormatStyles.parsed)} custom numFmts emitted; alias XML looks correct`,
|
|
890
|
+
"Structural proxy passed locally, but display correctness still needs Excel-family apps."
|
|
891
|
+
),
|
|
892
|
+
benchmarkResult(
|
|
893
|
+
"E5",
|
|
894
|
+
"E",
|
|
895
|
+
"Date formatting round-trip",
|
|
896
|
+
"Dates display as dates, not serials",
|
|
897
|
+
"blocked",
|
|
898
|
+
dateStyles.xml.includes('formatCode="yyyy-mm-dd"') && dateSheet.xml.includes('s="1"') ? "Auto date style emitted structurally" : "Date style emission failed structurally",
|
|
899
|
+
"Structural proxy passed locally, but round-trip editing still needs Excel."
|
|
900
|
+
),
|
|
901
|
+
benchmarkResult("E6", "E", "Alignment correctness", "All alignment modes render correctly", "blocked", "Manual app validation required", "Use `phase2-alignment-richtext.xlsx` for wrap, alignment, and rotation checks."),
|
|
902
|
+
benchmarkResult("E7", "E", "Preset visual match", "Presets match reference screenshots", "blocked", "Reference screenshots not captured yet", "Use `phase2-presets.xlsx` as the capture source."),
|
|
903
|
+
benchmarkResult("E8", "E", "Rich text rendering", "Per-run formatting preserved", "blocked", "Manual app validation required", "Use `phase2-alignment-richtext.xlsx` to verify there is no fallback font."),
|
|
904
|
+
benchmarkResult("E9", "E", "Conditional formatting \u2014 cellIs", "Differential format applies correctly", "blocked", cfXml.includes('type="cellIs"') ? "Structural XML emitted" : "Missing cellIs XML", "Visual app validation still required."),
|
|
905
|
+
benchmarkResult("E10", "E", "Conditional formatting \u2014 colorScale", "Visible gradient across range", "blocked", cfXml.includes('type="colorScale"') ? "Structural XML emitted" : "Missing colorScale XML", "Visual app validation still required."),
|
|
906
|
+
benchmarkResult("E11", "E", "Conditional formatting \u2014 dataBar", "Bars render proportionally", "blocked", cfXml.includes('type="dataBar"') ? "Structural XML emitted" : "Missing dataBar XML", "Visual app validation still required."),
|
|
907
|
+
benchmarkResult(
|
|
908
|
+
"E12",
|
|
909
|
+
"E",
|
|
910
|
+
"Style inheritance",
|
|
911
|
+
"Partial styles do not leak garbage defaults",
|
|
912
|
+
partialReferencesOnlyFont ? "pass" : "fail",
|
|
913
|
+
partialReferencesOnlyFont ? "Partial xf only references font overrides" : "Unexpected style component references"
|
|
914
|
+
),
|
|
915
|
+
benchmarkResult(
|
|
916
|
+
"E13",
|
|
917
|
+
"E",
|
|
918
|
+
"Empty styled cells",
|
|
919
|
+
"Style renders on empty cells",
|
|
920
|
+
partialSheet.xml.includes('<c r="B1" s="2"/>') ? "pass" : "fail",
|
|
921
|
+
partialSheet.xml.includes('<c r="B1" s="2"/>') ? "Styled empty cell emitted" : "Styled empty cell missing"
|
|
922
|
+
)
|
|
923
|
+
];
|
|
924
|
+
}
|
|
925
|
+
async function runPhase2BenchmarkSuite(iterations = 3) {
|
|
926
|
+
maybeGc();
|
|
927
|
+
const correctness = await runCorrectnessBenchmarks();
|
|
928
|
+
maybeGc();
|
|
929
|
+
const styleRegistry = await runStyleRegistryBenchmarks(iterations);
|
|
930
|
+
maybeGc();
|
|
931
|
+
const render = await runRenderBenchmarks(iterations);
|
|
932
|
+
maybeGc();
|
|
933
|
+
const efficiency = await runEfficiencyBenchmarks();
|
|
934
|
+
maybeGc();
|
|
935
|
+
const sizing = await runSizingBenchmarks(iterations);
|
|
936
|
+
const results = [
|
|
937
|
+
...correctness,
|
|
938
|
+
...styleRegistry,
|
|
939
|
+
...render,
|
|
940
|
+
...efficiency,
|
|
941
|
+
...sizing
|
|
942
|
+
];
|
|
943
|
+
const summary = results.reduce((accumulator, result) => {
|
|
944
|
+
accumulator.total += 1;
|
|
945
|
+
if (result.status === "pass") accumulator.passed += 1;
|
|
946
|
+
if (result.status === "warn") accumulator.warned += 1;
|
|
947
|
+
if (result.status === "fail") accumulator.failed += 1;
|
|
948
|
+
if (result.status === "blocked") accumulator.blocked += 1;
|
|
949
|
+
return accumulator;
|
|
950
|
+
}, {
|
|
951
|
+
total: 0,
|
|
952
|
+
passed: 0,
|
|
953
|
+
warned: 0,
|
|
954
|
+
failed: 0,
|
|
955
|
+
blocked: 0
|
|
956
|
+
});
|
|
957
|
+
return {
|
|
958
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
959
|
+
environment: {
|
|
960
|
+
node: process.version,
|
|
961
|
+
platform: process.platform,
|
|
962
|
+
arch: process.arch
|
|
963
|
+
},
|
|
964
|
+
summary,
|
|
965
|
+
results
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
async function renderPhase2BenchmarkReport(iterations = 3) {
|
|
969
|
+
const report = await runPhase2BenchmarkSuite(iterations);
|
|
970
|
+
const lines = [
|
|
971
|
+
"\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550",
|
|
972
|
+
" PaperJSX XLSX Engine \u2014 Phase 2 Local Benchmark Report",
|
|
973
|
+
` Node.js ${report.environment.node} | ${report.environment.platform} ${report.environment.arch}`,
|
|
974
|
+
"\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550",
|
|
975
|
+
"",
|
|
976
|
+
` SUMMARY pass=${report.summary.passed} warn=${report.summary.warned} fail=${report.summary.failed} blocked=${report.summary.blocked} total=${report.summary.total}`,
|
|
977
|
+
""
|
|
978
|
+
];
|
|
979
|
+
for (const group of ["E", "F", "G", "H"]) {
|
|
980
|
+
lines.push(` GROUP ${group}`);
|
|
981
|
+
for (const result of report.results.filter((entry) => entry.group === group)) {
|
|
982
|
+
const marker = result.status === "pass" ? "\u2713" : result.status === "warn" ? "!" : result.status === "fail" ? "\u2717" : "\u2022";
|
|
983
|
+
lines.push(` ${marker} ${result.id.padEnd(3)} ${result.name}`);
|
|
984
|
+
lines.push(` target: ${result.target}`);
|
|
985
|
+
lines.push(` observed: ${result.observed}`);
|
|
986
|
+
if (result.notes) {
|
|
987
|
+
lines.push(` notes: ${result.notes}`);
|
|
988
|
+
}
|
|
989
|
+
}
|
|
990
|
+
lines.push("");
|
|
991
|
+
}
|
|
992
|
+
lines.push("\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550\u2550");
|
|
993
|
+
return lines.join("\n");
|
|
994
|
+
}
|
|
995
|
+
|
|
996
|
+
export {
|
|
997
|
+
classifySizeEfficiencyStatus,
|
|
998
|
+
loadExcelJsBenchmarkModule,
|
|
999
|
+
runPhase2BenchmarkSuite,
|
|
1000
|
+
renderPhase2BenchmarkReport
|
|
1001
|
+
};
|
|
1002
|
+
//# sourceMappingURL=chunk-2IVXCH6I.js.map
|