Skip to content

Model

Central interface for managing and manipulating spreadsheet data in memory.

The Model class represents a collection of workbooks and provides a unified API for reading, writing, and calculating cell values across all workbooks. It manages workbook dependency graphs to ensure accurate recalculation when values change, and handles formula evaluation with support for cross-workbook references.

// Wait for formula parser to load before using models
await Model.preconditions;
// Create a model from JSF (recommended)
const parsedJSF = JSON.parse(jsf);
const model = Model.fromJSF(parsedJSF);
// Alternatively, create from CSF (for backward compatibility)
const parsedCSF = JSON.parse(csf);
const model = Model.fromCsf(parsedCSF);
// Write primitive values to cells
model.write('A1', "Hello, World!");
model.write('A2', 42);
model.write('A3', true);
// Read cell values
model.readValue("=A1");
model.readValue("=Sheet1!A1"); // Read from a specific sheet
model.readValue("=[budget.xlsx]Sheet1!A1"); // Read from a specific workbook
// Work with multiple workbooks
model.addWorkbook(anotherWorkbook);
model.write('Sheet1!A1', 100); // Write to specific sheet
model.write('[another_workbook.xlsx]Sheet1!A1', 200); // Write to specific workbook
  • Lazy recalculation: only changed cells and their dependents are recalculated
  • Batch writes: Use writeMultiple() for better performance when writing many cells
  • Functions may be volatile: functions like NOW() and RAND() recalculate every time

Some methods require the Model.preconditions promise to be resolved before use. This ensures the formula parser, a WebAssembly module, has finished loading. Always await this before calling static factory methods or methods marked with this precondition in their docs.

  • emitter

new Model(): Model

Model

EventEmitter.constructor

coerceNullToZero: CoercionMode = COERCE_NONE

EvaluationContext.coerceNullToZero


env: Map<"isPrint" | "isMobile" | "username", MaybeBoxedFormulaArgument>

Information about outside environment relevant to the model and functions. Writable from the outside, but functions get only read access to it.

EvaluationContext.env


evaluateAST: (ast, options) => MaybeBoxedFormulaValue

Evaluate a formula in the form of an AST, in a given evaluation context. This wraps the evaluateAST function as a Model method, just to enable calling it via evaluation context to dodge circular imports.

ASTNode

EvaluationContext

MaybeBoxedFormulaValue

EvaluationContext.evaluateAST


evaluateASTNode: FnEvaluateASTNode

Evaluate an AST node. This wraps the evaluateASTNodeUnbound function as a Model method, just to enable calling it via evaluation context to dodge circular imports.

EvaluationContext.evaluateASTNode


getEntities: (this) => ModelEntity[]

Returns a list of defined names and tables in the Model.

No guarantees are made about the order of the entities.

Model

ModelEntity[]


getGlobal: (this, name, workbookName?) => FormulaError | DefinedName

Model

string

null | string

FormulaError | DefinedName

the cell object for the given defined-name, or a #NAME? error if not found


getTable: (this, name, workbookName) => null | Table

Model

string

undefined | null | string

null | Table


getWorkbook: (this, name?) => undefined | Workbook

Get the workbook with the given name (case-insensitive), or undefined if no such workbook is in the model.

Model

null | string

undefined | Workbook


getWorkbookByKey: (this, key) => undefined | Workbook

Get the workbook with the given dependency-graph key (based uniquely on the workbook name in a case-insensitive way by the wbNameToKey function).

Get workbook with the given keyInDepGraph

Model

number

undefined | Workbook

EvaluationContext.getWorkbookByKey


instanceId: number


lastWrite: number = 0


mode: ModeBit = MODE_GRID_SHEET

EvaluationContext.mode


profile: null | Profile = null

Profiling information

EvaluationContext.profile


resolveName: (this, name, sheetName?) => FormulaError | DefinedName

Get the cell object for the given defined name (matched case-insensitively) in the indicated scope or in this context. If sheetName is given, then only sheet-scoped defined names will be considered, not workbook-scoped defined names. If this context permits cross-workbook name resolution, then a match in the current workbook will be preferred but if no match is found there, then the first match across all workbooks in model order will be returned. If no match is found, #NAME? is returned.

Model

string

null | string

FormulaError | DefinedName

EvaluationContext.resolveName


resolveSheet: (this, sheetName?, workbookName?) => null | WorkSheet

Get the sheet with the given name (matched case-insensitively), or the first sheet of the context workbook if no name is given. The context workbook is the default (first) workbook of the model if not evaluating in the context of a specific workbook.

Model

null | string

null | string

null | WorkSheet

EvaluationContext.resolveSheet


resolveTable: (this, name, workbookName) => null | Table

Get the table with the given name (matched case-insensitively) in the context workbook, or if not found there, then in the first workbook in the model that contains a table by this name.

Model

string

undefined | null | string

null | Table

EvaluationContext.resolveTable


resolveWorkbook: (this, name?) => undefined | Workbook

Get the workbook with the given case-insensitive name, or if no name is given, the context workbook, or the default (first) workbook of the model if not evaluating in the context of a specific workbook.

Get the workbook with the given name (case-insensitive), or undefined if no such workbook is in the model.

Model

null | string

undefined | Workbook

EvaluationContext.resolveWorkbook


subscribers: never[] = []


readonly workbookName: null = null

Name of the workbook containing the reference or formula, null for a GRID element formula

EvaluationContext.workbookName


writeState: (this, includeOverwrittenCells) => ModelStateTree

Returns an object tree of all current writes to the model. The outermost object’s keys will be workbook names, second level will be sheet names, and third level will be cell IDs or names with the written values as values:

{
"myworkbook": {
"sheet1": {
"A1": 1234
}
}
}

By default, writes that would not produce the same state are omitted from the output. These are writes to formula cells that have been overwritten again by the recalculation process. Re-applying them to a reset model would not reliably produce the same state.

Model

boolean = false

include writes to formula cells that have been overwritten by recalculation.

ModelStateTree

EvaluationContext.writeState


static preconditions: Promise<{ parse: FnParseFormula; replaceRefsOnDelete: ReplaceRefsOnDeleteFn; replaceRefsOnMove: ReplaceRefsOnMoveFn; replaceSheetReferences: ReplaceSheetReferencesFn; replaceWorkbookReferences: ReplaceWorkbookReferencesFn; }> = formulaParserReady

Promise that should be awaited before calling certain methods of Model (noted with a precondition in the documentation of those methods).

get defaultWorkbookName(): string

string


get defect(): undefined | null | string

undefined | null | string


get errors(): ModelError[]

ModelError[]


get hasData(): boolean

boolean


get lazyImportPromise(): Promise<void>

Promise<void>


get meta(): ModelMeta

ModelMeta


get ready(): boolean

boolean


get volatiles(): readonly (CellVertexId | NameVertexId)[]

readonly (CellVertexId | NameVertexId)[]

_attachWorkbook(wb, options): void

Legacy internal-looking name for the now-public method attachWorkbook

Workbook

AddWorkbookOptions = {}

void


addError(modelError): ModelError

ModelError

ModelError


addWorkbook(data, options): Workbook

Construct a workbook from the given JSF and then attach it to this model.

This is just a convenience wrapper calling Workbook.fromJsf and attachWorkbook.

If your workbook source is in CSF format, consider using Model.fromCsf instead, or if you have multiple CSF workbooks, use Workbook.fromCsf and attachWorkbook.

JSF

The workbook data in JSF format

AddWorkbookOptions = {}

Configuration options for the new workbook

Workbook

The newly created Workbook instance that was added to the model

The Model.preconditions promise must be resolved before calling this method

Error if data is invalid in some way

Error if a non-external workbook with the same name already exists in the model

Error if precondition is not satisfied (the formula parser has not finished importing)

// Add multiple related workbooks
const model = new Model();
model.addWorkbook(budget2024jsf); // From e.g. budget2024.xlsx
model.addWorkbook(budget2025jsf); // From e.g. budget2025.xlsx

addWorkbookFromXlsx(data, filename, options?): Promise<Workbook>

Add a workbook from XLSX binary data.

This is a convenience method that wraps @borgar/xlsx-convert to load XLSX data directly into the model. External references in the XLSX are also added to the model.

Binary XLSX data (works in browsers and Node.js)

ArrayBuffer | Buffer<ArrayBufferLike> | Uint8Array<ArrayBufferLike>

string

Filename to associate with the workbook

AddWorkbookOptions & object

Options for workbook loading

Promise<Workbook>

A Promise resolving to the main Workbook instance


addWorkbookFromXlsxFile(path, options?): Promise<Workbook>

Add a workbook from an XLSX file path.

This is a convenience method that wraps @borgar/xlsx-convert to load XLSX files directly into the model. External references in the XLSX are also added to the model.

string

Path to the XLSX file (Node.js only - uses fs)

AddWorkbookOptions & object

Options for workbook loading

Promise<Workbook>

A Promise resolving to the main Workbook instance

await Model.preconditions;
const model = new Model();
const budget2024 = await model.addWorkbookFromXlsxFile('budget2024.xlsx');
const budget2025 = await model.addWorkbookFromXlsxFile('budget2025.xlsx');

analyzeAndFixFormula(formula, options?): AnalyzeFormulaResult

string

string

null | string

AnalyzeFormulaResult


analyzeFormula(formula, options?): AnalyzeFormulaResult

string

string

null | string

AnalyzeFormulaResult


attachWorkbook(wb, options): void

Add an additional workbook to this model.

This method allows you to combine multiple spreadsheet workbooks into a single model, enabling cross-workbook formula references. Each workbook must have a name unique (case-insensitively) within the model, unless an existing workbook with the same name is an external reference stub (marked with isExternal: true), in which case it will be replaced by the new workbook.

Workbook

The workbook to add

AddWorkbookOptions = {}

Configuration options for which optimization and recalculation work to do

void

Error if a non-external workbook with the same name already exists in the model

// Add multiple related workbooks
const model = new Model();
model.attachWorkbook(Workbook.fromJsf(budget2024jsf));
model.attachWorkbook(Workbook.fromJsf(budget2025jsf);

clearCachedFormula(formula): undefined | null

undefined | null | string

undefined | null


clearCachedFormulasExcept(formulas): undefined | void

string[]

undefined | void


clearCells(ref): void

A reference to the cell, or range of cells, to clear. Defined names are not supported.

string | Reference

void


emit(event, …arguments_): Emitter

Emit an event, invoking all handlers registered for it.

string

The name of the event to emit.

any[]

Arguments to pass to the event handlers.

Emitter

The Emitter instance for method chaining.

EventEmitter.emit


evaluateExpression<Values, Single, Fallback>(expression, __namedParameters): EvaluateExpressionReturnType<Values, Single, Fallback>

Evaluate expression and return the resulting value, or cell, or 2-D array of values or of cells.

A 2-D array is the type returned by Reference.resolveArea: an Array of Arrays of values or Cell instances, plus the attributes top, left, bottom, right, sheetName.

expr may be a literal value or a formula, or nullish or empty-string or the string '='. In the latter three cases, fallBack is returned.

Also, if values is false and single is true, then fallBack is returned if a cell could not be resolved. This is not done if values is true or single is true. (The intent is to iron out this inconsistency when we get to removing the fallBack parameter altogether, in ENGINE-142.)

Values extends boolean = boolean

Single extends boolean = boolean

Fallback = unknown

string

what to evaluate. Treated as a formula if it begins with = and isn’t just that.

EvaluateExpressionOptions<Values, Single, Fallback>

EvaluateExpressionReturnType<Values, Single, Fallback>

the evaluated value, directly, or wrapped in a Cell instance or area array. If single is false and values is false, this returns an AreaCellArray. If single is false and values is true, this returns an AreaValueArray. If single is true and values is false, this returns a Cell. If single is true and values is true, this returns a CellValue.


getCell(cellId, sheetName?, workbookName?): null | Cell

string

null | string

null | string

null | Cell


getWorkbookById(id): undefined | Workbook

Get the workbook with the given ID, or null if no such workbook is in the model.

string

undefined | Workbook


getWorkbooks(): Workbook[]

Workbook[]


goalSeek(controlCell, targetCell, targetValue): number | FormulaError

string | Reference

string | Reference

number

number | FormulaError


hasListeners(event): boolean

Check if there are any handlers registered for a specific event.

string

The name of the event.

boolean

true if there are one or more handlers, false otherwise.

EventEmitter.hasListeners

hasListeners(): boolean

Check if there are any handlers registered for any event.

boolean

true if there are one or more handlers for any event, false otherwise.

EventEmitter.hasListeners


isGlobal(cellRef, workbookName?): boolean

string

string

name of a workbook to look in, if cellRef does not have a workbook prefix

boolean


iterativeCalculationSettings(): IterativeCalculationOptions

IterativeCalculationOptions


listenerCount(event): number

Get the count of listeners for a specific event.

string

The name of the event.

number

The number of listeners for the event.

EventEmitter.listenerCount

listenerCount(): number

Get the count of all event handlers in total.

number

The total number of event handlers.

EventEmitter.listenerCount


listeners(event): (…arguments_) => void[]

Retrieve the event handlers registered for a specific event.

string

The name of the event.

(…arguments_) => void[]

An array of functions registered as handlers for the event.

EventEmitter.listeners


off<T>(event, listener): object & Emitter

Remove a specific event handler for a specified event.

T extends keyof ModelEventArgs

T

The name of the event.

ModelEventListener<T>

The specific handler function to remove.

object & Emitter

The Emitter instance for method chaining.

EventEmitter.off

off(event): object & Emitter

Remove all event handlers for a specified event.

string

The name of the event for which to remove all handlers.

object & Emitter

The Emitter instance for method chaining.

EventEmitter.off

off(): object & Emitter

Remove all event handlers for all events.

object & Emitter

The Emitter instance for method chaining.

EventEmitter.off


on<T>(event, listener): object & Emitter

Register an event handler that listens to a specified event.

T extends keyof ModelEventArgs

T

The name of the event to listen to.

ModelEventListener<T>

The function to execute when the event is emitted.

object & Emitter

The Emitter instance for method chaining.

EventEmitter.on


once(event, listener): Emitter

Register a one-time event handler for a specified event.

string

The name of the event to listen to.

(…arguments_) => void

The function to execute once when the event is emitted.

Emitter

The Emitter instance for method chaining.

EventEmitter.once


orderWorkbooks(idList): void

Set the order in which workbooks are treated as default.

string[]

list of IDs in the order to use

void


readCell(expression, fallBack): Cell

Evaluate expression and return the resulting Cell.

See evaluateExpression.

string

the expression to evaluate. Treated as a formula if it begins with = and isn’t just that.

Cell = BLANK_CELL

value to return if expr is empty or null or just =, or failed to resolve to a cell.

Cell

the resulting Cell instance, or fallBack, or a Cell instance with a FormulaError as its value.


readCellOrDefinedName(expression, fallBack): CellItem

Evaluate expression, resolving any name reference result and recursing to that formula, until a result that isn’t a name reference is obtained. Return a single Cell which is:

  • if the result is a reference to a range of sheet cells, the top-left cell cell of that range
  • else if the result is that of a defined-name formula (not the original expression), return that last defined-name Cell object
  • else the result itself (or its top-left element if it is a Matrix), wrapped in a Cell with no id.

string

the expression to evaluate. Treated as a formula if it begins with = and isn’t just that.

CellItem = BLANK_CELL

value to return if expr is empty or null or just =, or failed to resolve to a cell.

CellItem

the resulting Cell instance, or fallBack, or a Cell instance with a FormulaError as its value.

evaluateExpression


readCells(expression, options): AreaCellArray

Evaluate expression and return the resulting 2-D array of cells.

See evaluateExpression.

string

the expression to evaluate. Treated as a formula if it begins with = and isn’t just that.

"any-cell-information" | "cells-with-non-blank-values"

AreaCellArray

the resulting 2-D array of cells, or an error or fallback wrapped into the same structure.


readValue(expression, fallBack): CellValue

Evaluate expression and return the resulting value.

See evaluateExpression.

string

the expression to evaluate. Treated as a formula if it begins with = and isn’t just that.

CellValue = null

value to return if expr is empty or null or just =.

CellValue

the resulting Cell instance, or fallBack, or a Cell instance with a FormulaError as its value.


recalculate(whichCells): object

Recalculates formula cells in the model and updates their values.

This method triggers the recalculation engine to evaluate formulas based on the dependency graph. It emits a ‘beforerecalc’ event before starting, handles any errors during calculation, and triggers an update event when complete.

If any sheets have GSDV cells (which should only happen if this is the initial recalculation), then Cells.clearGsdv is called and if any cells are deleted as a result, a second initial recalculation is performed (with CHANGED_ONLY, so not updating volatiles again) to update formulas depending on them.

WhichCellsToRecalculate = CHANGED_OR_VOLATILE

Controls which cells to recalculate:

  • "CHANGED_OR_VOLATILE" (default): recalculates cells that have changed or are marked volatile
  • "CHANGED_ONLY": recalculates only cells that have changed
  • "ALL_FORMULA_CELLS": forces recalculation of all formula cells

object

An object with a nativeWorkbooksChanged property containing any GRID-native workbooks that needed recalculation.

nativeWorkbooksChanged: Set<Workbook>


removeWorkbook(id): boolean

Remove a workbook from this model and clean up all related dependencies.

When a workbook is removed, all cells that depend on formulas in that workbook will be recalculated. Any cross-workbook references to the removed workbook will result in #REF! errors in dependent cells.

string

The unique identifier of the workbook to remove

boolean

true if the workbook was found and removed, false otherwise

const jsf = ...; // Load workbook as JSF
jsf.id = '1234';
const model = Model.fromJSF(jsf);
const removed = model.removeWorkbook('1234');

reset(): void

Reset all cell values to their initial state (as data was when read). Only values are reset; other cell attributes (function, style) are not affected. This also triggers recalculation, so volatile cells will update.

void


rewriteFormulaAfterMove(formula, from, to): string

string

formula to update.

reference containing a workbook and sheet prefix

string | A1Reference

reference containing a workbook and sheet prefix

string | A1Reference

string

the updated formula.

Formula parser has finished importing (formulaParserReady has resolved).


runFormula(formula, extraContext): FormulaValue

Evaluate a spreadsheet formula and return the computed result.

Results are automatically cached based on the formula text and will be reused if the same formula is evaluated multiple times with unchanged dependencies.

string

The Excel formula to evaluate, with or without leading ’=’ (e.g., ‘SUM(A1:A10)’ or ‘=SUM(A1:A10)‘)

extra options or properties to pass through to the formula runner. These will be available to each spreadsheet function as attributes on this

null | Partial<EvaluationContext>

FormulaValue

Result of the formula

The Model.preconditions promise must be resolved before calling this method

Error if precondition is not satisfied (the formula parser has not finished importing)

EvaluationError if AST is invalid or an unanticipated internal error occurs so evaluation can’t complete. This error will have its .ast and .formula properties populated.

FormulaSyntaxError if formula needs to be parsed and can’t be

import xlsxConvert from "@borgar/xlsx-convert";
import { Model } from '@grid-is/apiary';
await Model.preconditions;
const wb = await xlsxConvert("budget.xlsx");
const model = Model.fromJSF(wb);
const totalSpent = model.runFormula("=SUM(D:D)");

Model.readValue to read a value from a cell


setupInitRecalculation(): void

void


setWorkbookState(workbookId, state, defect): void

string

undefined | "uploading" | "processing" | "ready" | "invalid"

null | string

void


triggerMetrics(ev): void

(…args) => void

void


triggerUpdate(): void

void


write(cellRef, value, recalcNow?, skipVolatiles?, neutralizeFormulaOnSingleCellWrite?): void

(Deprecated signature) Write the given value to the given cell (A1 address or global name).

the address or global name to write to

string | Reference

CellValue

the value to write

boolean

true (the default) to recalculate immediately before returning

boolean

true to skip evaluation of volatile cells when recalculating

boolean

true to neutralize formula in single-cell write

void

write(cellRef, value, opts?): void

Write the given value to the given cell (A1 address or global name)

the address or global name to write to

string | Reference

CellValue

the value to write

WriteOptions

void


writeMultiple(writes, opts): void

Efficiently write multiple values to different cells in a single operation.

Perform each write in the given list of writes and recalculate once at the end (if the list was non-empty, or if forceRecalc is true).

This method is optimised for bulk data updates. Instead of recalculating after each individual write (which can be slow), it performs all writes first and then recalculates once at the end. This provides significantly better performance when updating many cells at once.

readonly [string, CellValue][]

Array of two-element arrays [cell, val] for individual writes

WriteOptions = {}

Options controlling the batch write behaviour

void

model.writeMultiple([
['A1', 100],
['A2', 200],
['A3', 300],
]);

writes(includeOverwrittenCells): [string, CellValue][]

Return a list of writes to the model.

By default, writes that would not produce the same state are omitted from the list. These are writes to formula cells that have been overwritten again by the recalculation process. Re-applying them to a reset model would not reliably produce the same state.

boolean = false

include writes to formula cells that have been overwritten by recalculation.

[string, CellValue][]


static empty(filename): Model

Create an empty Model instance with a single blank workbook and worksheet.

This is a convenience method for creating a new Model from scratch without needing to provide CSF data. The resulting model contains one workbook with one empty worksheet named “Sheet1”.

string = 'Book1.xlsx'

The name to assign to the empty workbook

Model

A new Model instance containing an empty workbook

The Model.preconditions promise must be resolved before calling this method

Error thrown if precondition is not satisfied (the formula parser has not finished importing)


static fromCsf(csf, options): Model

Make a Model instance and populate it with a workbook from the given CSF.

CSF (Common Spreadsheet Format) is a JSON representation of a workbook. This method is typically used when loading data that has been previously processed from XLSX to JSON. Packages to do that include xlsx-convert or xlsx.

WorkbookCSF

The workbook data in CSF format

AddWorkbookOptions = {}

Configuration options for workbook initialisation

Model

A new Model instance containing the loaded workbook

The Model.preconditions promise must be resolved before calling this method

Error thrown if precondition is not satisfied (the formula parser has not finished importing)

// Ensure parser is ready
await Model.preconditions;
// Create model from JSF data
const model = Model.fromJSF(jsf, {
// When a workbook is read-only, work required to support writes is skipped in order to load
// the workbook faster. In particular, formulas will not be parsed and the workbook will not
// be added to the dependency graph. No initial recalculation occurs, which may lead to
// inaccuracies in workbooks that depend on it.
readOnly: true
});
// Access the loaded data
const cellValue = model.readValue('=A1');

static fromData(csf, options): Model

WorkbookCSF

AddWorkbookOptions = {}

Model


static fromJSF(jsf, options): Model

Make a Model instance and populate it with a workbook from the given JSF.

JSF (JSON Spreadsheet Format) is a JSON representation of a workbook produced by the xlsx-convert library. This method is typically used when loading data that has been previously processed from XLSX to JSON.

JSF

A JSF object representing a workbook

AddWorkbookOptions & object = {}

Configuration options for workbook initialization

Model

A new Model instance containing the loaded workbook

The Model.preconditions promise must be resolved before calling this method

Error thrown if precondition is not satisfied (the formula parser has not finished importing)

// Load Excel file using xlsx-convert
import xlsxConvert from "@borgar/xlsx-convert";
await Model.preconditions;
const wb = await xlsxConvert("budget.xlsx");
const model = Model.fromJSF(wb);
const totalSpent = model.runFormula("=SUM(D:D)");

static fromXlsx(data, filename, options?): Promise<Model>

Load a Model from XLSX binary data.

This is a convenience method that wraps @borgar/xlsx-convert to load XLSX data directly into a Model without requiring users to manually import the conversion library.

Binary XLSX data (works in browsers and Node.js)

ArrayBuffer | Buffer<ArrayBufferLike> | Uint8Array<ArrayBufferLike>

string

Filename to associate with the workbook

AddWorkbookOptions & object

Options for workbook loading

Promise<Model>

A Promise resolving to a new Model instance

The Model.preconditions promise must be resolved before calling this method, or an error will be thrown.

await Model.preconditions;
const response = await fetch('https://example.com/budget.xlsx');
const data = await response.arrayBuffer();
const model = await Model.fromXlsx(data, 'budget.xlsx');

static fromXlsxFile(path, options?): Promise<Model>

Load a Model from an XLSX file path.

This is a convenience method that wraps @borgar/xlsx-convert to load XLSX files directly into a Model without requiring users to manually import the conversion library.

string

Path to the XLSX file (Node.js only - uses fs)

AddWorkbookOptions & object

Options for workbook loading

Promise<Model>

A Promise resolving to a new Model instance

The Model.preconditions promise must be resolved before calling this method, or an error will be thrown.

await Model.preconditions;
const model = await Model.fromXlsxFile('budget.xlsx');
const totalSpent = model.runFormula("=SUM(D:D)");