Skip to content

Core engine API

Every function exported by @tensorgrid/core — the TypeScript port of the Polars GridDataEngine, parity-verified against golden fixtures. These are the building blocks the adapters compose: tree build, pivot, sorting, filtering, windowing, drill-through, editing, formatting, conditional-format scales, the formula DSL, and CSV serialization. Use them directly for a fully custom surface.

NameTypeDescription
addSyntheticColumns
Aggregation
(tree: TreeNode[], synthetic: Record<string, (node: TreeNode) => unknown>) => TreeNode[]Attach computed synthetic columns to every node of a built tree. Each name->fn(node) is evaluated on every node (recursively) and stored as jsonSafe(fn(node)), in insertion order, children after the parent. An escape hatch for any display value (does not cross the wire). Mirrors add_synthetic_columns.
aggregateCells
Aggregation
(cells: CellDescriptor, funcs?: readonly AggFunc[]) => CellsResultAggregate a selected range's already-extracted (wire) values into per-field + overall stats ({ fields, overall }). funcs defaults to all five (sum/mean/min/max/count); count_all always present; an unknown func throws. Field-less values feed overall only. Byte-identical to GridDataEngine.aggregate_cells.
aggregateLevel
Aggregation
(rows: Row[], dimension: string, aggs: Record<string, string>) => Row[]Group rows by dimension and reduce each measure. Returns one record { [dimension]: key, ...measures } per distinct dimension value, ascending by dimension.
buildAdjacencyTree
Aggregation
(rows: AdjNode[], idField: string, parentField: string, opts?: BuildAdjacencyOptions) => unknown[]Build a nested tree from a flat parent-pointer (adjacency-list) frame. opts: measures ({field:agg} or list, default 'sum'; rolls interior nodes bottom-up), labelField (copied onto each node as label), orphans ('root' default | 'drop' for rows with an absent parent id).
calc
Aggregation
(expr: string) => (measures: Record<string, unknown>) => unknownCompile a formula-DSL string into (measures) => value for a calculated measure / having predicate. A small SAFE expression interpreter (tokenizer + recursive-descent parser + evaluator) mirroring Python operator semantics exactly (safe / // %, Python floor-div/modulo sign, and/or returning an operand, chained comparisons, a if c else b, function table abs/min/max/round/sqrt/floor/ceil/log/log10/exp/pow + coalesce/clamp/sign). Anything outside the allow-list throws FormulaError. agg()/sql() are NOT ported (backend-only).
CellAcc
Aggregation
class CellAcc { add(v: unknown): void; result(funcs: readonly AggFunc[]): CellStats }Streaming accumulator for one field's cell aggregates. Mirrors _CellAcc: a null value contributes nothing (not even to count_all); a non-null non-numeric value increments count_all only; a finite numeric value feeds sum/mean/min/max/count and count_all. result() builds stats for the requested funcs (unselected keys omitted; count_all always present).
columnFooter
Aggregation
(input: FooterInput, aggs: Record<string, string>) => Record<string, WireValue>Aggregate each { column: agg } over the whole frame into a JSON-safe { column: value } footer. agg is a built-in name (sum/mean/min/max/count/n_unique/median/first/last); other specs (std/var/product/mode/concat/quantile + expr/callable) are backend-evaluated and rejected here. A numeric-only agg on a non-numeric column is skipped. FooterInput = { columns: Record<string, WireValue[]>, numeric: string[] }.
FormulaError
Aggregation
class FormulaError extends ErrorThrown by calc for invalid syntax, a disallowed construct/function, or (at call time) an unknown name — the same safety model as the Python calc.
FRONTEND_AGG
Aggregation
Readonly<Record<string, string>>Built-in agg-name -> client-side aggregation-fn name. Mirrors engine _FRONTEND_AGG. Runtime-registered aggregations (register_aggregation) are Polars-factory-based and out of wire scope; a host can extend this table.
gridGrandTotal
Aggregation
(rows: Record<string, unknown>[], aggs: Record<string, string>, calculated?: Record<string, CalcFn>) => Record<string, WireValue>Aggregate every measure over the WHOLE frame into a JSON-safe { measure: value } footer (for backend-tree mode, where client-side auto-compute over pre-aggregated leaves would give mean-of-means). Optional calculated runs on the base measures. Reuses reduceColumn.
isNumericOnlyAgg
Aggregation
(agg: string) => booleanTrue if agg is a numeric-only aggregation (incl. quantile specs). For the footer skip.
iterCells
Aggregation
(cells: CellDescriptor) => Generator<[string | null, WireValue]>Yield [field, value] pairs from a liberal cells descriptor. Mirrors _iter_cells: a { field: [values] } mapping, or a sequence of { value, field? } objects (the range_selection shape) or bare scalars (field-less).
nodeDimKey
Aggregation
(node: TreeNode) => string | nullThe dimension-value key of a node (first non-numeric, non-meta key). Mirrors _node_dim_key.
nodeMeasureKeys
Aggregation
(node: TreeNode) => string[]Numeric measure keys of a node (not dimension / id / meta). Mirrors _node_measure_keys.
NUMERIC_ONLY_AGGS
Aggregation
ReadonlySet<string>Aggregations meaningful only over a numeric column (a footer skips them for text/bool). Mirrors the engine's _NUMERIC_ONLY_AGGS.
parseQuantile
Aggregation
(spec: string) => number | nullThe quantile q in [0,1] for a parametric spec ('quantile:0.9' / 'p90'), else null. Mirrors the engine's _parse_quantile.
reduceColumn
Aggregation
(values: readonly unknown[], agg: string) => unknownReduce values by the built-in aggregation agg (case-insensitive). Full vocabulary: sum/mean/min/max/count/n_unique/median/first/last/product/std/var/quantile/mode/concat (+ quantile:0.9 / p90 specs). Returns a raw (not-yet-json-safe) value. Throws for an unknown agg, mirroring the engine's 'Unknown aggregation' ValueError.
sortDimensionKeys
Aggregation
(keys: unknown[]) => unknown[]Order distinct group keys to mirror Polars group_by(dimension).agg(...).sort(dimension): a null key sorts FIRST, then numeric keys ascending numerically / string keys lexicographically.
SUPPORTED_REDUCERS
Aggregation
ReadonlySet<string>Built-in aggregation NAMES this core reduces directly (quantile specs handled dynamically via parseQuantile). Covers the full _AGG_FACTORIES vocabulary.
topNChildren
Aggregation
(tree: TreeNode[], by: string, n: number, opts?: { descending?: boolean; others?: boolean; othersLabel?: string }) => TreeNode[]Keep the top n children (by `by`) at every level; optionally roll the rest into a single 'Others' node that SUMS the numeric measures. Recurses into children first, then reorders/drops the current level. Mirrors top_n_children.
treePayload
Aggregation
(rows: Row[], groupBy: string[], aggs: Record<string,string>, opts?: TreePayloadOptions) => TreeNode[]Build the nested aggregation tree. groupBy = ordered dimension columns; aggs maps a measure/column name to a built-in string aggregation (applied at every level). Options: calculated (post-aggregation measures name->fn(measures)), grandTotal (prepend a 'Total' row), lazy + expanded (Mode-B: realise only expanded nodes' children), having (formula-DSL prune). Returns JSON-safe root nodes.
sparkline
Cell-viz
(values: unknown, opts?: SparklineOptions) => string | nullAn inline sparkline SVG STRING for an array of numbers, or null when there are fewer than 2 finite numbers. Wraps sparklineData; host-supplied colors are escaped. For the HTML string renderer.
sparklineData
Cell-viz
(values: unknown, opts?: SparklineOptions) => SparklineData | nullThe STRUCTURED sparkline geometry (points / fillPoints / rects, color, dims) for an array of numbers, or null when fewer than 2 finite numbers. Line by default; type:'bar' for a mini column chart. Color auto-picks up/down (green/red) unless color is set. React/Vue build the SVG via elements (XSS-safe).
SparklineOptions
Cell-viz
{ type?: 'line'|'bar'; width?: number; height?: number; color?: string; colorUp?: string; colorDown?: string; fill?: boolean; lineWidth?: number; from?: 'cell'|'children' }Options for sparkline / sparklineData. type default 'line'; width default 80; height default 22; color overrides up/down auto-color; colorUp/colorDown default green/red; fill (line only) default false; lineWidth default 1.5; from 'cell' (default, the cell's array value) or 'children' (gather this measure's values across the node's child rows — the OLAP trend).
GROUP_COLUMN_KEY
Column
const GROUP_COLUMN_KEY = '__group__'The columnWidths / data-resize-key key for the auto-group (dimension) column, which has no accessorKey. Adapters use this to persist + apply the group column's resize width.
makeActionColumn
Column
(actions: ActionItem[], header?: string, colId?: string) => ActionColumnDefBuild an action-button column def (type sentinel 'action'). Mirrors make_action_column: actions is a list of { id, label?, ... }; clicking one sets the component's action_triggered prop. Append the result to the list from makeColumnDefs.
makeColumnDefs
Column
(schema: GridSchema, args: MakeColumnDefsArgs) => ColumnDef[]Build the leaf/group column definitions (TanStack-style). schema = wire-portable { columns, colTypes, dateDefaultCols?, distinct? } (the backend-extracted stand-in for self.df); args = { groupBy, agg, calculated?, formats?, headers?, pivot?, filters? }. Mirrors make_column_defs exactly, incl. the always-present (possibly-null) formatStr and the dim/measure/calculated/pivot ordering.
pyTitle
Column
(s: string) => stringFaithful port of Python str.title(): the first cased char of each run titlecased, the rest lowercased; any non-cased char resets the run — so 'max_order' -> 'Max Order'. Used to derive default headers.
reorderMeasureCols
Column
<T extends { accessorKey?: string }>(cols: readonly T[], order: readonly string[] | undefined) => T[]Reorder measure column defs by order (a list of accessorKeys) — the shared primitive behind every adapter's column-reorder feature (drag / setColumnOrder / saved-view columnOrder). Listed keys come first in that order, then remaining defs in original order; unknown/duplicate keys ignored. PURE.
ColorScaleSpec
Conditional-format
{ min: string; max: string; mid?: string; domainMin?: number; domainMax?: number; domainMid?: number }A two- or three-stop linear color scale over a measure column. min/max = CSS hex colors at the domain ends; mid = optional diverging midpoint color; domainMin/domainMax fix the domain ends; domainMid = the value at which mid sits (default the domain midpoint; ignored without mid).
dataBarBackground
Conditional-format
(value: unknown, colMax: number, spec: DataBarSpec) => string | nullThe CSS background (a linear-gradient bar) for one cell, or null when null/non-numeric. Domain [domainMin ?? 0, domainMax ?? colMax]; value clamped; a degenerate domain yields null. Without origin the bar is LEFT-ANCHORED; with origin it DIVERGES (above paints rightward in color, below leftward in negativeColor ?? color; a value AT origin yields null).
DataBarSpec
Conditional-format
{ color: string; domainMin?: number; domainMax?: number; origin?: number; negativeColor?: string }An in-cell data bar over a measure column. color = fill (above-origin side for a diverging bar); domainMin (default 0) / domainMax (default column max) fix the domain; origin = the baseline value for a diverging bar; negativeColor = the below-origin fill (default color).
evalCellStyle
Conditional-format
(value: unknown, rules: CellStyleRule[]) => CellStyleResult | nullFirst-match-wins: return the { style, className, icon } of the first rule whose op is satisfied by value, else null. Numeric-vs-text mode inferred from the OPERATOR (= / < numeric; equals / contains textual). A value-needing operator with no value matches every cell. Reuses the filter's evalCondition — one operator vocabulary.
iconSet
Conditional-format
(value: unknown, lo: number, hi: number, spec: IconSetSpec) => { icon: string; color: string } | nullThe { icon, color } for one cell's value on a 3-level icon set, or null when null/non-numeric. lo/hi are the column domain ends; the level is chosen by thresholds (explicit or the domain thirds); reverse flips the mapping (low = good). Mirrors the Dash component's iconSet conditionalFormat.
IconSetSpec
Conditional-format
{ set?: 'arrows'|'triangles'|'traffic'; thresholds?: [number, number]; colors?: [string,string,string]; icons?: [string,string,string]; reverse?: boolean }A 3-level icon set over a measure column. set = named preset (default 'arrows', overridden by icons); thresholds = explicit level boundaries (default the domain thirds); colors = per-level (default red/amber/green); icons = per-level glyphs; reverse = flip level→icon/color so a LOW value is 'good'.
numericRange
Conditional-format
(values: unknown[]) => { min: number; max: number } | nullThe numeric range of a column's values (nulls / non-numbers ignored), or null if none are numeric. Adapters use it to compute the domain for scaleColor / dataBarBackground / iconSet.
scaleColor
Conditional-format
(value: unknown, lo: number, hi: number, spec: ColorScaleSpec) => string | nullThe CSS background color for one cell on a color scale, or null when the value is null/non-numeric. lo/hi are the domain ends (from numericRange, or spec.domainMin/domainMax). The value is clamped to [lo,hi]; a degenerate domain maps to mid ?? min. With mid, the low half interpolates min→mid and the high half mid→max. Presentation only.
inferSchema
Data
(rows: Row[], input: SchemaInput, opts?: InferSchemaOptions) => InferredSchemaClassify rows into { dimensions, measures }. Dimensions = the kept non-numeric columns ordered by ascending cardinality (stable); measures = the explicit agg or every numeric column at defaultAgg (default 'sum'). Drives the adapters' autoGroup. input = { columns, numeric }; opts = { agg?, maxLevels?, defaultAgg? }.
TreeNode
Data
interface TreeNode { [key: string]: unknown; subRows?: TreeNode[] }A tree node: arbitrary measure/dimension keys plus optional children. Engine-added markers include id, _level, _hasChildren, _isGrandTotal, _isOthers, _loading. The unit of every core tree transform.
applyEdit
Editing
(rows: Row[], groupBy: string[], edit: CellEdit, opts?: { validationRules?: Record<string, ValidationRule[]> }) => Row[]Apply one source-cell edit and return a NEW rows array (structural sharing: only the edited row object is replaced). Throws on a path longer than groupBy, a rowIndex past the node's matching rows, an unknown column, a stale oldValue, or (with opts.validationRules) a failed ValidationRule (EditRejected). The engine re-derives every aggregate; the frontend does ZERO math on edit.
CellEdit
Editing
{ path: unknown[]; rowIndex: number; column: string; oldValue: unknown; newValue: unknown }One source-cell edit. path = group-key path of the drilled node (JSON.parse of its id; may be a parent prefix); rowIndex = zero-based index within the node's drill-through order; column = source column (dimension columns allowed — the row REGROUPS on rebuild); oldValue = the value the editor saw (wire form) — a mismatch means a stale edit and throws.
EditRejected
Editing
class EditRejected extends Error { readonly code: string; readonly column: string; readonly rule: string | null }A source-cell edit rejected by a ValidationRule (mirrors Python EditValidationError). Carries machine-branchable metadata (code, column, rule) for a structured wire rejection.
validateEdit
Editing
(candidateRow: Record<string, unknown>, column: string, newValue: unknown, rules: ValidationRule[]) => voidEvaluate column's validation rules against a candidate edit; throw EditRejected on the FIRST failing rule, else return. candidateRow is the post-edit row env (value aliases the edited cell; other columns in scope for cross-field predicate rules). Mirrors GridDataEngine._validate_edit (default messages match the oracle verbatim).
pivotToCsv
Export
(pivot: PivotResult, rowDims: string[], opts?: PivotCsvOptions) => stringSerialize a pivotGrid result to CSV — the flat, complete cross-tab. One column per ROW dimension (full ancestor path) + one per pivoted MEASURE leaf, named by its flattened group-path ('2024 / Q1 / Revenue'; groupSeparator default ' / '). Every node at every level. Default RAW; { format: true } emits DISPLAYED values.
toCsv
Export
(rows: Row[], opts?: { columns?: string[]; floatColumns?: string[] }) => stringSerialize materialized rows to a CSV string matching Polars write_csv: a header row; comma-separated; RFC-4180 quoting; null -> empty field; true/false booleans; trailing \n after every row. opts.columns picks the column subset/order; opts.floatColumns names float columns (for .0 fidelity on whole values). Byte-identical to Polars on the fixtures.
treeToCsv
Export
(nodes: TreeNode[], groupBy: string[], measures: TreeCsvColumn[], opts?: CsvExportOptions) => stringSerialize an aggregated roll-up TREE to CSV — the 'what you see' export of the grid view. One column per groupBy dimension (each row carrying its full ancestor path; a parent/subtotal row leaves deeper cells empty) + one column per measures entry. Every node at every level (subtotals included), RFC-4180. Default RAW numerics; { format: true } emits DISPLAYED values (each measure's formatStr).
andPredicates
Filtering
(...predicates: (NodePredicate | null | undefined)[]) => NodePredicateAND-combine predicates (null/undefined entries skipped; empty input matches all).
applyHaving
Filtering
(tree: TreeNode[], having: string, measureKeys: string[]) => TreeNode[]Prune tree nodes whose measures fail the `having` predicate (a formula-DSL string), keeping any node on the path to a surviving descendant. Mirrors _apply_having (string path only; the callable path is backend-only). measureKeys are the keys the predicate may reference.
applyWindowFilters
Filtering
(rows: Row[], filters: Record<string, ColumnFilter>, columns: Set<string>) => Row[]Per-column server-side filter model. Mirrors _apply_window_filters. Exported so facetCounts can reuse the exact same predicates for its faceted (other-column) frame.
buildFilterDescriptors
Filtering
(columnDefs: ColumnDef[], rows: Row[], filters?: FilterState) => FilterDescriptor[]Build one FilterDescriptor ({ column, filterType, header?, value?, facet?, options? }) per filterable leaf column: which control to render, its current value, the facet (value list / bounds, faceted over the OTHER columns via facetCounts), and any static filterOptions. The adapter renders framework-idiomatic controls FROM the descriptors.
buildRootFilterControl
Filtering
React: (ctx: RootFilterControlCtx) => ReactNode; Vue: (h, ctx) => VNodeShared root-dimension filter control builder for the adapters (the grid's filter row and the virtual grid's filter bar). Renders one of the rootFilter variants (text / select / set / conditions / date), reading/writing the core FilterState through the passed-in ctx { col, rootFilter, filters, setFilter, clearFilter, rootValues }. Does ZERO math.
columnFilterPredicate
Filtering
(filters: Record<string, ColumnFilter>) => NodePredicateA per-COLUMN filter predicate over the same { column: { type, value|min|max } } model the server row window uses. Semantics mirror the engine: text = case-insensitive substring; range = numeric min/max; select/equals = stringified equality; blank/absent value deactivates a spec; active specs AND together. On the client tree it sees AGGREGATED node values (matching-parent-keeps-subtree applies).
conditionsMatch
Filtering
(v: unknown, fv: Record<string, unknown>, numeric: boolean) => boolean{ op1, val1, join, op2, val2 } -> condition 1 AND / OR condition 2 (an empty op1 matches all).
evalCondition
Filtering
(v: unknown, op: string, target: unknown, numeric: boolean) => booleanEvaluate ONE condition: blank / notBlank, numeric ops (= != > >= < <=, inclusive between over [lo,hi]), or case-insensitive text ops. A blank/unparseable value yields no constraint (matches every row). Byte-identical to the engine's _condition_expr; single-sourced for the server row window + the adapters' condition controls.
FACET_TOPN
Filtering
const FACET_TOPN = 200Default top-N cap on a `set` facet list — the high-cardinality guard (mirrors _FACET_TOPN).
facetCounts
Filtering
(rows: Row[], column: string, opts?: { filters?; kind?: 'set'|'range'; limit? }) => FacetCountsResultFaceted values + counts ('set') or min/max bounds ('range') for column, over the frame matching every OTHER filter (never column's own). set values ordered count-desc then value-asc; NULL dropped; capped at opts.limit (default FACET_TOPN=200) with truncated flagging more distinct values. Feeds the select/set filter controls.
filterControlType
Filtering
(def: ColumnDef) => FilterTypeThe control to render for a column: its explicit filterType, else derived from the type.
filterReducer
Filtering
(state: FilterState, action: FilterAction) => FilterStatePure reducer over the per-column filter state (set / clear / clearAll; new object on change, same ref on no-op). An adapter drives it from useReducer (React) or a reactive ref (Vue).
filterTree
Filtering
(nodes: TreeNode[], predicate: NodePredicate) => TreeNode[]Return a new tree keeping nodes that match predicate or have a surviving descendant, PRESERVING ancestor paths. A MATCHING node keeps its ENTIRE subtree. PURE (new arrays, parents shallow-copied). Grand-total rows get no special treatment.
inferNumeric
Filtering
(rows: Row[], col: string) => booleanNumeric-vs-text mode from the first non-null cell (mirrors the engine's dtype check).
NUMERIC_OPERATORS
Filtering
readonly NumericConditionOperator[]Operator list for a numeric column (order = the control's dropdown order): = != > >= < <= between.
operatorNeedsValue
Filtering
(op: string) => booleanTrue when op needs a value input (i.e. is not blank/notBlank/empty) — the control hides the value input otherwise.
quickFilterPredicate
Filtering
(query: string, keys: string[]) => NodePredicateA quick-filter predicate: case-insensitive substring test of query over the given node keys (values stringified; null/undefined never match). An empty/blank query matches all.
RootFilterKind
Filtering
'text' | 'select' | 'set' | 'conditions' | 'date'The root-dimension control variant. select/set need rootValues; the rest do not.
TEXT_OPERATORS
Filtering
readonly TextConditionOperator[]Operator list for a text column (contains / startsWith / endsWith / equals / … , blank / notBlank).
useColumnFilters
Filtering
(initial?: FilterState) => UseColumnFiltersReact (useReducer) / Vue (shallowRef) binding for the core filterReducer over the typed FilterState. Returns { filters, setFilter(column, value), clearFilter(column), clearAll() }. Pure state — no DOM. Backs a grid's per-column filter state.
UseColumnFilters
Filtering
{ filters: FilterState; setFilter(column, value: FilterValue): void; clearFilter(column): void; clearAll(): void }Return shape of useColumnFilters: the current per-column filter state plus set/clear/clearAll actions.
VALUELESS_OPERATORS
Filtering
readonly ConditionOperator[]Operators that take no value (blank / notBlank) — the control hides the value input.
formatValue
Formatting
(value: unknown, formatStr?: string | null, opts?: { locale?: string; currency?: string; nullText?: string }) => stringFormat a raw value by either supported formatStr grammar: (1) the Dash component's kind[:arg][:flag…][;modifier…] grammar (number/currency/percentage/date; flags compact/accounting/sign; unit=; date token patterns like YYYY-MM-DD); (2) numeral-style strings ('0,0.00','$0,0.00','0.0%' plus (…) accounting, trailing 'a' abbreviation, leading '+' sign, bare 'date'). NaN/Inf/null/undefined render the null token (default '—'); an empty formatStr renders the raw value.
filterPivotColumnDefs
Pivot
(columnDefs: ColumnDef[], keep: Set<string>, opts?: { totalHeader?: string; keepTotal?: boolean }) => ColumnDef[]Prune a pivot's column_defs to only the selected top-level column groups — a client-side DISPLAY filter (hides column groups, does NOT re-aggregate). Row-dim leaf columns always kept; the grand-total group kept when keepTotal (default true); an EMPTY keep set means 'no column filter'. Feed the result to layoutPivotHeader.
layoutPivotHeader
Pivot
(columnDefs: ColumnDef[]) => PivotHeaderLayoutLay out a pivot's column_defs into header rows + leaves (skipping the row-dim leaf defs — the auto-group column shows the row hierarchy). Returns { rows: HeaderCell[][], leaves: ColumnDef[], depth }. Framework adapters (the React pivot) consume this directly.
PIVOT_TOTAL_HEADER
Pivot
const PIVOT_TOTAL_HEADER = 'Total'Default header of the grand-total column group emitted by pivotGrid (grandTotal: true).
pivotColumnGroups
Pivot
(columnDefs: ColumnDef[], opts?: { totalHeader?: string }) => string[]The top-level pivoted column-group headers (each a distinct outer column-dimension value, e.g. '2024','2025') — the choices for a column-group filter control. The grand-total group is EXCLUDED. Sourced from column_defs directly so the strings match exactly (the same strings filterPivotColumnDefs prunes on).
pivotGrid
Pivot
(rows: Row[], rowDims: string[], colDims: string[], aggs: Record<string,string>, opts?: PivotOptions) => PivotResultBuild a full cross-tab: rowDims on the row hierarchy, colDims pivoted into nested column groups, measure cells at every intersection. Returns { row_data, column_defs }. Opts: colSubtotals, grandTotal, rowGrandTotal, formats, headers, rowColTypes, calculated, context. Byte-identical to the Polars oracle.
GridViewState
Saved-views
{ expanded?; sortKeys?: SortKey[]; search?; hiddenColumns?: string[]; density?; columnWidths?; selected?: string[]; keptColumnGroups?: string[]; columnOrder?: string[] }A JSON-serializable snapshot of a grid's VIEW state, shared by every adapter so getState()/applyState() round-trip the same shape across vanilla / React / Vue. Presentation state only (never DATA); every field optional so a partial preset touches only the axes it names. The setFilter predicate is intentionally NOT part of this snapshot.
drillThrough
Selection
(rows: Row[], groupBy: string[], pathValues: unknown[], opts?: { columns?: string[]; limit?: number }) => Row[]Return the source rows that rolled up into a node identified by pathValues (the raw dimension values from root to the node; may be shorter than groupBy to drill a parent). Mirrors drill_through.
isSyntheticNodeId
Selection
(id: unknown) => booleanWhether a node id belongs to an engine SYNTHETIC node — the grand-total ('Total') row or a top-N 'Others' roll-up. Such ids resolve to ZERO source rows, so selection affordances should skip them. Matches the node's own key (last path element), so a nested Others node is detected too.
matchesPath
Selection
(row: Record<string, unknown>, groupBy: string[], path: unknown[]) => booleanTrue when a row matches a group-key path prefix (zip(group_by, path), min length).
rowsForSelection
Selection
(rows: Row[], groupBy: string[], selectedRows: { id?: string }[], opts?: DrillOptions) => Row[]Return the deduplicated source rows underlying a component selection. Each entry's id is a JSON-encoded group-key path (makeNodeId); selecting a group also selects its descendants, so only TOP-MOST paths are kept, each filtered, and the union order-preservingly deduplicated. Mirrors rows_for_selection.
selectionRange
Selection
(orderedIds: string[], anchorId: string, targetId: string) => string[]The ids from anchorId to targetId INCLUSIVE, in orderedIds order, regardless of which endpoint comes first — the pure math behind a range / shift-click selection. Both endpoints located by first occurrence; if EITHER is absent the range is empty; anchor === target yields the single id.
summarizeRows
Selection
(rows: Record<string, unknown>[], columns: string[]) => Record<string, ColumnSummary>Per-column { count, sum, avg, min, max } over the FINITE numeric values in rows (non-numeric / null / NaN / Inf skipped; an all-non-numeric column reports count:0 with null stats). Backs the selection status bar; adapters compose summarizeRows(rowsForSelection(...), measures).
coerceNumber
Serialization
(v: unknown) => number | nullCoerce a wire value to a finite number for aggregation, else null. Mirrors _coerce_number: bool is EXCLUDED (a bool is not a measure); a finite number passes; everything else (strings, bigint, null, objects, non-finite) is rejected.
jsonSafe
Serialization
(value: unknown) => WireValuePort of the Python engine's json_safe: turn a computed JS value (number, bigint, boolean, string, null, Date, arrays, plain objects) into its on-the-wire form so @tensorgrid/core output is byte-identical to the Python engine. A whole-valued number above 2^53 is stringified; -0.0 serializes as 0 (float64 provenance limitation).
makeNodeId
Serialization
(pathValues: unknown[]) => stringDeterministic, injective node id from a path of key values. Matches Python _make_node_id = json.dumps([json_safe(v) for v], ensure_ascii=True) EXACTLY (comma-space separators, \uXXXX escaping). int 2025 vs str '2025' encode differently. CAVEAT: a float dimension value may render differently than Python — use int/string/date-string dimensions.
MAX_SAFE_INT
Serialization
const MAX_SAFE_INT: numberThe 2^53 integer-safety boundary used by jsonSafe (mirrors the Python engine's _MAX_SAFE_INT).
cacheFromTree
Server/Lazy
(nodes: TreeNode[], map: Map<string, TreeNode[]>) => Map<string, TreeNode[]>Seed/accumulate a node cache from a tree's inline subRows: every node carrying loaded children is recorded (id -> children), recursing. The passed map is the accumulator (mutated and returned). Re-exported from @tensorgrid/react and @tensorgrid/vue.
getRowsWindow
Server/Lazy
(rows: Row[], start: number, end: number, opts?: RowsWindowOptions) => RowsWindowResultServe one [start, end) block for the infinite/server row model. rows is the full materialized frame; opts.filters / opts.sort applied to the whole frame first. Returns { rows (each json-safed with an absolute _index), row_count } (the filtered count).
htmlEscape
Server/Lazy
const htmlEscape: (v: unknown) => stringHTML-escape a value for text/attribute contexts (shared by the reference renderers). Use it on interpolated host data inside a vanilla/HTML cellRenderers function.
idDepth
Server/Lazy
(id: string) => numberDepth of a node id — the length of its group-key path (id is the JSON-encoded path from makeNodeId). Returns 0 for a non-array/unparseable id.
makeSkeleton
Server/Lazy
(id: string, count: number) => TreeNode[]Synthesize count client-only loading-skeleton children for node id: placeholder rows marked _loading (rendered by renderGridHtml as tg-row-skeleton rows) at the child level, with no measure values. Spliced under an expanding node DURING an in-flight fetch and replaced on resolve. Re-exported from @tensorgrid/react and @tensorgrid/vue.
mergeChildren
Server/Lazy
(roots: TreeNode[], id: string, children: TreeNode[]) => TreeNode[]Immutably splice children in as the subRows of the node whose id === id, cloning ONLY the ancestor spine down to that node (siblings/subtrees shared by reference), and return the new roots. When id is not found, or the target already has exactly this children reference, the SAME roots reference is returned (a cheap identity-checkable no-op). Backs the lazy Mode-B display tree.
nodeChildrenState
Server/Lazy
(node: TreeNode, cache?: { has(id: string): boolean }) => 'leaf' | 'unloaded' | 'loaded'Classify a node's child-load state so an adapter can decide fetch-vs-instant: 'leaf' (no children), 'loaded' (inline subRows or id in cache), 'unloaded' (children exist on the server but not sent — triggers a Mode-B round-trip + skeleton).
renderGridHtml
Server/Lazy
(tree: TreeNode[], groupBy: string[], measures: ColumnDef[], opts?: RenderGridOptions) => stringFramework-neutral reference renderer: composes flattenTree + formatValue into a semantic HTML <table> string — an auto-group first column (dimension value indented by _level with a ▸/▾ expander) + one column per measure def (formatted via each def's formatStr). Opts cover expanded, groupHeader, selected, cellStyles, pinFirstColumn, noRowsMessage, resizable, columnWidths, footer/footerLabel, colorScales, dataBars, iconSets, sparklines, cellRenderers. No DOM/framework (SSR-friendly).
renderPivotHtml
Server/Lazy
(pivot: PivotResult, rowDims: string[], opts?: RenderPivotOptions) => stringFramework-neutral reference renderer for the PIVOT / cross-tab: composes pivotGrid's { row_data, column_defs } into an HTML <table> string with a multi-row header (nested groups colspan their leaves; leaf measure cells rowspan to the bottom row). Opts: expanded, groupHeader, cellStyles/colorScales/dataBars/iconSets/cellRenderers (keyed by MEASURE NAME), noRowsMessage, pinFirstColumn, sort.
RowNode
Server/Lazy
type RowNode = TreeNodeA tree node with the engine-added lazy markers (id, _level, _hasChildren, _loading, measures) — the same shape as core TreeNode; no distinct type.
useLazyTree
Server/Lazy
(options: UseLazyTreeOptions) => UseLazyTreeResultReact/Vue hook implementing the lazy Mode-B node-cache: the FIRST expand of a node fetches its children (fetchChildren(id) => Promise<RowNode[]>), a re-expand of an already-fetched node is INSTANT with zero network, an in-flight fetch shows a loading skeleton, and invalidate() clears the cache. Transport- and DOM-neutral; reuses core mergeChildren; effect-free (fetching driven by toggle/expand).
UseLazyTreeOptions
Server/Lazy
{ initialRoots: RowNode[]; fetchChildren: (id: string) => Promise<RowNode[]>; skeletonCount?: number; expanded?: Record<string, boolean>; onExpandedChange?: (next) => void }useLazyTree input: initialRoots (pristine level-0 nodes from an initial Mode-B payload), fetchChildren (transport/DOM-neutral fetch of ONE node's children by id), skeletonCount (placeholder rows during a first-expand fetch, default 4), and optional CONTROLLED expanded state + onExpandedChange.
UseLazyTreeResult
Server/Lazy
{ expanded; tree: RowNode[]; childrenCache: ReadonlyMap<string,RowNode[]>; pendingIds: string[]; isPending(id): boolean; fetchCount: number; toggle(id): Promise<void>; expand(id): Promise<void>; collapse(id): void; invalidate(freshRoots): void }useLazyTree result: the DISPLAY tree (roots + folded cached children + in-flight skeletons), the node cache, pending ids, a resolved-fetch counter (the honesty proof), and toggle/expand/collapse/invalidate operations. invalidate clears the cache, replaces the roots, and KEEPS the expanded state.
SortKey
Sorting
interface SortKey { by: string; desc?: boolean }One key of a multi-column sort (primary first, later keys break ties). desc default false = ascending. Consumed by sortTreeMulti and the adapters' click-to-sort headers.
sortTree
Sorting
(nodes: TreeNode[], by: string, opts?: { desc?: boolean }) => TreeNode[]Return a new tree with the siblings at EVERY level ordered by `by`. Nulls sort LAST both directions; grand-total rows (_isGrandTotal) pinned first; leaves returned by reference, parents shallow-copied with sorted subRows. Pure — never mutates.
sortTreeMulti
Sorting
(nodes: TreeNode[], sortKeys: SortKey[]) => TreeNode[]Multi-column sort: order siblings at every level by the FIRST key, breaking ties with each subsequent key. Same null/type/grand-total semantics as sortTree. An empty sortKeys is a no-op.
expandToDepth
Virtualization
(tree: TreeNode[], depth: number) => Record<string, boolean>Build the expanded-state ({ id: true }) revealing every node down to `depth` levels (each node at level < depth that has children marked expanded). depth<=0 collapses to roots ({}); a depth past the deepest fully expands. Backs 'expand all / collapse all / expand to level N'.
flattenTree
Virtualization
(nodes: TreeNode[], expanded: ExpandedState) => TreeNode[]Flatten the tree into the ordered list of VISIBLE rows: every node in order, followed by its subRows only when the node's id is expanded (recursively). Node objects returned by reference (each carries its _level for indentation).
flattenTreeWindow
Virtualization
(nodes: TreeNode[], expanded: ExpandedState, start: number, end: number) => TreeWindowThe ordered visible rows in the half-open window [start, end) plus the total visible count ({ rows, total }). One depth-first pass: O(visible) time, O(window) memory. Out-of-range windows yield rows: []; a negative/reversed range is clamped. Sizes a virtual scroller from total.
isNodeExpanded
Virtualization
(expanded: ExpandedState, id: unknown) => booleanWhether a node id is expanded (a non-string id is never expandable). Shared by the flatteners, the virtualization window, and the reference renderers.
visibleIndexOf
Virtualization
(nodes: TreeNode[], expanded: ExpandedState, id: string) => numberThe VISIBLE index of a node id under the given expanded-state, or -1 when the node is hidden inside a collapsed subtree or absent. Early-exits on the first match (supports scroll-to-node).
visibleRowCount
Virtualization
(nodes: TreeNode[], expanded: ExpandedState) => numberThe number of visible rows for the given expanded-state (sizes a virtual scroller).
movingAverage
Window
(tree: TreeNode[], measure: string, window: number, opts?: { into?: string }) => TreeNode[]Add a trailing simple moving average of measure over the last `window` siblings (inclusive) at each level. Partial at the start (min_periods=1). A null/non-numeric current yields null (still occupies a slot); a grand-total node is skipped (no slot). into default '<measure>_ma<window>'. Mirrors moving_average.
pctOfParent
Window
(tree: TreeNode[], measure: string, into?: string) => TreeNode[]Add each node's share of its PARENT's measure (a [0,1] fraction) under key into (default '<measure>_pct_parent'). Top-level nodes divide by the top-level total. A null/non-numeric measure or zero parent yields null.
pctOfTotal
Window
(tree: TreeNode[], measure: string, into?: string) => TreeNode[]Add each node's share of the GRAND TOTAL of measure (a [0,1] fraction; format with %) under key into (default '<measure>_pct'). Denominator = the sum of the top-level nodes (grand-total node excluded, resolves to 1). A null/non-numeric measure or zero total yields null.
rankChildren
Window
(tree: TreeNode[], by: string, opts?: { descending?: boolean; into?: string; dense?: boolean }) => TreeNode[]Add a 1-based rank of each node among its siblings by `by`. dense = ties share a rank, no gaps; else standard competition ranking. None-valued nodes get null; a grand-total node is excluded (null). Does NOT reorder. Mirrors rank_children.
runningTotal
Window
(tree: TreeNode[], measure: string, into?: string) => TreeNode[]Add a cumulative running total of measure across siblings at each level, under key into (default '<measure>_cumulative'). A grand-total node (_isGrandTotal) is skipped (null). Mirrors running_total. Mutates in place and returns the tree.
siblingDelta
Window
(tree: TreeNode[], measure: string, opts?: { into?: string; percent?: boolean }) => TreeNode[]Add the change in measure from the PREVIOUS sibling at each level (period-over-period). The first sibling gets null; each later gets current-previous (absolute) or, with percent, (current-previous)/previous as a [0,1] fraction. A null/non-numeric current/previous (or zero previous for percent) yields null and breaks the chain. A grand-total node is skipped. into default '<measure>_delta' (or '_delta_pct'). Mirrors sibling_delta.
WindowColumnSpec
Window
{ kind: 'runningTotal'|'rank'|'pctOfTotal'|'pctOfParent'|'delta'|'movingAverage'; by: string; as: string; header?: string; format?: string; descending?: boolean; dense?: boolean; percent?: boolean; window?: number }One derived window column. runningTotal = cumulative sum of `by` across siblings; rank = 1-based sibling rank (descending default true, dense optional); pctOfTotal = share of grand total; pctOfParent = share of parent (both percent kinds store a [0,1] fraction, format with %); delta = change from the previous sibling (absolute, or [0,1] fraction when percent); movingAverage = trailing SMA over the last `window` siblings (default 3).