json-as 1.1.26 → 1.2.0
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/.github/workflows/benchmark.yml +72 -0
- package/.prettierrc.json +1 -0
- package/CHANGELOG.md +4 -0
- package/README.md +44 -59
- package/assembly/__benches__/abc.bench.ts +5 -9
- package/assembly/__benches__/large.bench.ts +114 -120
- package/assembly/__benches__/lib/bench.ts +44 -1
- package/assembly/__benches__/medium.bench.ts +108 -29
- package/assembly/__benches__/small.bench.ts +28 -18
- package/assembly/__benches__/throughput.ts +172 -0
- package/assembly/__benches__/vec3.bench.ts +8 -3
- package/assembly/__tests__/string.spec.ts +4 -0
- package/assembly/deserialize/simple/arbitrary.ts +5 -2
- package/assembly/deserialize/simple/object.ts +3 -1
- package/assembly/deserialize/swar/string.ts +62 -62
- package/assembly/index.ts +6 -2
- package/assembly/serialize/simd/string.ts +1 -1
- package/assembly/serialize/swar/number.ts +0 -0
- package/assembly/serialize/swar/string.ts +25 -25
- package/assembly/test.tmp.ts +124 -2
- package/assembly/test.ts +0 -7
- package/bench/abc.bench.ts +6 -8
- package/bench/large.bench.ts +119 -118
- package/bench/lib/bench.d.ts +27 -0
- package/bench/lib/bench.js +53 -0
- package/bench/lib/chart.ts +217 -0
- package/bench/medium.bench.ts +55 -30
- package/bench/runners/assemblyscript.js +6 -1
- package/bench/small.bench.ts +7 -3
- package/bench/throughput.ts +87 -0
- package/bench/tsconfig.json +3 -2
- package/bench/vec3.bench.ts +7 -3
- package/ci/bench/runners/assemblyscript.js +29 -0
- package/ci/run-bench.as.sh +63 -0
- package/lib/as-bs.ts +13 -0
- package/package.json +3 -1
- package/run-bench.as.sh +21 -13
- package/run-bench.js.sh +9 -7
- package/run-tests.sh +34 -14
- package/scripts/build-chart01.ts +38 -0
- package/scripts/build-chart02.ts +38 -0
- package/scripts/build-chart03.ts +139 -0
- package/scripts/build-chart05.ts +47 -0
- package/scripts/generate-as-class.ts +50 -0
- package/scripts/lib/bench-utils.ts +308 -0
- package/transform/lib/index.js +2 -2
- package/transform/lib/index.js.map +1 -1
- package/transform/src/index.ts +2 -2
- package/assembly/__tests__/simd/string.spec.ts +0 -32
- /package/{bench → ci/bench}/lib/bench.ts +0 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { ChartJSNodeCanvas } from "chartjs-node-canvas";
|
|
4
|
+
import ChartDataLabels from "chartjs-plugin-datalabels";
|
|
5
|
+
import type { ChartConfiguration } from "chart.js";
|
|
6
|
+
|
|
7
|
+
const LOGS_DIR = "./build/logs";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* Load a bench log JSON by file path
|
|
11
|
+
*/
|
|
12
|
+
function loadJSON(file: string) {
|
|
13
|
+
const text = fs.readFileSync(file, "utf-8");
|
|
14
|
+
return JSON.parse(text);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Get throughput (mbps) and size (bytes) from a bench log
|
|
19
|
+
*/
|
|
20
|
+
function getBenchData(filePath: string) {
|
|
21
|
+
const data = loadJSON(filePath);
|
|
22
|
+
return {
|
|
23
|
+
bytes: typeof data.bytes === "number" ? data.bytes : 0,
|
|
24
|
+
mbps: typeof data.mbps === "number" ? data.mbps : 0,
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
/* ================================
|
|
29
|
+
* Payload Types — only strings
|
|
30
|
+
* ================================ */
|
|
31
|
+
const payloads = ["small-str", "medium-str", "large-str"];
|
|
32
|
+
const engines = ["swar", "simd"];
|
|
33
|
+
const modes = ["serialize", "deserialize"];
|
|
34
|
+
|
|
35
|
+
// Helper to build log file path
|
|
36
|
+
function logPath(payload: string, engine: string, mode: string) {
|
|
37
|
+
return path.join(LOGS_DIR, "as", engine, `${payload}.${mode}.as.json`);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/* ================================
|
|
41
|
+
* Prepare Chart Data
|
|
42
|
+
* ================================ */
|
|
43
|
+
interface ChartPoint { x: number; y: number; }
|
|
44
|
+
const chartData: Record<string, ChartPoint[]> = {};
|
|
45
|
+
|
|
46
|
+
for (const payload of payloads) {
|
|
47
|
+
for (const engine of engines) {
|
|
48
|
+
for (const mode of modes) {
|
|
49
|
+
const key = `${payload}-${engine}-${mode}`;
|
|
50
|
+
const data = getBenchData(logPath(payload, engine, mode));
|
|
51
|
+
const sizeKB = data.bytes / 1024;
|
|
52
|
+
|
|
53
|
+
if (!chartData[key]) chartData[key] = [];
|
|
54
|
+
chartData[key].push({ x: sizeKB, y: data.mbps });
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/* ================================
|
|
60
|
+
* Configure Chart
|
|
61
|
+
* ================================ */
|
|
62
|
+
const canvas = new ChartJSNodeCanvas({
|
|
63
|
+
width: 1200,
|
|
64
|
+
height: 700,
|
|
65
|
+
chartCallback: ChartJS => ChartJS.register(ChartDataLabels),
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
const colors: Record<string, string> = {
|
|
69
|
+
"swar-serialize": "34,197,94", // green
|
|
70
|
+
"swar-deserialize": "59,130,246", // blue
|
|
71
|
+
"simd-serialize": "234,179,8", // yellow
|
|
72
|
+
"simd-deserialize": "220,38,38", // red
|
|
73
|
+
};
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
const datasets = [];
|
|
77
|
+
|
|
78
|
+
for (const mode of modes) {
|
|
79
|
+
for (const engine of engines) {
|
|
80
|
+
const data: ChartPoint[] = payloads.map(p => chartData[`${p}-${engine}-${mode}`][0]);
|
|
81
|
+
datasets.push({
|
|
82
|
+
label: `${engine} ${mode}`,
|
|
83
|
+
data,
|
|
84
|
+
borderColor: `rgba(${colors[engine + "-" + mode]},0.9)`,
|
|
85
|
+
backgroundColor: `rgba(${colors[engine+"-"+mode]},0.3)`,
|
|
86
|
+
fill: false,
|
|
87
|
+
tension: 0.2,
|
|
88
|
+
pointStyle: mode === "serialize" ? "circle" : "rect",
|
|
89
|
+
pointRadius: 6,
|
|
90
|
+
borderDash: engine === "simd" ? [5, 5] : undefined,
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
/* ================================
|
|
97
|
+
* Chart Configuration
|
|
98
|
+
* ================================ */
|
|
99
|
+
const config: ChartConfiguration<"line"> = {
|
|
100
|
+
type: "line",
|
|
101
|
+
data: { datasets },
|
|
102
|
+
options: {
|
|
103
|
+
responsive: true,
|
|
104
|
+
plugins: {
|
|
105
|
+
title: {
|
|
106
|
+
display: true,
|
|
107
|
+
text: "String Serialization & Deserialization Throughput vs Payload Size (KB)",
|
|
108
|
+
font: { size: 20, weight: "bold" },
|
|
109
|
+
},
|
|
110
|
+
legend: { position: "top", labels: { font: { size: 14, weight: "bold" } } },
|
|
111
|
+
datalabels: {
|
|
112
|
+
anchor: "end",
|
|
113
|
+
align: "top",
|
|
114
|
+
font: { size: 10, weight: "bold" },
|
|
115
|
+
formatter: (value: any) => `${value.y.toFixed(0)} MB/s`,
|
|
116
|
+
},
|
|
117
|
+
},
|
|
118
|
+
scales: {
|
|
119
|
+
x: {
|
|
120
|
+
title: { display: true, text: "Payload Size (KB)", font: { size: 16, weight: "bold" } },
|
|
121
|
+
type: "linear",
|
|
122
|
+
},
|
|
123
|
+
y: {
|
|
124
|
+
title: { display: true, text: "Throughput (MB/s)", font: { size: 16, weight: "bold" } },
|
|
125
|
+
beginAtZero: true,
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
},
|
|
129
|
+
plugins: [ChartDataLabels],
|
|
130
|
+
};
|
|
131
|
+
|
|
132
|
+
/* ================================
|
|
133
|
+
* Render Chart to PNG
|
|
134
|
+
* ================================ */
|
|
135
|
+
console.log("Rendering chart03 for strings (serialize + deserialize)...");
|
|
136
|
+
const buffer = canvas.renderToBufferSync(config, "image/png");
|
|
137
|
+
const outFile = path.join(LOGS_DIR, "chart03.png");
|
|
138
|
+
fs.writeFileSync(outFile, buffer);
|
|
139
|
+
console.log(`chart03.png written → ${outFile}`);
|
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
import { getBenchResults, createLineChart, generateChart, BenchResults } from './lib/bench-utils';
|
|
2
|
+
|
|
3
|
+
const PAYLOADS_BY_TYPE: Record<string, string[]> = {
|
|
4
|
+
object: ['smallObj', 'mediumObj', 'largeObj'],
|
|
5
|
+
array: ['smallArr', 'mediumArr', 'largeArr'],
|
|
6
|
+
string: ['smallStr', 'mediumStr', 'largeStr']
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const payloadLabels: Record<string, string> = {
|
|
10
|
+
smallObj: 'Small Object',
|
|
11
|
+
mediumObj: 'Medium Object',
|
|
12
|
+
largeObj: 'Large Object',
|
|
13
|
+
smallArr: 'Small Array',
|
|
14
|
+
mediumArr: 'Medium Array',
|
|
15
|
+
largeArr: 'Large Array',
|
|
16
|
+
smallStr: 'Small String',
|
|
17
|
+
mediumStr: 'Medium String',
|
|
18
|
+
largeStr: 'Large String'
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
// Load all payloads
|
|
22
|
+
const allPayloads = Object.values(PAYLOADS_BY_TYPE).flat();
|
|
23
|
+
const results: BenchResults = getBenchResults(allPayloads);
|
|
24
|
+
|
|
25
|
+
// Generate datasets per type
|
|
26
|
+
const series = Object.entries(PAYLOADS_BY_TYPE).flatMap(([type, keys]) => {
|
|
27
|
+
const engines = ['JS', 'SWAR', 'SIMD'] as const;
|
|
28
|
+
|
|
29
|
+
return engines.map((engine, idx) => ({
|
|
30
|
+
label: `${type} (${engine})`,
|
|
31
|
+
data: keys.map(k => results[k].serialize[idx].mbps),
|
|
32
|
+
borderColor: engine === 'JS' ? '#6366f1' : engine === 'SWAR' ? '#22c55e' : '#ef4444'
|
|
33
|
+
}));
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
// Create chart
|
|
37
|
+
const chartConfig = createLineChart(
|
|
38
|
+
['small', 'medium', 'large'],
|
|
39
|
+
series,
|
|
40
|
+
{
|
|
41
|
+
title: 'Serialization Throughput by Type',
|
|
42
|
+
xLabel: 'Payload Size',
|
|
43
|
+
yLabel: 'Throughput (MB/s)'
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
|
|
47
|
+
generateChart(chartConfig, './data/chart03.svg');
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
// gen-obj-properties.ts
|
|
2
|
+
import fs from "fs";
|
|
3
|
+
import path from "path";
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Generates AssemblyScript class properties to reach a target byte size.
|
|
7
|
+
* Each i32 property is 4 bytes.
|
|
8
|
+
*
|
|
9
|
+
* @param targetBytes Total desired size in bytes
|
|
10
|
+
* @param prefix Property name prefix
|
|
11
|
+
*/
|
|
12
|
+
function generateProperties(targetBytes: number, prefix = "key"): string {
|
|
13
|
+
const bytesPerProp = 4; // i32
|
|
14
|
+
const numProps = Math.ceil(targetBytes / bytesPerProp);
|
|
15
|
+
let result = "";
|
|
16
|
+
|
|
17
|
+
for (let i = 0; i < numProps; i++) {
|
|
18
|
+
result += `${prefix}${i}: i32 = ${i}; `;
|
|
19
|
+
if ((i + 1) % 10 === 0) result += "\n ";
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
return result.trim();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Generate AssemblyScript class source
|
|
27
|
+
*/
|
|
28
|
+
function generateClass(name: string, targetBytes: number) {
|
|
29
|
+
const props = generateProperties(targetBytes);
|
|
30
|
+
return `@json
|
|
31
|
+
class ${name} {
|
|
32
|
+
${props}
|
|
33
|
+
}
|
|
34
|
+
`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/* =================================
|
|
38
|
+
* CONFIGURATION
|
|
39
|
+
* ================================= */
|
|
40
|
+
const classes = [
|
|
41
|
+
{ name: "ObjSmall", sizeKB: 100 },
|
|
42
|
+
{ name: "ObjMedium", sizeKB: 500 },
|
|
43
|
+
{ name: "ObjLarge", sizeKB: 1024 },
|
|
44
|
+
];
|
|
45
|
+
|
|
46
|
+
for (const cls of classes) {
|
|
47
|
+
const source = generateClass(cls.name, cls.sizeKB * 1024);
|
|
48
|
+
console.log(`// ===== ${cls.name} =====`);
|
|
49
|
+
console.log(source);
|
|
50
|
+
}
|
|
@@ -0,0 +1,308 @@
|
|
|
1
|
+
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
3
|
+
import { execSync } from "child_process";
|
|
4
|
+
import { ChartJSNodeCanvas } from "chartjs-node-canvas";
|
|
5
|
+
import ChartDataLabels from "chartjs-plugin-datalabels";
|
|
6
|
+
import type { ChartConfiguration } from "chart.js";
|
|
7
|
+
|
|
8
|
+
/* ================================
|
|
9
|
+
* Types
|
|
10
|
+
* ================================ */
|
|
11
|
+
|
|
12
|
+
export interface BenchResult {
|
|
13
|
+
language: "as" | "js";
|
|
14
|
+
description: string;
|
|
15
|
+
elapsed: number;
|
|
16
|
+
bytes: number;
|
|
17
|
+
operations: number;
|
|
18
|
+
features: string[];
|
|
19
|
+
mbps: number;
|
|
20
|
+
gbps: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export type BenchKind = "serialize" | "deserialize";
|
|
24
|
+
|
|
25
|
+
export interface BenchResults {
|
|
26
|
+
[payload: string]: {
|
|
27
|
+
serialize: BenchResult[];
|
|
28
|
+
deserialize: BenchResult[];
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export interface LineSeries {
|
|
33
|
+
label: string;
|
|
34
|
+
data: number[];
|
|
35
|
+
borderColor: string;
|
|
36
|
+
backgroundColor?: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
/* ================================
|
|
40
|
+
* Metadata
|
|
41
|
+
* ================================ */
|
|
42
|
+
|
|
43
|
+
const LOGS_DIR = "./build/logs";
|
|
44
|
+
|
|
45
|
+
const VERSION =
|
|
46
|
+
"v" + JSON.parse(fs.readFileSync("./package.json", "utf-8")).version;
|
|
47
|
+
|
|
48
|
+
const GIT_HASH = execSync("git rev-parse --short HEAD").toString().trim();
|
|
49
|
+
const GIT_BRANCH = execSync("git rev-parse --abbrev-ref HEAD").toString().trim();
|
|
50
|
+
|
|
51
|
+
function subtitle() {
|
|
52
|
+
return `${new Date().toLocaleString()} • ${VERSION} • ${GIT_HASH} • ${GIT_BRANCH}`;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
/* ================================
|
|
56
|
+
* IO helpers
|
|
57
|
+
* ================================ */
|
|
58
|
+
|
|
59
|
+
function readBenchLog(filePath: string): BenchResult {
|
|
60
|
+
return JSON.parse(fs.readFileSync("./" + filePath, "utf-8")) as BenchResult;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function benchPath(
|
|
64
|
+
kind: "js" | "as",
|
|
65
|
+
payload: string,
|
|
66
|
+
type: BenchKind,
|
|
67
|
+
engine?: "swar" | "simd"
|
|
68
|
+
): string {
|
|
69
|
+
if (kind === "js") {
|
|
70
|
+
return path.join(LOGS_DIR, "js", `${payload}.${type}.js.json`);
|
|
71
|
+
}
|
|
72
|
+
return path.join(LOGS_DIR, "as", engine!, `${payload}.${type}.as.json`);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/* ================================
|
|
76
|
+
* Public API
|
|
77
|
+
* ================================ */
|
|
78
|
+
|
|
79
|
+
export function getBenchResults(payloads: string[]): BenchResults {
|
|
80
|
+
const out: BenchResults = {};
|
|
81
|
+
|
|
82
|
+
for (const payload of payloads) {
|
|
83
|
+
out[payload] = { serialize: [], deserialize: [] };
|
|
84
|
+
|
|
85
|
+
for (const kind of ["serialize", "deserialize"] as const) {
|
|
86
|
+
const js = readBenchLog(benchPath("js", payload, kind));
|
|
87
|
+
const swar = readBenchLog(benchPath("as", payload, kind, "swar"));
|
|
88
|
+
const simd = readBenchLog(benchPath("as", payload, kind, "simd"));
|
|
89
|
+
|
|
90
|
+
out[payload][kind] = [js, swar, simd];
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/* ================================
|
|
98
|
+
* Bar chart (existing)
|
|
99
|
+
* ================================ */
|
|
100
|
+
|
|
101
|
+
export function createBarChart(
|
|
102
|
+
data: Record<string, BenchResult[]>,
|
|
103
|
+
payloadLabels: Record<string, string>,
|
|
104
|
+
options: {
|
|
105
|
+
title: string;
|
|
106
|
+
yLabel?: string;
|
|
107
|
+
xLabel?: string;
|
|
108
|
+
datasetLabels?: [string, string, string];
|
|
109
|
+
}
|
|
110
|
+
): ChartConfiguration<"bar"> {
|
|
111
|
+
const payloadKeys = Object.keys(data);
|
|
112
|
+
const labels = payloadKeys.map(k => payloadLabels[k] ?? k);
|
|
113
|
+
|
|
114
|
+
const maxMBps = Math.max(
|
|
115
|
+
...Object.values(data).flat().map(r => r.mbps)
|
|
116
|
+
);
|
|
117
|
+
|
|
118
|
+
const datasetNames = options.datasetLabels ?? [
|
|
119
|
+
"Built-in JSON (JS)",
|
|
120
|
+
"JSON-AS (SWAR)",
|
|
121
|
+
"JSON-AS (SIMD)"
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
return {
|
|
125
|
+
type: "bar",
|
|
126
|
+
data: {
|
|
127
|
+
labels,
|
|
128
|
+
datasets: [
|
|
129
|
+
{
|
|
130
|
+
label: datasetNames[0],
|
|
131
|
+
data: payloadKeys.map(k => data[k][0].mbps),
|
|
132
|
+
backgroundColor: "rgba(99,102,241,0.85)",
|
|
133
|
+
borderColor: "#6366f1",
|
|
134
|
+
borderWidth: 1
|
|
135
|
+
},
|
|
136
|
+
{
|
|
137
|
+
label: datasetNames[1],
|
|
138
|
+
data: payloadKeys.map(k => data[k][1].mbps),
|
|
139
|
+
backgroundColor: "rgba(34,197,94,0.85)",
|
|
140
|
+
borderColor: "#22c55e",
|
|
141
|
+
borderWidth: 1
|
|
142
|
+
},
|
|
143
|
+
{
|
|
144
|
+
label: datasetNames[2],
|
|
145
|
+
data: payloadKeys.map(k => data[k][2].mbps),
|
|
146
|
+
backgroundColor: "rgba(239,68,68,0.9)",
|
|
147
|
+
borderColor: "#ef4444",
|
|
148
|
+
borderWidth: 2
|
|
149
|
+
}
|
|
150
|
+
]
|
|
151
|
+
},
|
|
152
|
+
options: {
|
|
153
|
+
responsive: true,
|
|
154
|
+
plugins: {
|
|
155
|
+
title: {
|
|
156
|
+
display: !!options.title,
|
|
157
|
+
text: options.title,
|
|
158
|
+
font: { size: 20, weight: "bold" }
|
|
159
|
+
},
|
|
160
|
+
legend: {
|
|
161
|
+
position: "top",
|
|
162
|
+
labels: {
|
|
163
|
+
font: { size: 16, weight: "bold" },
|
|
164
|
+
padding: 20
|
|
165
|
+
}
|
|
166
|
+
},
|
|
167
|
+
datalabels: {
|
|
168
|
+
anchor: "end",
|
|
169
|
+
align: "end",
|
|
170
|
+
font: { weight: "bold", size: 12 },
|
|
171
|
+
formatter: (v: number) => v.toFixed(0)
|
|
172
|
+
},
|
|
173
|
+
subtitle: {
|
|
174
|
+
display: true,
|
|
175
|
+
text: subtitle(),
|
|
176
|
+
font: { size: 14, weight: "bold" },
|
|
177
|
+
color: "#6b7280",
|
|
178
|
+
padding: 16,
|
|
179
|
+
position: "right"
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
scales: {
|
|
183
|
+
y: {
|
|
184
|
+
beginAtZero: true,
|
|
185
|
+
max: Math.ceil(maxMBps / 500) * 500,
|
|
186
|
+
title: {
|
|
187
|
+
display: true,
|
|
188
|
+
text: options.yLabel ?? "Throughput (MB/s)",
|
|
189
|
+
font: { size: 16, weight: "bold" }
|
|
190
|
+
},
|
|
191
|
+
ticks: {
|
|
192
|
+
stepSize: 500,
|
|
193
|
+
font: { size: 14, weight: "bold" }
|
|
194
|
+
}
|
|
195
|
+
},
|
|
196
|
+
x: {
|
|
197
|
+
title: {
|
|
198
|
+
display: true,
|
|
199
|
+
text: options.xLabel ?? "Payload",
|
|
200
|
+
font: { size: 16, weight: "bold" }
|
|
201
|
+
},
|
|
202
|
+
ticks: {
|
|
203
|
+
maxRotation: 0,
|
|
204
|
+
minRotation: 0,
|
|
205
|
+
font: { size: 14, weight: "bold" }
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
},
|
|
210
|
+
plugins: [ChartDataLabels]
|
|
211
|
+
};
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
/* ================================
|
|
215
|
+
* NEW: Line chart (charts 3 & 4)
|
|
216
|
+
* ================================ */
|
|
217
|
+
|
|
218
|
+
export function createLineChart(
|
|
219
|
+
labels: string[],
|
|
220
|
+
series: LineSeries[],
|
|
221
|
+
options: {
|
|
222
|
+
title: string;
|
|
223
|
+
xLabel: string;
|
|
224
|
+
yLabel: string;
|
|
225
|
+
logX?: boolean;
|
|
226
|
+
}
|
|
227
|
+
): ChartConfiguration<"line"> {
|
|
228
|
+
return {
|
|
229
|
+
type: "line",
|
|
230
|
+
data: {
|
|
231
|
+
labels,
|
|
232
|
+
datasets: series.map(s => ({
|
|
233
|
+
label: s.label,
|
|
234
|
+
data: s.data,
|
|
235
|
+
borderColor: s.borderColor,
|
|
236
|
+
backgroundColor: s.backgroundColor ?? s.borderColor,
|
|
237
|
+
borderWidth: 3,
|
|
238
|
+
tension: 0.25,
|
|
239
|
+
pointRadius: 4
|
|
240
|
+
}))
|
|
241
|
+
},
|
|
242
|
+
options: {
|
|
243
|
+
responsive: true,
|
|
244
|
+
plugins: {
|
|
245
|
+
title: {
|
|
246
|
+
display: true,
|
|
247
|
+
text: options.title,
|
|
248
|
+
font: { size: 20, weight: "bold" }
|
|
249
|
+
},
|
|
250
|
+
legend: {
|
|
251
|
+
position: "top",
|
|
252
|
+
labels: {
|
|
253
|
+
font: { size: 16, weight: "bold" }
|
|
254
|
+
}
|
|
255
|
+
},
|
|
256
|
+
subtitle: {
|
|
257
|
+
display: true,
|
|
258
|
+
text: subtitle(),
|
|
259
|
+
font: { size: 14, weight: "bold" },
|
|
260
|
+
color: "#6b7280",
|
|
261
|
+
padding: { bottom: 16 }
|
|
262
|
+
}
|
|
263
|
+
},
|
|
264
|
+
scales: {
|
|
265
|
+
x: {
|
|
266
|
+
type: options.logX ? "logarithmic" : "category",
|
|
267
|
+
title: {
|
|
268
|
+
display: true,
|
|
269
|
+
text: options.xLabel,
|
|
270
|
+
font: { size: 16, weight: "bold" }
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
y: {
|
|
274
|
+
beginAtZero: true,
|
|
275
|
+
title: {
|
|
276
|
+
display: true,
|
|
277
|
+
text: options.yLabel,
|
|
278
|
+
font: { size: 16, weight: "bold" }
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
};
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/* ================================
|
|
287
|
+
* Render
|
|
288
|
+
* ================================ */
|
|
289
|
+
|
|
290
|
+
export function generateChart(
|
|
291
|
+
config: ChartConfiguration,
|
|
292
|
+
outfile: string
|
|
293
|
+
) {
|
|
294
|
+
const canvas = new ChartJSNodeCanvas({
|
|
295
|
+
width: 1000,
|
|
296
|
+
height: 600,
|
|
297
|
+
type: outfile.endsWith(".svg") ? "svg" : "png",
|
|
298
|
+
chartCallback: ChartJS => ChartJS.register(ChartDataLabels)
|
|
299
|
+
});
|
|
300
|
+
|
|
301
|
+
const buffer = canvas.renderToBufferSync(
|
|
302
|
+
config,
|
|
303
|
+
outfile.endsWith(".svg") ? "image/svg+xml" : "image/png"
|
|
304
|
+
);
|
|
305
|
+
|
|
306
|
+
fs.writeFileSync(outfile, buffer);
|
|
307
|
+
console.log(`Chart written -> ${outfile}`);
|
|
308
|
+
}
|
package/transform/lib/index.js
CHANGED
|
@@ -994,8 +994,8 @@ export class JSONTransform extends Visitor {
|
|
|
994
994
|
let fromPath = node.range.source.normalizedPath.replaceAll("/", path.sep);
|
|
995
995
|
const isLib = path.dirname(baseDir).endsWith("node_modules");
|
|
996
996
|
if (!isLib && !this.parser.sources.some((s) => s.normalizedPath.startsWith("assembly/index"))) {
|
|
997
|
-
const newPath = "
|
|
998
|
-
this.parser.parseFile(readFileSync(
|
|
997
|
+
const newPath = path.join(baseDir, "assembly", "index.ts");
|
|
998
|
+
this.parser.parseFile(readFileSync(newPath).toString(), newPath, false);
|
|
999
999
|
}
|
|
1000
1000
|
else if (isLib && !this.parser.sources.some((s) => s.normalizedPath.startsWith("~lib/json-as/assembly/index"))) {
|
|
1001
1001
|
const newPath = "~lib/json-as/assembly/index.ts";
|