brepjs-cad 0.1.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/CHANGELOG.md +1 -0
- package/LICENSE +191 -0
- package/README.md +85 -0
- package/dist/brepjs-cad.cjs +8 -0
- package/dist/brepjs-cad.js +2 -0
- package/dist/cli/exportPart.d.ts +13 -0
- package/dist/cli/main.cjs +317 -0
- package/dist/cli/main.d.ts +2 -0
- package/dist/cli/main.js +316 -0
- package/dist/cli/scaffold.d.ts +9 -0
- package/dist/cli/watch.d.ts +10 -0
- package/dist/diff-4UNdqx4k.cjs +550 -0
- package/dist/diff-ByiVwVrr.js +509 -0
- package/dist/disposeShape.d.ts +6 -0
- package/dist/index.d.ts +5 -0
- package/dist/snapshot/registry.cjs +50 -0
- package/dist/snapshot/registry.d.ts +12 -0
- package/dist/snapshot/registry.js +48 -0
- package/dist/snapshot/serve.cjs +27 -0
- package/dist/snapshot/serve.d.ts +12 -0
- package/dist/snapshot/serve.js +26 -0
- package/dist/snapshot/shoot.cjs +85 -0
- package/dist/snapshot/shoot.d.ts +14 -0
- package/dist/snapshot/shoot.js +61 -0
- package/dist/snapshot/static.cjs +99 -0
- package/dist/snapshot/static.d.ts +16 -0
- package/dist/snapshot/static.js +97 -0
- package/dist/verify/checks.d.ts +3 -0
- package/dist/verify/diff.d.ts +2 -0
- package/dist/verify/measure.d.ts +6 -0
- package/dist/verify/report.d.ts +63 -0
- package/dist/verify/runPart.d.ts +18 -0
- package/package.json +79 -0
- package/viewer/dist/assets/brepjs-CI5VXw8W.js +57 -0
- package/viewer/dist/assets/index-DivdJhNC.js +4167 -0
- package/viewer/dist/assets/kernelWorker-BtcMpY8t.js +1 -0
- package/viewer/dist/index.html +22 -0
- package/viewer/dist/wasm/occt-wasm.js +2 -0
- package/viewer/dist/wasm/occt-wasm.wasm +0 -0
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { acquireServer } from "./registry.js";
|
|
2
|
+
import { basename, dirname, resolve } from "node:path";
|
|
3
|
+
//#region src/snapshot/serve.ts
|
|
4
|
+
function viewerUrl(base, file) {
|
|
5
|
+
if (!file) return base;
|
|
6
|
+
const abs = resolve(file);
|
|
7
|
+
return `${base}/?dir=${encodeURIComponent(dirname(abs))}&file=${encodeURIComponent(basename(abs))}`;
|
|
8
|
+
}
|
|
9
|
+
/** Acquire (reuse or start) the persistent server and return its viewer URL. */
|
|
10
|
+
async function serve(opts = {}) {
|
|
11
|
+
const server = await acquireServer(opts);
|
|
12
|
+
const url = viewerUrl(server.url, opts.file);
|
|
13
|
+
if (!server.reused) {
|
|
14
|
+
const onSig = () => void server.close().then(() => process.exit(0));
|
|
15
|
+
process.once("SIGINT", onSig);
|
|
16
|
+
process.once("SIGTERM", onSig);
|
|
17
|
+
}
|
|
18
|
+
return {
|
|
19
|
+
port: server.port,
|
|
20
|
+
url,
|
|
21
|
+
reused: server.reused,
|
|
22
|
+
close: () => server.close()
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
//#endregion
|
|
26
|
+
export { serve };
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
//#region \0rolldown/runtime.js
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") for (var keys = __getOwnPropNames(from), i = 0, n = keys.length, key; i < n; i++) {
|
|
11
|
+
key = keys[i];
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except) __defProp(to, key, {
|
|
13
|
+
get: ((k) => from[k]).bind(null, key),
|
|
14
|
+
enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
return to;
|
|
18
|
+
};
|
|
19
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", {
|
|
20
|
+
value: mod,
|
|
21
|
+
enumerable: true
|
|
22
|
+
}) : target, mod));
|
|
23
|
+
//#endregion
|
|
24
|
+
const require_snapshot_registry = require("./registry.cjs");
|
|
25
|
+
let node_path = require("node:path");
|
|
26
|
+
let node_fs_promises = require("node:fs/promises");
|
|
27
|
+
let puppeteer = require("puppeteer");
|
|
28
|
+
puppeteer = __toESM(puppeteer, 1);
|
|
29
|
+
//#region src/snapshot/shoot.ts
|
|
30
|
+
var VIEWS = [
|
|
31
|
+
"iso",
|
|
32
|
+
"front",
|
|
33
|
+
"top",
|
|
34
|
+
"right"
|
|
35
|
+
];
|
|
36
|
+
var READY_TIMEOUT_MS = 9e4;
|
|
37
|
+
async function shoot(opts) {
|
|
38
|
+
const absFile = (0, node_path.resolve)(opts.file);
|
|
39
|
+
const dir = (0, node_path.dirname)(absFile);
|
|
40
|
+
const rel = (0, node_path.basename)(absFile);
|
|
41
|
+
const views = opts.views ?? VIEWS;
|
|
42
|
+
await (0, node_fs_promises.mkdir)(opts.outDir, { recursive: true });
|
|
43
|
+
const server = await require_snapshot_registry.acquireServer(opts);
|
|
44
|
+
const browser = await puppeteer.default.launch({
|
|
45
|
+
headless: true,
|
|
46
|
+
args: [
|
|
47
|
+
"--no-sandbox",
|
|
48
|
+
"--use-gl=angle",
|
|
49
|
+
"--use-angle=swiftshader",
|
|
50
|
+
"--enable-unsafe-swiftshader"
|
|
51
|
+
]
|
|
52
|
+
});
|
|
53
|
+
const pngs = [];
|
|
54
|
+
try {
|
|
55
|
+
const page = await browser.newPage();
|
|
56
|
+
await page.setViewport({
|
|
57
|
+
width: 1200,
|
|
58
|
+
height: 900
|
|
59
|
+
});
|
|
60
|
+
const target = `${server.url}/?dir=${encodeURIComponent(dir)}&file=${encodeURIComponent(rel)}`;
|
|
61
|
+
await page.goto(target, {
|
|
62
|
+
waitUntil: "domcontentloaded",
|
|
63
|
+
timeout: 3e4
|
|
64
|
+
});
|
|
65
|
+
await page.waitForFunction("window.__ready === true", { timeout: READY_TIMEOUT_MS });
|
|
66
|
+
for (const view of views) {
|
|
67
|
+
await page.evaluate((v) => {
|
|
68
|
+
globalThis.__renderView(v);
|
|
69
|
+
}, view);
|
|
70
|
+
await new Promise((r) => setTimeout(r, opts.settleMs ?? 400));
|
|
71
|
+
const path = (0, node_path.resolve)(opts.outDir, `${view}.png`);
|
|
72
|
+
await page.screenshot({ path });
|
|
73
|
+
pngs.push(path);
|
|
74
|
+
}
|
|
75
|
+
} finally {
|
|
76
|
+
await browser.close();
|
|
77
|
+
await server.close();
|
|
78
|
+
}
|
|
79
|
+
return {
|
|
80
|
+
outDir: opts.outDir,
|
|
81
|
+
pngs
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
//#endregion
|
|
85
|
+
exports.shoot = shoot;
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { AcquireOptions } from './registry.js';
|
|
2
|
+
export type ViewName = 'iso' | 'front' | 'top' | 'right';
|
|
3
|
+
export interface ShootOptions extends AcquireOptions {
|
|
4
|
+
file: string;
|
|
5
|
+
outDir: string;
|
|
6
|
+
views?: readonly ViewName[];
|
|
7
|
+
/** Wall-clock ms to let the camera settle before each capture (default 400; raise on slow CI). */
|
|
8
|
+
settleMs?: number;
|
|
9
|
+
}
|
|
10
|
+
export interface ShootResult {
|
|
11
|
+
outDir: string;
|
|
12
|
+
pngs: string[];
|
|
13
|
+
}
|
|
14
|
+
export declare function shoot(opts: ShootOptions): Promise<ShootResult>;
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import { acquireServer } from "./registry.js";
|
|
2
|
+
import { basename, dirname, resolve } from "node:path";
|
|
3
|
+
import { mkdir } from "node:fs/promises";
|
|
4
|
+
import puppeteer from "puppeteer";
|
|
5
|
+
//#region src/snapshot/shoot.ts
|
|
6
|
+
var VIEWS = [
|
|
7
|
+
"iso",
|
|
8
|
+
"front",
|
|
9
|
+
"top",
|
|
10
|
+
"right"
|
|
11
|
+
];
|
|
12
|
+
var READY_TIMEOUT_MS = 9e4;
|
|
13
|
+
async function shoot(opts) {
|
|
14
|
+
const absFile = resolve(opts.file);
|
|
15
|
+
const dir = dirname(absFile);
|
|
16
|
+
const rel = basename(absFile);
|
|
17
|
+
const views = opts.views ?? VIEWS;
|
|
18
|
+
await mkdir(opts.outDir, { recursive: true });
|
|
19
|
+
const server = await acquireServer(opts);
|
|
20
|
+
const browser = await puppeteer.launch({
|
|
21
|
+
headless: true,
|
|
22
|
+
args: [
|
|
23
|
+
"--no-sandbox",
|
|
24
|
+
"--use-gl=angle",
|
|
25
|
+
"--use-angle=swiftshader",
|
|
26
|
+
"--enable-unsafe-swiftshader"
|
|
27
|
+
]
|
|
28
|
+
});
|
|
29
|
+
const pngs = [];
|
|
30
|
+
try {
|
|
31
|
+
const page = await browser.newPage();
|
|
32
|
+
await page.setViewport({
|
|
33
|
+
width: 1200,
|
|
34
|
+
height: 900
|
|
35
|
+
});
|
|
36
|
+
const target = `${server.url}/?dir=${encodeURIComponent(dir)}&file=${encodeURIComponent(rel)}`;
|
|
37
|
+
await page.goto(target, {
|
|
38
|
+
waitUntil: "domcontentloaded",
|
|
39
|
+
timeout: 3e4
|
|
40
|
+
});
|
|
41
|
+
await page.waitForFunction("window.__ready === true", { timeout: READY_TIMEOUT_MS });
|
|
42
|
+
for (const view of views) {
|
|
43
|
+
await page.evaluate((v) => {
|
|
44
|
+
globalThis.__renderView(v);
|
|
45
|
+
}, view);
|
|
46
|
+
await new Promise((r) => setTimeout(r, opts.settleMs ?? 400));
|
|
47
|
+
const path = resolve(opts.outDir, `${view}.png`);
|
|
48
|
+
await page.screenshot({ path });
|
|
49
|
+
pngs.push(path);
|
|
50
|
+
}
|
|
51
|
+
} finally {
|
|
52
|
+
await browser.close();
|
|
53
|
+
await server.close();
|
|
54
|
+
}
|
|
55
|
+
return {
|
|
56
|
+
outDir: opts.outDir,
|
|
57
|
+
pngs
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
//#endregion
|
|
61
|
+
export { shoot };
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let node_fs = require("node:fs");
|
|
3
|
+
let node_path = require("node:path");
|
|
4
|
+
let node_url = require("node:url");
|
|
5
|
+
let node_http = require("node:http");
|
|
6
|
+
let node_fs_promises = require("node:fs/promises");
|
|
7
|
+
//#region src/snapshot/static.ts
|
|
8
|
+
var SERVER_API_VERSION = 1;
|
|
9
|
+
var VIEWER_DIST = (0, node_path.resolve)((0, node_path.dirname)((0, node_url.fileURLToPath)({}.url)), "../../viewer/dist");
|
|
10
|
+
var MIME = {
|
|
11
|
+
".html": "text/html; charset=utf-8",
|
|
12
|
+
".js": "text/javascript; charset=utf-8",
|
|
13
|
+
".mjs": "text/javascript; charset=utf-8",
|
|
14
|
+
".css": "text/css; charset=utf-8",
|
|
15
|
+
".json": "application/json; charset=utf-8",
|
|
16
|
+
".wasm": "application/wasm",
|
|
17
|
+
".png": "image/png",
|
|
18
|
+
".svg": "image/svg+xml",
|
|
19
|
+
".glb": "model/gltf-binary",
|
|
20
|
+
".gltf": "model/gltf+json",
|
|
21
|
+
".step": "application/step",
|
|
22
|
+
".stp": "application/step",
|
|
23
|
+
".stl": "model/stl",
|
|
24
|
+
".obj": "text/plain; charset=utf-8"
|
|
25
|
+
};
|
|
26
|
+
var mimeFor = (p) => MIME[(0, node_path.extname)(p).toLowerCase()] ?? "application/octet-stream";
|
|
27
|
+
async function sendFile(res, absPath) {
|
|
28
|
+
const info = await (0, node_fs_promises.stat)(absPath);
|
|
29
|
+
if (!info.isFile()) {
|
|
30
|
+
res.writeHead(404).end("not found");
|
|
31
|
+
return;
|
|
32
|
+
}
|
|
33
|
+
res.writeHead(200, {
|
|
34
|
+
"content-type": mimeFor(absPath),
|
|
35
|
+
"content-length": info.size
|
|
36
|
+
});
|
|
37
|
+
(0, node_fs.createReadStream)(absPath).pipe(res);
|
|
38
|
+
}
|
|
39
|
+
function safeJoin(root, rel) {
|
|
40
|
+
const abs = (0, node_path.resolve)(root, (0, node_path.normalize)(decodeURIComponent(rel.replace(/^\/+/, ""))));
|
|
41
|
+
return abs !== root && !abs.startsWith(root + node_path.sep) ? null : abs;
|
|
42
|
+
}
|
|
43
|
+
async function handle(req, res, port) {
|
|
44
|
+
const url = new URL(req.url ?? "/", `http://127.0.0.1:${port}`);
|
|
45
|
+
if (url.pathname === "/__cad/server") {
|
|
46
|
+
const d = {
|
|
47
|
+
app: "brepjs-cad-viewer",
|
|
48
|
+
port,
|
|
49
|
+
dynamicRoot: true,
|
|
50
|
+
serverApiVersion: 1
|
|
51
|
+
};
|
|
52
|
+
res.writeHead(200, { "content-type": "application/json; charset=utf-8" });
|
|
53
|
+
res.end(JSON.stringify(d));
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
if (url.pathname.startsWith("/__model/")) {
|
|
57
|
+
const root = url.searchParams.get("dir");
|
|
58
|
+
if (!root) {
|
|
59
|
+
res.writeHead(400).end("missing dir");
|
|
60
|
+
return;
|
|
61
|
+
}
|
|
62
|
+
const abs = safeJoin((0, node_path.resolve)(root), url.pathname.slice(8));
|
|
63
|
+
if (!abs) {
|
|
64
|
+
res.writeHead(403).end("forbidden");
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
await sendFile(res, abs).catch(() => res.writeHead(404).end("not found"));
|
|
68
|
+
return;
|
|
69
|
+
}
|
|
70
|
+
if (url.searchParams.has("dir") && url.pathname !== "/") {
|
|
71
|
+
res.writeHead(403).end("forbidden");
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
const abs = safeJoin(VIEWER_DIST, url.pathname === "/" ? "index.html" : url.pathname);
|
|
75
|
+
if (!abs) {
|
|
76
|
+
res.writeHead(403).end("forbidden");
|
|
77
|
+
return;
|
|
78
|
+
}
|
|
79
|
+
await sendFile(res, abs).catch(() => sendFile(res, (0, node_path.join)(VIEWER_DIST, "index.html")).catch(() => res.writeHead(404).end("not found")));
|
|
80
|
+
}
|
|
81
|
+
function startStaticServer(opts = {}) {
|
|
82
|
+
return new Promise((resolvePromise, reject) => {
|
|
83
|
+
const server = (0, node_http.createServer)((req, res) => {
|
|
84
|
+
handle(req, res, server.address().port);
|
|
85
|
+
});
|
|
86
|
+
server.on("error", reject);
|
|
87
|
+
server.listen(opts.port ?? 0, "127.0.0.1", () => {
|
|
88
|
+
const port = server.address().port;
|
|
89
|
+
resolvePromise({
|
|
90
|
+
port,
|
|
91
|
+
url: `http://127.0.0.1:${port}`,
|
|
92
|
+
close: () => new Promise((done, fail) => server.close((e) => e ? fail(e) : done()))
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
//#endregion
|
|
98
|
+
exports.SERVER_API_VERSION = SERVER_API_VERSION;
|
|
99
|
+
exports.startStaticServer = startStaticServer;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export declare const SERVER_API_VERSION = 1;
|
|
2
|
+
export interface StaticServerOptions {
|
|
3
|
+
port?: number;
|
|
4
|
+
}
|
|
5
|
+
export interface StaticServer {
|
|
6
|
+
port: number;
|
|
7
|
+
url: string;
|
|
8
|
+
close(): Promise<void>;
|
|
9
|
+
}
|
|
10
|
+
export interface ServerDescriptor {
|
|
11
|
+
app: 'brepjs-cad-viewer';
|
|
12
|
+
port: number;
|
|
13
|
+
dynamicRoot: true;
|
|
14
|
+
serverApiVersion: number;
|
|
15
|
+
}
|
|
16
|
+
export declare function startStaticServer(opts?: StaticServerOptions): Promise<StaticServer>;
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { createReadStream } from "node:fs";
|
|
2
|
+
import { dirname, extname, join, normalize, resolve, sep } from "node:path";
|
|
3
|
+
import { fileURLToPath } from "node:url";
|
|
4
|
+
import { createServer } from "node:http";
|
|
5
|
+
import { stat } from "node:fs/promises";
|
|
6
|
+
//#region src/snapshot/static.ts
|
|
7
|
+
var SERVER_API_VERSION = 1;
|
|
8
|
+
var VIEWER_DIST = resolve(dirname(fileURLToPath(import.meta.url)), "../../viewer/dist");
|
|
9
|
+
var MIME = {
|
|
10
|
+
".html": "text/html; charset=utf-8",
|
|
11
|
+
".js": "text/javascript; charset=utf-8",
|
|
12
|
+
".mjs": "text/javascript; charset=utf-8",
|
|
13
|
+
".css": "text/css; charset=utf-8",
|
|
14
|
+
".json": "application/json; charset=utf-8",
|
|
15
|
+
".wasm": "application/wasm",
|
|
16
|
+
".png": "image/png",
|
|
17
|
+
".svg": "image/svg+xml",
|
|
18
|
+
".glb": "model/gltf-binary",
|
|
19
|
+
".gltf": "model/gltf+json",
|
|
20
|
+
".step": "application/step",
|
|
21
|
+
".stp": "application/step",
|
|
22
|
+
".stl": "model/stl",
|
|
23
|
+
".obj": "text/plain; charset=utf-8"
|
|
24
|
+
};
|
|
25
|
+
var mimeFor = (p) => MIME[extname(p).toLowerCase()] ?? "application/octet-stream";
|
|
26
|
+
async function sendFile(res, absPath) {
|
|
27
|
+
const info = await stat(absPath);
|
|
28
|
+
if (!info.isFile()) {
|
|
29
|
+
res.writeHead(404).end("not found");
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
res.writeHead(200, {
|
|
33
|
+
"content-type": mimeFor(absPath),
|
|
34
|
+
"content-length": info.size
|
|
35
|
+
});
|
|
36
|
+
createReadStream(absPath).pipe(res);
|
|
37
|
+
}
|
|
38
|
+
function safeJoin(root, rel) {
|
|
39
|
+
const abs = resolve(root, normalize(decodeURIComponent(rel.replace(/^\/+/, ""))));
|
|
40
|
+
return abs !== root && !abs.startsWith(root + sep) ? null : abs;
|
|
41
|
+
}
|
|
42
|
+
async function handle(req, res, port) {
|
|
43
|
+
const url = new URL(req.url ?? "/", `http://127.0.0.1:${port}`);
|
|
44
|
+
if (url.pathname === "/__cad/server") {
|
|
45
|
+
const d = {
|
|
46
|
+
app: "brepjs-cad-viewer",
|
|
47
|
+
port,
|
|
48
|
+
dynamicRoot: true,
|
|
49
|
+
serverApiVersion: 1
|
|
50
|
+
};
|
|
51
|
+
res.writeHead(200, { "content-type": "application/json; charset=utf-8" });
|
|
52
|
+
res.end(JSON.stringify(d));
|
|
53
|
+
return;
|
|
54
|
+
}
|
|
55
|
+
if (url.pathname.startsWith("/__model/")) {
|
|
56
|
+
const root = url.searchParams.get("dir");
|
|
57
|
+
if (!root) {
|
|
58
|
+
res.writeHead(400).end("missing dir");
|
|
59
|
+
return;
|
|
60
|
+
}
|
|
61
|
+
const abs = safeJoin(resolve(root), url.pathname.slice(8));
|
|
62
|
+
if (!abs) {
|
|
63
|
+
res.writeHead(403).end("forbidden");
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
await sendFile(res, abs).catch(() => res.writeHead(404).end("not found"));
|
|
67
|
+
return;
|
|
68
|
+
}
|
|
69
|
+
if (url.searchParams.has("dir") && url.pathname !== "/") {
|
|
70
|
+
res.writeHead(403).end("forbidden");
|
|
71
|
+
return;
|
|
72
|
+
}
|
|
73
|
+
const abs = safeJoin(VIEWER_DIST, url.pathname === "/" ? "index.html" : url.pathname);
|
|
74
|
+
if (!abs) {
|
|
75
|
+
res.writeHead(403).end("forbidden");
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
await sendFile(res, abs).catch(() => sendFile(res, join(VIEWER_DIST, "index.html")).catch(() => res.writeHead(404).end("not found")));
|
|
79
|
+
}
|
|
80
|
+
function startStaticServer(opts = {}) {
|
|
81
|
+
return new Promise((resolvePromise, reject) => {
|
|
82
|
+
const server = createServer((req, res) => {
|
|
83
|
+
handle(req, res, server.address().port);
|
|
84
|
+
});
|
|
85
|
+
server.on("error", reject);
|
|
86
|
+
server.listen(opts.port ?? 0, "127.0.0.1", () => {
|
|
87
|
+
const port = server.address().port;
|
|
88
|
+
resolvePromise({
|
|
89
|
+
port,
|
|
90
|
+
url: `http://127.0.0.1:${port}`,
|
|
91
|
+
close: () => new Promise((done, fail) => server.close((e) => e ? fail(e) : done()))
|
|
92
|
+
});
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
97
|
+
export { SERVER_API_VERSION, startStaticServer };
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export interface VerifyCheck {
|
|
2
|
+
name: string;
|
|
3
|
+
passed: boolean;
|
|
4
|
+
detail?: string;
|
|
5
|
+
}
|
|
6
|
+
export interface VerifyMeasurements {
|
|
7
|
+
volume?: number;
|
|
8
|
+
area?: number;
|
|
9
|
+
bounds?: {
|
|
10
|
+
xMin: number;
|
|
11
|
+
xMax: number;
|
|
12
|
+
yMin: number;
|
|
13
|
+
yMax: number;
|
|
14
|
+
zMin: number;
|
|
15
|
+
zMax: number;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/** A failure captured with whatever structured context it carried (a `BrepError` code/suggestion). */
|
|
19
|
+
export interface ErrorInfo {
|
|
20
|
+
message: string;
|
|
21
|
+
code?: string | undefined;
|
|
22
|
+
suggestion?: string | undefined;
|
|
23
|
+
}
|
|
24
|
+
export interface VerifyHint {
|
|
25
|
+
code: string;
|
|
26
|
+
message: string;
|
|
27
|
+
fix: string;
|
|
28
|
+
nextStep: string;
|
|
29
|
+
}
|
|
30
|
+
export interface VerifyReport {
|
|
31
|
+
shapeType: string | null;
|
|
32
|
+
checks: VerifyCheck[];
|
|
33
|
+
measurements: VerifyMeasurements;
|
|
34
|
+
errors: string[];
|
|
35
|
+
/** Structured copies of `errors`, carrying any `BrepError` code/suggestion. Drives `hints`. */
|
|
36
|
+
errorInfos: ErrorInfo[];
|
|
37
|
+
/** Actionable, code-keyed guidance derived from `errorInfos`. */
|
|
38
|
+
hints: VerifyHint[];
|
|
39
|
+
}
|
|
40
|
+
export interface BoundsDelta {
|
|
41
|
+
xMin: number;
|
|
42
|
+
xMax: number;
|
|
43
|
+
yMin: number;
|
|
44
|
+
yMax: number;
|
|
45
|
+
zMin: number;
|
|
46
|
+
zMax: number;
|
|
47
|
+
}
|
|
48
|
+
export interface DiffReport {
|
|
49
|
+
volumeDelta: number;
|
|
50
|
+
areaDelta: number;
|
|
51
|
+
bboxDelta: BoundsDelta;
|
|
52
|
+
symmetricDifferenceVolume: number;
|
|
53
|
+
errors: string[];
|
|
54
|
+
}
|
|
55
|
+
export declare function emptyReport(): VerifyReport;
|
|
56
|
+
/** Record a failure on the report, keeping the flat `errors` string list and structured `errorInfos` in sync. */
|
|
57
|
+
export declare function pushError(r: VerifyReport, info: ErrorInfo): void;
|
|
58
|
+
export declare function reportOk(r: VerifyReport): boolean;
|
|
59
|
+
/** Synthetic code attached to validity-check failures (validSolid returns a plain string error). */
|
|
60
|
+
export declare const VALIDITY_FAILURE_CODE = "VALIDATION_FAILED";
|
|
61
|
+
export declare function hintFor(info: ErrorInfo): VerifyHint | null;
|
|
62
|
+
export declare function buildHints(r: VerifyReport): VerifyHint[];
|
|
63
|
+
export declare function serializeReport(r: VerifyReport): string;
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { AnyShape } from 'brepjs';
|
|
2
|
+
import { VerifyReport } from './report.js';
|
|
3
|
+
export interface RunPartOptions {
|
|
4
|
+
step?: boolean;
|
|
5
|
+
glb?: boolean;
|
|
6
|
+
}
|
|
7
|
+
export interface RunPartResult {
|
|
8
|
+
/**
|
|
9
|
+
* The built shape, or null on failure. This is a LIVE WASM-backed kernel handle: the CLI exits
|
|
10
|
+
* right after use, but long-running programmatic callers own its lifetime and should release it
|
|
11
|
+
* (e.g. `using s = result.shape` or a `DisposalScope`) once done, or WASM memory accumulates.
|
|
12
|
+
*/
|
|
13
|
+
shape: AnyShape | null;
|
|
14
|
+
report: VerifyReport;
|
|
15
|
+
step?: ArrayBuffer | undefined;
|
|
16
|
+
glb?: ArrayBuffer | undefined;
|
|
17
|
+
}
|
|
18
|
+
export declare function runPart(modulePath: string, opts?: RunPartOptions): Promise<RunPartResult>;
|
package/package.json
ADDED
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "brepjs-cad",
|
|
3
|
+
"version": "0.1.0",
|
|
4
|
+
"description": "Agent skill + verify/preview tooling for authoring parametric brepjs CAD code",
|
|
5
|
+
"license": "Apache-2.0",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"sideEffects": false,
|
|
8
|
+
"engines": {
|
|
9
|
+
"node": ">=24"
|
|
10
|
+
},
|
|
11
|
+
"repository": {
|
|
12
|
+
"type": "git",
|
|
13
|
+
"url": "https://github.com/andymai/brepjs",
|
|
14
|
+
"directory": "packages/brepjs-cad"
|
|
15
|
+
},
|
|
16
|
+
"main": "./dist/brepjs-cad.cjs",
|
|
17
|
+
"module": "./dist/brepjs-cad.js",
|
|
18
|
+
"types": "./dist/index.d.ts",
|
|
19
|
+
"exports": {
|
|
20
|
+
".": {
|
|
21
|
+
"import": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"default": "./dist/brepjs-cad.js"
|
|
24
|
+
},
|
|
25
|
+
"require": {
|
|
26
|
+
"types": "./dist/index.d.ts",
|
|
27
|
+
"default": "./dist/brepjs-cad.cjs"
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
},
|
|
31
|
+
"bin": {
|
|
32
|
+
"brepjs": "./dist/cli/main.js",
|
|
33
|
+
"brepjs-agent-verify": "./dist/cli/main.js"
|
|
34
|
+
},
|
|
35
|
+
"files": [
|
|
36
|
+
"dist",
|
|
37
|
+
"viewer/dist",
|
|
38
|
+
"CHANGELOG.md",
|
|
39
|
+
"LICENSE",
|
|
40
|
+
"README.md"
|
|
41
|
+
],
|
|
42
|
+
"scripts": {
|
|
43
|
+
"build": "vite build && vite build --config viewer/vite.config.ts",
|
|
44
|
+
"typecheck": "tsc --noEmit && tsc -p viewer/tsconfig.json --noEmit && tsc -p bench/tsconfig.json",
|
|
45
|
+
"lint": "eslint src tests viewer bench",
|
|
46
|
+
"test": "vitest run",
|
|
47
|
+
"eval": "tsx bench/run.ts",
|
|
48
|
+
"prepack": "npm run build"
|
|
49
|
+
},
|
|
50
|
+
"peerDependencies": {
|
|
51
|
+
"brepjs": ">=18.0.0",
|
|
52
|
+
"occt-wasm": ">=3.0.0"
|
|
53
|
+
},
|
|
54
|
+
"dependencies": {
|
|
55
|
+
"commander": "^13.0.0"
|
|
56
|
+
},
|
|
57
|
+
"optionalDependencies": {
|
|
58
|
+
"puppeteer": "^25.0.4"
|
|
59
|
+
},
|
|
60
|
+
"devDependencies": {
|
|
61
|
+
"@react-three/drei": "^10.7.7",
|
|
62
|
+
"@react-three/fiber": "^9.6.1",
|
|
63
|
+
"@types/node": "^25.9.1",
|
|
64
|
+
"@types/react": "^19.2.15",
|
|
65
|
+
"@types/react-dom": "^19.2.3",
|
|
66
|
+
"@types/three": "^0.184.1",
|
|
67
|
+
"@vitejs/plugin-react": "^6.0.2",
|
|
68
|
+
"brepjs-viewer": "^0.1.0",
|
|
69
|
+
"eslint": "^10.4.0",
|
|
70
|
+
"react": "^19.2.6",
|
|
71
|
+
"react-dom": "^19.2.6",
|
|
72
|
+
"three": "^0.184.0",
|
|
73
|
+
"tsx": "^4.22.3",
|
|
74
|
+
"typescript": "^6.0.3",
|
|
75
|
+
"vite": "^8.0.0",
|
|
76
|
+
"vite-plugin-dts": "^5.0.1",
|
|
77
|
+
"vitest": "^4.0.0"
|
|
78
|
+
}
|
|
79
|
+
}
|