@bldrs-ai/conway 0.15.836 → 0.15.839

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.
Files changed (37) hide show
  1. package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmNode.js +1 -1
  2. package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmNodeMT.js +1 -1
  3. package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmWeb.js +1 -1
  4. package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmWebMT.js +1 -1
  5. package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmWebMT.wasm +0 -0
  6. package/compiled/dependencies/conway-geom/index.d.ts +0 -2
  7. package/compiled/dependencies/conway-geom/index.d.ts.map +1 -1
  8. package/compiled/dependencies/conway-geom/index.js +0 -2
  9. package/compiled/dependencies/conway-geom/interface/conway_geometry.d.ts +0 -6
  10. package/compiled/dependencies/conway-geom/interface/conway_geometry.d.ts.map +1 -1
  11. package/compiled/dependencies/conway-geom/interface/conway_geometry.js +0 -7
  12. package/compiled/examples/browser-bundled.js +69044 -0
  13. package/compiled/examples/browser.d.ts +3 -0
  14. package/compiled/examples/browser.d.ts.map +1 -0
  15. package/compiled/examples/browser.js +393 -0
  16. package/compiled/examples/validator-bundled.js +69057 -0
  17. package/compiled/examples/validator.d.ts +3 -0
  18. package/compiled/examples/validator.d.ts.map +1 -0
  19. package/compiled/examples/validator.js +378 -0
  20. package/compiled/src/ifc/ifc_geometry_extraction.d.ts +1 -22
  21. package/compiled/src/ifc/ifc_geometry_extraction.d.ts.map +1 -1
  22. package/compiled/src/ifc/ifc_geometry_extraction.js +38 -109
  23. package/compiled/src/ifc/ifc_step_model.d.ts +1 -0
  24. package/compiled/src/ifc/ifc_step_model.d.ts.map +1 -1
  25. package/compiled/src/ifc/ifc_step_model.js +1 -0
  26. package/compiled/src/ifc/ifc_step_parser.d.ts +1 -0
  27. package/compiled/src/ifc/ifc_step_parser.d.ts.map +1 -1
  28. package/compiled/src/ifc/ifc_step_parser.js +1 -0
  29. package/compiled/src/version/version.js +1 -1
  30. package/compiled/tsconfig.tsbuildinfo +1 -1
  31. package/package.json +49 -42
  32. package/compiled/dependencies/conway-geom/interface/parameters/flattened_points.d.ts +0 -6
  33. package/compiled/dependencies/conway-geom/interface/parameters/flattened_points.d.ts.map +0 -1
  34. package/compiled/dependencies/conway-geom/interface/parameters/flattened_points.js +0 -1
  35. package/compiled/dependencies/conway-geom/interface/parameters/params_add_face_to_geometry_simple.d.ts +0 -10
  36. package/compiled/dependencies/conway-geom/interface/parameters/params_add_face_to_geometry_simple.d.ts.map +0 -1
  37. package/compiled/dependencies/conway-geom/interface/parameters/params_add_face_to_geometry_simple.js +0 -1
@@ -0,0 +1,3 @@
1
+ #!/usr/bin/env node
2
+ export {};
3
+ //# sourceMappingURL=browser.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"browser.d.ts","sourceRoot":"","sources":["../../examples/browser.ts"],"names":[],"mappings":""}
@@ -0,0 +1,393 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'fs';
3
+ import { exit } from 'process';
4
+ import { stdin as input, stdout as output } from 'node:process';
5
+ import * as readline from 'node:readline';
6
+ import IfcStepParser from '../src/ifc/ifc_step_parser';
7
+ import EntityTypesIfc from '../src/ifc/ifc4_gen/entity_types_ifc.gen';
8
+ import Logger from '../src/logging/logger';
9
+ import ParsingBuffer from '../src/parsing/parsing_buffer';
10
+ import { ParseResult } from '../src/step/parsing/step_parser';
11
+ import Environment from '../src/utilities/environment';
12
+ /**
13
+ * IFC Model Browser
14
+ *
15
+ * @see Browser.md
16
+ */
17
+ // ---------------------------------------------------------------------
18
+ // 1. Grab the path to the model from command line arguments
19
+ // ---------------------------------------------------------------------
20
+ Environment.checkEnvironment();
21
+ Logger.initializeWasmCallbacks();
22
+ const modelPath = process.argv[2];
23
+ if (!modelPath) {
24
+ console.error('Usage: browser <path_to_model>.ifc');
25
+ process.exit(1);
26
+ }
27
+ // ---------------------------------------------------------------------
28
+ // 2. Load & Parse the IFC
29
+ // ---------------------------------------------------------------------
30
+ let indexIfcBuffer;
31
+ try {
32
+ indexIfcBuffer = fs.readFileSync(modelPath);
33
+ }
34
+ catch (ex) {
35
+ Logger.error('Couldn\'t read file, check that it is accessible at the specified path.');
36
+ exit();
37
+ }
38
+ if (indexIfcBuffer === void 0) {
39
+ Logger.error('Couldn\'t read file, check that it is accessible at the specified path.');
40
+ exit();
41
+ }
42
+ Logger.createStatistics(0);
43
+ const parser = IfcStepParser.Instance;
44
+ const bufferInput = new ParsingBuffer(indexIfcBuffer);
45
+ // Parse header
46
+ // eslint-disable-next-line no-unused-vars
47
+ const [stepHeader, resultHeader] = parser.parseHeader(bufferInput);
48
+ // Parse main data
49
+ const [parseResult, model] = parser.parseDataToModel(bufferInput);
50
+ // Check parse result
51
+ switch (parseResult) {
52
+ case ParseResult.COMPLETE:
53
+ break;
54
+ case ParseResult.INCOMPLETE:
55
+ Logger.warning('Parse incomplete but no errors');
56
+ break;
57
+ case ParseResult.INVALID_STEP:
58
+ Logger.error('Invalid STEP detected');
59
+ break;
60
+ case ParseResult.MISSING_TYPE:
61
+ Logger.error('Missing STEP type');
62
+ break;
63
+ case ParseResult.SYNTAX_ERROR:
64
+ Logger.error(`Syntax error at line ${bufferInput.lineCount}`);
65
+ break;
66
+ default:
67
+ }
68
+ if (!model) {
69
+ Logger.error('Model not loaded.');
70
+ process.exit(1);
71
+ }
72
+ // ---------------------------------------------------------------------
73
+ // 3. Build arrays for IFC classes & entity types
74
+ // ---------------------------------------------------------------------
75
+ const nonEmptyTypeIDNoSubtypes = model.nonEmptyTypeIDs();
76
+ const ifcClasses = Array.from(nonEmptyTypeIDNoSubtypes || []).map((item) => String(EntityTypesIfc[item]));
77
+ const entityTypes = Array.from(nonEmptyTypeIDNoSubtypes || []);
78
+ // A helper to parse something like
79
+ // "IFCBUILDINGELEMENTPROXY[#30]" => { className: "IFCBUILDINGELEMENTPROXY", expressID: 30 }
80
+ /**
81
+ * Parses a token containing a class name and an optional express ID.
82
+ *
83
+ * @param {string} token - The token to be parsed. Expected format: "CLASSNAME[#ID]".
84
+ * @return {{ className: string, expressID?: number }}
85
+ */
86
+ function parseClassAndOptionalID(token) {
87
+ // e.g. IFCBUILDINGELEMENTPROXY[#30]
88
+ const match = token.match(/^(.+)\[#(\d+)\]$/);
89
+ if (match) {
90
+ const className = match[1].toUpperCase();
91
+ const expressID = parseInt(match[2], 10);
92
+ return { className, expressID };
93
+ }
94
+ return { className: token.toUpperCase() };
95
+ }
96
+ // Gather suggestions like "IFCBUILDINGELEMENTPROXY[#30]" for each instance of a class
97
+ /**
98
+ * Retrieves instance suggestions based on the specified class name within an IFC model.
99
+ *
100
+ * @param {string} clsName - The name of the class for which to retrieve suggestions.
101
+ * @param {IfcStepModel} theModel - The IFC model containing class and entity information.
102
+ * @return {string[]} An array of instance suggestions in the format "ClassName[#ID]".
103
+ */
104
+ function getInstanceSuggestions(clsName, theModel) {
105
+ const idx = ifcClasses.indexOf(clsName);
106
+ if (idx < 0) {
107
+ return [];
108
+ }
109
+ const elementTypeID = entityTypes[idx];
110
+ const ctor = theModel.schema.constructors[elementTypeID];
111
+ if (!ctor) {
112
+ return [];
113
+ }
114
+ const results = [];
115
+ for (const ent of theModel.types(ctor)) {
116
+ // We'll assume ent.expressID is the numeric ID
117
+ const stepId = ent.expressID;
118
+ if (typeof stepId === 'number') {
119
+ results.push(`${clsName}[#${stepId}]`);
120
+ }
121
+ }
122
+ return results;
123
+ }
124
+ // We build a 3-tuple for each field => [fieldName, fieldDescription, fieldData]
125
+ /**
126
+ * Retrieves an array of local fields from the given IFC entity along with
127
+ * their descriptions and associated data.
128
+ *
129
+ * @param {StepEntityBase<EntityTypesIfc>} entity - The IFC entity from which
130
+ * to extract fields and their data.
131
+ * @return {[string, EntityFieldDescription<EntityTypesIfc>, unknown][]}
132
+ * An array of tuples, where each tuple contains:
133
+ * - The field name as a string.
134
+ * - The field description object.
135
+ * - The associated data for the field.
136
+ */
137
+ function getLocalFieldsWithData(entity) {
138
+ return entity.orderedFields.map(([fieldName, fieldDesc]) => {
139
+ // For this example, we assume dynamic property references the actual data
140
+ const data = entity[fieldName];
141
+ return [fieldName, fieldDesc, data];
142
+ });
143
+ }
144
+ // The main recursive function that navigates dotted paths, now also supporting "Class[#ID]"
145
+ /**
146
+ * Retrieves the value of a field or subfields from an IFC model by traversing a dotted path.
147
+ *
148
+ * @param {string} dottedPath - The path to the desired field,
149
+ * with tokens separated by dots (e.g., "ClassName[#ID].fieldName").
150
+ * @param {IfcStepModel} theModel - The IFC model containing class and entity information.
151
+ * @return {{
152
+ * isEntity: boolean;
153
+ * subfieldNames: string[];
154
+ * value: any;
155
+ * }} An object containing:
156
+ * - `isEntity`: A boolean indicating whether the final value is an entity with subfields.
157
+ * - `subfieldNames`: An array of subfield names if the final value is an entity,
158
+ * otherwise an empty array.
159
+ * - `value`: The value of the field if it is a primitive type, otherwise `null`.
160
+ */
161
+ function getFieldValueOrSubfields(dottedPath, theModel) {
162
+ const tokens = dottedPath.split('.');
163
+ if (!tokens.length) {
164
+ return { isEntity: false, subfieldNames: [], value: null };
165
+ }
166
+ // parse first token => e.g. "IFCBUILDINGELEMENTPROXY[#30]"
167
+ const { className, expressID } = parseClassAndOptionalID(tokens[0]);
168
+ // find the class in ifcClasses
169
+ const idx = ifcClasses.indexOf(className);
170
+ if (idx < 0) {
171
+ return { isEntity: false, subfieldNames: [], value: null };
172
+ }
173
+ const elementTypeID = entityTypes[idx];
174
+ const ctor = theModel.schema.constructors[elementTypeID];
175
+ if (!ctor) {
176
+ return { isEntity: false, subfieldNames: [], value: null };
177
+ }
178
+ // If an ID is specified, find that particular entity; otherwise take the first instance
179
+ let currentEntity = null;
180
+ if (expressID !== undefined) {
181
+ for (const e of theModel.types(ctor)) {
182
+ if (e.expressID === expressID) {
183
+ currentEntity = e;
184
+ break;
185
+ }
186
+ }
187
+ if (!currentEntity) {
188
+ // user specified e.g. "IFCBUILDINGELEMENTPROXY[#999]" but no such entity
189
+ return { isEntity: false, subfieldNames: [], value: null };
190
+ }
191
+ }
192
+ else {
193
+ // no ID => pick first
194
+ for (const e of theModel.types(ctor)) {
195
+ currentEntity = e;
196
+ break;
197
+ }
198
+ if (!currentEntity) {
199
+ return { isEntity: false, subfieldNames: [], value: null };
200
+ }
201
+ }
202
+ let currentValue = currentEntity;
203
+ // Navigate subfields for subsequent tokens
204
+ for (let i = 1; i < tokens.length; i++) {
205
+ const fieldName = tokens[i].toLowerCase();
206
+ if (!currentValue || typeof currentValue !== 'object') {
207
+ return { isEntity: false, subfieldNames: [], value: currentValue };
208
+ }
209
+ if (!('orderedFields' in currentValue)) {
210
+ return { isEntity: false, subfieldNames: [], value: currentValue };
211
+ }
212
+ const entity = currentValue;
213
+ const localFields = getLocalFieldsWithData(entity);
214
+ const tuple = localFields.find(([fName]) => fName.toLowerCase() === fieldName);
215
+ if (!tuple) {
216
+ return { isEntity: false, subfieldNames: [], value: null };
217
+ }
218
+ // eslint-disable-next-line no-unused-vars
219
+ const [foundName, foundDesc, foundData] = tuple;
220
+ if (foundData === null) {
221
+ return { isEntity: false, subfieldNames: [], value: null };
222
+ }
223
+ // If it's an array, pick first item
224
+ if (Array.isArray(foundData)) {
225
+ currentValue = foundData.length ? foundData[0] : [];
226
+ }
227
+ else {
228
+ currentValue = foundData;
229
+ }
230
+ }
231
+ // If final value is an entity => gather subfields
232
+ if (currentValue &&
233
+ typeof currentValue === 'object' &&
234
+ 'orderedFields' in currentValue) {
235
+ const subEntity = currentValue;
236
+ const subLocal = getLocalFieldsWithData(subEntity);
237
+ const subfieldNames = subLocal.map(([fn]) => fn);
238
+ return { isEntity: true, subfieldNames, value: null };
239
+ }
240
+ // Otherwise it's a primitive
241
+ return { isEntity: false, subfieldNames: [], value: currentValue };
242
+ }
243
+ // ---------------------------------------------------------------------
244
+ // 4. The completer function
245
+ // - No dot => also show instance suggestions like IFCBUILDINGELEMENTPROXY[#30]
246
+ // - With dot => partial subfields
247
+ // ---------------------------------------------------------------------
248
+ /**
249
+ * Provides autocomplete suggestions based on the user's input, which can include class names,
250
+ * instance IDs, or subfields within IFC entities.
251
+ *
252
+ * @param {string} line - The current line of input entered by the user.
253
+ * @return {[string[], string]} A tuple containing:
254
+ * - An array of suggestions matching the user's input.
255
+ * - The current token being matched, which can be a class name, partial ID, or subfield.
256
+ */
257
+ function completer(line) {
258
+ const trimmed = line.trim();
259
+ // If there's whitespace, skip advanced completions
260
+ if (trimmed.includes(' ')) {
261
+ return [[], trimmed];
262
+ }
263
+ const tokens = trimmed.split('.');
264
+ if (tokens.length === 1) {
265
+ // ========== CASE A: top-level (no dot) ==========
266
+ const partial = tokens[0].toLowerCase();
267
+ // 1) Detect if user typed something like Class[# or Class[#3
268
+ // Example: "IFCPROPERTYSINGLEVALUE[#3"
269
+ // eslint-disable-next-line no-useless-escape
270
+ const bracketMatch = partial.match(/^([^\[]+)\[#(\d*)$/);
271
+ if (bracketMatch) {
272
+ // bracketMatch[1] => the class name (lowercased)
273
+ // bracketMatch[2] => the partial ID number as a string (possibly empty)
274
+ const partialClass = bracketMatch[1].trim(); // e.g. "ifcpropertysinglevalue"
275
+ // eslint-disable-next-line no-unused-vars
276
+ const partialId = bracketMatch[2].trim(); // e.g. "3" (may be empty "" if user typed "[#")
277
+ // Filter IFC classes by partialClass
278
+ const classMatches = ifcClasses.filter((cls) => cls.toLowerCase().startsWith(partialClass));
279
+ // For each matched class, gather instance suggestions, then filter out
280
+ // only those that still match the entire typed string so far (including the bracket).
281
+ let instanceMatches = [];
282
+ for (const cls of classMatches) {
283
+ const allInstances = getInstanceSuggestions(cls, model);
284
+ // the user typed something like "ifcpropertysinglevalue[#3"
285
+ // so we want to see which of our suggestions (e.g. "IFCPROPERTYSINGLEVALUE[#37]")
286
+ // starts with exactly what the user typed
287
+ instanceMatches = instanceMatches.concat(allInstances.filter((s) => s.toLowerCase().startsWith(partial)));
288
+ }
289
+ // If no classes match, or if no instance suggestions match, you could choose to:
290
+ // 1) fallback to all classes, or
291
+ // 2) show nothing
292
+ // For simplicity, let's show the instanceMatches if available; if none found,
293
+ // fallback to the original classMatches.
294
+ if (instanceMatches.length > 0) {
295
+ return [instanceMatches, tokens[0]];
296
+ }
297
+ else {
298
+ // We might also allow the user to see class names again in case they typed
299
+ // "IFCPROPERTYSINGLEVALUE[#" but there's no partial ID to match yet.
300
+ return [classMatches, tokens[0]];
301
+ }
302
+ }
303
+ else {
304
+ // ========== No bracket => fallback to your original approach ==========
305
+ // Filter IFC classes by partial
306
+ const classMatches = ifcClasses.filter((cls) => cls.toLowerCase().startsWith(partial));
307
+ // Also gather instance suggestions for each matched class
308
+ let instanceMatches = [];
309
+ for (const cls of classMatches) {
310
+ const allInstances = getInstanceSuggestions(cls, model);
311
+ instanceMatches = instanceMatches.concat(allInstances.filter((s) => s.toLowerCase().startsWith(partial)));
312
+ }
313
+ // Combine
314
+ const suggestions = [...classMatches, ...instanceMatches];
315
+ // If no suggestions found, fallback to all classes:
316
+ if (!suggestions.length) {
317
+ return [ifcClasses, tokens[0]];
318
+ }
319
+ else {
320
+ return [suggestions, tokens[0]];
321
+ }
322
+ }
323
+ }
324
+ else {
325
+ // ========== CASE B: There's at least one dot ==========
326
+ const lastToken = tokens[tokens.length - 1];
327
+ // The path up to (but not including) the final token
328
+ const pathTokens = tokens.slice(0, -1);
329
+ const pathForNavigation = pathTokens.join('.');
330
+ const { isEntity, subfieldNames } = getFieldValueOrSubfields(pathForNavigation, model);
331
+ if (!isEntity) {
332
+ return [[], trimmed];
333
+ }
334
+ // If lastToken is empty => user typed a trailing dot => show all subfields
335
+ if (!lastToken) {
336
+ return [subfieldNames, lastToken];
337
+ }
338
+ else {
339
+ // partial text => filter subfields
340
+ const lower = lastToken.toLowerCase();
341
+ const matches = subfieldNames.filter((name) => name.toLowerCase().startsWith(lower));
342
+ const suggestions = matches.length ? matches : subfieldNames;
343
+ return [suggestions, lastToken];
344
+ }
345
+ }
346
+ }
347
+ // ---------------------------------------------------------------------
348
+ // 5. Create Readline with the custom completer
349
+ // ---------------------------------------------------------------------
350
+ const rl = readline.createInterface({
351
+ input,
352
+ output,
353
+ prompt: '> ',
354
+ completer,
355
+ terminal: true, // ensure readline sees TAB in many terminals
356
+ });
357
+ // ---------------------------------------------------------------------
358
+ // 6. Prompt user
359
+ // ---------------------------------------------------------------------
360
+ console.log(`Loaded model: ${modelPath}`);
361
+ console.log('Type an IFC class name (partial) and press Tab (e.g. IfcB -> IFCBUILDING).');
362
+ console.log('Also try "IFCBUILDINGELEMENTPROXY[#" to see instance completions.');
363
+ console.log('Use a dot for subfields (e.g. IFCBUILDINGELEMENTPROXY[#30].Name). ' +
364
+ 'Press Enter to see final value.\n');
365
+ rl.prompt();
366
+ // ---------------------------------------------------------------------
367
+ // 7. On Enter: parse entire dotted path again to see if it's a primitive
368
+ // ---------------------------------------------------------------------
369
+ rl.on('line', (inputLine) => {
370
+ const command = inputLine.trim();
371
+ if (command.toLowerCase() === 'exit') {
372
+ console.log('Goodbye!');
373
+ process.exit(0);
374
+ }
375
+ const { isEntity, subfieldNames, value } = getFieldValueOrSubfields(command, model);
376
+ if (isEntity) {
377
+ console.log(`This is still an entity. Possible subfields:\n ${subfieldNames.join(', ')}`);
378
+ }
379
+ else if (value !== null && value !== undefined) {
380
+ console.log(`${JSON.stringify(value)}`);
381
+ }
382
+ else {
383
+ console.log(`No or invalid value for "${command}".`);
384
+ }
385
+ rl.prompt();
386
+ });
387
+ // ---------------------------------------------------------------------
388
+ // 8. Handle Ctrl+C
389
+ // ---------------------------------------------------------------------
390
+ rl.on('SIGINT', () => {
391
+ console.log('\nCaught interrupt signal. Exiting.');
392
+ process.exit(0);
393
+ });