HF-307 PR 4/4: describe only the functions the license grants - #1731
Open
marcin-kordas-hoc wants to merge 2 commits into
Open
HF-307 PR 4/4: describe only the functions the license grants#1731marcin-kordas-hoc wants to merge 2 commits into
marcin-kordas-hoc wants to merge 2 commits into
Conversation
Contributor
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
hyperformula-docs | 5d67b5d | Commit Preview URL Branch Preview URL |
Aug 17 2026, 06:20 AM |
Performance comparison of head (5d67b5d) vs base (05882e1) |
marcin-kordas-hoc
force-pushed
the
hf-307-entitlement-gating-pr4
branch
3 times, most recently
from
August 16, 2026 06:51
8d05428 to
199f4f1
Compare
`getAvailableFunctions` and `getFunctionDetails` read straight from the function registry, with no license filter, while the interpreter gates the same functions per call. A restricted key therefore advertised functions that return `#LIC!` when called - the exact failure removing the static metadata methods (HF-349) was meant to prevent, left half-delivered because the instance methods never learned to read the key their rationale said they could. Both now filter through `licenseListsFunction`, which shares one `licenseAllowsFunction` rule with the interpreter rather than spelling the same condition out twice, and canonicalises aliases the same way. Extracting that rule is the point: two copies would drift, and the drift is invisible until a customer's picker offers a function that fails. Gate B only, deliberately - never the validity state. A missing, invalid or expired key resolves to an unrestricted entitlement (the invariant), so it reaches the filter with `unrestricted` set and keeps the whole catalogue. That falls out of the invariant rather than being a second decision, and it is the useful answer: narrowing to the two protected built-ins would hand an integrator who has not wired up their key yet an empty function picker and no clue why. The list narrows only for a *valid* key that genuinely excludes a function. Also documents `#LIC!` in types-of-errors.md, which listed only key problems and not "function not in your package", and adds the CHANGELOG entry the feature has not carried so far - PRs 1-3 were internals by design. The guide deliberately documents the mechanism, not the package contents: HF-306 is still in review with six open questions, so publishing the lists now would put moving targets in the public docs. Tests: handsontable/hyperformula-tests#33 Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GuUdq242TtFaepKRNdEkj9
marcin-kordas-hoc
force-pushed
the
hf-307-entitlement-gating-pr4
branch
from
August 16, 2026 08:53
199f4f1 to
cd56f70
Compare
Re-derived every function's lowest package straight from the 21 fun:<family>.<A|B|C> group tokens in CU doc 8cnjcyf-33175/8cnjcyf-47835 and re-partitioned MATH_ENGINE_FUNCTIONS, CALCULATED_FIELDS_FUNCTIONS, SPREADSHEET_FUNCTIONS and EXCEL_SIMULATOR_FUNCTIONS to match. No function was added or removed (353 total, before and after) - only reassigned to its correct tier. The prior table was materially stale: missing 6/22/50 functions at the three lower tiers respectively, with some (e.g. INT, STDEV.S) sitting a tier too high. OFFSET and VERSION remain deliberately excluded from every list: both are named by the doc but are protected built-ins outside the token system today (see hf-306-token-vocabulary-final memory for the two different root causes and what closing each would take - out of scope here). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GuUdq242TtFaepKRNdEkj9
Codecov Report✅ All modified and coverable lines are covered by tests. Additional details and impacted files@@ Coverage Diff @@
## hf-307-entitlement-gating-pr3 #1731 +/- ##
==============================================================
Coverage 97.38% 97.38%
==============================================================
Files 204 204
Lines 16194 16208 +14
Branches 3482 3486 +4
==============================================================
+ Hits 15770 15784 +14
Misses 424 424
🚀 New features to boost your workflow:
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Last of four. Stacks on
hf-307-entitlement-gating-pr3(#1730) — merge that first. Tests: handsontable/hyperformula-tests#33.Context
HF-307, decision D2.
getAvailableFunctions()andgetFunctionDetails()read straight from the function registry with no license filter, while the interpreter gates the same functions per call. A restricted key therefore advertised functions that return#LIC!when called — precisely the failure #1724 (HF-349) removed the static variants to prevent:That PR removed the static methods on the rationale that "an instance knows its license key, so it can answer for the engine the caller actually holds". The instance methods never learned to read it. This finishes the job.
The change
Both methods now filter through
licenseListsFunction, which shares onelicenseAllowsFunctionrule with the interpreter rather than spelling the same condition out twice, and canonicalises aliases the same way. Extracting that rule is the point of the PR as much as the filter is: two copies would drift, and the drift is invisible until a customer's function picker offers something that fails.The one decision worth reviewing
The filter reads gate B only — never the key's validity state.
A missing, invalid or expired key resolves to an unrestricted entitlement (the invariant PR 3 documents), so it reaches the filter with
unrestrictedset and keeps the whole catalogue. That falls out of the invariant rather than being a second decision — but it is a deliberate one, and the alternative is defensible, so it is worth an explicit look.I chose it because narrowing on gate A would return two functions (the protected built-ins) to anyone who calls the API before configuring a key — an empty function picker with no clue why, for the exact integrator still wiring things up. A key problem is already reported on the console and by
#LIC!in cells. The list narrows only for a valid key that genuinely excludes a function, which is when the answer is useful.Pinned by tests in both directions; folding gate A into the filter fails 3 of them.
Also
types-of-errors.mddescribed#LIC!as only ever meaning a key problem. It now also means "not in your package".How did you test your changes?
npx tsc --noEmit: clean.npm run lint: 0 errorsnpm run docs:generate-function-docs: succeeds, and the generated reference still documentsBITAND/VLOOKUP/XIRR— the generator builds withgpl-v3, so the published docs do not narrow to a tierNot run here:
npm run test:browser(Karma needs Firefox, unavailable in this environment). No Jest-only matcher forms were used.Types of changes
Not marked breaking: the narrowing only happens for a valid restricted key, and no such key can exist for a released version yet.
Related issues
Checklist
The three compatibility boxes are left unticked as not applicable: this change touches no formula semantics.
Found while working on this, NOT fixed here
=OFFSET()with no arguments throws aTypeErrorout of the parser instead of returning an error value —handleOffsetHeuristic(src/parser/FormulaParser.ts:764) readsargs[0].typewith no arity check. Pre-existing, unrelated to licensing, and out of scope; flagging it for a separate issue.Note
Medium Risk
Changes public API surface for restricted keys and reshuffles which built-ins belong to each package tier; behavior for invalid keys is intentionally unchanged for listing but still fails in formulas.
Overview
getAvailableFunctions()andgetFunctionDetails()now list only functions the instance’s license can evaluate, so a function picker built from them won’t advertise ids that return#LIC!.Both paths filter through
licenseListsFunction, which uses the samelicenseAllowsFunctionrule and alias canonicalization as the interpreter’s gate-B check.licenseAllowsFunctionis extracted inCapabilityRegistryso the metadata API and interpreter can’t drift.Gate B only: a missing, invalid, or expired key does not shorten the catalogue (unrestricted entitlement); cells still get
#LIC!, but integrators can build pickers before a key is configured. Custom registered functions stay listed.Docs and CHANGELOG cover feature packages,
#LIC!semantics, and the updated function-to-package lists incapabilities.ts(aligned with Kuba’s packaging doc).Reviewed by Cursor Bugbot for commit 5d67b5d. Bugbot is set up for automated code reviews on this repo. Configure here.