@the-trybe/formula-engine 1.0.0

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.
Files changed (43) hide show
  1. package/.claude/settings.local.json +6 -0
  2. package/PRD_FORMULA_ENGINE.md +1863 -0
  3. package/README.md +382 -0
  4. package/dist/decimal-utils.d.ts +180 -0
  5. package/dist/decimal-utils.js +355 -0
  6. package/dist/dependency-extractor.d.ts +20 -0
  7. package/dist/dependency-extractor.js +103 -0
  8. package/dist/dependency-graph.d.ts +60 -0
  9. package/dist/dependency-graph.js +252 -0
  10. package/dist/errors.d.ts +161 -0
  11. package/dist/errors.js +260 -0
  12. package/dist/evaluator.d.ts +51 -0
  13. package/dist/evaluator.js +494 -0
  14. package/dist/formula-engine.d.ts +79 -0
  15. package/dist/formula-engine.js +355 -0
  16. package/dist/functions.d.ts +3 -0
  17. package/dist/functions.js +720 -0
  18. package/dist/index.d.ts +10 -0
  19. package/dist/index.js +61 -0
  20. package/dist/lexer.d.ts +25 -0
  21. package/dist/lexer.js +357 -0
  22. package/dist/parser.d.ts +32 -0
  23. package/dist/parser.js +372 -0
  24. package/dist/types.d.ts +228 -0
  25. package/dist/types.js +62 -0
  26. package/jest.config.js +23 -0
  27. package/package.json +35 -0
  28. package/src/decimal-utils.ts +408 -0
  29. package/src/dependency-extractor.ts +117 -0
  30. package/src/dependency-graph.test.ts +238 -0
  31. package/src/dependency-graph.ts +288 -0
  32. package/src/errors.ts +296 -0
  33. package/src/evaluator.ts +604 -0
  34. package/src/formula-engine.test.ts +660 -0
  35. package/src/formula-engine.ts +430 -0
  36. package/src/functions.ts +770 -0
  37. package/src/index.ts +103 -0
  38. package/src/lexer.test.ts +288 -0
  39. package/src/lexer.ts +394 -0
  40. package/src/parser.test.ts +349 -0
  41. package/src/parser.ts +449 -0
  42. package/src/types.ts +347 -0
  43. package/tsconfig.json +29 -0
package/src/types.ts ADDED
@@ -0,0 +1,347 @@
1
+ import { Decimal } from 'decimal.js';
2
+
3
+ // ============================================================================
4
+ // Value Types
5
+ // ============================================================================
6
+
7
+ export type ValueType = 'number' | 'decimal' | 'string' | 'boolean' | 'array' | 'object' | 'null' | 'any';
8
+
9
+ export type FormulaValue = Decimal | number | string | boolean | null | FormulaValue[] | { [key: string]: FormulaValue };
10
+
11
+ // ============================================================================
12
+ // Decimal Configuration
13
+ // ============================================================================
14
+
15
+ export enum DecimalRoundingMode {
16
+ CEIL = 'CEIL',
17
+ FLOOR = 'FLOOR',
18
+ DOWN = 'DOWN',
19
+ UP = 'UP',
20
+ HALF_UP = 'HALF_UP',
21
+ HALF_DOWN = 'HALF_DOWN',
22
+ HALF_EVEN = 'HALF_EVEN',
23
+ HALF_ODD = 'HALF_ODD',
24
+ }
25
+
26
+ export interface DecimalConfig {
27
+ precision?: number;
28
+ roundingMode?: DecimalRoundingMode;
29
+ divisionScale?: number;
30
+ preserveTrailingZeros?: boolean;
31
+ autoConvertFloats?: boolean;
32
+ maxExponent?: number;
33
+ minExponent?: number;
34
+ }
35
+
36
+ // ============================================================================
37
+ // Rounding Configuration
38
+ // ============================================================================
39
+
40
+ export interface RoundingConfig {
41
+ mode: 'HALF_UP' | 'HALF_DOWN' | 'FLOOR' | 'CEIL' | 'NONE';
42
+ precision: number;
43
+ }
44
+
45
+ // ============================================================================
46
+ // Error Behavior
47
+ // ============================================================================
48
+
49
+ export interface ErrorBehavior {
50
+ type: 'THROW' | 'NULL' | 'ZERO' | 'DEFAULT' | 'SKIP';
51
+ defaultValue?: unknown;
52
+ }
53
+
54
+ // ============================================================================
55
+ // Formula Definition
56
+ // ============================================================================
57
+
58
+ export interface FormulaDefinition {
59
+ id: string;
60
+ expression: string;
61
+ dependencies?: string[];
62
+ onError?: ErrorBehavior;
63
+ defaultValue?: unknown;
64
+ rounding?: RoundingConfig;
65
+ metadata?: Record<string, unknown>;
66
+ }
67
+
68
+ // ============================================================================
69
+ // Evaluation Context
70
+ // ============================================================================
71
+
72
+ export interface EvaluationContext {
73
+ variables: Record<string, unknown>;
74
+ collections?: Record<string, unknown[]>;
75
+ extra?: Record<string, unknown>;
76
+ }
77
+
78
+ // ============================================================================
79
+ // Engine Configuration
80
+ // ============================================================================
81
+
82
+ export interface OperatorDefinition {
83
+ symbol: string;
84
+ precedence: number;
85
+ associativity: 'left' | 'right';
86
+ handler: (left: unknown, right: unknown) => unknown;
87
+ }
88
+
89
+ export interface SecurityConfig {
90
+ maxExpressionLength?: number;
91
+ maxRecursionDepth?: number;
92
+ maxIterations?: number;
93
+ maxExecutionTime?: number;
94
+ allowedFunctions?: string[];
95
+ blockedFunctions?: string[];
96
+ }
97
+
98
+ export interface FormulaEngineConfig {
99
+ enableCache?: boolean;
100
+ maxCacheSize?: number;
101
+ defaultErrorBehavior?: ErrorBehavior;
102
+ defaultRounding?: RoundingConfig;
103
+ variablePrefix?: string;
104
+ contextPrefix?: string;
105
+ strictMode?: boolean;
106
+ operators?: OperatorDefinition[];
107
+ functions?: FunctionDefinition[];
108
+ decimal?: DecimalConfig;
109
+ security?: SecurityConfig;
110
+ }
111
+
112
+ // ============================================================================
113
+ // AST Node Types
114
+ // ============================================================================
115
+
116
+ export interface DecimalLiteral {
117
+ type: 'DecimalLiteral';
118
+ value: string;
119
+ raw: string;
120
+ }
121
+
122
+ export interface NumberLiteral {
123
+ type: 'NumberLiteral';
124
+ value: number;
125
+ }
126
+
127
+ export interface StringLiteral {
128
+ type: 'StringLiteral';
129
+ value: string;
130
+ }
131
+
132
+ export interface BooleanLiteral {
133
+ type: 'BooleanLiteral';
134
+ value: boolean;
135
+ }
136
+
137
+ export interface NullLiteral {
138
+ type: 'NullLiteral';
139
+ }
140
+
141
+ export interface ArrayLiteral {
142
+ type: 'ArrayLiteral';
143
+ elements: ASTNode[];
144
+ }
145
+
146
+ export interface VariableReference {
147
+ type: 'VariableReference';
148
+ prefix: '$' | '@';
149
+ name: string;
150
+ }
151
+
152
+ export interface BinaryOperation {
153
+ type: 'BinaryOperation';
154
+ operator: string;
155
+ left: ASTNode;
156
+ right: ASTNode;
157
+ }
158
+
159
+ export interface UnaryOperation {
160
+ type: 'UnaryOperation';
161
+ operator: string;
162
+ operand: ASTNode;
163
+ }
164
+
165
+ export interface ConditionalExpression {
166
+ type: 'ConditionalExpression';
167
+ condition: ASTNode;
168
+ consequent: ASTNode;
169
+ alternate: ASTNode;
170
+ }
171
+
172
+ export interface FunctionCall {
173
+ type: 'FunctionCall';
174
+ name: string;
175
+ arguments: ASTNode[];
176
+ }
177
+
178
+ export interface MemberAccess {
179
+ type: 'MemberAccess';
180
+ object: ASTNode;
181
+ property: string;
182
+ }
183
+
184
+ export interface IndexAccess {
185
+ type: 'IndexAccess';
186
+ object: ASTNode;
187
+ index: ASTNode;
188
+ }
189
+
190
+ export type ASTNode =
191
+ | DecimalLiteral
192
+ | NumberLiteral
193
+ | StringLiteral
194
+ | BooleanLiteral
195
+ | NullLiteral
196
+ | ArrayLiteral
197
+ | VariableReference
198
+ | BinaryOperation
199
+ | UnaryOperation
200
+ | ConditionalExpression
201
+ | FunctionCall
202
+ | MemberAccess
203
+ | IndexAccess;
204
+
205
+ // ============================================================================
206
+ // Function Definition
207
+ // ============================================================================
208
+
209
+ export interface ArgumentType {
210
+ name: string;
211
+ type: ValueType;
212
+ required: boolean;
213
+ default?: unknown;
214
+ }
215
+
216
+ export type FunctionImplementation = (
217
+ args: unknown[],
218
+ context: EvaluationContext,
219
+ engine: unknown
220
+ ) => unknown;
221
+
222
+ export interface FunctionDefinition {
223
+ name: string;
224
+ minArgs: number;
225
+ maxArgs: number;
226
+ argTypes?: ArgumentType[];
227
+ returnType: ValueType;
228
+ implementation: FunctionImplementation;
229
+ description?: string;
230
+ }
231
+
232
+ // ============================================================================
233
+ // Dependency Graph
234
+ // ============================================================================
235
+
236
+ export interface DependencyGraph {
237
+ nodes: Set<string>;
238
+ edges: Map<string, Set<string>>;
239
+ hasCycles(): boolean;
240
+ getRoots(): Set<string>;
241
+ getDependents(nodeId: string): Set<string>;
242
+ getDependencies(nodeId: string): Set<string>;
243
+ getTransitiveDependencies(nodeId: string): Set<string>;
244
+ topologicalSort(): string[];
245
+ }
246
+
247
+ // ============================================================================
248
+ // Evaluation Results
249
+ // ============================================================================
250
+
251
+ export interface EvaluationResult {
252
+ value: unknown;
253
+ success: boolean;
254
+ error?: Error;
255
+ executionTimeMs: number;
256
+ accessedVariables: Set<string>;
257
+ }
258
+
259
+ export interface EvaluationResultSet {
260
+ results: Map<string, EvaluationResult>;
261
+ success: boolean;
262
+ errors: Error[];
263
+ totalExecutionTimeMs: number;
264
+ evaluationOrder: string[];
265
+ }
266
+
267
+ // ============================================================================
268
+ // Validation Result
269
+ // ============================================================================
270
+
271
+ export interface ValidationResult {
272
+ valid: boolean;
273
+ errors: Error[];
274
+ warnings: string[];
275
+ dependencyGraph: DependencyGraph;
276
+ evaluationOrder: string[];
277
+ }
278
+
279
+ // ============================================================================
280
+ // Cache Statistics
281
+ // ============================================================================
282
+
283
+ export interface CacheStats {
284
+ size: number;
285
+ hits: number;
286
+ misses: number;
287
+ hitRate: number;
288
+ }
289
+
290
+ // ============================================================================
291
+ // Token Types
292
+ // ============================================================================
293
+
294
+ export enum TokenType {
295
+ // Literals
296
+ NUMBER = 'NUMBER',
297
+ STRING = 'STRING',
298
+ BOOLEAN = 'BOOLEAN',
299
+ NULL = 'NULL',
300
+
301
+ // Identifiers and Variables
302
+ IDENTIFIER = 'IDENTIFIER',
303
+ VARIABLE = 'VARIABLE',
304
+ CONTEXT_VAR = 'CONTEXT_VAR',
305
+
306
+ // Operators
307
+ PLUS = 'PLUS',
308
+ MINUS = 'MINUS',
309
+ MULTIPLY = 'MULTIPLY',
310
+ DIVIDE = 'DIVIDE',
311
+ MODULO = 'MODULO',
312
+ POWER = 'POWER',
313
+
314
+ // Comparison
315
+ EQ = 'EQ',
316
+ NEQ = 'NEQ',
317
+ LT = 'LT',
318
+ GT = 'GT',
319
+ LTE = 'LTE',
320
+ GTE = 'GTE',
321
+
322
+ // Logical
323
+ AND = 'AND',
324
+ OR = 'OR',
325
+ NOT = 'NOT',
326
+
327
+ // Punctuation
328
+ LPAREN = 'LPAREN',
329
+ RPAREN = 'RPAREN',
330
+ LBRACKET = 'LBRACKET',
331
+ RBRACKET = 'RBRACKET',
332
+ COMMA = 'COMMA',
333
+ DOT = 'DOT',
334
+ QUESTION = 'QUESTION',
335
+ COLON = 'COLON',
336
+
337
+ // Special
338
+ EOF = 'EOF',
339
+ }
340
+
341
+ export interface Token {
342
+ type: TokenType;
343
+ value: string | number | boolean | null;
344
+ position: number;
345
+ line: number;
346
+ column: number;
347
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,29 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "commonjs",
5
+ "lib": ["ES2020"],
6
+ "declaration": true,
7
+ "strict": true,
8
+ "noImplicitAny": true,
9
+ "strictNullChecks": true,
10
+ "noImplicitThis": true,
11
+ "alwaysStrict": true,
12
+ "noUnusedLocals": false,
13
+ "noUnusedParameters": false,
14
+ "noImplicitReturns": true,
15
+ "noFallthroughCasesInSwitch": true,
16
+ "inlineSourceMap": true,
17
+ "inlineSources": true,
18
+ "experimentalDecorators": true,
19
+ "emitDecoratorMetadata": true,
20
+ "outDir": "./dist",
21
+ "rootDir": "./src",
22
+ "skipLibCheck": true,
23
+ "esModuleInterop": true,
24
+ "resolveJsonModule": true,
25
+ "moduleResolution": "node"
26
+ },
27
+ "include": ["src/**/*"],
28
+ "exclude": ["node_modules", "dist", "**/*.test.ts"]
29
+ }