@bldrs-ai/conway 1.356.1126 → 1.357.1128

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.
@@ -69622,7 +69622,7 @@ IfcStepParser.Instance = new IfcStepParser();
69622
69622
  var ifc_step_parser_default = IfcStepParser;
69623
69623
 
69624
69624
  // compiled/src/version/version.js
69625
- var versionString = "Conway Web-Ifc Shim v1.356.1126";
69625
+ var versionString = "Conway Web-Ifc Shim v1.357.1128";
69626
69626
 
69627
69627
  // compiled/src/statistics/statistics.js
69628
69628
  var Statistics = class {
@@ -85957,7 +85957,7 @@ var IfcSceneBuilder = class {
85957
85957
  };
85958
85958
 
85959
85959
  // compiled/src/version/version.js
85960
- var versionString = "Conway Web-Ifc Shim v1.356.1126";
85960
+ var versionString = "Conway Web-Ifc Shim v1.357.1128";
85961
85961
 
85962
85962
  // compiled/src/statistics/statistics.js
85963
85963
  var Statistics = class {
@@ -79408,7 +79408,7 @@ var ExtractResult;
79408
79408
  })(ExtractResult || (ExtractResult = {}));
79409
79409
 
79410
79410
  // compiled/src/version/version.js
79411
- var versionString = "Conway Web-Ifc Shim v1.356.1126";
79411
+ var versionString = "Conway Web-Ifc Shim v1.357.1128";
79412
79412
 
79413
79413
  // compiled/src/statistics/statistics.js
79414
79414
  var Statistics = class {
@@ -69620,7 +69620,7 @@ IfcStepParser.Instance = new IfcStepParser();
69620
69620
  var ifc_step_parser_default = IfcStepParser;
69621
69621
 
69622
69622
  // compiled/src/version/version.js
69623
- var versionString = "Conway Web-Ifc Shim v1.356.1126";
69623
+ var versionString = "Conway Web-Ifc Shim v1.357.1128";
69624
69624
 
69625
69625
  // compiled/src/statistics/statistics.js
69626
69626
  var Statistics = class {
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=ap214_regression_main.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ap214_regression_main.d.ts","sourceRoot":"","sources":["../../../src/AP214E3_2010/ap214_regression_main.ts"],"names":[],"mappings":""}
@@ -0,0 +1,277 @@
1
+ import { exit } from "process";
2
+ import AP214StepParser from "./ap214_step_parser.js";
3
+ import { AP214GeometryExtraction } from "./ap214_geometry_extraction.js";
4
+ import ParsingBuffer from "../parsing/parsing_buffer.js";
5
+ import { ParseResult } from "../step/parsing/step_parser.js";
6
+ import yargs from "yargs/yargs";
7
+ import fs from "fs";
8
+ import fsPromises from "fs/promises";
9
+ import path from "path";
10
+ import crypto from "crypto";
11
+ import { ConwayGeometry } from "../../dependencies/conway-geom/index.js";
12
+ import Logger from "../logging/logger.js";
13
+ import Environment from "../utilities/environment.js";
14
+ import { ExtractResult } from "../core/shared_constants.js";
15
+ import { CanonicalMeshType } from "../core/canonical_mesh.js";
16
+ import EntityTypesAP214 from "./AP214E3_2010_gen/entity_types_ap214.gen.js";
17
+ import { Console } from "console";
18
+ const conwayGeom = new ConwayGeometry();
19
+ main();
20
+ /**
21
+ * Encapsultes a string in a CSV safe way.
22
+ *
23
+ * @param from
24
+ * @return {string}
25
+ */
26
+ function csvSafeString(from) {
27
+ if (from.includes("\n") ||
28
+ from.includes("\r") ||
29
+ from.includes("\"") ||
30
+ from.includes(",")) {
31
+ return `"${from.replaceAll("\"", "\"\"")}"`;
32
+ }
33
+ return from;
34
+ }
35
+ // Bytes per megabyte for memory-stat formatting in the perf CSV.
36
+ // eslint-disable-next-line no-magic-numbers
37
+ const BYTES_PER_MB = 1024 * 1024;
38
+ // Fixed-point precision for perf MB values.
39
+ // eslint-disable-next-line no-magic-numbers
40
+ const PERF_MB_PRECISION = 2;
41
+ /**
42
+ * Write a single-row per-file perf CSV at the given path, matching the
43
+ * column layout of the IFC regression child so the batch aggregator can
44
+ * merge STEP and IFC rows into one perf CSV. No-op when perfPath is empty.
45
+ *
46
+ * @param perfPath Path to write the CSV to. Empty string disables.
47
+ * @param stepFile Source STEP file path (basename used as the row key).
48
+ * @param status OK or FAIL.
49
+ * @param parseTimeMs Parse stage duration in ms.
50
+ * @param geometryTimeMs Geometry extraction duration in ms.
51
+ * @param totalTimeMs Sum of parse + geometry in ms.
52
+ */
53
+ async function writePerfCsvIfRequested(perfPath, stepFile, status, parseTimeMs, geometryTimeMs, totalTimeMs) {
54
+ if (perfPath.length === 0) {
55
+ return;
56
+ }
57
+ const mem = process.memoryUsage();
58
+ const rssMb = (mem.rss / BYTES_PER_MB).toFixed(PERF_MB_PRECISION);
59
+ const heapUsedMb = (mem.heapUsed / BYTES_PER_MB).toFixed(PERF_MB_PRECISION);
60
+ const heapTotalMb = (mem.heapTotal / BYTES_PER_MB).toFixed(PERF_MB_PRECISION);
61
+ const fileName = csvSafeString(path.basename(stepFile));
62
+ const header = "file,status,parseTimeMs,geometryTimeMs,totalTimeMs,rssMb,heapUsedMb,heapTotalMb\n";
63
+ const row = `${fileName},${status},${parseTimeMs},${geometryTimeMs},${totalTimeMs},` +
64
+ `${rssMb},${heapUsedMb},${heapTotalMb}\n`;
65
+ try {
66
+ await fsPromises.writeFile(perfPath, header + row);
67
+ }
68
+ catch (e) {
69
+ // Perf is best-effort; never fail the regression run because of a perf write.
70
+ console.error(`Failed to write perf CSV at ${perfPath}:`, e);
71
+ }
72
+ }
73
+ /**
74
+ * Display errors and dump errors to stderr in the batch runner's
75
+ * `message,count,expressids,file` CSV format.
76
+ *
77
+ * @param filePath
78
+ */
79
+ function displayErrors(filePath) {
80
+ const fileName = csvSafeString(path.basename(filePath));
81
+ if (Logger.getLogs().length > 0) {
82
+ Logger.displayLogs();
83
+ const errors = Logger.getErrors();
84
+ if (errors.length > 0) {
85
+ const errConsole = new Console(process.stderr);
86
+ errConsole.log("message,count,expressids,file");
87
+ for (const error of errors) {
88
+ errConsole.log(`${csvSafeString(error.message)},${error.count},${csvSafeString(Array.from(error.expressIDs.keys()).join(" "))},${fileName}`);
89
+ }
90
+ }
91
+ }
92
+ }
93
+ /**
94
+ * Generalised error handling wrapper
95
+ */
96
+ async function main() {
97
+ try {
98
+ await conwayGeom.initialize();
99
+ Environment.checkEnvironment();
100
+ Logger.initializeWasmCallbacks();
101
+ doWork();
102
+ }
103
+ catch (error) {
104
+ console.error("An error occurred:", error);
105
+ }
106
+ }
107
+ /**
108
+ * Actual execution function.
109
+ */
110
+ function doWork() {
111
+ const SKIP_PARAMS = 2;
112
+ // eslint-disable-next-line @typescript-eslint/no-unused-vars
113
+ const args = yargs(process.argv.slice(SKIP_PARAMS))
114
+ .command("$0 <filename> [output]", "Digest a STEP (AP214) file", (yargs2) => {
115
+ yargs2.option("strict", {
116
+ describe: "Makes parser/reference errors on nullable fields return null instead of an error",
117
+ type: "boolean",
118
+ alias: "s",
119
+ default: false,
120
+ });
121
+ yargs2.option("digest", {
122
+ describe: "Output a digest ",
123
+ type: "boolean",
124
+ alias: "d",
125
+ default: false,
126
+ });
127
+ yargs2.option("perf", {
128
+ describe: "Write a single-row perf CSV (parse/geometry/total time + memory) at this path",
129
+ type: "string",
130
+ alias: "p",
131
+ default: "",
132
+ });
133
+ yargs2.positional("filename", { describe: "STEP (AP214) File Path", type: "string" });
134
+ yargs2.positional("output", { describe: "Output path", type: "string" });
135
+ }, async (argv) => {
136
+ const stepFile = argv["filename"];
137
+ const outputPath = argv["output"] ??
138
+ path.join(path.dirname(stepFile), path.parse(stepFile).name);
139
+ let stepBuffer;
140
+ const strict = argv["strict"] ?? false;
141
+ const digest = argv["digest"] ?? false;
142
+ const perfPath = argv["perf"] ?? "";
143
+ try {
144
+ stepBuffer = fs.readFileSync(stepFile);
145
+ }
146
+ catch {
147
+ Logger.error("Couldn't read file, check that it is accessible at the specified path.");
148
+ displayErrors(stepFile);
149
+ exit();
150
+ }
151
+ if (stepBuffer === void 0) {
152
+ Logger.error("Couldn't read file, check that it is accessible at the specified path.");
153
+ displayErrors(stepFile);
154
+ exit();
155
+ }
156
+ const parser = AP214StepParser.Instance;
157
+ const bufferInput = new ParsingBuffer(stepBuffer);
158
+ const parseStartMs = Date.now();
159
+ const result0 = parser.parseHeader(bufferInput)[1];
160
+ switch (result0) {
161
+ case ParseResult.COMPLETE:
162
+ break;
163
+ case ParseResult.INCOMPLETE:
164
+ Logger.warning("Parse incomplete but no errors");
165
+ break;
166
+ case ParseResult.INVALID_STEP:
167
+ Logger.error("Invalid STEP detected in parse, but no syntax error detected");
168
+ break;
169
+ case ParseResult.MISSING_TYPE:
170
+ Logger.error("Missing STEP type, but no syntax error detected");
171
+ break;
172
+ case ParseResult.SYNTAX_ERROR:
173
+ Logger.error(`Syntax error detected on line ${bufferInput.lineCount}`);
174
+ break;
175
+ default:
176
+ }
177
+ const [result1, model] = parser.parseDataToModel(bufferInput);
178
+ switch (result1) {
179
+ case ParseResult.COMPLETE:
180
+ break;
181
+ case ParseResult.INCOMPLETE:
182
+ Logger.warning("Parse incomplete but no errors");
183
+ break;
184
+ case ParseResult.INVALID_STEP:
185
+ Logger.error("Invalid STEP detected in parse, but no syntax error detected");
186
+ break;
187
+ case ParseResult.MISSING_TYPE:
188
+ Logger.error("Missing STEP type, but no syntax error detected");
189
+ break;
190
+ case ParseResult.SYNTAX_ERROR:
191
+ Logger.error(`Syntax error detected on line ${bufferInput.lineCount}`);
192
+ break;
193
+ default:
194
+ }
195
+ const parseEndMs = Date.now();
196
+ const parseTimeMs = parseEndMs - parseStartMs;
197
+ if (model === void 0) {
198
+ await writePerfCsvIfRequested(perfPath, stepFile, "FAIL", parseTimeMs, 0, parseTimeMs);
199
+ displayErrors(stepFile);
200
+ return;
201
+ }
202
+ model.nullOnErrors = !strict;
203
+ const geomStartMs = Date.now();
204
+ const extraction = geometryExtraction(model);
205
+ const geomEndMs = Date.now();
206
+ const geometryTimeMs = geomEndMs - geomStartMs;
207
+ const totalTimeMs = geomEndMs - parseStartMs;
208
+ const perfStatus = extraction === void 0 ? "FAIL" : "OK";
209
+ await writePerfCsvIfRequested(perfPath, stepFile, perfStatus, parseTimeMs, geometryTimeMs, totalTimeMs);
210
+ if (extraction === void 0) {
211
+ Logger.error("Couldn't extract geometry");
212
+ }
213
+ else if (digest) {
214
+ // Digest layout matches the IFC regression digest
215
+ // (ID,Hash,Type,Operand 1,Operand2,Void) so the same diff/bless
216
+ // tooling applies. STEP digests cover final meshes and memoized
217
+ // curves; AP214 has no void/CSG-operand columns to fill.
218
+ const csvLines = [];
219
+ const csvPath = `${outputPath}.csv`;
220
+ const csvFileHandle = await fsPromises.open(csvPath, "w");
221
+ await csvFileHandle.write(`ID,Hash,Type,Operand 1,Operand2,Void\n`);
222
+ for (const mesh of model.geometry) {
223
+ if (mesh.type !== CanonicalMeshType.BUFFER_GEOMETRY) {
224
+ continue;
225
+ }
226
+ const objContents = mesh.geometry.dumpToOBJ("");
227
+ const hash = crypto.createHash("sha1").update(objContents).digest("hex");
228
+ const element = model.getElementByLocalID(mesh.localID);
229
+ const rowID = element?.expressID ?? mesh.localID;
230
+ const typeName = element !== void 0 ? EntityTypesAP214[element.type] : "";
231
+ csvLines.push([rowID, `${rowID},${hash},${typeName},,,FALSE\n`]);
232
+ }
233
+ for (const [curveItem, objContents] of model.curves.objs()) {
234
+ const hash = crypto.createHash("sha1").update(objContents).digest("hex");
235
+ const rowID = curveItem.expressID ?? curveItem.toString();
236
+ csvLines.push([rowID, `${rowID},${hash},${EntityTypesAP214[curveItem.type]},,,\n`]);
237
+ }
238
+ csvLines.sort((a, b) => {
239
+ const a0 = a[0];
240
+ const b0 = b[0];
241
+ if (typeof a0 === "number") {
242
+ if (typeof b0 === "number") {
243
+ return a0 - b0;
244
+ }
245
+ return -1;
246
+ }
247
+ if (typeof b0 === "number") {
248
+ return 1;
249
+ }
250
+ return a0.localeCompare(b0);
251
+ });
252
+ // Note, we cast to any here because the writeFile supports an
253
+ // iterable but the typescript bindings don't have the option
254
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
255
+ await csvFileHandle.writeFile(csvLines.map((line) => line[1]));
256
+ await csvFileHandle.close();
257
+ }
258
+ displayErrors(stepFile);
259
+ })
260
+ .help().argv;
261
+ }
262
+ /**
263
+ * Function to extract geometry from an AP214StepModel.
264
+ *
265
+ * @param model
266
+ * @return {AP214GeometryExtraction | undefined} The extraction, or undefined
267
+ * on failure.
268
+ */
269
+ function geometryExtraction(model) {
270
+ const conwayModel = new AP214GeometryExtraction(conwayGeom, model);
271
+ const [extractionResult] = conwayModel.extractAP214GeometryData(false);
272
+ if (extractionResult !== ExtractResult.COMPLETE) {
273
+ console.error("Could not extract geometry, exiting...");
274
+ return void 0;
275
+ }
276
+ return conwayModel;
277
+ }
@@ -189,7 +189,12 @@ async function runForFile(filePath, outputPath, maxTimeout, perfPath) {
189
189
  const MAX_TIMEOUT_MS = maxTimeout;
190
190
  const startTime = Date.now(); // Start time
191
191
  const perfFlag = perfPath ? ` --perf "${perfPath}"` : '';
192
- const safeExecCommand = `node --experimental-specifier-resolution=node ./compiled/src/ifc/ifc_regression_main.js -d${perfFlag} "${filePath}" "${outputPath}"`;
192
+ // STEP (AP214) models are digested by the AP214 pipeline, IFC models by
193
+ // the IFC pipeline; both children emit the same digest/error/perf formats.
194
+ const childScript = isStepFile(filePath) ?
195
+ './compiled/src/AP214E3_2010/ap214_regression_main.js' :
196
+ './compiled/src/ifc/ifc_regression_main.js';
197
+ const safeExecCommand = `node --experimental-specifier-resolution=node ${childScript} -d${perfFlag} "${filePath}" "${outputPath}"`;
193
198
  console.log(`Current File: ${filePath}`);
194
199
  // Use safeExecWithCancellation, will kill the process if it takes longer than MAX_TIMEOUT_MS.
195
200
  const process = await safeExecWithCancellation(safeExecCommand, MAX_TIMEOUT_MS);
@@ -227,6 +232,27 @@ async function runForFile(filePath, outputPath, maxTimeout, perfPath) {
227
232
  hash: fileHash,
228
233
  };
229
234
  }
235
+ // Model files the regression harness understands: IFC plus STEP AP214.
236
+ const SUPPORTED_MODEL_EXTENSIONS = ['.ifc', '.stp', '.step'];
237
+ /**
238
+ * Whether this is a STEP (AP214) model file by extension.
239
+ *
240
+ * @param filePath
241
+ * @return {boolean}
242
+ */
243
+ function isStepFile(filePath) {
244
+ const extension = path.extname(filePath).toLowerCase();
245
+ return extension === '.stp' || extension === '.step';
246
+ }
247
+ /**
248
+ * Whether this file is a supported model type (IFC or STEP), by extension.
249
+ *
250
+ * @param filePath
251
+ * @return {boolean}
252
+ */
253
+ function isSupportedModelFile(filePath) {
254
+ return SUPPORTED_MODEL_EXTENSIONS.includes(path.extname(filePath).toLowerCase());
255
+ }
230
256
  /**
231
257
  * Recursively collect all IFC file paths (instead of processing them immediately).
232
258
  *
@@ -255,7 +281,7 @@ async function collectIFCFiles(parentPath, excludeRegex) {
255
281
  if (item.isDirectory()) {
256
282
  await recursiveWalk(resolved);
257
283
  }
258
- else if (path.extname(resolved).toLowerCase() === '.ifc') {
284
+ else if (isSupportedModelFile(resolved)) {
259
285
  ifcFiles.push(resolved);
260
286
  }
261
287
  }
@@ -305,9 +331,9 @@ async function processIFCFilesInParallel(ifcFiles, outputPath, errorLines, fileL
305
331
  activeTasks++;
306
332
  console.log(`Starting task for "${path.basename(ifcPath)}". Active tasks: ${activeTasks}`);
307
333
  const perfChildPath = perfDir ?
308
- path.join(perfDir, `${path.basename(ifcPath, '.ifc')}.perf.csv`) :
334
+ path.join(perfDir, `${path.parse(ifcPath).name}.perf.csv`) :
309
335
  undefined;
310
- const fileResults = await runForFile(ifcPath, path.join(outputPath, path.basename(ifcPath, '.ifc')), maxTimeout, perfChildPath);
336
+ const fileResults = await runForFile(ifcPath, path.join(outputPath, path.parse(ifcPath).name), maxTimeout, perfChildPath);
311
337
  activeTasks--;
312
338
  console.log(`Completed task for "${path.basename(ifcPath)}". Active tasks: ${activeTasks}`);
313
339
  return { ifcPath, fileResults };
@@ -355,11 +381,11 @@ async function recursiveWalk(parentPath, excludeRegex, outputPath, errorLines, f
355
381
  if (item.isDirectory()) {
356
382
  await recursiveWalk(resolved, excludeRegex, outputPath, errorLines, fileLines, failedLines, maxTimeout, perfDir);
357
383
  }
358
- else if (path.extname(resolved).toLowerCase() === '.ifc') {
384
+ else if (isSupportedModelFile(resolved)) {
359
385
  const perfChildPath = perfDir ?
360
- path.join(perfDir, `${path.basename(resolved, '.ifc')}.perf.csv`) :
386
+ path.join(perfDir, `${path.parse(resolved).name}.perf.csv`) :
361
387
  undefined;
362
- const fileResults = await runForFile(resolved, path.join(outputPath, path.basename(resolved, '.ifc')), maxTimeout, perfChildPath);
388
+ const fileResults = await runForFile(resolved, path.join(outputPath, path.parse(resolved).name), maxTimeout, perfChildPath);
363
389
  if (fileResults.type === 'Run') {
364
390
  if (fileResults.errorLines !== void 0) {
365
391
  errorLines.push(...fileResults.errorLines);
@@ -5,5 +5,5 @@
5
5
  // only the first segment (major) is meaningful and is the one CI carries forward.
6
6
  // Must stay in `vN.N.N` shape: the CI stamp regex, scripts/updateVersion.mjs, and
7
7
  // statistics.ts all match `v\d+\.\d+\.\d+`.
8
- const versionString = 'Conway Web-Ifc Shim v1.356.1126';
8
+ const versionString = 'Conway Web-Ifc Shim v1.357.1128';
9
9
  export { versionString };