Skip to content

Package Surfaces ​

SurfacePackageRuntime
JavaScript / WASM@libraz/formulonBrowser, worker, Node
Native Node@libraz/formulon-nativeNode.js N-API addon. Not published to npm; build it from a source checkout (Native Node). Release CI builds darwin-arm64, linux-x64, and linux-arm64
Pythonformulonpy3-none-any wheel run by wasmtime
CLIformulon-<version>-<os>-<arch>.tar.gz on GitHub ReleasesStandalone formulon binary for darwin-arm64, linux-x64, linux-arm64
C ABIsrc/c_api/formulon_c.h + formulon_static (or shared formulon)Custom hosts, built from source
MCP@libraz/formulon-mcpstdio MCP server for agents
Spreadsheet UI@libraz/formulon-cellBrowser spreadsheet UI kit

All surfaces use the same calculation core. They differ in packaging, host language, memory ownership, and the method differences listed below.

CoreC ABISurfacesBuilt on WASMC++17 calculation coreshared by every surfaceformulon_c.h + core libraryreached directly or transitively@libraz/formulonWASM · browser,worker, Node@libraz/formulon-nativeNative Node, builtfrom sourceformulon (Python)py3-none-any wheel onwasmtimeformulon (CLI)standalone binaryformulon-cellbrowser spreadsheet UI kitformulon-mcpstdio MCP server for agentsCoreC ABISurfacesBuilt on WASMC++17 calculation coreshared by every surfaceformulon_c.h + core libraryreached directly or transitively@libraz/formulonWASM · browser, worker, Node@libraz/formulon-nativeNative Node, built from sourceformulon (Python)py3-none-any wheel on wasmtimeformulon (CLI)standalone binaryformulon-cellbrowser spreadsheet UI kitformulon-mcpstdio MCP server for agents
Every surface shares one calculation core; formulon-cell and formulon-mcp build on the WASM package instead of binding the C ABI themselves.

formulon-cell and formulon-mcp are built specifically on the WASM package, not directly on the C ABI — they reach the calculation core the same way any browser or Node consumer of @libraz/formulon does.

Glossary: surface

A packaging boundary on top of the shared C++17 engine. Every surface speaks to the engine through the C ABI (directly or transitively). Host language, memory ownership, IO style, and documented compatibility boundaries can differ between surfaces; the shared core defines the common formula behavior.

Surface maturity ​

SurfaceMaturityNotes
WASMbroadest JS APIGenerated formulon.d.ts; browser, worker, and Node
Pythonbroad workbook APIwasmtime-backed wrapper; with block releases the workbook
CLIfocused toolseval, recalc, dump, paginate
Native NodeWASM method set, native binarySame result envelopes and value semantics as WASM, minus the rows marked below
C ABIbinding contractEvery entry point the other surfaces wrap
MCPagent-facing surfaceBuilt on top of WASM; allowlisted method dispatch
formulon-cellUI kitEmbeddable spreadsheet surface with TypeScript, React, and Vue entrypoints

Method differences ​

The three library bindings share the workbook model, geometry, themes and styles, typed AutoFilters, validation, comments, images, PivotTables, print settings, and profile helpers. These are the rows where they differ:

CapabilityWASMNative NodePython
Release a workbookdelete(); throws TypeError during an active method calldispose() (GC finalizer as fallback); throws TypeError during an active method callclose() or a with block
Memory estimate—memoryUsage()memory_usage()
Parallel recalcrecalcParallel(); workers only on @libraz/formulon/threadsrecalcParallel()—
Scalar read-only evaluationevaluateFormulaText()evaluateFormulaText()— (use evaluate_formula_array())
Iterative-progress callbacksetIterativeProgress()setIterativeProgress()—
Log sinksetLogSink()setLogSink()— (set_log_min_level() only)
Table authoringcreateTable() / updateTable() / removeTable()—table_create() / table_update() / table_remove()
Worksheet AutoFilter XMLgetSheetAutoFilterXml() / setSheetAutoFilterXml()—get_auto_filter_xml() / set_auto_filter_xml()
Named-style XF recordsaddCellStyleXf()—add_cell_style_xf()
Bulk style registration, enable-only iterative toggle——add_batch(), set_iterative_enabled()

Argument contracts are the same across the bindings. Both JS packages check every positional argument before any engine call: a missing argument, a wrong type, or a primitive or null where an object is declared throws TypeError, and neither package substitutes defaults. A malformed argument that a method reports through its status returns kInvalidArgument (2) on both. The @libraz/formulon-native TypeScript declarations use the same enum types as @libraz/formulon (PivotAxis, PivotAggregation, PivotSortOrder, and the rest), so code written against one package's types compiles against the other's. Python validates Value payloads (ValueError or TypeError) and range-checks integer arguments (ValueError). The C ABI rejects ill-formed UTF-8 with kBindingUtf8EncodingError (7002) and range-checks index, count, and outline-level arguments; every binding surfaces those statuses. Public C records have fixed layouts, so a native client must be recompiled against the header it links.

The CLI exposes a fixed subset: --threads for parallel recalc, --iterative, and the four subcommands.

When surfaces disagree ​

If two surfaces produce different values for the same workbook and the same profile, treat it as a bug or a documented compatibility gap. The repository's make parity-test runner evaluates shared fixtures through the CLI, the npm WASM package, and the Python wheel, and reports channels that were not built separately from mismatched values. Other differences are operational, such as native threads, copy costs, and the WASM memory ceiling.