Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
7f3c9da
Merge branch 'release/3.4.0' into develop
sequba Aug 10, 2026
61ead73
Minor change to the text showed by the release script
sequba Aug 10, 2026
a81332f
Improve the docs search with frontmatter tags (HF-353) (#1750)
sequba Aug 27, 2026
b6b0331
Fix the stale documentation links in the changelog and release notes …
sequba Aug 27, 2026
3195e00
Fix MOD to return the remainder with the sign of the divisor (HF-357)…
sequba Aug 28, 2026
419028a
Fix MAXPOOL and MEDIANPOOL throwing on non-tiling dimensions (#1718)
sequba Aug 28, 2026
114fd5d
Update AGENTS.md
sequba Aug 28, 2026
286a731
docs(guide): named columns vs structured references (SU-637) (#1753)
AMBudnik Aug 31, 2026
50e7170
Fix localized VSTACK and HSTACK names (#1748)
Tobiadefami Sep 2, 2026
f9c50c1
fix: preserve zero results from AVERAGEIF (#1733)
Tobiadefami Sep 2, 2026
c920375
docs(HF-282): auto-derive function & language counts in docs (#1715)
marcin-kordas-hoc Sep 2, 2026
5abbbd9
Promote the first-party docs MCP server in the agent docs (#1775)
GreenFlux Sep 18, 2026
3a9c34b
HF-307: license-key entitlement gating — capability model, key reader…
marcin-kordas-hoc Oct 7, 2026
eff7928
Fix precision loss in variance, standard deviation and related statis…
marcin-kordas-hoc Oct 8, 2026
a704172
3.5.0
sequba Oct 8, 2026
55467f6
Point the README demo link at 3.5.x and make the release script rewri…
claude Oct 8, 2026
00c0dd7
Format MAXPOOL and MEDIANPOOL as code in the 3.5.0 changelog
claude Oct 8, 2026
60734d6
Fix the check:licenses exclusion and make the release script bump it
claude Oct 8, 2026
fe16eed
Correct the MAXPOOL, MEDIANPOOL and STEYX descriptions and the 3.5.0 …
claude Oct 8, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,11 @@ docs/examples/
# 3rd party
src/interpreter/plugin/3rdparty

# A byte-identical copy of handsontable/license-key's vendor/entitlement-key-reader - see
# PROVENANCE.md in that directory. Linting it would mean editing it, and editing it is the one
# thing it must not have; upstream runs strict tsc on it instead.
src/license/handsontable-license-key-parser/*.ts

# Configurations
*.config.js
karma.*
Expand Down
2 changes: 1 addition & 1 deletion .github/pull_request_template.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
### Checklist:
<!--- Go through the points below, and put an `x` in each box that applies. -->
<!--- If you're unsure about any of these, contact us. We're always glad to help! -->
- [ ] I have reviewed the guidelines about [Contributing to HyperFormula](https://hyperformula.handsontable.com/guide/contributing.html) and I confirm that my code follows the code style of this project.
- [ ] I have reviewed the guidelines about [Contributing to HyperFormula](https://hyperformula.handsontable.com/docs/guide/contributing.html) and I confirm that my code follows the code style of this project.
- [ ] I have signed the [Contributor License Agreement](https://goo.gl/forms/yuutGuN0RjsikVpM2).
- [ ] My change is compliant with the [OpenDocument](https://docs.oasis-open.org/office/OpenDocument/v1.3/os/part4-formula/OpenDocument-v1.3-os-part4-formula.html) standard.
- [ ] My change is compatible with Microsoft Excel.
Expand Down
42 changes: 42 additions & 0 deletions .github/workflows/vendored-parser.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
name: Vendored parser

# The entitlement-key reader under src/license/handsontable-license-key-parser/ is a verbatim
# copy of a private upstream directory. This job is the drift check that copy's integration guide
# asks for: it fails when any byte differs from the pinned tag, when a file shadows the copy, or
# when upstream has released a newer tag. See PROVENANCE.md in that directory.
#
# It needs LICENSE_KEY_REPO_TOKEN - a read-only token for handsontable/license-key - as a repository
# secret. Without it the check fails on purpose ("could not verify" is not "verified"), so this job
# stays red until the secret is set. Pull requests from forks never receive secrets and are skipped.

on:
pull_request:
types: [ opened, reopened, synchronize ]
paths:
- 'src/license/handsontable-license-key-parser/**'
- 'script/check-license-key-parser-drift.js'
- '.github/workflows/vendored-parser.yml'
push:
branches: [ master, develop ]
schedule:
- cron: '17 6 * * 1-5' # weekday mornings: a newer upstream tag is news even when nothing here changed

permissions:
contents: read

jobs:
drift:
if: github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository
runs-on: ubuntu-latest
timeout-minutes: 10
steps:
- name: Checkout
uses: actions/checkout@722adc63f1aa60a57ec37892e133b1d319cae598 # https://github.com/actions/checkout/releases/tag/v2.0.0
- name: Setup Node.js 22
uses: actions/setup-node@56899e050abffc08c2b3b61f3ec6a79a9dc3223d # https://github.com/actions/setup-node/releases/tag/v1.4.4
with:
node-version: '22'
- name: Compare the vendored reader with upstream
run: node script/check-license-key-parser-drift.js
env:
LICENSE_KEY_REPO_TOKEN: ${{ secrets.LICENSE_KEY_REPO_TOKEN }}
1 change: 1 addition & 0 deletions .typedoc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ module.exports = {
"./src/dependencyTransformers/**",
"./src/DependencyGraph/**",
"./src/ColumnSearch/**",
"./src/license/handsontable-license-key-parser/**",
],
"mode": "file",
"out": "./typedoc",
Expand Down
13 changes: 13 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,19 @@ Instructions for AI coding agents (Cursor, Claude Code, Codex, Aider, and any ot

Whatever you do, start by reading entire [DEV_DOCS.md](DEV_DOCS.md). Only then proceed to your task.

## Never publish sensitive information

Never write any of the following into a commit message, branch name, pull request title or description, GitHub issue or comment, code comment, changelog entry, or documentation page:

- client, customer, and partner names, or details that identify them indirectly (their domains, deployments, or the wording of their reports)
- personal data of any kind &mdash; names, e-mail addresses, phone numbers, user accounts, IP addresses
- credentials and secrets &mdash; API keys, tokens, passwords, license keys, private URLs
- internal-only material &mdash; contents of private repositories and internal tickets, unreleased plans, contract and pricing details

Describe the change on its own technical terms instead: write "fix an off-by-one error in `SUMIFS` when the criteria range is empty", not "fix the bug reported by \<company\>". An internal ticket identifier such as `HF-123` is fine on its own; the contents of that ticket are not.

If a change cannot be described without such information, stop and ask the user how to proceed.

## Other important resources

- the repository [README.md](README.md) &mdash; high-level project description and quick install/usage
Expand Down
31 changes: 28 additions & 3 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,31 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

## [Unreleased]

## [3.5.0] - 2026-10-09

### Added

- Added support for the new license key format. A proprietary key can now grant a subset of the library: a function your key does not include evaluates to a `#LIC!` error, and the matching parts of the API throw a `LicenseCapabilityMissingError`. `getAvailableFunctions()` and `getFunctionDetails()` describe only the functions your key includes. [#1728](https://github.com/handsontable/hyperformula/pull/1728)

### Changed

- Changed the `VERSION` function to return only the HyperFormula version (e.g. `HyperFormula v3.4.0`), without the license key status. [#1728](https://github.com/handsontable/hyperformula/pull/1728)
- Changed the API methods gated by the license key to throw a `LicenseCapabilityMissingError` when the key is missing or invalid, when a classic key has expired, or when a trial key is past its grace period. The gated methods are the ones that edit cells, rows, columns, and sheets, `copy()`, `cut()`, `paste()`, `undo()`, `redo()`, `batch()`, `suspendEvaluation()`, and the methods that add, change, or remove named expressions. Building an engine with named expressions throws the same error, and the matching `isItPossibleTo*()` methods, `isThereSomethingToUndo()`, and `isThereSomethingToRedo()` return `false`. [#1728](https://github.com/handsontable/hyperformula/pull/1728)
- Changed the `MAXPOOL` and `MEDIANPOOL` functions to accept a stride greater than the window size in a cell, where they returned the `#VALUE!` error before. The windows then skip the rows and columns between them, as `calculateFormula()` already did. [#1718](https://github.com/handsontable/hyperformula/pull/1718)

### Fixed

- Fixed the validation of classic (25-character) license keys depending on the time zone: east of UTC, a key that expired the day before the build was released was still accepted, and west of UTC, the console message printed an expiry date one day too early. [#1728](https://github.com/handsontable/hyperformula/pull/1728)
- Fixed the `AVERAGEIF` function returning a division-by-zero error when the calculated average was `0`. [#1733](https://github.com/handsontable/hyperformula/pull/1733)
- Fixed the localized names of `VSTACK` and `HSTACK` in 14 language packs to match Microsoft Excel. [#1748](https://github.com/handsontable/hyperformula/pull/1748)
- Fixed the `MAXPOOL` and `MEDIANPOOL` functions throwing an uncaught `TypeError` or `RangeError`. They now return the `#VALUE!` error when the window is larger than the range or the range dimensions, reduced by the window size, are not whole multiples of the stride, and the `#NUM!` error when the window size or the stride is not a positive integer. [#1718](https://github.com/handsontable/hyperformula/pull/1718)
- Fixed the `VAR`, `STDEV`, `DEVSQ`, `COVARIANCE.P`, `COVARIANCE.S`, `SLOPE`, `STEYX`, `DVAR` and `DSTDEV` functions, their variants, and the matching `SUBTOTAL` modes losing precision on data with a large mean and a small spread. [#1784](https://github.com/handsontable/hyperformula/pull/1784)
- Fixed a bug where `SLOPE` and `STEYX` returned `#NUM!` or an arbitrary number instead of `#DIV/0!` when all the x values are equal. [#1784](https://github.com/handsontable/hyperformula/pull/1784)
- Fixed a bug where `STEYX` returned `#NUM!` or `0` instead of the standard error for points that lie almost on a line. [#1784](https://github.com/handsontable/hyperformula/pull/1784)
- Fixed a bug where `VAR`, `STDEV`, `DVAR`, `DSTDEV`, `COVARIANCE.P`, `COVARIANCE.S`, `SLOPE`, `STEYX`, their variants, and the matching `SUBTOTAL` modes returned an error or `0` for very large or very small values although the result was within the range of numbers, and `DEVSQ` returned `0` for very small values. [#1784](https://github.com/handsontable/hyperformula/pull/1784)
- Fixed the `MOD` function returning a remainder with the sign of the dividend instead of the sign of the divisor, which made the results differ from Excel and Google Sheets for arguments with opposite signs (e.g. `=MOD(-3, 12)` now returns `9` instead of `-3`). [#1747](https://github.com/handsontable/hyperformula/issues/1747)
- Fixed a bug where moving or pasting a formula with an undefined name to another sheet incorrectly added an empty global named expression. [#1728](https://github.com/handsontable/hyperformula/pull/1728)

## [3.4.0] - 2026-08-10

### Added
Expand Down Expand Up @@ -242,7 +267,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
### Removed

- Removed all polyfills from the CommonJS build and the ES modules build. In the UMD build, kept only the polyfills
required by the [supported browsers](https://hyperformula.handsontable.com/guide/supported-browsers.html).
required by the [supported browsers](https://hyperformula.handsontable.com/docs/guide/supported-browsers.html).
[#1011](https://github.com/handsontable/hyperformula/issues/1011)

## [2.0.1] - 2022-06-14
Expand All @@ -261,9 +286,9 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),

For more information on this release, see:

- [Release notes](https://hyperformula.handsontable.com/guide/release-notes.html)
- [Release notes](https://hyperformula.handsontable.com/docs/guide/release-notes.html)
- [Blog post](https://handsontable.com/blog/articles/2022/04/whats-new-in-hyperformula-2.0.0)
- [Migration guide](https://hyperformula.handsontable.com/guide/migration-from-1.0-to-2.0.html)
- [Migration guide](https://hyperformula.handsontable.com/docs/guide/migration-from-1.x-to-2.0.html)

### Added

Expand Down
36 changes: 32 additions & 4 deletions DEV_DOCS.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,9 @@ Canonical reference for everyone working on the HyperFormula source code: mainta
│ │ └── plugin/ # Built-in spreadsheet function plugins
│ ├── DependencyGraph/ # Cell dependency tracking and recalculation order
│ ├── CrudOperations.ts # Create/read/update/delete operations on sheets and cells
│ └── i18n/ # Function-name translations per language
│ ├── i18n/ # Function-name translations per language
│ └── license/ # License key reading, capability tables, and license gating
│ └── handsontable-license-key-parser/ # Vendored key reader; do not edit
├── test/ # Test suite
├── docs/ # Public documentation portal (VuePress)
│ ├── guide/ # Markdown guides (building, contributing, usage…)
Expand Down Expand Up @@ -51,6 +53,13 @@ Canonical reference for everyone working on the HyperFormula source code: mainta
- `src/interpreter/` &mdash; formula evaluation engine
- `src/DependencyGraph/` &mdash; cell dependency tracking and recalculation order
- `src/CrudOperations.ts` &mdash; create/read/update/delete operations on sheets and cells
- `src/license/` &mdash; license key handling: reads the key, maps its capability tokens to functions (`functionCapabilities.ts`) and public API features (`featureCapabilities.ts`), and gates function calls and API methods

### License key reader (`src/license/handsontable-license-key-parser/`)

A copy of the entitlement-key reader published by the `handsontable/license-key` repository, taken whole from a tagged release. Don't edit it: fix the problem upstream and take the copy again, as described in `PROVENANCE.md` in that directory. ESLint ignores the directory.

`npm run check:license-key-parser-drift` compares the copy with the pinned upstream commit and fails on any difference or on a newer upstream tag. It needs read access to the upstream repository, so it runs in its own CI workflow rather than in `npm run test` or `npm run lint`.

### Function plugins (`src/interpreter/plugin/`)

Expand Down Expand Up @@ -125,8 +134,9 @@ Adding a built-in function is similar to adding a [custom function](docs/guide/c
2. Add function metadata to `implementedFunctions`.
3. Implement the function method.
4. Add a catalogue entry to `src/interpreter/functionMetadata/categories/<category>.ts` (see below).
5. Add translations to all language files in `src/i18n/languages/`.
6. Add tests in `test/unit/interpreter/`.
5. Add the function to the license capability table in `src/license/functionCapabilities.ts` (see below).
6. Add translations to all language files in `src/i18n/languages/`.
7. Add tests in `test/unit/interpreter/`.

### The function metadata catalogue

Expand All @@ -153,17 +163,35 @@ It does **not** turn ordinary English into identifiers. A parameter's own descri

Note what the drift warning does **not** cover: **optionality is not cross-checked.** The catalogue authors no optionality of its own &mdash; a parameter's `optional` flag is derived entirely from `optionalArg`/`defaultValue` in `implementedFunctions` &mdash; so a description that calls an argument optional can sit next to `optional: false` with nothing failing. When a function accepts a call that arity alone does not express (`SHEET()`, `ROW()`, and anything else served by `runFunctionWithReferenceArgument`'s zero-argument path), the plugin must declare `optionalArg: true` explicitly, or the public API will advertise the argument as required. `ROW`, `COLUMN`, `SHEET` and `SHEETS` all declare it; `ISFORMULA` takes the same path and correctly does not, because its zero-argument call is an error rather than a shorthand.

Descriptions must describe **HyperFormula's** behaviour, not Excel's. Much of the catalogue was seeded from a hand-written page that documented Excel, and HyperFormula deliberately deviates in places (`INT` truncates toward zero, `MOD` takes the sign of the dividend, `ISEVEN`/`ISODD` do not truncate, `CEILING.MATH`/`FLOOR.MATH` honour only `mode` = 1). Verify a claim against the implementation before authoring it, and record any deviation in [the list of differences](docs/guide/list-of-differences.md).
Descriptions must describe **HyperFormula's** behaviour, not Excel's. Much of the catalogue was seeded from a hand-written page that documented Excel, and HyperFormula deliberately deviates in places (`INT` truncates toward zero, `ISEVEN`/`ISODD` do not truncate, `CEILING.MATH`/`FLOOR.MATH` honour only `mode` = 1). Verify a claim against the implementation before authoring it, and record any deviation in [the list of differences](docs/guide/list-of-differences.md).

### The license capability table

`src/license/functionCapabilities.ts` decides which license keys can call a built-in function. Add every new built-in either to its group in `FUNCTION_GROUPS` or, if it belongs to no group, to `UNGROUPED_FUNCTIONS`. An ungrouped function is granted only by `fun:all` and by its own `fun:<name>` token. Both lists use canonical names: an alias needs no entry, because it is gated as its canonical function.

A built-in missing from the table escapes the entitlement check: every key that lets formulas evaluate can call it, including a key that grants no functions. The completeness check in the private test suite (`unit/license/capability-registry.spec.ts`) fails on such a function, but the public smoke tests do not.

Group names are part of the license key format, so never rename a group or remove a function from one. Adding a function to a group grants it to every key that already carries that group's token.

## Internationalization and function translations

HyperFormula supports internationalization and provides localized function names for all built-in languages. Translation files live in `src/i18n/languages/`. New functions must include translations for all built-in languages.

Only add a localized function name after confirming that Microsoft Excel ships that exact name. If an authoritative source does not provide a localized name, keep the English name instead of translating or inferring one.

When looking for the valid translations for new functions, try these sources:

- https://support.microsoft.com/en-us/office/excel-functions-translator-f262d0c0-991c-485b-89b6-32cc8d326889
- http://dolf.trieschnigg.nl/excel/index.php

For recent Excel functions that are absent from those sources, use Microsoft's localized alphabetical function list:

```text
https://support.microsoft.com/<locale>/office/excel-functions-alphabetical-b3944572-255d-4efb-bb96-c6d90033e188
```

Find the link whose URL contains `functions/<english-name>-function`, then follow it and confirm that the individual function page uses the same localized name in its title and formula syntax. Check each locale independently because Excel keeps some function names in English. If the alphabetical list and the individual page disagree, do not copy the list entry without additional product verification.

For languages not officially supported by Microsoft Excel, the two sources above do not apply. For these languages, use Google Sheets as the reference. Switch the `hl` query parameter to the target locale, for example:

- https://support.google.com/docs/table/25273?hl=id (Indonesian)
Expand Down
7 changes: 7 additions & 0 deletions DOCS_CONTENT_GUIDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -220,6 +220,13 @@ and hallucinate the parts you omit.
description: Register and use your own functions in HyperFormula.
---
```
- Add a `tags` frontmatter **list** to make a page findable by words it does not use in
its title or its `##`/`###` headings — the search box matches those three things only,
never the body text (see `docs/guide/setup-coding-agent.md`, tagged `skills`, `Cursor`,
`MCP`). Keep it to at most 10 tags a page, each one a word a reader would really type, and
leave a query to the page that answers it best — a tag ten pages claim is decided by file
order, not relevance, once the 10-result limit bites. A plain string instead of a list breaks
the search box site-wide.
- Use containers for asides: `::: tip`, `::: warning`, `::: danger` … `:::`. Put
essential steps in the body, not hidden inside a tip.
- Use **relative links** between guide pages (`./named-expressions.md`) and link to
Expand Down
Loading
Loading