HF-307 PR 4/4: describe only the functions the license grants - #1731
Open
marcin-kordas-hoc wants to merge 1 commit into
Open
HF-307 PR 4/4: describe only the functions the license grants#1731marcin-kordas-hoc wants to merge 1 commit into
marcin-kordas-hoc wants to merge 1 commit into
Conversation
Contributor
Deploying with
|
| Status | Name | Latest Commit | Preview URL | Updated (UTC) |
|---|---|---|---|---|
| ✅ Deployment successful! View logs |
hyperformula-docs | 8d05428 | Commit Preview URL Branch Preview URL |
Aug 13 2026, 01:59 PM |
Performance comparison of head (8d05428) vs base (7c25bbc) |
marcin-kordas-hoc
force-pushed
the
hf-307-entitlement-gating-pr4
branch
from
August 12, 2026 17:13
7dd0eb2 to
bb874d8
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 13, 2026 13:55
bb874d8 to
8d05428
Compare
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 3557 3486 -71
==============================================================
+ Hits 15770 15784 +14
- Misses 416 424 +8
+ Partials 8 0 -8
🚀 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 metadata API behaviour for restricted proprietary keys and must stay in sync with interpreter licensing; scope is narrow and gate-A vs gate-B split is explicit and tested.
Overview
Function pickers no longer advertise built-ins that would return
#LIC!on a valid restricted key.getAvailableFunctions()andgetFunctionDetails()filter throughlicenseListsFunction, which uses the same gate-B rule and alias canonicalisation as the interpreter via new shared helperlicenseAllowsFunctioninCapabilityRegistry. The interpreter’sFUNCTION_CALLpath is updated to call that helper instead of duplicating the condition.Listing behaviour for key problems is intentionally unchanged. The filter consults entitlement only, not
LicenseKeyValidityState: a missing, invalid, or expired key still yields the full catalogue (cells still get#LIC!), so integrators can build a picker before a key is wired. Custom registered functions stay listed regardless of the key.User-facing docs:
license-key.mdadds “feature packages and add-ons”;#LIC!intypes-of-errors.mdalso covers “not in your package”; CHANGELOG records the metadata API change under Unreleased.Reviewed by Cursor Bugbot for commit 8d05428. Bugbot is set up for automated code reviews on this repo. Configure here.