@reekon-tools/boldr-utils 1.4.15 → 1.4.17

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.
@@ -1,5 +1,54 @@
1
1
  import { Units } from '../types/firestore.js';
2
- import { compile, unit as mathUnit } from 'mathjs';
2
+ import { create, all } from 'mathjs';
3
+ // Helper to convert degrees to radians
4
+ const degToRad = (degrees) => (degrees * Math.PI) / 180;
5
+ // Helper to convert radians to degrees
6
+ const radToDeg = (radians) => (radians * 180) / Math.PI;
7
+ // Create a custom mathjs instance with degree-based trigonometric functions
8
+ const math = create(all);
9
+ // Create degree-based trigonometric functions that accept degrees as input
10
+ // and return results in degrees for inverse functions
11
+ // These functions handle mathjs types by converting to numbers
12
+ const degreeTrigFunctions = {
13
+ sin: (x) => {
14
+ const degrees = typeof x === 'number' ? x : Number(x);
15
+ return Math.sin(degToRad(degrees));
16
+ },
17
+ cos: (x) => {
18
+ const degrees = typeof x === 'number' ? x : Number(x);
19
+ return Math.cos(degToRad(degrees));
20
+ },
21
+ tan: (x) => {
22
+ const degrees = typeof x === 'number' ? x : Number(x);
23
+ return Math.tan(degToRad(degrees));
24
+ },
25
+ asin: (x) => {
26
+ const value = typeof x === 'number' ? x : Number(x);
27
+ return radToDeg(Math.asin(value));
28
+ },
29
+ acos: (x) => {
30
+ const value = typeof x === 'number' ? x : Number(x);
31
+ return radToDeg(Math.acos(value));
32
+ },
33
+ atan: (x) => {
34
+ const value = typeof x === 'number' ? x : Number(x);
35
+ return radToDeg(Math.atan(value));
36
+ },
37
+ atan2: (y, x) => {
38
+ const yVal = typeof y === 'number' ? y : Number(y);
39
+ const xVal = typeof x === 'number' ? x : Number(x);
40
+ return radToDeg(Math.atan2(yVal, xVal));
41
+ },
42
+ };
43
+ // Override the trigonometric functions in mathjs using import
44
+ // The override: true option ensures existing functions are replaced
45
+ math.import(degreeTrigFunctions, { override: true });
46
+ // Verify the functions are overridden
47
+ if (typeof math.sin !== 'function') {
48
+ console.warn('Warning: math.sin override may have failed');
49
+ }
50
+ const compile = math.compile.bind(math);
51
+ const mathUnit = math.unit.bind(math);
3
52
  // Cache for evaluated formulas to improve performance
4
53
  const formulaCache = new Map();
5
54
  function normalizeUnitForMathJS(unit) {
@@ -120,6 +169,30 @@ export function evaluateFormula({ expression, mappings, valueMap, unit, formulas
120
169
  }
121
170
  evaluationStack.add(formulaId);
122
171
  try {
172
+ // Validate that all required inputs are available before building scope
173
+ const missingInputs = [];
174
+ for (const [variable, rawMapping] of Object.entries(currentMappings)) {
175
+ const mapping = normalizeMapping(rawMapping);
176
+ if (mapping.type === 'measurement') {
177
+ // Check if column reference exists in valueMap
178
+ if (!(mapping.id in valueMap)) {
179
+ missingInputs.push(variable);
180
+ }
181
+ }
182
+ else if (mapping.type === 'formula') {
183
+ // Check if formula reference exists
184
+ const referencedFormula = formulas.find((f) => f.id === mapping.id);
185
+ if (!referencedFormula) {
186
+ missingInputs.push(variable);
187
+ }
188
+ // Note: We don't validate nested formula inputs here to avoid double evaluation
189
+ // The nested formula will validate its own inputs when evaluated
190
+ }
191
+ }
192
+ // If any direct inputs are missing, throw an error
193
+ if (missingInputs.length > 0) {
194
+ throw new Error(`Missing required inputs for formula ${formulaId}: ${missingInputs.join(', ')}`);
195
+ }
123
196
  const scope = {};
124
197
  const normalizedUnit = normalizeUnitForMathJS(unit);
125
198
  // Build scope by resolving each variable
@@ -127,6 +200,7 @@ export function evaluateFormula({ expression, mappings, valueMap, unit, formulas
127
200
  const mapping = normalizeMapping(rawMapping);
128
201
  if (mapping.type === 'measurement') {
129
202
  // Handle measurement/column reference
203
+ // At this point we know it exists (validated above), but use ?? 0 as fallback
130
204
  const micrometers = valueMap[mapping.id] ?? 0;
131
205
  if (micrometers === 0) {
132
206
  console.warn(`⚠️ Zero or missing value for mapping ID "${mapping.id}"`);
@@ -141,6 +215,7 @@ export function evaluateFormula({ expression, mappings, valueMap, unit, formulas
141
215
  throw new Error(`Referenced formula not found: ${mapping.id}`);
142
216
  }
143
217
  // Recursively evaluate the referenced formula
218
+ // If it has missing inputs, it will throw an error which we'll catch
144
219
  const nestedResult = evaluateNestedFormula(referencedFormula.id, referencedFormula.expression, referencedFormula.variableToColumnMap);
145
220
  // Convert result to current unit with error handling
146
221
  let resultInUnit;
@@ -217,8 +292,14 @@ export function evaluateFormula({ expression, mappings, valueMap, unit, formulas
217
292
  return result;
218
293
  }
219
294
  catch (err) {
220
- console.error('❌ Formula evaluation failed:', err);
221
- return err instanceof Error ? err.message : 'Error';
295
+ const errorMessage = err instanceof Error ? err.message : String(err);
296
+ // If the error is about missing inputs, return null to match the behavior in temp.ts
297
+ if (errorMessage.includes('Missing required inputs') || errorMessage.includes('missing')) {
298
+ console.warn('❌ Formula evaluation failed due to missing inputs:', errorMessage);
299
+ return null;
300
+ }
301
+ console.warn('❌ Formula evaluation failed:', err);
302
+ return errorMessage;
222
303
  }
223
304
  }
224
305
  // Legacy createFormulaScope function for backward compatibility
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@reekon-tools/boldr-utils",
3
- "version": "1.4.15",
3
+ "version": "1.4.17",
4
4
  "description": "Shared utilities for formulas and measurement conversion used in Reekon apps",
5
5
  "author": "REEKON Tools",
6
6
  "license": "MIT",