@reekon-tools/boldr-utils 1.3.7 ā 1.3.8
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/formulas/evaluateFormula.d.ts +25 -4
- package/dist/formulas/evaluateFormula.js +169 -13
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/package.json +1 -1
|
@@ -1,11 +1,32 @@
|
|
|
1
1
|
import { Units } from '../types/firestore.js';
|
|
2
|
+
export interface EnhancedMapping {
|
|
3
|
+
id: string;
|
|
4
|
+
type?: 'measurement' | 'formula' | 'column';
|
|
5
|
+
}
|
|
6
|
+
export interface FormulaDefinition {
|
|
7
|
+
id: string;
|
|
8
|
+
name: string;
|
|
9
|
+
expression: string;
|
|
10
|
+
variableToColumnMap: Record<string, EnhancedMapping>;
|
|
11
|
+
}
|
|
12
|
+
export interface FormulaEvaluationOptions {
|
|
13
|
+
expression: string;
|
|
14
|
+
mappings: Record<string, EnhancedMapping | string>;
|
|
15
|
+
valueMap?: Record<string, number>;
|
|
16
|
+
unit: Units;
|
|
17
|
+
formulas?: FormulaDefinition[];
|
|
18
|
+
scope?: Record<string, number>;
|
|
19
|
+
}
|
|
20
|
+
export declare function evaluateFormula({ expression, mappings, valueMap, unit, formulas, scope, }: FormulaEvaluationOptions): number | string | null;
|
|
2
21
|
export declare function createFormulaScope({ mappings, valueMap, unit, }: {
|
|
3
22
|
mappings: Record<string, string>;
|
|
4
23
|
valueMap: Record<string, number>;
|
|
5
24
|
unit: Units;
|
|
6
25
|
}): Record<string, number>;
|
|
7
|
-
export declare function
|
|
8
|
-
|
|
9
|
-
|
|
26
|
+
export declare function createEnhancedFormulaScope({ mappings, valueMap, unit, formulas, }: {
|
|
27
|
+
mappings: Record<string, EnhancedMapping | string>;
|
|
28
|
+
valueMap: Record<string, number>;
|
|
10
29
|
unit: Units;
|
|
11
|
-
|
|
30
|
+
formulas?: FormulaDefinition[];
|
|
31
|
+
}): Record<string, number>;
|
|
32
|
+
export declare function clearFormulaCache(): void;
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { Units } from '../types/firestore.js';
|
|
2
2
|
import { compile, unit as mathUnit } from 'mathjs';
|
|
3
|
+
// Cache for evaluated formulas to improve performance
|
|
4
|
+
const formulaCache = new Map();
|
|
3
5
|
function normalizeUnitForMathJS(unit) {
|
|
4
6
|
switch (unit) {
|
|
5
7
|
case Units.Centimeters:
|
|
@@ -23,6 +25,135 @@ function normalizeUnitForMathJS(unit) {
|
|
|
23
25
|
return 'mm'; // fallback
|
|
24
26
|
}
|
|
25
27
|
}
|
|
28
|
+
// Normalize mapping to EnhancedMapping format
|
|
29
|
+
function normalizeMapping(mapping) {
|
|
30
|
+
if (typeof mapping === 'string') {
|
|
31
|
+
// Legacy format: just the ID
|
|
32
|
+
console.log('š§ Converting legacy string mapping:', mapping);
|
|
33
|
+
return { id: mapping, type: 'measurement' };
|
|
34
|
+
}
|
|
35
|
+
// New format: ensure type defaults to 'measurement' and handle 'column' as 'measurement'
|
|
36
|
+
const normalizedType = mapping.type === 'column' ? 'measurement' : mapping.type || 'measurement';
|
|
37
|
+
console.log('š§ Normalizing mapping:', mapping, 'ā', {
|
|
38
|
+
id: mapping.id,
|
|
39
|
+
type: normalizedType,
|
|
40
|
+
});
|
|
41
|
+
return { id: mapping.id, type: normalizedType };
|
|
42
|
+
}
|
|
43
|
+
// Unified formula evaluation function
|
|
44
|
+
export function evaluateFormula({ expression, mappings, valueMap, unit, formulas = [], scope, }) {
|
|
45
|
+
// Legacy mode: if scope is provided, use it directly
|
|
46
|
+
if (scope) {
|
|
47
|
+
console.log('š Using legacy scope mode');
|
|
48
|
+
try {
|
|
49
|
+
const normalizedUnit = normalizeUnitForMathJS(unit);
|
|
50
|
+
const compiled = compile(expression);
|
|
51
|
+
const result = compiled.evaluate(scope);
|
|
52
|
+
const asUnit = mathUnit(result, normalizedUnit);
|
|
53
|
+
if (!asUnit.equalBase(mathUnit('1 um'))) {
|
|
54
|
+
throw new Error(`Incompatible unit: ${asUnit.formatUnits()} is not compatible with ${normalizedUnit}`);
|
|
55
|
+
}
|
|
56
|
+
return Math.round(asUnit.toNumber('um'));
|
|
57
|
+
}
|
|
58
|
+
catch (err) {
|
|
59
|
+
console.error(`Failed to evaluate formula`, err);
|
|
60
|
+
return null;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
// Enhanced mode: use mappings and valueMap
|
|
64
|
+
if (!valueMap) {
|
|
65
|
+
console.error('ā valueMap is required when not using scope');
|
|
66
|
+
return 'Error: valueMap is required';
|
|
67
|
+
}
|
|
68
|
+
const evaluationStack = new Set(); // For circular dependency detection
|
|
69
|
+
const cache = new Map(); // Local cache for this evaluation session
|
|
70
|
+
function evaluateNestedFormula(formulaId, currentExpression, currentMappings) {
|
|
71
|
+
console.log(`š Evaluating nested formula: ${formulaId}`);
|
|
72
|
+
console.log(`š Expression: "${currentExpression}"`);
|
|
73
|
+
console.log('šŗļø Current mappings:', currentMappings);
|
|
74
|
+
// Check for circular dependency
|
|
75
|
+
if (evaluationStack.has(formulaId)) {
|
|
76
|
+
throw new Error(`Circular dependency detected involving formula: ${formulaId}`);
|
|
77
|
+
}
|
|
78
|
+
// Check cache first
|
|
79
|
+
const cacheKey = `${formulaId}_${unit}`;
|
|
80
|
+
if (cache.has(cacheKey)) {
|
|
81
|
+
console.log(`š¾ Using cached result for ${formulaId}`);
|
|
82
|
+
return cache.get(cacheKey);
|
|
83
|
+
}
|
|
84
|
+
evaluationStack.add(formulaId);
|
|
85
|
+
try {
|
|
86
|
+
const scope = {};
|
|
87
|
+
const normalizedUnit = normalizeUnitForMathJS(unit);
|
|
88
|
+
console.log(`š Converting to unit: ${unit} ā ${normalizedUnit}`);
|
|
89
|
+
// Build scope by resolving each variable
|
|
90
|
+
for (const [variable, rawMapping] of Object.entries(currentMappings)) {
|
|
91
|
+
console.log(`\nš§ Processing variable "${variable}":`, rawMapping);
|
|
92
|
+
const mapping = normalizeMapping(rawMapping);
|
|
93
|
+
console.log(`ā
Normalized mapping:`, mapping);
|
|
94
|
+
if (mapping.type === 'measurement') {
|
|
95
|
+
// Handle measurement/column reference
|
|
96
|
+
console.log(`š Looking up measurement ID "${mapping.id}" in valueMap`);
|
|
97
|
+
const micrometers = valueMap[mapping.id] ?? 0;
|
|
98
|
+
console.log(`š Found value: ${micrometers} micrometers`);
|
|
99
|
+
if (micrometers === 0) {
|
|
100
|
+
console.warn(`ā ļø Zero or missing value for mapping ID "${mapping.id}"`);
|
|
101
|
+
console.log('šļø Available valueMap keys:', Object.keys(valueMap));
|
|
102
|
+
}
|
|
103
|
+
const valueInUnit = mathUnit(micrometers, 'um').toNumber(normalizedUnit);
|
|
104
|
+
console.log(`š¢ Converted to ${normalizedUnit}: ${valueInUnit}`);
|
|
105
|
+
scope[variable] = valueInUnit;
|
|
106
|
+
console.log(`ā
Set scope["${variable}"] = ${valueInUnit}`);
|
|
107
|
+
}
|
|
108
|
+
else if (mapping.type === 'formula') {
|
|
109
|
+
// Handle formula reference - recursively evaluate
|
|
110
|
+
console.log(`š Recursively evaluating formula "${mapping.id}"`);
|
|
111
|
+
const referencedFormula = formulas.find((f) => f.id === mapping.id);
|
|
112
|
+
if (!referencedFormula) {
|
|
113
|
+
throw new Error(`Referenced formula not found: ${mapping.id}`);
|
|
114
|
+
}
|
|
115
|
+
// Recursively evaluate the referenced formula
|
|
116
|
+
const nestedResult = evaluateNestedFormula(referencedFormula.id, referencedFormula.expression, referencedFormula.variableToColumnMap);
|
|
117
|
+
// Convert result to current unit
|
|
118
|
+
const resultInUnit = mathUnit(nestedResult, 'um').toNumber(normalizedUnit);
|
|
119
|
+
console.log(`š¢ Nested formula result in ${normalizedUnit}: ${resultInUnit}`);
|
|
120
|
+
scope[variable] = resultInUnit;
|
|
121
|
+
console.log(`ā
Set scope["${variable}"] = ${resultInUnit} (from formula)`);
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
console.log('\nšÆ Final scope built:', scope);
|
|
125
|
+
// Evaluate the formula expression
|
|
126
|
+
console.log(`š Compiling expression: "${currentExpression}"`);
|
|
127
|
+
const compiled = compile(currentExpression);
|
|
128
|
+
console.log('š§® Evaluating with scope:', scope);
|
|
129
|
+
const result = compiled.evaluate(scope);
|
|
130
|
+
console.log('š Raw evaluation result:', result, typeof result);
|
|
131
|
+
const asUnit = mathUnit(result, normalizedUnit);
|
|
132
|
+
console.log('š As unit object:', asUnit.toString());
|
|
133
|
+
if (!asUnit.equalBase(mathUnit('1 um'))) {
|
|
134
|
+
throw new Error(`Incompatible unit: ${asUnit.formatUnits()} is not compatible with ${normalizedUnit}`);
|
|
135
|
+
}
|
|
136
|
+
const finalResult = Math.round(asUnit.toNumber('um'));
|
|
137
|
+
console.log(`šÆ Final result: ${finalResult} micrometers`);
|
|
138
|
+
// Cache the result
|
|
139
|
+
cache.set(cacheKey, finalResult);
|
|
140
|
+
return finalResult;
|
|
141
|
+
}
|
|
142
|
+
finally {
|
|
143
|
+
evaluationStack.delete(formulaId);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
try {
|
|
147
|
+
const result = evaluateNestedFormula('root', expression, mappings);
|
|
148
|
+
console.log(`š Formula evaluation completed successfully: ${result}`);
|
|
149
|
+
return result;
|
|
150
|
+
}
|
|
151
|
+
catch (err) {
|
|
152
|
+
console.error('ā Formula evaluation failed:', err);
|
|
153
|
+
return err instanceof Error ? err.message : 'Error';
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// Legacy createFormulaScope function for backward compatibility
|
|
26
157
|
export function createFormulaScope({ mappings, valueMap, unit, }) {
|
|
27
158
|
const scope = {};
|
|
28
159
|
const normalizedUnit = normalizeUnitForMathJS(unit);
|
|
@@ -34,19 +165,44 @@ export function createFormulaScope({ mappings, valueMap, unit, }) {
|
|
|
34
165
|
}
|
|
35
166
|
return scope;
|
|
36
167
|
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
if (
|
|
44
|
-
|
|
168
|
+
// Enhanced createFormulaScope for new mapping structure
|
|
169
|
+
export function createEnhancedFormulaScope({ mappings, valueMap, unit, formulas = [], }) {
|
|
170
|
+
const scope = {};
|
|
171
|
+
const normalizedUnit = normalizeUnitForMathJS(unit);
|
|
172
|
+
for (const [variable, rawMapping] of Object.entries(mappings)) {
|
|
173
|
+
const mapping = normalizeMapping(rawMapping);
|
|
174
|
+
if (mapping.type === 'measurement') {
|
|
175
|
+
const micrometers = valueMap[mapping.id] ?? 0;
|
|
176
|
+
const valueInUnit = mathUnit(micrometers, 'um').toNumber(normalizedUnit);
|
|
177
|
+
scope[variable] = valueInUnit;
|
|
178
|
+
}
|
|
179
|
+
else if (mapping.type === 'formula') {
|
|
180
|
+
// Find and evaluate the referenced formula
|
|
181
|
+
const referencedFormula = formulas.find((f) => f.id === mapping.id);
|
|
182
|
+
if (referencedFormula) {
|
|
183
|
+
const result = evaluateFormula({
|
|
184
|
+
expression: referencedFormula.expression,
|
|
185
|
+
mappings: referencedFormula.variableToColumnMap,
|
|
186
|
+
valueMap,
|
|
187
|
+
unit,
|
|
188
|
+
formulas,
|
|
189
|
+
});
|
|
190
|
+
if (typeof result === 'number') {
|
|
191
|
+
const valueInUnit = mathUnit(result, 'um').toNumber(normalizedUnit);
|
|
192
|
+
scope[variable] = valueInUnit;
|
|
193
|
+
}
|
|
194
|
+
else {
|
|
195
|
+
scope[variable] = 0; // Error case
|
|
196
|
+
}
|
|
197
|
+
}
|
|
198
|
+
else {
|
|
199
|
+
scope[variable] = 0; // Formula not found
|
|
200
|
+
}
|
|
45
201
|
}
|
|
46
|
-
return Math.round(asUnit.toNumber('um'));
|
|
47
|
-
}
|
|
48
|
-
catch (err) {
|
|
49
|
-
console.error(`Failed to evaluate formula`, err);
|
|
50
|
-
return null;
|
|
51
202
|
}
|
|
203
|
+
return scope;
|
|
204
|
+
}
|
|
205
|
+
// Utility function to clear formula cache
|
|
206
|
+
export function clearFormulaCache() {
|
|
207
|
+
formulaCache.clear();
|
|
52
208
|
}
|
package/dist/index.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { evaluateFormula, createFormulaScope, } from './formulas/evaluateFormula.js';
|
|
1
|
+
export { evaluateFormula, createFormulaScope, createEnhancedFormulaScope, clearFormulaCache, type EnhancedMapping, type FormulaDefinition, type FormulaEvaluationOptions, } from './formulas/evaluateFormula.js';
|
|
2
2
|
export { convertMicrometers } from './utils/micrometersToUnit.js';
|
|
3
3
|
export { parseMeasurement } from './utils/parseMeasurement.js';
|
|
4
4
|
export { useParseMeasurement } from './hooks/useParseMeasurement.js';
|
package/dist/index.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export { evaluateFormula, createFormulaScope, } from './formulas/evaluateFormula.js';
|
|
1
|
+
export { evaluateFormula, createFormulaScope, createEnhancedFormulaScope, clearFormulaCache, } from './formulas/evaluateFormula.js';
|
|
2
2
|
export { convertMicrometers } from './utils/micrometersToUnit.js';
|
|
3
3
|
export { parseMeasurement } from './utils/parseMeasurement.js';
|
|
4
4
|
export { useParseMeasurement } from './hooks/useParseMeasurement.js';
|