@bldrs-ai/conway 0.15.838 → 0.16.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.
- package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmNode.js +1 -1
- package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmNodeMT.js +1 -1
- package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmWeb.js +1 -1
- package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmWebMT.js +1 -1
- package/compiled/dependencies/conway-geom/Dist/ConwayGeomWasmWebMT.wasm +0 -0
- package/compiled/dependencies/conway-geom/index.d.ts +2 -0
- package/compiled/dependencies/conway-geom/index.d.ts.map +1 -1
- package/compiled/dependencies/conway-geom/index.js +2 -0
- package/compiled/dependencies/conway-geom/interface/conway_geometry.d.ts +13 -1
- package/compiled/dependencies/conway-geom/interface/conway_geometry.d.ts.map +1 -1
- package/compiled/dependencies/conway-geom/interface/conway_geometry.js +16 -0
- package/compiled/dependencies/conway-geom/interface/parameters/flattened_points.d.ts +6 -0
- package/compiled/dependencies/conway-geom/interface/parameters/flattened_points.d.ts.map +1 -0
- package/compiled/dependencies/conway-geom/interface/parameters/flattened_points.js +1 -0
- package/compiled/dependencies/conway-geom/interface/parameters/params_add_face_to_geometry_simple.d.ts +10 -0
- package/compiled/dependencies/conway-geom/interface/parameters/params_add_face_to_geometry_simple.d.ts.map +1 -0
- package/compiled/dependencies/conway-geom/interface/parameters/params_add_face_to_geometry_simple.js +1 -0
- package/compiled/dependencies/conway-geom/interface/parameters/params_get_ifc_indexed_poly_curve.d.ts +6 -0
- package/compiled/dependencies/conway-geom/interface/parameters/params_get_ifc_indexed_poly_curve.d.ts.map +1 -1
- package/compiled/src/ifc/ifc_command_line_main.js +9 -1
- package/compiled/src/ifc/ifc_geometry_extraction.d.ts +22 -1
- package/compiled/src/ifc/ifc_geometry_extraction.d.ts.map +1 -1
- package/compiled/src/ifc/ifc_geometry_extraction.js +138 -58
- package/compiled/src/ifc/ifc_step_model.d.ts +0 -1
- package/compiled/src/ifc/ifc_step_model.d.ts.map +1 -1
- package/compiled/src/ifc/ifc_step_model.js +0 -1
- package/compiled/src/ifc/ifc_step_parser.d.ts +0 -1
- package/compiled/src/ifc/ifc_step_parser.d.ts.map +1 -1
- package/compiled/src/ifc/ifc_step_parser.js +0 -1
- package/compiled/src/version/version.js +1 -1
- package/compiled/tsconfig.tsbuildinfo +1 -1
- package/package.json +43 -43
- package/compiled/examples/browser.d.ts +0 -3
- package/compiled/examples/browser.d.ts.map +0 -1
- package/compiled/examples/browser.js +0 -393
- package/compiled/examples/validator.d.ts +0 -3
- package/compiled/examples/validator.d.ts.map +0 -1
- package/compiled/examples/validator.js +0 -378
|
@@ -1,378 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
import fs from 'fs';
|
|
3
|
-
import { exit } from 'process';
|
|
4
|
-
import EntityTypesIfc from '../src/ifc/ifc4_gen/entity_types_ifc.gen';
|
|
5
|
-
import IfcStepParser from '../src/ifc/ifc_step_parser';
|
|
6
|
-
import Logger from '../src/logging/logger';
|
|
7
|
-
import ParsingBuffer from '../src/parsing/parsing_buffer';
|
|
8
|
-
import { ParseResult } from '../src/step/parsing/step_parser';
|
|
9
|
-
import Environment from '../src/utilities/environment';
|
|
10
|
-
/**
|
|
11
|
-
* IFC Model Validator
|
|
12
|
-
*
|
|
13
|
-
* @see Validtor.md
|
|
14
|
-
*/
|
|
15
|
-
// ---------------------------------------------------------------------
|
|
16
|
-
// 1. Parse Command-Line Args
|
|
17
|
-
// ---------------------------------------------------------------------
|
|
18
|
-
Environment.checkEnvironment();
|
|
19
|
-
Logger.initializeWasmCallbacks();
|
|
20
|
-
const maxCommandArgs = 4;
|
|
21
|
-
if (process.argv.length < maxCommandArgs) {
|
|
22
|
-
// eslint-disable-next-line max-len
|
|
23
|
-
console.error(`Usage: validator <path_to_model>.ifc "IFCCLASS[#OptionalID].property <operator> value"`);
|
|
24
|
-
exit(1);
|
|
25
|
-
}
|
|
26
|
-
const modelPath = process.argv[2];
|
|
27
|
-
const query = process.argv[3];
|
|
28
|
-
// ---------------------------------------------------------------------
|
|
29
|
-
// 2. Load & Parse the IFC
|
|
30
|
-
// ---------------------------------------------------------------------
|
|
31
|
-
let ifcBuffer;
|
|
32
|
-
try {
|
|
33
|
-
ifcBuffer = fs.readFileSync(modelPath);
|
|
34
|
-
}
|
|
35
|
-
catch (err) {
|
|
36
|
-
Logger.error(`Error reading "${modelPath}": ${err}`);
|
|
37
|
-
exit(1);
|
|
38
|
-
}
|
|
39
|
-
Logger.createStatistics(0);
|
|
40
|
-
const parser = IfcStepParser.Instance;
|
|
41
|
-
const bufferInput = new ParsingBuffer(ifcBuffer);
|
|
42
|
-
// eslint-disable-next-line no-unused-vars
|
|
43
|
-
const [stepHeader, resultHeader] = parser.parseHeader(bufferInput);
|
|
44
|
-
const [parseResult, model] = parser.parseDataToModel(bufferInput);
|
|
45
|
-
switch (parseResult) {
|
|
46
|
-
case ParseResult.COMPLETE:
|
|
47
|
-
break;
|
|
48
|
-
case ParseResult.INCOMPLETE:
|
|
49
|
-
Logger.warning('Parse incomplete but no errors reported.');
|
|
50
|
-
break;
|
|
51
|
-
case ParseResult.INVALID_STEP:
|
|
52
|
-
Logger.error('Invalid STEP detected.');
|
|
53
|
-
exit(1);
|
|
54
|
-
break;
|
|
55
|
-
case ParseResult.MISSING_TYPE:
|
|
56
|
-
Logger.error('Missing STEP type.');
|
|
57
|
-
exit(1);
|
|
58
|
-
break;
|
|
59
|
-
case ParseResult.SYNTAX_ERROR:
|
|
60
|
-
Logger.error(`Syntax error at line ${bufferInput.lineCount}.`);
|
|
61
|
-
exit(1);
|
|
62
|
-
break;
|
|
63
|
-
default:
|
|
64
|
-
}
|
|
65
|
-
if (!model) {
|
|
66
|
-
Logger.error('Failed to load model.');
|
|
67
|
-
exit(1);
|
|
68
|
-
}
|
|
69
|
-
// ---------------------------------------------------------------------
|
|
70
|
-
// 3. Build arrays for IFC classes & entity types
|
|
71
|
-
// ---------------------------------------------------------------------
|
|
72
|
-
const nonEmptyTypeIDNoSubtypes = model.nonEmptyTypeIDs();
|
|
73
|
-
const ifcClasses = Array.from(nonEmptyTypeIDNoSubtypes || []).map((item) => String(EntityTypesIfc[item]));
|
|
74
|
-
const entityTypes = Array.from(nonEmptyTypeIDNoSubtypes || []);
|
|
75
|
-
/**
|
|
76
|
-
* Parses a query string used for querying IFC classes, properties, and values.
|
|
77
|
-
*
|
|
78
|
-
* @param {string} query_ - The query string to be parsed.
|
|
79
|
-
* @return {ParsedQuery} An object containing:
|
|
80
|
-
* @throws {Error} If the query string contains multiple operators or has an invalid format.
|
|
81
|
-
*/
|
|
82
|
-
function parseQueryString(query_) {
|
|
83
|
-
// This pattern finds any of these operators: <= >= < > == != === !==
|
|
84
|
-
const operatorPatternGlobal = /(<=|>=|<|>|==|!=|===|!==)/g;
|
|
85
|
-
const matches = [...query_.matchAll(operatorPatternGlobal)];
|
|
86
|
-
if (matches.length > 1) {
|
|
87
|
-
// If we detect more than 1 operator, we stop and notify the user
|
|
88
|
-
const found = matches.map((m) => m[0]).join(' ');
|
|
89
|
-
throw new Error(`Multiple operators found in query: ${found}`);
|
|
90
|
-
}
|
|
91
|
-
// Now handle the single operator (or none) with your existing logic
|
|
92
|
-
if (matches.length === 1) {
|
|
93
|
-
// e.g. "IFCWINDOW.OverallHeight <= 5"
|
|
94
|
-
// operator = "<="
|
|
95
|
-
// we can split around that operator or do a single match pattern
|
|
96
|
-
const operatorPattern = /\s(<=|>=|<|>|==|!=|===|!==)\s/;
|
|
97
|
-
const opMatch = query_.match(operatorPattern);
|
|
98
|
-
if (!opMatch) {
|
|
99
|
-
throw new Error(`Could not split query around operator.`);
|
|
100
|
-
}
|
|
101
|
-
const operator = opMatch[1];
|
|
102
|
-
// Split out the left and right sides
|
|
103
|
-
const parts = query_.split(operatorPattern).map((s) => s.trim()).filter(Boolean);
|
|
104
|
-
// e.g. ["IFCWINDOW.OverallHeight", "<=", "5"]
|
|
105
|
-
// eslint-disable-next-line no-magic-numbers
|
|
106
|
-
if (parts.length < 3) {
|
|
107
|
-
throw new Error(`Invalid query format around operator "${operator}"`);
|
|
108
|
-
}
|
|
109
|
-
const classAndPropPart = parts[0];
|
|
110
|
-
// eslint-disable-next-line no-magic-numbers
|
|
111
|
-
const valueStr = parts.slice(2).join(' '); // e.g. "5" (or "myStringValue")
|
|
112
|
-
// parse left side => "IFCWINDOW.OverallHeight"
|
|
113
|
-
const match = classAndPropPart.match(/^([A-Za-z0-9_]+)(\[#(\d+)\])?(?:\.(.+))?$/);
|
|
114
|
-
if (!match) {
|
|
115
|
-
throw new Error(`Invalid left side: "${classAndPropPart}"`);
|
|
116
|
-
}
|
|
117
|
-
const classNameRaw = match[1].toUpperCase();
|
|
118
|
-
const expressIDStr = match[3];
|
|
119
|
-
const propertyName = match[4];
|
|
120
|
-
let expressID;
|
|
121
|
-
if (expressIDStr !== undefined) {
|
|
122
|
-
expressID = parseInt(expressIDStr, 10);
|
|
123
|
-
}
|
|
124
|
-
return {
|
|
125
|
-
className: classNameRaw,
|
|
126
|
-
expressID,
|
|
127
|
-
property: propertyName,
|
|
128
|
-
operator,
|
|
129
|
-
value: valueStr,
|
|
130
|
-
};
|
|
131
|
-
}
|
|
132
|
-
else {
|
|
133
|
-
// No operator => user might just be checking for "IFCWINDOW" or "IFCWINDOW[#15].Height"
|
|
134
|
-
// We'll parse that similarly:
|
|
135
|
-
const match = query_.match(/^([A-Za-z0-9_]+)(\[#(\d+)\])?(?:\.(.+))?$/);
|
|
136
|
-
if (!match) {
|
|
137
|
-
throw new Error(`Invalid query format (no operator, unable to parse): "${query_}"`);
|
|
138
|
-
}
|
|
139
|
-
const classNameRaw = match[1].toUpperCase();
|
|
140
|
-
const expressIDStr = match[3];
|
|
141
|
-
const propertyName = match[4];
|
|
142
|
-
let expressID;
|
|
143
|
-
if (expressIDStr !== undefined) {
|
|
144
|
-
expressID = parseInt(expressIDStr, 10);
|
|
145
|
-
}
|
|
146
|
-
// Return with no operator or value
|
|
147
|
-
return {
|
|
148
|
-
className: classNameRaw,
|
|
149
|
-
expressID,
|
|
150
|
-
property: propertyName,
|
|
151
|
-
operator: undefined,
|
|
152
|
-
value: undefined,
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
// ---------------------------------------------------------------------
|
|
157
|
-
// 5. Utility to read a property from an IFC entity
|
|
158
|
-
// We want "entity[propertyName]" or entity.orderedFields, but ignoring case
|
|
159
|
-
// ---------------------------------------------------------------------
|
|
160
|
-
/**
|
|
161
|
-
* Retrieves an array of local fields from the given IFC entity along with
|
|
162
|
-
* their descriptions and associated data.
|
|
163
|
-
*
|
|
164
|
-
* @param {StepEntityBase<EntityTypesIfc>} entity - The IFC entity from which
|
|
165
|
-
* to extract fields and their data.
|
|
166
|
-
* @return {[string, EntityFieldDescription<EntityTypesIfc>, unknown][]}
|
|
167
|
-
* An array of tuples, where each tuple contains:
|
|
168
|
-
* - The field name as a string.
|
|
169
|
-
* - The field description object.
|
|
170
|
-
* - The associated data for the field.
|
|
171
|
-
*/
|
|
172
|
-
function getLocalFieldsWithData(entity) {
|
|
173
|
-
return entity.orderedFields.reduce((acc, [fieldName, fieldDesc]) => {
|
|
174
|
-
try {
|
|
175
|
-
// Attempt to access the field
|
|
176
|
-
const data = entity[fieldName];
|
|
177
|
-
acc.push([fieldName, fieldDesc, data]);
|
|
178
|
-
}
|
|
179
|
-
catch (err) {
|
|
180
|
-
// If an error occurs while accessing the field, skip it
|
|
181
|
-
// Optionally, log or handle the error:
|
|
182
|
-
// console.warn(`Skipping field ${fieldName}: ${err}`);
|
|
183
|
-
}
|
|
184
|
-
return acc;
|
|
185
|
-
}, []);
|
|
186
|
-
}
|
|
187
|
-
/**
|
|
188
|
-
* Retrieves the value of a specified property from the given IFC entity.
|
|
189
|
-
*
|
|
190
|
-
* @param {StepEntityBase<EntityTypesIfc>} entity - The IFC entity containing the property.
|
|
191
|
-
* @param {string} propertyName - The name of the property to retrieve
|
|
192
|
-
* @return {any} The value of the specified property, or `undefined`
|
|
193
|
-
* if the property is not found.
|
|
194
|
-
*/
|
|
195
|
-
function getPropertyValue(entity, propertyName) {
|
|
196
|
-
// We do a case-insensitive match on the name
|
|
197
|
-
const fields = getLocalFieldsWithData(entity);
|
|
198
|
-
const found = fields.find(([fName]) => fName.toLowerCase() === propertyName.toLowerCase());
|
|
199
|
-
if (!found) {
|
|
200
|
-
return undefined;
|
|
201
|
-
}
|
|
202
|
-
// eslint-disable-next-line no-unused-vars
|
|
203
|
-
const [_, _desc, data] = found;
|
|
204
|
-
// If it's an array, you might want to handle it specifically.
|
|
205
|
-
// For example, returning the first element or the entire array.
|
|
206
|
-
// For now, let's just return the raw data.
|
|
207
|
-
return data;
|
|
208
|
-
}
|
|
209
|
-
// ---------------------------------------------------------------------
|
|
210
|
-
// 6. Perform Validation
|
|
211
|
-
// ---------------------------------------------------------------------
|
|
212
|
-
/**
|
|
213
|
-
* Validates an IFC model against a parsed query to check class existence,
|
|
214
|
-
* property values, or specific conditions.
|
|
215
|
-
*
|
|
216
|
-
* @param {IfcStepModel} model_ - The IFC model to validate.
|
|
217
|
-
* @param {ParsedQuery} query_ - The parsed query containing class, property,
|
|
218
|
-
* and condition details.
|
|
219
|
-
* @return {void} Outputs validation results directly to the console.
|
|
220
|
-
*
|
|
221
|
-
* - Checks if the specified class and instance(s) exist in the model.
|
|
222
|
-
* - Validates property existence or evaluates conditions using operators and values.
|
|
223
|
-
* - Provides a summary of passing and failing entities.
|
|
224
|
-
*/
|
|
225
|
-
function validateModel(model_, query_) {
|
|
226
|
-
const { className, expressID, property, operator, value } = query_;
|
|
227
|
-
// 1) Check if the class exists in the model
|
|
228
|
-
const clsIndex = ifcClasses.indexOf(className);
|
|
229
|
-
if (clsIndex < 0) {
|
|
230
|
-
console.error(`❌ IFC class "${className}" does not exist in this model.`);
|
|
231
|
-
return;
|
|
232
|
-
}
|
|
233
|
-
// Get the type + constructor
|
|
234
|
-
const elementTypeID = entityTypes[clsIndex];
|
|
235
|
-
const ctor = model_.schema.constructors[elementTypeID];
|
|
236
|
-
if (!ctor) {
|
|
237
|
-
console.error(`❌ IFC class "${className}" not recognized in schema (missing constructor).`);
|
|
238
|
-
return;
|
|
239
|
-
}
|
|
240
|
-
// 2) Gather relevant entities
|
|
241
|
-
let entities = [];
|
|
242
|
-
if (expressID !== undefined) {
|
|
243
|
-
// Single entity check
|
|
244
|
-
const all = Array.from(model_.types(ctor));
|
|
245
|
-
const found = all.find((e) => e.expressID === expressID);
|
|
246
|
-
if (!found) {
|
|
247
|
-
console.error(`❌ No instance with Express ID #${expressID} found for class ${className}`);
|
|
248
|
-
return;
|
|
249
|
-
}
|
|
250
|
-
entities.push(found);
|
|
251
|
-
}
|
|
252
|
-
else {
|
|
253
|
-
// Convert the iterator to an array explicitly
|
|
254
|
-
const allEntities = Array.from(model_.types(ctor));
|
|
255
|
-
if (!allEntities.length) {
|
|
256
|
-
console.error(`❌ No instances of class "${className}" found in model.`);
|
|
257
|
-
return;
|
|
258
|
-
}
|
|
259
|
-
entities = allEntities;
|
|
260
|
-
}
|
|
261
|
-
// If user only specified className (and possibly ID), with no property or operator =>
|
|
262
|
-
// That might mean "check existence" or do nothing but list them.
|
|
263
|
-
if (!property && !operator && !value) {
|
|
264
|
-
console.log(`✅ Found ${entities.length} instance(s) of ${className}:`);
|
|
265
|
-
for (const e of entities) {
|
|
266
|
-
const id = e.expressID;
|
|
267
|
-
console.log(` - ${className}[#${id}]`);
|
|
268
|
-
}
|
|
269
|
-
return;
|
|
270
|
-
}
|
|
271
|
-
if (property && !operator) {
|
|
272
|
-
console.log(`Checking if property "${property}" exists on ${className}`);
|
|
273
|
-
let passCount = 0;
|
|
274
|
-
for (const e of entities) {
|
|
275
|
-
const id = e.expressID;
|
|
276
|
-
const val = getPropertyValue(e, property);
|
|
277
|
-
if (val !== undefined) {
|
|
278
|
-
passCount++;
|
|
279
|
-
console.log(` ✔️ ${className}[#${id}] has .${property} = ${JSON.stringify(val)}`);
|
|
280
|
-
}
|
|
281
|
-
else {
|
|
282
|
-
console.log(` ❌ ${className}[#${id}] has no .${property}`);
|
|
283
|
-
}
|
|
284
|
-
}
|
|
285
|
-
console.log(`\nProperty existence check: ${passCount} / ${entities.length} have property "${property}"`);
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
// Otherwise, user specified property + operator + value => do a pass/fail
|
|
289
|
-
if (!property || !operator || value === undefined) {
|
|
290
|
-
console.error(`❌ Invalid query format. Please provide "Class.Property <operator> <value>"`);
|
|
291
|
-
return;
|
|
292
|
-
}
|
|
293
|
-
const numericValue = tryParseNumber(value);
|
|
294
|
-
// eslint-disable-next-line no-unused-vars
|
|
295
|
-
const isNumeric = numericValue !== null;
|
|
296
|
-
let passCount = 0;
|
|
297
|
-
let failCount = 0;
|
|
298
|
-
const passes = [];
|
|
299
|
-
const fails = [];
|
|
300
|
-
console.log(`\nValidation Report for Query: ${className}${expressID !==
|
|
301
|
-
undefined ? `[#${expressID}]` : ''}.${property} ${operator} ${value}`);
|
|
302
|
-
for (const e of entities) {
|
|
303
|
-
const id = e.expressID;
|
|
304
|
-
const propVal = getPropertyValue(e, property);
|
|
305
|
-
// If property is undefined => automatically fail
|
|
306
|
-
if (propVal === undefined || propVal === null) {
|
|
307
|
-
fails.push({ id, propVal, reason: `no "${property}" property` });
|
|
308
|
-
failCount++;
|
|
309
|
-
continue;
|
|
310
|
-
}
|
|
311
|
-
const leftSide = JSON.stringify(propVal);
|
|
312
|
-
const rightSide = value;
|
|
313
|
-
let pass;
|
|
314
|
-
try {
|
|
315
|
-
const expression = `${leftSide} ${operator} ${rightSide}`;
|
|
316
|
-
// eslint-disable-next-line no-eval
|
|
317
|
-
pass = eval(expression);
|
|
318
|
-
}
|
|
319
|
-
catch (err) {
|
|
320
|
-
fails.push({ id, propVal, reason: `eval error: ${err}` });
|
|
321
|
-
failCount++;
|
|
322
|
-
continue;
|
|
323
|
-
}
|
|
324
|
-
if (pass) {
|
|
325
|
-
passes.push({ id, propVal });
|
|
326
|
-
passCount++;
|
|
327
|
-
}
|
|
328
|
-
else {
|
|
329
|
-
fails.push({ id, propVal,
|
|
330
|
-
reason: `failed comparison (${property}: ${JSON.stringify(propVal)})` });
|
|
331
|
-
failCount++;
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
// Sort by expressID
|
|
335
|
-
passes.sort((a, b) => a.id - b.id);
|
|
336
|
-
fails.sort((a, b) => a.id - b.id);
|
|
337
|
-
// Print passes
|
|
338
|
-
console.log(`\n✔️ Passing Entries:`);
|
|
339
|
-
for (const { id, propVal } of passes) {
|
|
340
|
-
console.log(`✔️ ${className}[#${id}] => PASSED (${property}: ${JSON.stringify(propVal)})`);
|
|
341
|
-
}
|
|
342
|
-
// Print fails
|
|
343
|
-
console.log(`\n❌ Failing Entries:`);
|
|
344
|
-
// eslint-disable-next-line no-unused-vars
|
|
345
|
-
for (const { id, propVal, reason } of fails) {
|
|
346
|
-
console.log(`❌ ${className}[#${id}] => FAILED (${reason})`);
|
|
347
|
-
}
|
|
348
|
-
// Summary
|
|
349
|
-
console.log(`\n✅ Total Passing: ${passCount}`);
|
|
350
|
-
console.log(`❌ Total Failing: ${failCount}`);
|
|
351
|
-
}
|
|
352
|
-
/**
|
|
353
|
-
* Attempts to parse a given string as a number, returning `null` if the parsing fails.
|
|
354
|
-
*
|
|
355
|
-
* @param {string} text - The input string to be parsed.
|
|
356
|
-
* @return {number | null} The parsed number, or `null` if the input is not a valid number.
|
|
357
|
-
*/
|
|
358
|
-
function tryParseNumber(text) {
|
|
359
|
-
// e.g. "3.14" => 3.14
|
|
360
|
-
// e.g. "5" => 5
|
|
361
|
-
// e.g. "abc" => null
|
|
362
|
-
// We also want to ignore quotes like "3.14" => might become NaN if we don't strip them
|
|
363
|
-
const cleaned = text.trim().replace(/^['"]+|['"]+$/g, ''); // remove surrounding quotes
|
|
364
|
-
const val = parseFloat(cleaned);
|
|
365
|
-
return Number.isNaN(val) ? null : val;
|
|
366
|
-
}
|
|
367
|
-
// ---------------------------------------------------------------------
|
|
368
|
-
// 7. Run the validator
|
|
369
|
-
// ---------------------------------------------------------------------
|
|
370
|
-
let parsedQuery;
|
|
371
|
-
try {
|
|
372
|
-
parsedQuery = parseQueryString(query);
|
|
373
|
-
}
|
|
374
|
-
catch (err) {
|
|
375
|
-
console.error(`❌ Error parsing query: ${err.message}`);
|
|
376
|
-
exit(1);
|
|
377
|
-
}
|
|
378
|
-
validateModel(model, parsedQuery);
|