@bushscad/cli 0.0.0-stage → 0.3.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/LICENSE +202 -0
- package/README.md +4 -2
- package/dist/classic-benchmark.js +35250 -0
- package/dist/fonts/NotoSans-LICENSE.txt +94 -0
- package/dist/fonts/NotoSans-Regular.ttf +0 -0
- package/dist/fonts/NotoSansMono-LICENSE.txt +94 -0
- package/dist/fonts/NotoSansMono-Regular.ttf +0 -0
- package/dist/fonts/NotoSerif-LICENSE.txt +94 -0
- package/dist/fonts/NotoSerif-Regular.ttf +0 -0
- package/dist/index.js +40765 -0
- package/dist/index.js.map +7 -0
- package/dist/occt-cancellation-probe.js +107 -0
- package/dist/occt-probe-runner.js +232 -0
- package/dist/occt-probe-worker.js +13936 -0
- package/dist/p7-benchmark.js +623 -0
- package/package.json +49 -6
- package/src/dto/bufferDto.ts +22 -0
- package/src/dto/index.ts +28 -0
- package/src/dto/json.ts +39 -0
- package/src/dto/meshDto.ts +26 -0
- package/src/dto/message-parser.ts +158 -0
- package/src/dto/request-fields.ts +174 -0
- package/src/dto/request-parser.ts +214 -0
- package/src/dto/types.ts +195 -0
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
// scripts/parallel-compilation/occt-cancellation-probe.ts
|
|
2
|
+
import { fork } from "node:child_process";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
|
|
5
|
+
// scripts/parallel-compilation/report-output.ts
|
|
6
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
7
|
+
import { dirname } from "node:path";
|
|
8
|
+
async function writeJsonReport(outPath, report, stdoutReport = report) {
|
|
9
|
+
await mkdir(dirname(outPath), { recursive: true });
|
|
10
|
+
await writeFile(outPath, `${JSON.stringify(report, null, 2)}
|
|
11
|
+
`, "utf8");
|
|
12
|
+
process.stdout.write(`${JSON.stringify(stdoutReport, null, 2)}
|
|
13
|
+
`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// scripts/parallel-compilation/occt-cancellation-probe.ts
|
|
17
|
+
async function run() {
|
|
18
|
+
const workerPath = resolve(
|
|
19
|
+
readArg("--worker", "packages/cli/dist/occt-probe-worker.js")
|
|
20
|
+
);
|
|
21
|
+
const outPath = resolve(
|
|
22
|
+
readArg(
|
|
23
|
+
"--out",
|
|
24
|
+
".bushscad-analysis/parallel-compilation/p0-process-cancellation.json"
|
|
25
|
+
)
|
|
26
|
+
);
|
|
27
|
+
const startedAt = performance.now();
|
|
28
|
+
const cancelled = launch(workerPath, 1);
|
|
29
|
+
await new Promise((resolvePromise) => setTimeout(resolvePromise, 1e3));
|
|
30
|
+
const cancelStartedAt = performance.now();
|
|
31
|
+
cancelled.kill();
|
|
32
|
+
const cancelledExit = await waitForExit(cancelled);
|
|
33
|
+
const cancelLatencyMs = performance.now() - cancelStartedAt;
|
|
34
|
+
const replacement = launch(workerPath, 2);
|
|
35
|
+
const ready = await waitUntilReady(replacement);
|
|
36
|
+
const shutdownResult = await requestShutdown(replacement);
|
|
37
|
+
const replacementExit = await waitForExit(replacement);
|
|
38
|
+
const report = {
|
|
39
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
40
|
+
cancelledExit,
|
|
41
|
+
cancelLatencyMs,
|
|
42
|
+
replacementReadyMs: ready.initializationMs,
|
|
43
|
+
replacementRssBytes: ready.rssBytes,
|
|
44
|
+
shutdownElapsedMs: shutdownResult.elapsedMs,
|
|
45
|
+
replacementExit,
|
|
46
|
+
totalWallMs: performance.now() - startedAt,
|
|
47
|
+
coordinatorSurvived: true
|
|
48
|
+
};
|
|
49
|
+
await writeJsonReport(outPath, report);
|
|
50
|
+
}
|
|
51
|
+
function launch(workerPath, workerId) {
|
|
52
|
+
return fork(workerPath, [], {
|
|
53
|
+
env: { ...process.env, BUSHSCAD_PROBE_WORKER_ID: String(workerId) },
|
|
54
|
+
serialization: "advanced",
|
|
55
|
+
stdio: ["ignore", "inherit", "inherit", "ipc"]
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
function waitUntilReady(child) {
|
|
59
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
60
|
+
const timer = setTimeout(
|
|
61
|
+
() => rejectPromise(new Error("Replacement worker readiness timed out.")),
|
|
62
|
+
12e4
|
|
63
|
+
);
|
|
64
|
+
child.on("message", (raw) => {
|
|
65
|
+
const message = raw;
|
|
66
|
+
if (message.kind === "ready") {
|
|
67
|
+
clearTimeout(timer);
|
|
68
|
+
resolvePromise(message);
|
|
69
|
+
} else if (message.kind === "error") {
|
|
70
|
+
clearTimeout(timer);
|
|
71
|
+
rejectPromise(new Error(message.message));
|
|
72
|
+
}
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
}
|
|
76
|
+
function requestShutdown(child) {
|
|
77
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
78
|
+
const requestId = 1;
|
|
79
|
+
child.on("message", (raw) => {
|
|
80
|
+
const message = raw;
|
|
81
|
+
if (message.kind === "result" && message.requestId === requestId)
|
|
82
|
+
resolvePromise(message);
|
|
83
|
+
else if (message.kind === "error")
|
|
84
|
+
rejectPromise(new Error(message.message));
|
|
85
|
+
});
|
|
86
|
+
const command = { kind: "shutdown", requestId };
|
|
87
|
+
child.send(command);
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
function waitForExit(child) {
|
|
91
|
+
if (child.exitCode !== null)
|
|
92
|
+
return Promise.resolve({ code: child.exitCode, signal: child.signalCode });
|
|
93
|
+
return new Promise(
|
|
94
|
+
(resolvePromise) => child.once("exit", (code, signal) => resolvePromise({ code, signal }))
|
|
95
|
+
);
|
|
96
|
+
}
|
|
97
|
+
function readArg(name, fallback) {
|
|
98
|
+
const index = process.argv.indexOf(name);
|
|
99
|
+
return index >= 0 ? process.argv[index + 1] ?? fallback : fallback;
|
|
100
|
+
}
|
|
101
|
+
void run().catch((error) => {
|
|
102
|
+
process.stderr.write(
|
|
103
|
+
`${error instanceof Error ? error.stack ?? error.message : String(error)}
|
|
104
|
+
`
|
|
105
|
+
);
|
|
106
|
+
process.exitCode = 1;
|
|
107
|
+
});
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
// scripts/parallel-compilation/occt-probe-runner.ts
|
|
2
|
+
import { fork } from "node:child_process";
|
|
3
|
+
import { resolve } from "node:path";
|
|
4
|
+
import { Worker } from "node:worker_threads";
|
|
5
|
+
|
|
6
|
+
// scripts/parallel-compilation/report-output.ts
|
|
7
|
+
import { mkdir, writeFile } from "node:fs/promises";
|
|
8
|
+
import { dirname } from "node:path";
|
|
9
|
+
async function writeJsonReport(outPath, report, stdoutReport = report) {
|
|
10
|
+
await mkdir(dirname(outPath), { recursive: true });
|
|
11
|
+
await writeFile(outPath, `${JSON.stringify(report, null, 2)}
|
|
12
|
+
`, "utf8");
|
|
13
|
+
process.stdout.write(`${JSON.stringify(stdoutReport, null, 2)}
|
|
14
|
+
`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// scripts/parallel-compilation/occt-probe-runner.ts
|
|
18
|
+
async function run() {
|
|
19
|
+
const args = parseArgs(process.argv.slice(2));
|
|
20
|
+
const launchedAt = performance.now();
|
|
21
|
+
const workers = Array.from(
|
|
22
|
+
{ length: args.workerCount },
|
|
23
|
+
(_, index) => createWorker(args.mode, index + 1, args.workerPath)
|
|
24
|
+
);
|
|
25
|
+
const ready = await Promise.all(workers.map(waitUntilReady));
|
|
26
|
+
const wallInitializationMs = performance.now() - launchedAt;
|
|
27
|
+
const clients = workers.map(createClient);
|
|
28
|
+
try {
|
|
29
|
+
const firstBuilds = await Promise.all(
|
|
30
|
+
clients.map((client) => client.request("build-cuboid"))
|
|
31
|
+
);
|
|
32
|
+
const warmBuilds = await Promise.all(
|
|
33
|
+
clients.map((client) => client.request("build-cuboid"))
|
|
34
|
+
);
|
|
35
|
+
const beforeIdle = await Promise.all(
|
|
36
|
+
clients.map((client) => client.request("memory"))
|
|
37
|
+
);
|
|
38
|
+
await new Promise(
|
|
39
|
+
(resolvePromise) => setTimeout(resolvePromise, args.idleMs)
|
|
40
|
+
);
|
|
41
|
+
const afterIdle = await Promise.all(
|
|
42
|
+
clients.map((client) => client.request("memory"))
|
|
43
|
+
);
|
|
44
|
+
const exporter = clients[0];
|
|
45
|
+
const importer = clients[1] ?? clients[0];
|
|
46
|
+
if (!exporter || !importer)
|
|
47
|
+
throw new Error("At least one worker is required.");
|
|
48
|
+
const exportResult = await exporter.request("export-cuboid");
|
|
49
|
+
if (!exportResult.dataBase64)
|
|
50
|
+
throw new Error("Transfer export returned no STEP payload.");
|
|
51
|
+
const importResult = await importer.request(
|
|
52
|
+
"import-step",
|
|
53
|
+
exportResult.dataBase64
|
|
54
|
+
);
|
|
55
|
+
const brepExportResult = await exporter.request("export-brep");
|
|
56
|
+
if (!brepExportResult.dataBase64)
|
|
57
|
+
throw new Error("Native transfer export returned no BRep payload.");
|
|
58
|
+
const brepImportResult = await importer.request(
|
|
59
|
+
"import-brep",
|
|
60
|
+
brepExportResult.dataBase64
|
|
61
|
+
);
|
|
62
|
+
const report = {
|
|
63
|
+
generatedAt: (/* @__PURE__ */ new Date()).toISOString(),
|
|
64
|
+
mode: args.mode,
|
|
65
|
+
workerCount: args.workerCount,
|
|
66
|
+
idleMs: args.idleMs,
|
|
67
|
+
wallInitializationMs,
|
|
68
|
+
ready,
|
|
69
|
+
firstBuilds,
|
|
70
|
+
warmBuilds,
|
|
71
|
+
beforeIdle,
|
|
72
|
+
afterIdle,
|
|
73
|
+
transfer: {
|
|
74
|
+
exportResult,
|
|
75
|
+
importResult,
|
|
76
|
+
stepBytes: Buffer.byteLength(exportResult.dataBase64, "base64")
|
|
77
|
+
},
|
|
78
|
+
nativeBrepTransfer: {
|
|
79
|
+
exportResult: brepExportResult,
|
|
80
|
+
importResult: brepImportResult,
|
|
81
|
+
brepBytes: Buffer.byteLength(brepExportResult.dataBase64, "base64")
|
|
82
|
+
}
|
|
83
|
+
};
|
|
84
|
+
await writeJsonReport(args.outPath, report, reportSummary(report));
|
|
85
|
+
} finally {
|
|
86
|
+
await Promise.all(clients.map((client) => client.shutdown()));
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
function createClient(handle) {
|
|
90
|
+
let nextRequestId = 1;
|
|
91
|
+
const pending = /* @__PURE__ */ new Map();
|
|
92
|
+
handle.onMessage((message) => {
|
|
93
|
+
if (message.kind === "ready") return;
|
|
94
|
+
if (message.kind === "error") {
|
|
95
|
+
if (message.requestId !== void 0) {
|
|
96
|
+
const request3 = pending.get(message.requestId);
|
|
97
|
+
pending.delete(message.requestId);
|
|
98
|
+
request3?.reject(new Error(message.message));
|
|
99
|
+
}
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
const request2 = pending.get(message.requestId);
|
|
103
|
+
pending.delete(message.requestId);
|
|
104
|
+
request2?.resolve(message);
|
|
105
|
+
});
|
|
106
|
+
const request = (kind, dataBase64) => {
|
|
107
|
+
const requestId = nextRequestId++;
|
|
108
|
+
const command = kind === "import-step" || kind === "import-brep" ? { kind, requestId, dataBase64: dataBase64 ?? "" } : { kind, requestId };
|
|
109
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
110
|
+
pending.set(requestId, {
|
|
111
|
+
resolve: resolvePromise,
|
|
112
|
+
reject: rejectPromise
|
|
113
|
+
});
|
|
114
|
+
handle.send(command);
|
|
115
|
+
});
|
|
116
|
+
};
|
|
117
|
+
return {
|
|
118
|
+
request,
|
|
119
|
+
shutdown: async () => {
|
|
120
|
+
const requestId = nextRequestId++;
|
|
121
|
+
await new Promise((resolvePromise, rejectPromise) => {
|
|
122
|
+
pending.set(requestId, {
|
|
123
|
+
resolve: () => resolvePromise(),
|
|
124
|
+
reject: rejectPromise
|
|
125
|
+
});
|
|
126
|
+
handle.send({ kind: "shutdown", requestId });
|
|
127
|
+
});
|
|
128
|
+
await handle.terminate();
|
|
129
|
+
}
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function reportSummary(report) {
|
|
133
|
+
return {
|
|
134
|
+
...report,
|
|
135
|
+
transfer: {
|
|
136
|
+
...report.transfer,
|
|
137
|
+
exportResult: {
|
|
138
|
+
...report.transfer.exportResult,
|
|
139
|
+
dataBase64: report.transfer.exportResult.dataBase64 ? `(omitted ${report.transfer.stepBytes} bytes)` : void 0
|
|
140
|
+
}
|
|
141
|
+
},
|
|
142
|
+
nativeBrepTransfer: {
|
|
143
|
+
...report.nativeBrepTransfer,
|
|
144
|
+
exportResult: {
|
|
145
|
+
...report.nativeBrepTransfer.exportResult,
|
|
146
|
+
dataBase64: report.nativeBrepTransfer.exportResult.dataBase64 ? `(omitted ${report.nativeBrepTransfer.brepBytes} bytes)` : void 0
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
};
|
|
150
|
+
}
|
|
151
|
+
function waitUntilReady(handle) {
|
|
152
|
+
return new Promise((resolvePromise, rejectPromise) => {
|
|
153
|
+
const timer = setTimeout(
|
|
154
|
+
() => rejectPromise(new Error("OCCT probe worker readiness timed out.")),
|
|
155
|
+
12e4
|
|
156
|
+
);
|
|
157
|
+
handle.onMessage((message) => {
|
|
158
|
+
if (message.kind === "error" && message.requestId === void 0) {
|
|
159
|
+
clearTimeout(timer);
|
|
160
|
+
rejectPromise(new Error(message.message));
|
|
161
|
+
} else if (message.kind === "ready") {
|
|
162
|
+
clearTimeout(timer);
|
|
163
|
+
resolvePromise(message);
|
|
164
|
+
}
|
|
165
|
+
});
|
|
166
|
+
});
|
|
167
|
+
}
|
|
168
|
+
function createWorker(mode, workerId, workerPath) {
|
|
169
|
+
if (mode === "thread")
|
|
170
|
+
return threadHandle(new Worker(workerPath, { workerData: { workerId } }));
|
|
171
|
+
return processHandle(
|
|
172
|
+
fork(workerPath, [], {
|
|
173
|
+
env: { ...process.env, BUSHSCAD_PROBE_WORKER_ID: String(workerId) },
|
|
174
|
+
serialization: "advanced",
|
|
175
|
+
stdio: ["ignore", "inherit", "inherit", "ipc"]
|
|
176
|
+
})
|
|
177
|
+
);
|
|
178
|
+
}
|
|
179
|
+
function threadHandle(worker) {
|
|
180
|
+
return {
|
|
181
|
+
send: (command) => worker.postMessage(command),
|
|
182
|
+
onMessage: (listener) => worker.on("message", listener),
|
|
183
|
+
terminate: async () => void await worker.terminate()
|
|
184
|
+
};
|
|
185
|
+
}
|
|
186
|
+
function processHandle(child) {
|
|
187
|
+
return {
|
|
188
|
+
send: (command) => child.send?.(command),
|
|
189
|
+
onMessage: (listener) => child.on("message", (message) => listener(message)),
|
|
190
|
+
terminate: async () => {
|
|
191
|
+
if (child.exitCode !== null) return;
|
|
192
|
+
await new Promise((resolvePromise) => {
|
|
193
|
+
child.once("exit", () => resolvePromise());
|
|
194
|
+
setTimeout(() => {
|
|
195
|
+
if (child.exitCode === null) child.kill();
|
|
196
|
+
}, 2e3);
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
};
|
|
200
|
+
}
|
|
201
|
+
function parseArgs(args) {
|
|
202
|
+
const read = (name, fallback) => {
|
|
203
|
+
const index = args.indexOf(name);
|
|
204
|
+
return index >= 0 ? args[index + 1] ?? fallback : fallback;
|
|
205
|
+
};
|
|
206
|
+
const mode = read("--mode", "process");
|
|
207
|
+
if (mode !== "thread" && mode !== "process")
|
|
208
|
+
throw new Error(`Unsupported mode '${mode}'.`);
|
|
209
|
+
const workerCount = Math.max(1, Number(read("--workers", "1")));
|
|
210
|
+
const idleMs = Math.max(0, Number(read("--idle-ms", "10000")));
|
|
211
|
+
return {
|
|
212
|
+
mode,
|
|
213
|
+
workerCount,
|
|
214
|
+
idleMs,
|
|
215
|
+
outPath: resolve(
|
|
216
|
+
read(
|
|
217
|
+
"--out",
|
|
218
|
+
`.bushscad-analysis/parallel-compilation/p0-${mode}-${workerCount}.json`
|
|
219
|
+
)
|
|
220
|
+
),
|
|
221
|
+
workerPath: resolve(
|
|
222
|
+
read("--worker", "packages/cli/dist/occt-probe-worker.js")
|
|
223
|
+
)
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
void run().catch((error) => {
|
|
227
|
+
process.stderr.write(
|
|
228
|
+
`${error instanceof Error ? error.stack ?? error.message : String(error)}
|
|
229
|
+
`
|
|
230
|
+
);
|
|
231
|
+
process.exitCode = 1;
|
|
232
|
+
});
|