From 6f0688bb157982019833769fa013fa5cd7052442 Mon Sep 17 00:00:00 2001 From: marcin-kordas-hoc Date: Sun, 25 Oct 2026 01:50:36 +0000 Subject: [PATCH] HF-307: re-take the vendored entitlement-key reader from license-key 6.1.0 The reader in hyperformula/src/license/handsontable-license-key-parser/ is now upstream's vendor/entitlement-key-reader at tag 6.1.0. Between 5.1.1 and 6.1.0 only README.md changed in that directory, so the reader code, the one declared TypeScript 4.0 cast and HyperFormula's behaviour stay the same. The pin in upstream.json and the table in PROVENANCE.md name the new tag. Co-Authored-By: Claude Opus 5.5 --- .../PROVENANCE.md | 2 +- .../handsontable-license-key-parser/README.md | 19 +++++++++++++------ .../upstream.json | 6 +++--- 3 files changed, 17 insertions(+), 10 deletions(-) diff --git a/hyperformula/src/license/handsontable-license-key-parser/PROVENANCE.md b/hyperformula/src/license/handsontable-license-key-parser/PROVENANCE.md index 36aa544e8..e13725ba3 100644 --- a/hyperformula/src/license/handsontable-license-key-parser/PROVENANCE.md +++ b/hyperformula/src/license/handsontable-license-key-parser/PROVENANCE.md @@ -10,7 +10,7 @@ and re-taken: a local fix makes two products disagree about the same key. |---|---| | Repository | `handsontable/license-key` (private) | | Directory | `vendor/entitlement-key-reader` | -| Tag | `5.1.1` (`124660718`, 2026-10-06) | +| Tag | `6.1.0` (`824cfd4bc`, 2026-10-20) | | Pin | `upstream.json` | ## One declared divergence diff --git a/hyperformula/src/license/handsontable-license-key-parser/README.md b/hyperformula/src/license/handsontable-license-key-parser/README.md index c94788a67..fee1842f4 100644 --- a/hyperformula/src/license/handsontable-license-key-parser/README.md +++ b/hyperformula/src/license/handsontable-license-key-parser/README.md @@ -62,7 +62,7 @@ A key is plain-English text for humans, then a block for the library: ``` This is a Handsontable license key for Acme Corp, issued on 2026-08-12. It includes 1 license: -> 1. Subscription license under Handsontable Subscription License Agreement 2.0 of 2022-05-21, for Handsontable on the Enterprise package, for internal use, valid until 2027-08-12 (UTC). Use after that date is not permitted. To renew, contact sales@handsontable.com. +> 1. Subscription license under Handsontable Subscription License Agreement 2.0 of 2022-05-21, for Handsontable, for internal use, valid until 2027-08-12 (UTC). Use after that date is not permitted. To renew, contact sales@handsontable.com. [eyJwcm9kdWN0cyI6eyJoYW5k......<128 hex characters of SHA-512>] ``` @@ -85,7 +85,7 @@ This is a Handsontable license key for Acme Corp, issued on 2026-08-12. It inclu { "products": { "hyperformula": { - "capabilities": ["functions_1", "functions_2", "spreadsheet"], + "capabilities": ["fun:math.a", "fun:stat.a", "feat:crud"], "release_until": "2027-03-31", "notice": 0, "grace": 0, @@ -199,7 +199,10 @@ export function checkLicense(rawKey: unknown): LicenseGrants { // Anywhere a feature is gated: const grants = checkLicense(settings.licenseKey); -if (!hasCapability(grants, PRODUCT, 'spreadsheet')) { +// The token exactly as the key carries it. A product whose tokens mean more than +// themselves (HyperFormula's `fun:all`, `feat:all`, `fun:math.a`) resolves them in +// its own registry instead - see "What each product supplies". +if (!hasCapability(grants, PRODUCT, 'core')) { // not unlocked by this key } ``` @@ -275,9 +278,13 @@ Each of these has broken a real implementation, or a specification fixture exist | `product` | `'handsontable'` | `'hyperformula'` | | Build date | `process.env.HOT_RELEASE_DATE` (`DD/MM/YYYY`) | `process.env.HT_RELEASE_DATE` (`DD/MM/YYYY`) | | Literal keys | `non-commercial-and-evaluation`, `ht68e-1f2b7-47158-70b05-0842f` | `gpl-v3`, `internal-use-in-handsontable`, `hftrial-0168e-1f2b7-47158-70b05-0842f` | -| Capability tokens | `core` | `functions_1` ... `functions_4`, `spreadsheet`, `import_export` | +| Capability tokens | `core` | `fun:` and `feat:` tokens of the HyperFormula license key syntax spec (`fun:all`, `fun:math.a`, `fun:xnpv`, `feat:all`, `feat:crud`, ...) | | Legacy keys | own obfuscated validator | own obfuscated validator | +`hasCapability` compares a token exactly, as the key spells it, and expands nothing. The generator issues lowercase tokens only, so compare against lowercase. A product that wants to be lenient (HyperFormula lowercases and trims) does it in its own code - the reader keeps the token as the key carries it. + +**Tokens that stand for more than themselves are the product's to expand.** A HyperFormula key issued today carries `["core", "fun:all", "feat:all"]`. `hasCapability(grants, 'hyperformula', 'feat:crud')` answers `false` for it - the key does not contain `feat:crud`, it contains `feat:all`. Gate on what the product's own registry makes of the tokens (`getProductCapabilities` returns them; it returns `null` for unrestricted grants, which unlock everything, and for a product the key does not grant): HyperFormula resolves `fun:all`, `feat:all` and group tokens such as `fun:math.a` in its own capability table. Asking `hasCapability` for one feature's token would lock out a customer who bought all of them. Do not gate on `core` either: HyperFormula ignores it, and HyperFormula keys issued by license-key 5.2.0, or from a schema without the default, carry none. + Pass **every** literal key the product accepts today, including the ones shaped like a legacy key: checked first, they never reach the legacy validator. The lists above are what each product accepted when this reader was written - check the product's own validator before copying them. A new product needs the same five things: a product name agreed with my.handsontable.com, a build date, its literal keys (maybe none), its capability tokens, and a decision about its legacy keys (maybe none). @@ -307,12 +314,12 @@ The windows are evaluated in this directory and are already covered by `license- - the build date constant reaches `readEntitlementLicense` in your real bundle, not only in tests, - pin `now` (or mock `Date.now`) in every test - never rely on the real clock. -For fixture keys, generate real ones with the `license-key` CLI, so a misunderstanding shared by your test and your code cannot pass: +For fixture keys, generate real ones with the `license-key` CLI, so a misunderstanding shared by your test and your code cannot pass. List the capability tokens in the record - the default schema gives both Handsontable and HyperFormula `core` (HyperFormula does not read it), and adds what the record lists. The tokens below are the HyperFormula license key syntax spec's Example 1: ```bash # in a checkout of license-key npm install && npm run build -npm run generate-entitlement-key --record='{"holder":"Test Fixture","issued":"2026-08-12","licenses":[{"product":"hyperformula","contractType":"perpetual","agreement":"perpetual-2.0","package":"Pro","mode":"internal","date":"2027-03-31","notice":0,"grace":0}]}' +npm run generate-entitlement-key --record='{"holder":"Test Fixture","issued":"2026-08-12","licenses":[{"product":"hyperformula","contractType":"perpetual","agreement":"perpetual-2.0","capabilities":["fun:math.a","fun:stat.a","fun:logic.a","fun:operator.a","fun:info.a","fun:lookup.a","fun:time.b","fun:text.b","fun:logic.b","fun:math.b","fun:stat.b","feat:crud","feat:undo_redo"],"mode":"internal","date":"2027-03-31","notice":0,"grace":0}]}' ``` For the shapes the generator refuses (both dates, a bad date, an unknown token), build the key yourself in a test-only helper - kept in a `__tests__/` directory beside the copy, and never imported from your source: diff --git a/hyperformula/src/license/handsontable-license-key-parser/upstream.json b/hyperformula/src/license/handsontable-license-key-parser/upstream.json index 05188fce1..4235f7191 100644 --- a/hyperformula/src/license/handsontable-license-key-parser/upstream.json +++ b/hyperformula/src/license/handsontable-license-key-parser/upstream.json @@ -14,9 +14,9 @@ ], "repository": "handsontable/license-key", "directory": "vendor/entitlement-key-reader", - "tag": "5.1.1", - "commit": "12466071875bfb3b807e8321d9d5e9c9a8551b09", - "taken_on": "2026-10-07", + "tag": "6.1.0", + "commit": "824cfd4bc478619b0f994001f087650a5f7366ae", + "taken_on": "2026-10-25", "own_files": [ "PROVENANCE.md", "upstream.json"