@reekon-tools/boldr-utils 1.9.0 → 1.9.1
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/dist/calculator/validate.js +33 -8
- package/package.json +1 -1
|
@@ -321,6 +321,10 @@ export const validateCalculatorDefinition = (input) => {
|
|
|
321
321
|
push('variable-unit-dimension-mismatch', unitPath, `Unit "${unit}" is not a ${dim} unit (variable ${variable} maps to "${mapped.name}")`);
|
|
322
322
|
}
|
|
323
323
|
}
|
|
324
|
+
// The unit the EXPRESSION produces, which is what `resultUnit` claims to
|
|
325
|
+
// be. Hoisted out of the `target` branch because the number-target check
|
|
326
|
+
// further down needs it too.
|
|
327
|
+
const inferred = inferExpressionUnit(def.fields, eq);
|
|
324
328
|
if (target) {
|
|
325
329
|
const dim = fieldDimension(target);
|
|
326
330
|
if (eq.resultUnit != null) {
|
|
@@ -331,11 +335,16 @@ export const validateCalculatorDefinition = (input) => {
|
|
|
331
335
|
push('result-unit-dimension-mismatch', `${path}.resultUnit`, `Result unit "${eq.resultUnit}" is not a ${dim} unit (target "${target.name}")`);
|
|
332
336
|
}
|
|
333
337
|
}
|
|
334
|
-
//
|
|
335
|
-
//
|
|
336
|
-
//
|
|
337
|
-
//
|
|
338
|
-
|
|
338
|
+
// Dimension checks against the target field can't see a same-dimension
|
|
339
|
+
// scale error: three inch variables multiplied are in³, so `cu_ft`
|
|
340
|
+
// passes every check above while reporting 1728× the truth.
|
|
341
|
+
//
|
|
342
|
+
// Deliberately NOT applied to a dimensionless target. Literals carry no
|
|
343
|
+
// units here, so `area / 33` infers as an area whether the area arrives
|
|
344
|
+
// in µm² or ft² — the inference cannot tell a broken count from a
|
|
345
|
+
// correct one, and every legitimate count written as
|
|
346
|
+
// `area_in_ft² / 33_ft²` would be flagged. The canonical-leak check
|
|
347
|
+
// below is the one that separates them.
|
|
339
348
|
if (inferred.ok && dim !== 'none') {
|
|
340
349
|
if (inferred.dimension !== dim) {
|
|
341
350
|
push('expression-dimension-mismatch', `${path}.expression`, `Expression produces ${describeDimension(inferred.dimension)} but "${target.name}" is ${describeDimension(dim)} — check the variable units and the expression`);
|
|
@@ -365,14 +374,30 @@ export const validateCalculatorDefinition = (input) => {
|
|
|
365
374
|
}
|
|
366
375
|
}
|
|
367
376
|
// The canonical-leak trap: a number target consuming a unit-carrying
|
|
368
|
-
// field without a variable unit receives raw µm-scale values.
|
|
369
|
-
|
|
377
|
+
// field without a variable unit receives raw µm-scale values. Reported
|
|
378
|
+
// per variable, because the fix is per variable ("pick a unit for A").
|
|
379
|
+
//
|
|
380
|
+
// Gated on the inference: unit-carrying variables are fine as long as
|
|
381
|
+
// their dimensions CANCEL, which is the common shape for a count —
|
|
382
|
+
// `ceil(area / area)` sheets, `(12 * rise) / run` pitch. Flagging those
|
|
383
|
+
// put a permanent warning on two correct calculators, and a check that
|
|
384
|
+
// cries wolf on correct authoring is one authors learn to scroll past.
|
|
385
|
+
//
|
|
386
|
+
// An ERROR, not a warning. It was a warning so that pre-annotation docs
|
|
387
|
+
// kept validating, but "keeps validating" is how the roofing estimator
|
|
388
|
+
// shipped `Bundles = A / 33` over a canonical µm² area and showed
|
|
389
|
+
// 184,990,000,000 — the right answer times µm²-per-ft². There is no
|
|
390
|
+
// authoring where a µm-scale value in a unitless field is intended, and
|
|
391
|
+
// nothing gates on `ok`: mobile only console.warns in __DEV__, and the
|
|
392
|
+
// web editor shows the banner without blocking save.
|
|
393
|
+
if (target?.kind === ColumnType.Number &&
|
|
394
|
+
(!inferred.ok || inferred.dimension !== 'none')) {
|
|
370
395
|
for (const [variable, fieldId] of Object.entries(eq.variableToFieldId)) {
|
|
371
396
|
const mapped = fieldById.get(fieldId);
|
|
372
397
|
if (!mapped || fieldDimension(mapped) === 'none')
|
|
373
398
|
continue;
|
|
374
399
|
if (eq.variableUnits?.[variable] == null) {
|
|
375
|
-
push('canonical-value-into-number-target', `${path}.variableToFieldId.${variable}`, `"${target.name}" is a number field but variable ${variable} ("${mapped.name}") has no display unit
|
|
400
|
+
push('canonical-value-into-number-target', `${path}.variableToFieldId.${variable}`, `"${target.name}" is a number field, so it shows the raw result — but variable ${variable} ("${mapped.name}") has no display unit, so it arrives as a canonical µm-scale value and the result will be off by a large factor. Pick a unit for ${variable}.`);
|
|
376
401
|
}
|
|
377
402
|
}
|
|
378
403
|
}
|