@molecule/api-formula-engine 1.0.0 → 1.0.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/README.md +683 -0
- package/package.json +8 -2
package/README.md
ADDED
|
@@ -0,0 +1,683 @@
|
|
|
1
|
+
<!--
|
|
2
|
+
AUTO-GENERATED — DO NOT EDIT THIS FILE.
|
|
3
|
+
Generated by `mlcl sync-docs` from the package's src/index.ts JSDoc + mlcl/registry.json.
|
|
4
|
+
Edits here are overwritten on the next commit (molecule's pre-commit hook regenerates).
|
|
5
|
+
To change this document, edit the module-level JSDoc in src/index.ts.
|
|
6
|
+
Generated: 2026-08-04T00:36:31.815Z
|
|
7
|
+
-->
|
|
8
|
+
|
|
9
|
+
# @molecule/api-formula-engine
|
|
10
|
+
|
|
11
|
+
> **Auto-generated, AI-first package reference** for the [molecule.dev](https://molecule.dev) ecosystem.
|
|
12
|
+
> It is written to be read by coding agents as much as by people, and is generated from this
|
|
13
|
+
> package's source — edit `src/index.ts` JSDoc, not this file.
|
|
14
|
+
|
|
15
|
+
Spreadsheet-style formula engine for molecule.dev.
|
|
16
|
+
|
|
17
|
+
Pure-function parser + evaluator + dependency tracker for
|
|
18
|
+
A1-notation formulas (`=SUM(A1:B5)`, `=IF(A1>0, "y", "n")`, etc.).
|
|
19
|
+
Designed for the spreadsheet flagship app and for ad-hoc line-item
|
|
20
|
+
math (e.g. invoice/billing computed columns).
|
|
21
|
+
|
|
22
|
+
Capabilities:
|
|
23
|
+
|
|
24
|
+
- **Arithmetic**: `+ - * / % ^` (with Excel-compatible coercions).
|
|
25
|
+
- **Comparison**: `= <> < <= > >=` (numbers, strings, dates, booleans).
|
|
26
|
+
- **String**: `&` and `CONCAT()` / `CONCATENATE()`.
|
|
27
|
+
- **References**: `A1`, `$A$1`, `BC42`, ranges `A1:B5`.
|
|
28
|
+
- **Aggregations**: `SUM`, `AVERAGE`/`AVG`, `MIN`, `MAX`, `COUNT`,
|
|
29
|
+
`COUNTA`, `COUNTIF`, `SUMIF`.
|
|
30
|
+
- **Conditionals**: `IF`, `AND`, `OR`, `NOT`, `IFS`, `SWITCH`,
|
|
31
|
+
`IFERROR`, `ISERROR`, `ISNUMBER`, `ISBLANK`.
|
|
32
|
+
- **Math**: `ROUND`, `ABS`, `MOD`, `POWER`, `SQRT`, `INT`, `CEILING`,
|
|
33
|
+
`FLOOR`.
|
|
34
|
+
- **Dates**: `DATE`, `TODAY`, `NOW`, `YEAR`, `MONTH`, `DAY`,
|
|
35
|
+
`DATEDIFF`/`DATEDIF` (units: D/M/Y/H/MIN/S).
|
|
36
|
+
- **Text**: `LEFT`, `RIGHT`, `MID`, `LEN`, `TRIM`, `UPPER`, `LOWER`,
|
|
37
|
+
`SUBSTITUTE`.
|
|
38
|
+
- **Errors**: `#DIV/0!`, `#VALUE!`, `#REF!`, `#NAME?`, `#NUM!`,
|
|
39
|
+
`#N/A`, plus engine-emitted `#CIRC!` for circular references.
|
|
40
|
+
|
|
41
|
+
The `Sheet` class wraps the lower-level helpers and provides
|
|
42
|
+
incremental recompute (only the transitive dependents of a changed
|
|
43
|
+
cell are re-evaluated). Use `parseFormula` + `evaluate` directly for
|
|
44
|
+
stateless one-shot math.
|
|
45
|
+
|
|
46
|
+
Pure functions, zero I/O, zero DB access, zero hardcoded UI text.
|
|
47
|
+
|
|
48
|
+
## Quick Start
|
|
49
|
+
|
|
50
|
+
```typescript
|
|
51
|
+
import { Sheet, parseFormula, evaluate } from '@molecule/api-formula-engine'
|
|
52
|
+
|
|
53
|
+
// Stateful: a small in-memory spreadsheet.
|
|
54
|
+
const sheet = new Sheet()
|
|
55
|
+
sheet.setValue('A1', 10)
|
|
56
|
+
sheet.setValue('A2', 20)
|
|
57
|
+
sheet.setFormula('A3', '=SUM(A1:A2)')
|
|
58
|
+
sheet.getValue('A3') // → 30
|
|
59
|
+
|
|
60
|
+
sheet.setValue('A1', 100)
|
|
61
|
+
sheet.getValue('A3') // → 120 (auto-recomputed)
|
|
62
|
+
|
|
63
|
+
// Stateless: ad-hoc evaluation against a lookup function.
|
|
64
|
+
const ast = parseFormula('=A1 * 1.0875')
|
|
65
|
+
const lineTotal = evaluate(ast, (coord) => (coord.col === 0 && coord.row === 0 ? 49.99 : null))
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
## Type
|
|
69
|
+
|
|
70
|
+
`utility`
|
|
71
|
+
|
|
72
|
+
## Installation
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
npm install @molecule/api-formula-engine
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## API
|
|
79
|
+
|
|
80
|
+
### Interfaces
|
|
81
|
+
|
|
82
|
+
#### `BinaryOpNode`
|
|
83
|
+
|
|
84
|
+
AST: binary operation.
|
|
85
|
+
|
|
86
|
+
```typescript
|
|
87
|
+
interface BinaryOpNode {
|
|
88
|
+
readonly kind: 'binary'
|
|
89
|
+
readonly op: BinaryOperator
|
|
90
|
+
readonly left: AstNode
|
|
91
|
+
readonly right: AstNode
|
|
92
|
+
}
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
#### `BooleanLiteralNode`
|
|
96
|
+
|
|
97
|
+
AST: boolean literal (TRUE / FALSE).
|
|
98
|
+
|
|
99
|
+
```typescript
|
|
100
|
+
interface BooleanLiteralNode {
|
|
101
|
+
readonly kind: 'boolean'
|
|
102
|
+
readonly value: boolean
|
|
103
|
+
}
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
#### `CellCoord`
|
|
107
|
+
|
|
108
|
+
Cell coordinate as a 0-indexed `{ col, row }`. Column 0 = "A", row 0 = 1
|
|
109
|
+
(i.e. `A1` is `{ col: 0, row: 0 }`).
|
|
110
|
+
|
|
111
|
+
```typescript
|
|
112
|
+
interface CellCoord {
|
|
113
|
+
readonly col: number
|
|
114
|
+
readonly row: number
|
|
115
|
+
}
|
|
116
|
+
```
|
|
117
|
+
|
|
118
|
+
#### `CellRange`
|
|
119
|
+
|
|
120
|
+
A rectangular range of cells, half-inclusive of both corners.
|
|
121
|
+
|
|
122
|
+
```typescript
|
|
123
|
+
interface CellRange {
|
|
124
|
+
readonly start: CellCoord
|
|
125
|
+
readonly end: CellCoord
|
|
126
|
+
}
|
|
127
|
+
```
|
|
128
|
+
|
|
129
|
+
#### `ErrorLiteralNode`
|
|
130
|
+
|
|
131
|
+
AST: error literal (`#DIV/0!`, etc.).
|
|
132
|
+
|
|
133
|
+
```typescript
|
|
134
|
+
interface ErrorLiteralNode {
|
|
135
|
+
readonly kind: 'errorLiteral'
|
|
136
|
+
readonly code: FormulaErrorCode
|
|
137
|
+
}
|
|
138
|
+
```
|
|
139
|
+
|
|
140
|
+
#### `EvaluateOptions`
|
|
141
|
+
|
|
142
|
+
Options shared by `evaluate` and the `Sheet.recompute` family.
|
|
143
|
+
|
|
144
|
+
```typescript
|
|
145
|
+
interface EvaluateOptions {
|
|
146
|
+
/** Override the clock used by `TODAY()` / `NOW()`. Defaults to `Date.now()`. */
|
|
147
|
+
readonly now?: () => Date
|
|
148
|
+
}
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
#### `FormulaError`
|
|
152
|
+
|
|
153
|
+
Spreadsheet error value. Returned (never thrown) by `evaluate`/`getValue`
|
|
154
|
+
to allow downstream cells to propagate it like Excel.
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
interface FormulaError {
|
|
158
|
+
readonly type: 'error'
|
|
159
|
+
readonly code: FormulaErrorCode
|
|
160
|
+
readonly message?: string
|
|
161
|
+
}
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
#### `FunctionCallNode`
|
|
165
|
+
|
|
166
|
+
AST: function call (e.g. `SUM(A1:A10)`, `IF(A1>0, "y", "n")`).
|
|
167
|
+
|
|
168
|
+
```typescript
|
|
169
|
+
interface FunctionCallNode {
|
|
170
|
+
readonly kind: 'call'
|
|
171
|
+
readonly name: string
|
|
172
|
+
readonly args: AstNode[]
|
|
173
|
+
}
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
#### `FunctionContext`
|
|
177
|
+
|
|
178
|
+
Context handed to function implementations. Currently exposes the
|
|
179
|
+
`now` function so date/time helpers can be made deterministic for
|
|
180
|
+
tests via `evaluateOptions.now`.
|
|
181
|
+
|
|
182
|
+
```typescript
|
|
183
|
+
interface FunctionContext {
|
|
184
|
+
/** Returns the current date-time. */
|
|
185
|
+
readonly now: () => Date
|
|
186
|
+
}
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
#### `NumberLiteralNode`
|
|
190
|
+
|
|
191
|
+
AST: numeric literal.
|
|
192
|
+
|
|
193
|
+
```typescript
|
|
194
|
+
interface NumberLiteralNode {
|
|
195
|
+
readonly kind: 'number'
|
|
196
|
+
readonly value: number
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
#### `RangeNode`
|
|
201
|
+
|
|
202
|
+
AST: cell range (e.g. `A1:B5`).
|
|
203
|
+
|
|
204
|
+
```typescript
|
|
205
|
+
interface RangeNode {
|
|
206
|
+
readonly kind: 'range'
|
|
207
|
+
readonly range: CellRange
|
|
208
|
+
readonly text: string
|
|
209
|
+
}
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
#### `ReferenceNode`
|
|
213
|
+
|
|
214
|
+
AST: cell reference (e.g. `A1`, `$A$1`).
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
interface ReferenceNode {
|
|
218
|
+
readonly kind: 'reference'
|
|
219
|
+
readonly coord: CellCoord
|
|
220
|
+
/** Original textual representation, preserved for diagnostics. */
|
|
221
|
+
readonly text: string
|
|
222
|
+
}
|
|
223
|
+
```
|
|
224
|
+
|
|
225
|
+
#### `SheetConfig`
|
|
226
|
+
|
|
227
|
+
Sheet-level configuration. `numCols` / `numRows` are advisory bounds
|
|
228
|
+
used when validating references; out-of-bounds references produce
|
|
229
|
+
`#REF!`.
|
|
230
|
+
|
|
231
|
+
```typescript
|
|
232
|
+
interface SheetConfig {
|
|
233
|
+
/** Maximum number of columns. Default: 16384 (Excel's XFD). */
|
|
234
|
+
readonly numCols?: number
|
|
235
|
+
/** Maximum number of rows. Default: 1048576 (Excel's row count). */
|
|
236
|
+
readonly numRows?: number
|
|
237
|
+
}
|
|
238
|
+
```
|
|
239
|
+
|
|
240
|
+
#### `StringLiteralNode`
|
|
241
|
+
|
|
242
|
+
AST: string literal (double-quoted).
|
|
243
|
+
|
|
244
|
+
```typescript
|
|
245
|
+
interface StringLiteralNode {
|
|
246
|
+
readonly kind: 'string'
|
|
247
|
+
readonly value: string
|
|
248
|
+
}
|
|
249
|
+
```
|
|
250
|
+
|
|
251
|
+
#### `Token`
|
|
252
|
+
|
|
253
|
+
Lexical token. `value` is the raw source text; `op` carries the
|
|
254
|
+
normalized operator string for `kind: 'op'` tokens.
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
interface Token {
|
|
258
|
+
readonly kind: TokenKind
|
|
259
|
+
readonly value: string
|
|
260
|
+
readonly position: number
|
|
261
|
+
readonly errorCode?: FormulaErrorCode
|
|
262
|
+
}
|
|
263
|
+
```
|
|
264
|
+
|
|
265
|
+
#### `UnaryOpNode`
|
|
266
|
+
|
|
267
|
+
AST: unary operation (e.g. `-A1`, `A1%`).
|
|
268
|
+
|
|
269
|
+
```typescript
|
|
270
|
+
interface UnaryOpNode {
|
|
271
|
+
readonly kind: 'unary'
|
|
272
|
+
readonly op: UnaryOperator
|
|
273
|
+
readonly operand: AstNode
|
|
274
|
+
}
|
|
275
|
+
```
|
|
276
|
+
|
|
277
|
+
### Types
|
|
278
|
+
|
|
279
|
+
#### `AstNode`
|
|
280
|
+
|
|
281
|
+
AST node kinds emitted by the parser.
|
|
282
|
+
|
|
283
|
+
```typescript
|
|
284
|
+
type AstNode =
|
|
285
|
+
| NumberLiteralNode
|
|
286
|
+
| StringLiteralNode
|
|
287
|
+
| BooleanLiteralNode
|
|
288
|
+
| ErrorLiteralNode
|
|
289
|
+
| ReferenceNode
|
|
290
|
+
| RangeNode
|
|
291
|
+
| UnaryOpNode
|
|
292
|
+
| BinaryOpNode
|
|
293
|
+
| FunctionCallNode
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
#### `BinaryOperator`
|
|
297
|
+
|
|
298
|
+
Binary operators. `^` = power, `&` = string concat,
|
|
299
|
+
`=`/`<>` = equality, `<`/`<=`/`>`/`>=` = ordering.
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
type BinaryOperator = '+' | '-' | '*' | '/' | '^' | '&' | '=' | '<>' | '<' | '<=' | '>' | '>='
|
|
303
|
+
```
|
|
304
|
+
|
|
305
|
+
#### `CellValue`
|
|
306
|
+
|
|
307
|
+
Possible cell values. `null` represents an empty cell; numbers, strings,
|
|
308
|
+
booleans, and `Date`s are all valid scalar values. `FormulaError`
|
|
309
|
+
represents a propagating error.
|
|
310
|
+
|
|
311
|
+
```typescript
|
|
312
|
+
type CellValue = number | string | boolean | Date | null | FormulaError
|
|
313
|
+
```
|
|
314
|
+
|
|
315
|
+
#### `FormulaErrorCode`
|
|
316
|
+
|
|
317
|
+
Spreadsheet error codes. Errors propagate through expressions: any
|
|
318
|
+
arithmetic or function call involving a `FormulaError` returns the
|
|
319
|
+
left-most error unchanged (Excel-compatible semantics).
|
|
320
|
+
|
|
321
|
+
```typescript
|
|
322
|
+
type FormulaErrorCode = '#DIV/0!' | '#VALUE!' | '#REF!' | '#NAME?' | '#NUM!' | '#N/A' | '#CIRC!'
|
|
323
|
+
```
|
|
324
|
+
|
|
325
|
+
#### `FormulaFunction`
|
|
326
|
+
|
|
327
|
+
Function implementation signature. Functions receive already-evaluated
|
|
328
|
+
argument expressions: ranges are expanded into flat `CellValue[]`
|
|
329
|
+
arrays. Functions return a `CellValue` (including `FormulaError` for
|
|
330
|
+
domain errors).
|
|
331
|
+
|
|
332
|
+
```typescript
|
|
333
|
+
type FormulaFunction = (
|
|
334
|
+
args: ReadonlyArray<CellValue | ReadonlyArray<CellValue>>,
|
|
335
|
+
ctx: FunctionContext,
|
|
336
|
+
) => CellValue
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
#### `ResolveCell`
|
|
340
|
+
|
|
341
|
+
Function used to resolve a cell coordinate to its value. Returns
|
|
342
|
+
`null` for empty cells. Implementations may evaluate dependent
|
|
343
|
+
formulas (in which case they should detect cycles and return a
|
|
344
|
+
`#CIRC!` error to the caller).
|
|
345
|
+
|
|
346
|
+
```typescript
|
|
347
|
+
type ResolveCell = (coord: CellCoord) => CellValue
|
|
348
|
+
```
|
|
349
|
+
|
|
350
|
+
#### `TokenKind`
|
|
351
|
+
|
|
352
|
+
Lexical token kinds.
|
|
353
|
+
|
|
354
|
+
```typescript
|
|
355
|
+
type TokenKind =
|
|
356
|
+
| 'number'
|
|
357
|
+
| 'string'
|
|
358
|
+
| 'boolean'
|
|
359
|
+
| 'error'
|
|
360
|
+
| 'identifier'
|
|
361
|
+
| 'reference'
|
|
362
|
+
| 'range'
|
|
363
|
+
| 'op'
|
|
364
|
+
| 'lparen'
|
|
365
|
+
| 'rparen'
|
|
366
|
+
| 'comma'
|
|
367
|
+
```
|
|
368
|
+
|
|
369
|
+
#### `UnaryOperator`
|
|
370
|
+
|
|
371
|
+
Unary operators supported by the parser.
|
|
372
|
+
|
|
373
|
+
```typescript
|
|
374
|
+
type UnaryOperator = '+' | '-' | '%'
|
|
375
|
+
```
|
|
376
|
+
|
|
377
|
+
### Classes
|
|
378
|
+
|
|
379
|
+
#### `Sheet`
|
|
380
|
+
|
|
381
|
+
In-memory single-sheet engine. References use A1 notation (`A1`,
|
|
382
|
+
`$A$1`, `BC42`).
|
|
383
|
+
|
|
384
|
+
### Functions
|
|
385
|
+
|
|
386
|
+
#### `collectReferences(node)`
|
|
387
|
+
|
|
388
|
+
Walk an AST and collect every coordinate it references (single
|
|
389
|
+
references and every cell inside ranges).
|
|
390
|
+
|
|
391
|
+
```typescript
|
|
392
|
+
function collectReferences(node: AstNode): CellCoord[]
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
- `node` — Root AST node.
|
|
396
|
+
|
|
397
|
+
**Returns:** Flat list of referenced coordinates (may contain duplicates).
|
|
398
|
+
|
|
399
|
+
#### `columnIndexToLetters(index)`
|
|
400
|
+
|
|
401
|
+
Convert a 0-indexed column to letters (`0`→`A`, `25`→`Z`, `26`→`AA`).
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
function columnIndexToLetters(index: number): string
|
|
405
|
+
```
|
|
406
|
+
|
|
407
|
+
- `index` — 0-indexed column.
|
|
408
|
+
|
|
409
|
+
**Returns:** Column letters (uppercase).
|
|
410
|
+
|
|
411
|
+
#### `columnLettersToIndex(letters)`
|
|
412
|
+
|
|
413
|
+
Convert a column letter (`A`, `Z`, `AA`, `XFD`) to a 0-indexed column.
|
|
414
|
+
|
|
415
|
+
```typescript
|
|
416
|
+
function columnLettersToIndex(letters: string): number
|
|
417
|
+
```
|
|
418
|
+
|
|
419
|
+
- `letters` — Column letters (case-insensitive).
|
|
420
|
+
|
|
421
|
+
**Returns:** 0-indexed column number.
|
|
422
|
+
|
|
423
|
+
#### `compareValues(left, right)`
|
|
424
|
+
|
|
425
|
+
Compare two scalar cell values (Excel ordering: number < string,
|
|
426
|
+
boolean compared as 0/1 within numbers).
|
|
427
|
+
|
|
428
|
+
```typescript
|
|
429
|
+
function compareValues(left: CellValue, right: CellValue): number | FormulaError
|
|
430
|
+
```
|
|
431
|
+
|
|
432
|
+
- `left` — Left operand.
|
|
433
|
+
- `right` — Right operand.
|
|
434
|
+
|
|
435
|
+
**Returns:** -1, 0, 1, or `FormulaError` if uncomparable.
|
|
436
|
+
|
|
437
|
+
#### `coordKey(coord)`
|
|
438
|
+
|
|
439
|
+
Encode a coordinate to a cache-friendly string key (`"col,row"`).
|
|
440
|
+
|
|
441
|
+
```typescript
|
|
442
|
+
function coordKey(coord: CellCoord): string
|
|
443
|
+
```
|
|
444
|
+
|
|
445
|
+
- `coord` — Cell coordinate.
|
|
446
|
+
|
|
447
|
+
**Returns:** Stable string key.
|
|
448
|
+
|
|
449
|
+
#### `dateToSerial(date)`
|
|
450
|
+
|
|
451
|
+
Convert a `Date` to an Excel serial number (days since 1899-12-30).
|
|
452
|
+
|
|
453
|
+
```typescript
|
|
454
|
+
function dateToSerial(date: Date): number
|
|
455
|
+
```
|
|
456
|
+
|
|
457
|
+
- `date` — Date instance.
|
|
458
|
+
|
|
459
|
+
**Returns:** Excel serial date.
|
|
460
|
+
|
|
461
|
+
#### `evaluate(node, resolveCell, options)`
|
|
462
|
+
|
|
463
|
+
Evaluate an AST in the context of a cell-resolution function.
|
|
464
|
+
|
|
465
|
+
```typescript
|
|
466
|
+
function evaluate(node: AstNode, resolveCell: ResolveCell, options?: EvaluateOptions): CellValue
|
|
467
|
+
```
|
|
468
|
+
|
|
469
|
+
- `node` — Root AST node.
|
|
470
|
+
- `resolveCell` — Function returning the value of a referenced cell.
|
|
471
|
+
- `options` — Evaluation options.
|
|
472
|
+
|
|
473
|
+
**Returns:** Evaluated cell value (errors are returned, not thrown).
|
|
474
|
+
|
|
475
|
+
#### `firstError(values)`
|
|
476
|
+
|
|
477
|
+
Returns the first error encountered in a flat list, or `null` if none.
|
|
478
|
+
|
|
479
|
+
```typescript
|
|
480
|
+
function firstError(values: readonly CellValue[]): FormulaError | null
|
|
481
|
+
```
|
|
482
|
+
|
|
483
|
+
- `values` — Cell values to scan.
|
|
484
|
+
|
|
485
|
+
**Returns:** The first error value, or `null`.
|
|
486
|
+
|
|
487
|
+
#### `formatCellReference(coord)`
|
|
488
|
+
|
|
489
|
+
Format a cell coordinate as A1-style text (`{col:0,row:0}` → `A1`).
|
|
490
|
+
|
|
491
|
+
```typescript
|
|
492
|
+
function formatCellReference(coord: CellCoord): string
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
- `coord` — Cell coordinate.
|
|
496
|
+
|
|
497
|
+
**Returns:** A1-style reference text.
|
|
498
|
+
|
|
499
|
+
#### `isError(value)`
|
|
500
|
+
|
|
501
|
+
Type-guard for `FormulaError`.
|
|
502
|
+
|
|
503
|
+
```typescript
|
|
504
|
+
function isError(value: unknown): boolean
|
|
505
|
+
```
|
|
506
|
+
|
|
507
|
+
- `value` — Cell value to inspect.
|
|
508
|
+
|
|
509
|
+
**Returns:** `true` if the value is a propagating error.
|
|
510
|
+
|
|
511
|
+
#### `iterateRange(range)`
|
|
512
|
+
|
|
513
|
+
Iterate every coordinate in a range. Yields top-to-bottom, left-to-right.
|
|
514
|
+
|
|
515
|
+
```typescript
|
|
516
|
+
function iterateRange(range: CellRange): Generator<CellCoord, any, any>
|
|
517
|
+
```
|
|
518
|
+
|
|
519
|
+
- `range` — Cell range.
|
|
520
|
+
|
|
521
|
+
#### `makeError(code, message)`
|
|
522
|
+
|
|
523
|
+
Construct a `FormulaError` value.
|
|
524
|
+
|
|
525
|
+
```typescript
|
|
526
|
+
function makeError(code: FormulaErrorCode, message?: string): FormulaError
|
|
527
|
+
```
|
|
528
|
+
|
|
529
|
+
- `code` — Error code.
|
|
530
|
+
- `message` — Optional human-readable description.
|
|
531
|
+
|
|
532
|
+
**Returns:** The error value.
|
|
533
|
+
|
|
534
|
+
#### `parseCellRange(ref)`
|
|
535
|
+
|
|
536
|
+
Parse a cell range (e.g. `A1:B5`). Normalizes so `start` is top-left
|
|
537
|
+
and `end` is bottom-right regardless of input ordering.
|
|
538
|
+
|
|
539
|
+
```typescript
|
|
540
|
+
function parseCellRange(ref: string): CellRange | null
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
- `ref` — Range text.
|
|
544
|
+
|
|
545
|
+
**Returns:** Parsed range, or `null` if invalid.
|
|
546
|
+
|
|
547
|
+
#### `parseCellReference(ref)`
|
|
548
|
+
|
|
549
|
+
Parse a single cell reference (e.g. `A1`, `$A$1`, `BC42`).
|
|
550
|
+
|
|
551
|
+
```typescript
|
|
552
|
+
function parseCellReference(ref: string): CellCoord | null
|
|
553
|
+
```
|
|
554
|
+
|
|
555
|
+
- `ref` — Reference text.
|
|
556
|
+
|
|
557
|
+
**Returns:** Parsed cell coordinate, or `null` if invalid.
|
|
558
|
+
|
|
559
|
+
#### `parseFormula(input)`
|
|
560
|
+
|
|
561
|
+
Parse a formula string into an AST.
|
|
562
|
+
|
|
563
|
+
```typescript
|
|
564
|
+
function parseFormula(input: string): AstNode
|
|
565
|
+
```
|
|
566
|
+
|
|
567
|
+
- `input` — Formula text (with or without a leading `=`).
|
|
568
|
+
|
|
569
|
+
**Returns:** Root AST node.
|
|
570
|
+
|
|
571
|
+
#### `refOf(coord)`
|
|
572
|
+
|
|
573
|
+
Reformat a cell coordinate as A1-style text. Re-exported for
|
|
574
|
+
convenience.
|
|
575
|
+
|
|
576
|
+
```typescript
|
|
577
|
+
function refOf(coord: CellCoord): string
|
|
578
|
+
```
|
|
579
|
+
|
|
580
|
+
- `coord` — Cell coordinate.
|
|
581
|
+
|
|
582
|
+
**Returns:** A1-style reference text.
|
|
583
|
+
|
|
584
|
+
#### `serialToDate(serial)`
|
|
585
|
+
|
|
586
|
+
Convert an Excel serial number to a `Date` (UTC).
|
|
587
|
+
|
|
588
|
+
```typescript
|
|
589
|
+
function serialToDate(serial: number): Date
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
- `serial` — Excel serial date.
|
|
593
|
+
|
|
594
|
+
**Returns:** Date instance.
|
|
595
|
+
|
|
596
|
+
#### `toBoolean(value)`
|
|
597
|
+
|
|
598
|
+
Coerce a cell value to a boolean (Excel-compatible).
|
|
599
|
+
|
|
600
|
+
```typescript
|
|
601
|
+
function toBoolean(value: CellValue): boolean | FormulaError
|
|
602
|
+
```
|
|
603
|
+
|
|
604
|
+
- `value` — Cell value.
|
|
605
|
+
|
|
606
|
+
**Returns:** Boolean, or `FormulaError` on failure.
|
|
607
|
+
|
|
608
|
+
#### `tokenize(input)`
|
|
609
|
+
|
|
610
|
+
Tokenize a formula string. The leading `=` (if present) is stripped
|
|
611
|
+
by the caller — pass the _body_ of the formula here.
|
|
612
|
+
|
|
613
|
+
```typescript
|
|
614
|
+
function tokenize(input: string): Token[]
|
|
615
|
+
```
|
|
616
|
+
|
|
617
|
+
- `input` — Raw formula text (no leading `=`).
|
|
618
|
+
|
|
619
|
+
**Returns:** Flat token list.
|
|
620
|
+
|
|
621
|
+
#### `toNumber(value)`
|
|
622
|
+
|
|
623
|
+
Coerce a cell value to a number (Excel-compatible).
|
|
624
|
+
|
|
625
|
+
- `null` → 0
|
|
626
|
+
- boolean → 0/1
|
|
627
|
+
- `Date` → days since 1899-12-30 (Excel's epoch)
|
|
628
|
+
- string → parsed as number, `#VALUE!` if not parseable
|
|
629
|
+
- error → propagated unchanged
|
|
630
|
+
|
|
631
|
+
```typescript
|
|
632
|
+
function toNumber(value: CellValue): number | FormulaError
|
|
633
|
+
```
|
|
634
|
+
|
|
635
|
+
- `value` — Cell value.
|
|
636
|
+
|
|
637
|
+
**Returns:** Number, or `FormulaError` on failure.
|
|
638
|
+
|
|
639
|
+
#### `topologicalSort(dependents, keys)`
|
|
640
|
+
|
|
641
|
+
Topologically sort a set of coordinate keys by their dependency graph
|
|
642
|
+
such that, for any edge `a → b` (b depends on a), `a` precedes `b`
|
|
643
|
+
in the result.
|
|
644
|
+
|
|
645
|
+
Uses Kahn's algorithm. If a cycle is detected, the cycle members are
|
|
646
|
+
returned in `cycle` and excluded from `order` — callers typically
|
|
647
|
+
mark cycle members with a `#CIRC!` error.
|
|
648
|
+
|
|
649
|
+
```typescript
|
|
650
|
+
function topologicalSort(
|
|
651
|
+
dependents: ReadonlyMap<string, ReadonlySet<string>>,
|
|
652
|
+
keys: readonly string[],
|
|
653
|
+
): { order: string[]; cycle: Set<string> }
|
|
654
|
+
```
|
|
655
|
+
|
|
656
|
+
- `dependents` — Map from a key to the set of keys that depend on it. (i.e. forward edges in the recompute order.)
|
|
657
|
+
- `keys` — Keys to sort.
|
|
658
|
+
|
|
659
|
+
**Returns:** `{ order, cycle }` — `order` is the topological order; `cycle` is the set of keys participating in (or downstream of) a cycle.
|
|
660
|
+
|
|
661
|
+
#### `toStringValue(value)`
|
|
662
|
+
|
|
663
|
+
Coerce a cell value to a string for display / `&` concatenation.
|
|
664
|
+
|
|
665
|
+
```typescript
|
|
666
|
+
function toStringValue(value: CellValue): string | FormulaError
|
|
667
|
+
```
|
|
668
|
+
|
|
669
|
+
- `value` — Cell value.
|
|
670
|
+
|
|
671
|
+
**Returns:** String, or `FormulaError` for propagating errors.
|
|
672
|
+
|
|
673
|
+
### Constants
|
|
674
|
+
|
|
675
|
+
#### `BUILTIN_FUNCTIONS`
|
|
676
|
+
|
|
677
|
+
Map of function names (uppercase) to implementations. Aliases like
|
|
678
|
+
`AVG → AVERAGE` and `DATEDIF → DATEDIFF` map to the same function
|
|
679
|
+
reference.
|
|
680
|
+
|
|
681
|
+
```typescript
|
|
682
|
+
const BUILTIN_FUNCTIONS: Readonly<Record<string, FormulaFunction>>
|
|
683
|
+
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@molecule/api-formula-engine",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Spreadsheet-style formula evaluator: parser, evaluator, dependency tracker for cell graphs with arithmetic, comparison, string ops, references, aggregations, conditionals, date/text functions, and circular-ref detection.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -17,7 +17,8 @@
|
|
|
17
17
|
}
|
|
18
18
|
},
|
|
19
19
|
"files": [
|
|
20
|
-
"dist"
|
|
20
|
+
"dist",
|
|
21
|
+
"README.md"
|
|
21
22
|
],
|
|
22
23
|
"keywords": [
|
|
23
24
|
"molecule",
|
|
@@ -25,6 +26,11 @@
|
|
|
25
26
|
"engine"
|
|
26
27
|
],
|
|
27
28
|
"license": "Apache-2.0",
|
|
29
|
+
"repository": {
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/molecule-dev/molecule.git",
|
|
32
|
+
"directory": "packages/api/utilities/formula-engine"
|
|
33
|
+
},
|
|
28
34
|
"devDependencies": {
|
|
29
35
|
"@types/node": "26.1.2",
|
|
30
36
|
"typescript": "6.0.3",
|