@reekon-tools/boldr-utils 1.4.16 → 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.
@@ -102,24 +102,6 @@ export const calculateFormula = (formula, formulas, columns, tableConfig, measur
102
102
  currentMappings[variable] = mapping;
103
103
  }
104
104
  }
105
- // Validate that all required inputs are filled out
106
- const missingInputs = [];
107
- for (const [variable, mapping] of Object.entries(currentMappings)) {
108
- const mappingObj = typeof mapping === 'string'
109
- ? { id: mapping, type: 'column' }
110
- : mapping;
111
- // Only check column references (formula references are handled separately)
112
- if (mappingObj.type === 'column' || !mappingObj.type) {
113
- const columnId = mappingObj.id;
114
- if (!(columnId in valueMap)) {
115
- missingInputs.push(variable);
116
- }
117
- }
118
- }
119
- if (missingInputs.length > 0) {
120
- console.warn(`Not all inputs are filled out. Missing values for: ${missingInputs.join(', ')}`);
121
- return null;
122
- }
123
105
  const result = evaluateFormula({
124
106
  expression: formula.expression,
125
107
  mappings: currentMappings,
@@ -1,19 +1,52 @@
1
1
  import { Units } from '../types/firestore.js';
2
2
  import { create, all } from 'mathjs';
3
- // Create a custom mathjs instance with degree-based trigonometric functions
4
- const math = create(all);
5
3
  // Helper to convert degrees to radians
6
4
  const degToRad = (degrees) => (degrees * Math.PI) / 180;
7
- // Import degree-based trigonometric functions
8
- math.import({
9
- sin: (x) => Math.sin(degToRad(x)),
10
- cos: (x) => Math.cos(degToRad(x)),
11
- tan: (x) => Math.tan(degToRad(x)),
12
- asin: (x) => (Math.asin(x) * 180) / Math.PI,
13
- acos: (x) => (Math.acos(x) * 180) / Math.PI,
14
- atan: (x) => (Math.atan(x) * 180) / Math.PI,
15
- atan2: (y, x) => (Math.atan2(y, x) * 180) / Math.PI,
16
- }, { override: true });
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
+ }
17
50
  const compile = math.compile.bind(math);
18
51
  const mathUnit = math.unit.bind(math);
19
52
  // Cache for evaluated formulas to improve performance
@@ -136,6 +169,30 @@ export function evaluateFormula({ expression, mappings, valueMap, unit, formulas
136
169
  }
137
170
  evaluationStack.add(formulaId);
138
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
+ }
139
196
  const scope = {};
140
197
  const normalizedUnit = normalizeUnitForMathJS(unit);
141
198
  // Build scope by resolving each variable
@@ -143,6 +200,7 @@ export function evaluateFormula({ expression, mappings, valueMap, unit, formulas
143
200
  const mapping = normalizeMapping(rawMapping);
144
201
  if (mapping.type === 'measurement') {
145
202
  // Handle measurement/column reference
203
+ // At this point we know it exists (validated above), but use ?? 0 as fallback
146
204
  const micrometers = valueMap[mapping.id] ?? 0;
147
205
  if (micrometers === 0) {
148
206
  console.warn(`⚠️ Zero or missing value for mapping ID "${mapping.id}"`);
@@ -157,6 +215,7 @@ export function evaluateFormula({ expression, mappings, valueMap, unit, formulas
157
215
  throw new Error(`Referenced formula not found: ${mapping.id}`);
158
216
  }
159
217
  // Recursively evaluate the referenced formula
218
+ // If it has missing inputs, it will throw an error which we'll catch
160
219
  const nestedResult = evaluateNestedFormula(referencedFormula.id, referencedFormula.expression, referencedFormula.variableToColumnMap);
161
220
  // Convert result to current unit with error handling
162
221
  let resultInUnit;
@@ -233,8 +292,14 @@ export function evaluateFormula({ expression, mappings, valueMap, unit, formulas
233
292
  return result;
234
293
  }
235
294
  catch (err) {
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
+ }
236
301
  console.warn('❌ Formula evaluation failed:', err);
237
- return err instanceof Error ? err.message : 'Error';
302
+ return errorMessage;
238
303
  }
239
304
  }
240
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.16",
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",