handsontable 18.1.1 → 18.1.2-rc2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +8 -0
- package/base.js +2 -2
- package/base.mjs +2 -2
- package/dataMap/metaManager/metaSchema.js +4 -2
- package/dataMap/metaManager/metaSchema.mjs +4 -2
- package/dist/handsontable.full.js +223 -44
- package/dist/handsontable.full.min.js +129 -129
- package/dist/handsontable.js +223 -44
- package/dist/handsontable.min.js +129 -129
- package/dist/themes/classic.js +2 -2
- package/dist/themes/classic.min.js +2 -2
- package/dist/themes/horizon.js +2 -2
- package/dist/themes/horizon.min.js +2 -2
- package/dist/themes/main.js +2 -2
- package/dist/themes/main.min.js +2 -2
- package/dist/themes/static/variables/colors/ant.js +2 -2
- package/dist/themes/static/variables/colors/ant.min.js +2 -2
- package/dist/themes/static/variables/colors/classic.js +2 -2
- package/dist/themes/static/variables/colors/classic.min.js +2 -2
- package/dist/themes/static/variables/colors/horizon.js +2 -2
- package/dist/themes/static/variables/colors/horizon.min.js +2 -2
- package/dist/themes/static/variables/colors/main.js +2 -2
- package/dist/themes/static/variables/colors/main.min.js +2 -2
- package/dist/themes/static/variables/colors/material.js +2 -2
- package/dist/themes/static/variables/colors/material.min.js +2 -2
- package/dist/themes/static/variables/colors/shadcn.js +2 -2
- package/dist/themes/static/variables/colors/shadcn.min.js +2 -2
- package/dist/themes/static/variables/density.js +2 -2
- package/dist/themes/static/variables/density.min.js +2 -2
- package/dist/themes/static/variables/helpers/iconsMap.js +2 -2
- package/dist/themes/static/variables/helpers/iconsMap.min.js +2 -2
- package/dist/themes/static/variables/icons/horizon.js +2 -2
- package/dist/themes/static/variables/icons/horizon.min.js +2 -2
- package/dist/themes/static/variables/icons/main.js +2 -2
- package/dist/themes/static/variables/icons/main.min.js +2 -2
- package/dist/themes/static/variables/sizing.js +2 -2
- package/dist/themes/static/variables/sizing.min.js +2 -2
- package/dist/themes/static/variables/tokens/classic.js +2 -2
- package/dist/themes/static/variables/tokens/classic.min.js +2 -2
- package/dist/themes/static/variables/tokens/horizon.js +2 -2
- package/dist/themes/static/variables/tokens/horizon.min.js +2 -2
- package/dist/themes/static/variables/tokens/main.js +2 -2
- package/dist/themes/static/variables/tokens/main.min.js +2 -2
- package/helpers/mixed.js +5 -5
- package/helpers/mixed.mjs +5 -5
- package/package.json +1 -1
- package/styles/handsontable.css +2 -2
- package/styles/handsontable.min.css +2 -2
- package/styles/handsontableStyles.js +1 -1
- package/styles/handsontableStyles.mjs +1 -1
- package/styles/ht-icons-horizon.min.css +2 -2
- package/styles/ht-icons-main.min.css +2 -2
- package/styles/ht-theme-classic-no-icons.min.css +2 -2
- package/styles/ht-theme-classic.min.css +2 -2
- package/styles/ht-theme-horizon-no-icons.min.css +2 -2
- package/styles/ht-theme-horizon.min.css +2 -2
- package/styles/ht-theme-main-no-icons.min.css +2 -2
- package/styles/ht-theme-main.min.css +2 -2
- package/utils/entitlementLicenseKey/constants.d.ts +7 -1
- package/utils/entitlementLicenseKey/constants.js +5 -1
- package/utils/entitlementLicenseKey/constants.mjs +6 -1
- package/utils/entitlementLicenseKey/encoding.d.ts +1 -1
- package/utils/entitlementLicenseKey/encoding.js +4 -1
- package/utils/entitlementLicenseKey/encoding.mjs +4 -1
- package/utils/entitlementLicenseKey/extractKeyData.d.ts +47 -8
- package/utils/entitlementLicenseKey/extractKeyData.js +155 -14
- package/utils/entitlementLicenseKey/extractKeyData.mjs +188 -23
- package/utils/entitlementLicenseKey/types.d.ts +5 -0
- package/utils/licenseBranding/index.js +2 -2
- package/utils/licenseBranding/index.mjs +2 -2
- package/utils/licenseNotification.js +2 -2
- package/utils/licenseNotification.mjs +2 -2
|
@@ -1,14 +1,90 @@
|
|
|
1
|
-
import { CHECKSUM_LENGTH, DATE_FIELDS } from './constants.mjs';
|
|
1
|
+
import { CHECKSUM_LENGTH, DATE_FIELDS, PROSE_DIGEST_LENGTH } from './constants.mjs';
|
|
2
2
|
import { sha512 } from './sha512.mjs';
|
|
3
3
|
import { base64ToString, stringToUtf8Bytes, parseIsoDateToTimestamp } from './encoding.mjs';
|
|
4
4
|
/**
|
|
5
|
-
* The
|
|
6
|
-
*
|
|
7
|
-
* split by a fixed length from the right.
|
|
5
|
+
* The alphabets of the two parts of the machine-readable block. The second
|
|
6
|
+
* part has a fixed length, so the two are split from the right.
|
|
8
7
|
*
|
|
9
8
|
* @type {RegExp}
|
|
10
9
|
*/ const ENCODED_PAYLOAD = /^[A-Za-z0-9\-_]+$/;
|
|
11
10
|
const CHECKSUM = /^[0-9a-f]+$/;
|
|
11
|
+
/**
|
|
12
|
+
* The whitespace the reader ignores, in the text and inside the block: TAB,
|
|
13
|
+
* LF, VT, FF, CR, SPACE, NO-BREAK SPACE,
|
|
14
|
+
* OGHAM SPACE MARK, the U+2000-U+200A spaces, LINE SEPARATOR, PARAGRAPH
|
|
15
|
+
* SEPARATOR, NARROW NO-BREAK SPACE, MEDIUM MATHEMATICAL SPACE, IDEOGRAPHIC
|
|
16
|
+
* SPACE, and the BOM.
|
|
17
|
+
*
|
|
18
|
+
* Listed explicitly instead of `\s`, whose set has changed between JavaScript
|
|
19
|
+
* engines (U+180E) and differs in other languages (U+0085), so it matches the
|
|
20
|
+
* key generator exactly.
|
|
21
|
+
*
|
|
22
|
+
* @type {RegExp}
|
|
23
|
+
*/ const PROSE_WHITESPACE = /[\t\n\v\f\r \u00a0\u1680\u2000-\u200a\u2028\u2029\u202f\u205f\u3000\ufeff]+/g;
|
|
24
|
+
/**
|
|
25
|
+
* A line break or tab that was saved as text - a backslash followed by "n",
|
|
26
|
+
* "r", or "t" - also ignored.
|
|
27
|
+
*
|
|
28
|
+
* Several places a key is stored keep a line break that way rather than as a
|
|
29
|
+
* real one: a single-quoted or unquoted `.env` value, Docker's `--env-file`,
|
|
30
|
+
* and many CI secret fields. The block survives that intact, so without this
|
|
31
|
+
* a genuine key would fail only because of how it was stored. The generator
|
|
32
|
+
* strips backslashes from every free-text field, so a key it issues never
|
|
33
|
+
* carries one of its own.
|
|
34
|
+
*
|
|
35
|
+
* @type {RegExp}
|
|
36
|
+
*/ const ESCAPED_WHITESPACE = /\\[nrt]/g;
|
|
37
|
+
/**
|
|
38
|
+
* Removes every escaped line break or tab (`\n`, `\r`, `\t` saved as text) and
|
|
39
|
+
* then every whitespace character, in that order.
|
|
40
|
+
*
|
|
41
|
+
* The block is one long word, so a mail client or an editor that wraps the key
|
|
42
|
+
* can break it across lines, and a `.env` file can save that line break as the
|
|
43
|
+
* text `\n`. The block's alphabet has neither, so removing them cannot change
|
|
44
|
+
* a genuine block.
|
|
45
|
+
*
|
|
46
|
+
* @param {string} text The text to remove the whitespace from.
|
|
47
|
+
* @returns {string}
|
|
48
|
+
*/ function removeWhitespace(text) {
|
|
49
|
+
return text.replace(ESCAPED_WHITESPACE, '').replace(PROSE_WHITESPACE, '');
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Brings the human-readable text of a key to the form the key protects.
|
|
53
|
+
*
|
|
54
|
+
* Only the whitespace, its escaped forms, and the Unicode composition are
|
|
55
|
+
* ignored. A mail client that rewraps the text (also between two CJK
|
|
56
|
+
* characters or inside a word) or collapses a blank line, a `.env` file that
|
|
57
|
+
* saves a line break as `\n`, or a system that stores "u" + U+0308 instead of
|
|
58
|
+
* U+00FC leaves the key valid. A changed, added, or removed letter, digit, or
|
|
59
|
+
* symbol does not.
|
|
60
|
+
* Exported for the test key builder; the library calls `extractEntitlementKeyData`.
|
|
61
|
+
*
|
|
62
|
+
* @param {string} prose The text in front of the machine-readable block.
|
|
63
|
+
* @returns {string}
|
|
64
|
+
*/ export function canonicalizeProse(prose) {
|
|
65
|
+
// NFC runs last: a line break between a letter and its combining mark (an
|
|
66
|
+
// NFD copy rewrapped there) has to be gone before the two can compose.
|
|
67
|
+
return removeWhitespace(prose).normalize('NFC');
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Computes the checksum that closes the block. It must match the canonical
|
|
71
|
+
* reader in every key format, so Handsontable 18.1 keeps reading newer keys.
|
|
72
|
+
* Exported for the test key builder.
|
|
73
|
+
*
|
|
74
|
+
* @param {string} encodedPayload The encoded payload.
|
|
75
|
+
* @returns {string}
|
|
76
|
+
*/ export function computePayloadChecksum(encodedPayload) {
|
|
77
|
+
return sha512(stringToUtf8Bytes(encodedPayload));
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Computes the value a current key stores for its text. Exported for the test
|
|
81
|
+
* key builder.
|
|
82
|
+
*
|
|
83
|
+
* @param {string} canonicalProse The text, already passed through `canonicalizeProse`.
|
|
84
|
+
* @returns {string}
|
|
85
|
+
*/ export function computeProseDigest(canonicalProse) {
|
|
86
|
+
return sha512(stringToUtf8Bytes(canonicalProse)).slice(0, PROSE_DIGEST_LENGTH);
|
|
87
|
+
}
|
|
12
88
|
/**
|
|
13
89
|
* Reports own-property presence without trusting a payload's inherited or
|
|
14
90
|
* overridden `hasOwnProperty`.
|
|
@@ -45,6 +121,39 @@ const CHECKSUM = /^[0-9a-f]+$/;
|
|
|
45
121
|
*/ function isStringArray(value) {
|
|
46
122
|
return Array.isArray(value) && value.every((item)=>typeof item === 'string');
|
|
47
123
|
}
|
|
124
|
+
/**
|
|
125
|
+
* Freezes the value and everything nested in it. The read result is shared
|
|
126
|
+
* between callers (see the memo below), so a caller that sorted or pushed into
|
|
127
|
+
* it would silently rewrite what the next caller reads.
|
|
128
|
+
*
|
|
129
|
+
* It walks with an explicit stack, not by recursion. An unknown extra field is
|
|
130
|
+
* kept as it is, and a key can nest one thousands of levels deep - recursion
|
|
131
|
+
* would then overflow the call stack and throw out of the reader, where a key
|
|
132
|
+
* is only ever allowed to read as data or as `null`.
|
|
133
|
+
*
|
|
134
|
+
* @param {*} value The value to freeze.
|
|
135
|
+
* @returns {*}
|
|
136
|
+
*/ function deepFreeze(value) {
|
|
137
|
+
const pending = [
|
|
138
|
+
value
|
|
139
|
+
];
|
|
140
|
+
while(pending.length > 0){
|
|
141
|
+
const current = pending.pop();
|
|
142
|
+
if (isFreezable(current)) {
|
|
143
|
+
Object.freeze(current);
|
|
144
|
+
Object.keys(current).forEach((key)=>pending.push(current[key]));
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
return value;
|
|
148
|
+
}
|
|
149
|
+
/**
|
|
150
|
+
* Narrows an unknown value to an object (or array) that is not frozen yet.
|
|
151
|
+
*
|
|
152
|
+
* @param {*} value The value to check.
|
|
153
|
+
* @returns {boolean}
|
|
154
|
+
*/ function isFreezable(value) {
|
|
155
|
+
return value !== null && typeof value === 'object' && !Object.isFrozen(value);
|
|
156
|
+
}
|
|
48
157
|
/**
|
|
49
158
|
* Adds an own, ordinary property.
|
|
50
159
|
*
|
|
@@ -97,7 +206,7 @@ const CHECKSUM = /^[0-9a-f]+$/;
|
|
|
97
206
|
if (presentDateFields.length !== 1) {
|
|
98
207
|
return null;
|
|
99
208
|
}
|
|
100
|
-
if (parseIsoDateToTimestamp(
|
|
209
|
+
if (parseIsoDateToTimestamp(entry[presentDateFields[0]]) === null) {
|
|
101
210
|
return null;
|
|
102
211
|
}
|
|
103
212
|
if (!isNonNegativeInteger(entry.notice) || !isNonNegativeInteger(entry.grace)) {
|
|
@@ -122,6 +231,41 @@ const CHECKSUM = /^[0-9a-f]+$/;
|
|
|
122
231
|
defineOwn(normalized, presentDateFields[0], entry[presentDateFields[0]]);
|
|
123
232
|
return normalized;
|
|
124
233
|
}
|
|
234
|
+
/**
|
|
235
|
+
* Reads the format version of a payload. Returns `null` for a value no
|
|
236
|
+
* generator writes. A version newer than this reader knows is accepted, so a
|
|
237
|
+
* build already in the field keeps reading newer keys.
|
|
238
|
+
*
|
|
239
|
+
* @param {object} payload The decoded payload.
|
|
240
|
+
* @returns {number|null}
|
|
241
|
+
*/ function readFormatVersion(payload) {
|
|
242
|
+
// Own properties only, so a value another script put on `Object.prototype`
|
|
243
|
+
// cannot change how a key is read.
|
|
244
|
+
if (!hasOwn(payload, 'v')) {
|
|
245
|
+
return 1;
|
|
246
|
+
}
|
|
247
|
+
if (!isNonNegativeInteger(payload.v) || payload.v < 2) {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
return payload.v;
|
|
251
|
+
}
|
|
252
|
+
/**
|
|
253
|
+
* Checks that the text of a current key is intact and that nothing but
|
|
254
|
+
* whitespace follows its block. A key always states its terms, so the bare
|
|
255
|
+
* block is rejected. A key in the earlier format is read the way Handsontable
|
|
256
|
+
* 18.1 reads it.
|
|
257
|
+
*
|
|
258
|
+
* @param {object} payload The decoded payload.
|
|
259
|
+
* @param {string} prose The text in front of the block.
|
|
260
|
+
* @param {string} textAfterBlock The text after the block.
|
|
261
|
+
* @returns {boolean}
|
|
262
|
+
*/ function coversItsText(payload, prose, textAfterBlock) {
|
|
263
|
+
if (canonicalizeProse(textAfterBlock) !== '') {
|
|
264
|
+
return false;
|
|
265
|
+
}
|
|
266
|
+
const canonicalProse = canonicalizeProse(prose);
|
|
267
|
+
return canonicalProse !== '' && hasOwn(payload, 'prose') && typeof payload.prose === 'string' && computeProseDigest(canonicalProse) === payload.prose;
|
|
268
|
+
}
|
|
125
269
|
/**
|
|
126
270
|
* Reads and verifies one key. Split out from the memoized public entry point so
|
|
127
271
|
* the memo can wrap every exit path uniformly.
|
|
@@ -139,7 +283,7 @@ const CHECKSUM = /^[0-9a-f]+$/;
|
|
|
139
283
|
if (blockEnd === -1) {
|
|
140
284
|
return null;
|
|
141
285
|
}
|
|
142
|
-
const content = licenseKey.slice(blockStart + 1, blockEnd);
|
|
286
|
+
const content = removeWhitespace(licenseKey.slice(blockStart + 1, blockEnd));
|
|
143
287
|
if (content.length <= CHECKSUM_LENGTH) {
|
|
144
288
|
return null;
|
|
145
289
|
}
|
|
@@ -148,7 +292,7 @@ const CHECKSUM = /^[0-9a-f]+$/;
|
|
|
148
292
|
if (!ENCODED_PAYLOAD.test(encodedPayload) || !CHECKSUM.test(checksum)) {
|
|
149
293
|
return null;
|
|
150
294
|
}
|
|
151
|
-
if (
|
|
295
|
+
if (computePayloadChecksum(encodedPayload) !== checksum) {
|
|
152
296
|
return null;
|
|
153
297
|
}
|
|
154
298
|
const payloadJson = base64ToString(encodedPayload);
|
|
@@ -164,6 +308,13 @@ const CHECKSUM = /^[0-9a-f]+$/;
|
|
|
164
308
|
if (!isPlainObject(payload) || !isPlainObject(payload.products)) {
|
|
165
309
|
return null;
|
|
166
310
|
}
|
|
311
|
+
const version = readFormatVersion(payload);
|
|
312
|
+
if (version === null) {
|
|
313
|
+
return null;
|
|
314
|
+
}
|
|
315
|
+
if (version >= 2 && !coversItsText(payload, licenseKey.slice(0, blockStart), licenseKey.slice(blockEnd + 1))) {
|
|
316
|
+
return null;
|
|
317
|
+
}
|
|
167
318
|
const products = {};
|
|
168
319
|
const entries = payload.products;
|
|
169
320
|
let malformed = false;
|
|
@@ -178,40 +329,54 @@ const CHECKSUM = /^[0-9a-f]+$/;
|
|
|
178
329
|
if (malformed) {
|
|
179
330
|
return null;
|
|
180
331
|
}
|
|
181
|
-
return {
|
|
332
|
+
return deepFreeze({
|
|
333
|
+
version,
|
|
182
334
|
products
|
|
183
|
-
};
|
|
335
|
+
});
|
|
184
336
|
}
|
|
185
337
|
// The license key is read twice per grid init - the bottom bar
|
|
186
338
|
// (`initLicenseNotification`) and the branding UI (`initLicenseBranding`) each resolve the license
|
|
187
|
-
// state - and reading runs the full
|
|
188
|
-
// the second read free. The returned data is
|
|
339
|
+
// state - and reading runs the full verification and decoding. A one-entry memo on the key makes
|
|
340
|
+
// the second read free. The returned data is frozen, as in the canonical reader, so sharing one
|
|
189
341
|
// object is safe.
|
|
190
342
|
let memoizedKey = null;
|
|
191
343
|
let memoizedData = null;
|
|
192
344
|
/**
|
|
193
345
|
* Extracts the machine-readable data from an entitlement license key.
|
|
194
346
|
*
|
|
195
|
-
* The
|
|
196
|
-
*
|
|
197
|
-
*
|
|
347
|
+
* The key is verified first, so the returned data is guaranteed to belong to
|
|
348
|
+
* an intact key. A malformed or tampered key reads as `null` - reporting an
|
|
349
|
+
* invalid key is the caller's job, not this function's.
|
|
350
|
+
*
|
|
351
|
+
* A current key protects its text as well: edited or removed text (the bare
|
|
352
|
+
* `[...]` block), or anything but whitespace after the block, reads as `null`.
|
|
353
|
+
* A key in the earlier format reads the way Handsontable 18.1 reads it. The
|
|
354
|
+
* verification rules are those of the canonical reader in the private
|
|
355
|
+
* `license-key` repository.
|
|
198
356
|
*
|
|
199
|
-
*
|
|
200
|
-
*
|
|
201
|
-
*
|
|
202
|
-
*
|
|
203
|
-
* it makes the key unreadable, exactly as it does for the key generator.
|
|
357
|
+
* The caller passes the whole key. The text is never parsed, and its
|
|
358
|
+
* whitespace and Unicode composition are ignored, so rewrapped or re-pasted
|
|
359
|
+
* text still validates. Whitespace inside the block is ignored too, so a
|
|
360
|
+
* block wrapped by a mail client still validates.
|
|
204
361
|
*
|
|
205
362
|
* Unknown products, capability tokens and flags are all tolerated, so nothing
|
|
206
363
|
* about reading a key depends on the commercial vocabulary.
|
|
207
364
|
*
|
|
365
|
+
* The result is frozen. Copy an array before sorting or changing it.
|
|
366
|
+
*
|
|
208
367
|
* @param {string} licenseKey The license key to extract the data from.
|
|
209
368
|
* @returns {EntitlementKeyData|null}
|
|
210
369
|
*/ export function extractEntitlementKeyData(licenseKey) {
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
370
|
+
if (typeof licenseKey !== 'string') {
|
|
371
|
+
return null;
|
|
372
|
+
}
|
|
373
|
+
if (licenseKey !== memoizedKey) {
|
|
374
|
+
// Read first, remember second. Were the key remembered before the read,
|
|
375
|
+
// a read that throws would leave the new key paired with the previous
|
|
376
|
+
// key's data, and the next read of the new key would return it.
|
|
377
|
+
const data = readEntitlementKeyData(licenseKey);
|
|
378
|
+
memoizedKey = licenseKey;
|
|
379
|
+
memoizedData = data;
|
|
215
380
|
}
|
|
216
381
|
return memoizedData;
|
|
217
382
|
}
|
|
@@ -29,6 +29,11 @@ export interface ProductEntitlement {
|
|
|
29
29
|
* known ones.
|
|
30
30
|
*/
|
|
31
31
|
export interface EntitlementKeyData {
|
|
32
|
+
/**
|
|
33
|
+
* The format version of the key; 1 for the keys issued before versions
|
|
34
|
+
* existed.
|
|
35
|
+
*/
|
|
36
|
+
version: number;
|
|
32
37
|
products: {
|
|
33
38
|
[productName: string]: ProductEntitlement;
|
|
34
39
|
};
|
|
@@ -62,9 +62,9 @@ const _lockScreen = require("./lockScreen");
|
|
|
62
62
|
}
|
|
63
63
|
function initLicenseBranding(hotInstance) {
|
|
64
64
|
// DO NOT add a `typeof process` guard here - see the matching comment in `initLicenseNotification`.
|
|
65
|
-
// The bundler inlines `"
|
|
65
|
+
// The bundler inlines `"07/10/2026"` to a string literal (the bare read cannot
|
|
66
66
|
// crash); a guard is left un-inlined and compiles to `false` in browser bundles, blanking the date
|
|
67
67
|
// and silently killing expired-key detection.
|
|
68
|
-
const releaseDate = "
|
|
68
|
+
const releaseDate = "07/10/2026" || '';
|
|
69
69
|
mountBrandingSurface(hotInstance, (0, _mixed._getLicenseState)(hotInstance.getSettings().licenseKey, releaseDate));
|
|
70
70
|
}
|
|
@@ -63,9 +63,9 @@ import { mountLicenseLock } from './lockScreen.mjs';
|
|
|
63
63
|
* @returns {void}
|
|
64
64
|
*/ export function initLicenseBranding(hotInstance) {
|
|
65
65
|
// DO NOT add a `typeof process` guard here - see the matching comment in `initLicenseNotification`.
|
|
66
|
-
// The bundler inlines `"
|
|
66
|
+
// The bundler inlines `"07/10/2026"` to a string literal (the bare read cannot
|
|
67
67
|
// crash); a guard is left un-inlined and compiles to `false` in browser bundles, blanking the date
|
|
68
68
|
// and silently killing expired-key detection.
|
|
69
|
-
const releaseDate = "
|
|
69
|
+
const releaseDate = "07/10/2026" || '';
|
|
70
70
|
mountBrandingSurface(hotInstance, _getLicenseState(hotInstance.getSettings().licenseKey, releaseDate));
|
|
71
71
|
}
|
|
@@ -84,12 +84,12 @@ function initLicenseNotification(hotInstance) {
|
|
|
84
84
|
}
|
|
85
85
|
const licenseKey = hotInstance.getSettings().licenseKey;
|
|
86
86
|
// DO NOT wrap this in a `typeof process` guard (or any `process` existence check). The bundler
|
|
87
|
-
// replaces the whole `"
|
|
87
|
+
// replaces the whole `"07/10/2026"` expression with a string literal at build time,
|
|
88
88
|
// so the bare read never touches a runtime `process` object and cannot crash. A guard is NOT
|
|
89
89
|
// inlined - it survives as `typeof process !== 'undefined' && "..."`, which is `false` in every
|
|
90
90
|
// browser bundle, blanks the release date, and silently kills expired-key detection. That was the
|
|
91
91
|
// 18.0.0 regression this read restores; re-adding the guard reintroduces it.
|
|
92
|
-
const releaseDate = "
|
|
92
|
+
const releaseDate = "07/10/2026" || '';
|
|
93
93
|
const notificationElement = (0, _mixed._injectProductInfo)({
|
|
94
94
|
className: LICENSE_INFO_CLASS,
|
|
95
95
|
key: licenseKey,
|
|
@@ -88,12 +88,12 @@ export const LICENSE_INFO_CLASS = 'hot-display-license-info';
|
|
|
88
88
|
}
|
|
89
89
|
const licenseKey = hotInstance.getSettings().licenseKey;
|
|
90
90
|
// DO NOT wrap this in a `typeof process` guard (or any `process` existence check). The bundler
|
|
91
|
-
// replaces the whole `"
|
|
91
|
+
// replaces the whole `"07/10/2026"` expression with a string literal at build time,
|
|
92
92
|
// so the bare read never touches a runtime `process` object and cannot crash. A guard is NOT
|
|
93
93
|
// inlined - it survives as `typeof process !== 'undefined' && "..."`, which is `false` in every
|
|
94
94
|
// browser bundle, blanks the release date, and silently kills expired-key detection. That was the
|
|
95
95
|
// 18.0.0 regression this read restores; re-adding the guard reintroduces it.
|
|
96
|
-
const releaseDate = "
|
|
96
|
+
const releaseDate = "07/10/2026" || '';
|
|
97
97
|
const notificationElement = _injectProductInfo({
|
|
98
98
|
className: LICENSE_INFO_CLASS,
|
|
99
99
|
key: licenseKey,
|