roll-parser 3.0.0-alpha.0 → 3.0.0

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.
Files changed (163) hide show
  1. package/CHANGELOG.md +189 -0
  2. package/MIGRATION.md +147 -0
  3. package/README.md +985 -43
  4. package/dist/cli/args.d.ts +1 -0
  5. package/dist/cli/args.d.ts.map +1 -1
  6. package/dist/cli/args.js +81 -0
  7. package/dist/cli/args.js.map +1 -0
  8. package/dist/cli/format.d.ts +16 -4
  9. package/dist/cli/format.d.ts.map +1 -1
  10. package/dist/cli/format.js +17 -0
  11. package/dist/cli/format.js.map +1 -0
  12. package/dist/cli/index.d.ts +3 -0
  13. package/dist/cli/index.d.ts.map +1 -1
  14. package/dist/cli/index.js +14 -0
  15. package/dist/cli/index.js.map +1 -0
  16. package/dist/cli/main.d.ts +36 -0
  17. package/dist/cli/main.d.ts.map +1 -0
  18. package/dist/cli/main.js +83 -0
  19. package/dist/cli/main.js.map +1 -0
  20. package/dist/errors.d.ts +332 -14
  21. package/dist/errors.d.ts.map +1 -1
  22. package/dist/errors.js +141 -0
  23. package/dist/errors.js.map +1 -0
  24. package/dist/evaluator/die.d.ts +26 -0
  25. package/dist/evaluator/die.d.ts.map +1 -0
  26. package/dist/evaluator/die.js +19 -0
  27. package/dist/evaluator/die.js.map +1 -0
  28. package/dist/evaluator/env.d.ts +58 -0
  29. package/dist/evaluator/env.d.ts.map +1 -0
  30. package/dist/evaluator/env.js +11 -0
  31. package/dist/evaluator/env.js.map +1 -0
  32. package/dist/evaluator/evaluator.d.ts +62 -40
  33. package/dist/evaluator/evaluator.d.ts.map +1 -1
  34. package/dist/evaluator/evaluator.js +906 -0
  35. package/dist/evaluator/evaluator.js.map +1 -0
  36. package/dist/evaluator/modifiers/compare.d.ts +1 -1
  37. package/dist/evaluator/modifiers/compare.d.ts.map +1 -1
  38. package/dist/evaluator/modifiers/compare.js +15 -0
  39. package/dist/evaluator/modifiers/compare.js.map +1 -0
  40. package/dist/evaluator/modifiers/crit-threshold.d.ts +27 -0
  41. package/dist/evaluator/modifiers/crit-threshold.d.ts.map +1 -0
  42. package/dist/evaluator/modifiers/crit-threshold.js +23 -0
  43. package/dist/evaluator/modifiers/crit-threshold.js.map +1 -0
  44. package/dist/evaluator/modifiers/die-bound.d.ts +26 -0
  45. package/dist/evaluator/modifiers/die-bound.d.ts.map +1 -0
  46. package/dist/evaluator/modifiers/die-bound.js +14 -0
  47. package/dist/evaluator/modifiers/die-bound.js.map +1 -0
  48. package/dist/evaluator/modifiers/explode.d.ts +18 -6
  49. package/dist/evaluator/modifiers/explode.d.ts.map +1 -1
  50. package/dist/evaluator/modifiers/explode.js +103 -0
  51. package/dist/evaluator/modifiers/explode.js.map +1 -0
  52. package/dist/evaluator/modifiers/flags.d.ts +37 -0
  53. package/dist/evaluator/modifiers/flags.d.ts.map +1 -0
  54. package/dist/evaluator/modifiers/flags.js +18 -0
  55. package/dist/evaluator/modifiers/flags.js.map +1 -0
  56. package/dist/evaluator/modifiers/keep-drop.d.ts +12 -28
  57. package/dist/evaluator/modifiers/keep-drop.d.ts.map +1 -1
  58. package/dist/evaluator/modifiers/keep-drop.js +82 -0
  59. package/dist/evaluator/modifiers/keep-drop.js.map +1 -0
  60. package/dist/evaluator/modifiers/reroll.d.ts +14 -6
  61. package/dist/evaluator/modifiers/reroll.d.ts.map +1 -1
  62. package/dist/evaluator/modifiers/reroll.js +62 -0
  63. package/dist/evaluator/modifiers/reroll.js.map +1 -0
  64. package/dist/evaluator/modifiers/sort.d.ts +27 -0
  65. package/dist/evaluator/modifiers/sort.d.ts.map +1 -0
  66. package/dist/evaluator/modifiers/sort.js +13 -0
  67. package/dist/evaluator/modifiers/sort.js.map +1 -0
  68. package/dist/evaluator/modifiers/success-count.d.ts +2 -6
  69. package/dist/evaluator/modifiers/success-count.d.ts.map +1 -1
  70. package/dist/evaluator/modifiers/success-count.js +24 -0
  71. package/dist/evaluator/modifiers/success-count.js.map +1 -0
  72. package/dist/index.d.ts +35 -13
  73. package/dist/index.d.ts.map +1 -1
  74. package/dist/index.js +12 -1723
  75. package/dist/index.js.map +1 -0
  76. package/dist/lexer/lexer.d.ts +68 -8
  77. package/dist/lexer/lexer.d.ts.map +1 -1
  78. package/dist/lexer/lexer.js +260 -0
  79. package/dist/lexer/lexer.js.map +1 -0
  80. package/dist/lexer/tokens.d.ts +52 -7
  81. package/dist/lexer/tokens.d.ts.map +1 -1
  82. package/dist/lexer/tokens.js +42 -0
  83. package/dist/lexer/tokens.js.map +1 -0
  84. package/dist/parser/ast.d.ts +419 -80
  85. package/dist/parser/ast.d.ts.map +1 -1
  86. package/dist/parser/ast.js +52 -0
  87. package/dist/parser/ast.js.map +1 -0
  88. package/dist/parser/guards.d.ts +106 -0
  89. package/dist/parser/guards.d.ts.map +1 -0
  90. package/dist/parser/guards.js +121 -0
  91. package/dist/parser/guards.js.map +1 -0
  92. package/dist/parser/parser.d.ts +162 -15
  93. package/dist/parser/parser.d.ts.map +1 -1
  94. package/dist/parser/parser.js +751 -0
  95. package/dist/parser/parser.js.map +1 -0
  96. package/dist/rng/mock.d.ts +74 -13
  97. package/dist/rng/mock.d.ts.map +1 -1
  98. package/dist/rng/mock.js +30 -0
  99. package/dist/rng/mock.js.map +1 -0
  100. package/dist/rng/seeded.d.ts +149 -10
  101. package/dist/rng/seeded.d.ts.map +1 -1
  102. package/dist/rng/seeded.js +138 -0
  103. package/dist/rng/seeded.js.map +1 -0
  104. package/dist/rng/types.d.ts +57 -0
  105. package/dist/rng/types.d.ts.map +1 -1
  106. package/dist/rng/types.js +2 -0
  107. package/dist/rng/types.js.map +1 -0
  108. package/dist/roll.d.ts +59 -25
  109. package/dist/roll.d.ts.map +1 -1
  110. package/dist/roll.js +8 -0
  111. package/dist/roll.js.map +1 -0
  112. package/dist/testing.d.ts +5 -4
  113. package/dist/testing.d.ts.map +1 -1
  114. package/dist/testing.js +2 -38
  115. package/dist/testing.js.map +1 -0
  116. package/dist/types.d.ts +427 -24
  117. package/dist/types.d.ts.map +1 -1
  118. package/dist/types.js +8 -0
  119. package/dist/types.js.map +1 -0
  120. package/dist/version.d.ts +2 -0
  121. package/dist/version.d.ts.map +1 -0
  122. package/dist/version.js +2 -0
  123. package/dist/version.js.map +1 -0
  124. package/package.json +93 -40
  125. package/src/cli/args.ts +66 -9
  126. package/src/cli/format.ts +30 -7
  127. package/src/cli/index.ts +27 -67
  128. package/src/cli/main.ts +129 -0
  129. package/src/errors.ts +488 -23
  130. package/src/evaluator/die.ts +50 -0
  131. package/src/evaluator/env.ts +73 -0
  132. package/src/evaluator/evaluator.ts +1296 -349
  133. package/src/evaluator/modifiers/compare.ts +1 -1
  134. package/src/evaluator/modifiers/crit-threshold.ts +56 -0
  135. package/src/evaluator/modifiers/die-bound.ts +39 -0
  136. package/src/evaluator/modifiers/explode.ts +82 -76
  137. package/src/evaluator/modifiers/flags.ts +61 -0
  138. package/src/evaluator/modifiers/keep-drop.ts +124 -126
  139. package/src/evaluator/modifiers/reroll.ts +36 -64
  140. package/src/evaluator/modifiers/sort.ts +43 -0
  141. package/src/evaluator/modifiers/success-count.ts +6 -9
  142. package/src/index.ts +73 -34
  143. package/src/lexer/lexer.ts +201 -35
  144. package/src/lexer/tokens.ts +72 -7
  145. package/src/parser/ast.ts +453 -104
  146. package/src/parser/guards.ts +248 -0
  147. package/src/parser/parser.ts +835 -135
  148. package/src/rng/mock.ts +75 -14
  149. package/src/rng/seeded.ts +323 -58
  150. package/src/rng/types.ts +57 -0
  151. package/src/roll.ts +66 -41
  152. package/src/testing.ts +5 -9
  153. package/src/types.ts +416 -24
  154. package/src/version.ts +2 -0
  155. package/dist/cli.js +0 -1775
  156. package/dist/evaluator/index.d.ts +0 -8
  157. package/dist/evaluator/index.d.ts.map +0 -1
  158. package/dist/index.mjs +0 -1724
  159. package/dist/rng/index.d.ts +0 -8
  160. package/dist/rng/index.d.ts.map +0 -1
  161. package/dist/testing.mjs +0 -39
  162. package/src/evaluator/index.ts +0 -14
  163. package/src/rng/index.ts +0 -8
@@ -9,49 +9,37 @@
9
9
  * @module evaluator/modifiers/reroll
10
10
  */
11
11
 
12
- import type { RNG } from '../../rng/types';
13
- import type { CompareOp, DieResult } from '../../types';
14
- import { EvaluatorError, type EvalEnv } from '../evaluator';
15
- import { matchesCondition } from './compare';
12
+ import { EvaluatorError } from '../../errors.js';
13
+ import type { RNG } from '../../rng/types.js';
14
+ import type { CompareOp, DieResult } from '../../types.js';
15
+ import { createDieResult, createFateDieResult } from '../die.js';
16
+ import { chargeDie, type EvalEnv } from '../env.js';
17
+ import { matchesCondition } from './compare.js';
18
+ import { isVersusDc, REROLL_SLOT_FLAGS, rewriteFlags } from './flags.js';
16
19
 
17
- /** Default maximum reroll iterations per die. */
20
+ /**
21
+ * Default value of `EvaluationOptions.maxRerollIterations`: how many times a
22
+ * single die may be re-rolled by `r` before `REROLL_LIMIT_EXCEEDED` is
23
+ * thrown.
24
+ *
25
+ * Per die, not per pool — it bounds notations like `1d1r<2`, whose reroll
26
+ * condition can never stop matching. `ro` re-rolls exactly once and is
27
+ * unaffected.
28
+ *
29
+ * @category Limits
30
+ */
18
31
  export const DEFAULT_MAX_REROLL_ITERATIONS = 1_000;
19
32
 
20
- export { matchesCondition };
21
-
22
33
  /**
23
34
  * Rolls a replacement die for the given sides, charging it against the global
24
35
  * dice limit. Fate dice (sides === 0) re-roll on the {-1, 0, +1} range.
25
36
  */
26
37
  function rollReplacement(sides: number, rng: RNG, env: EvalEnv): DieResult {
27
- if (env.totalDiceRolled + 1 > env.maxDice) {
28
- throw new EvaluatorError(
29
- `Total dice count ${env.totalDiceRolled + 1} exceeds limit of ${env.maxDice}`,
30
- 'DICE_LIMIT_EXCEEDED',
31
- 'Reroll',
32
- );
33
- }
34
- env.totalDiceRolled += 1;
35
-
36
- if (sides === 0) {
37
- const result = rng.nextInt(-1, 1);
38
- return {
39
- sides: 0,
40
- result,
41
- modifiers: [],
42
- critical: false,
43
- fumble: false,
44
- };
45
- }
38
+ chargeDie(env, 'Reroll');
46
39
 
47
- const result = rng.nextInt(1, sides);
48
- return {
49
- sides,
50
- result,
51
- modifiers: [],
52
- critical: result === sides && sides > 1,
53
- fumble: result === 1,
54
- };
40
+ if (sides === 0) return createFateDieResult(rng.nextInt(-1, 1), []);
41
+
42
+ return createDieResult(sides, rng.nextInt(1, sides), []);
55
43
  }
56
44
 
57
45
  function rerollLimitError(maxIterations: number): EvaluatorError {
@@ -67,16 +55,7 @@ function rerollLimitError(maxIterations: number): EvaluatorError {
67
55
  * keep/drop modifier) are left alone.
68
56
  */
69
57
  function canReroll(die: DieResult): boolean {
70
- return !die.modifiers.includes('dropped');
71
- }
72
-
73
- /**
74
- * Returns the die's modifiers with any "slot" flags removed. Slot flags
75
- * (`kept`, `dropped`, `rerolled`) are controlled by the surrounding logic
76
- * and should be reassigned each pass.
77
- */
78
- function stripSlotFlags(modifiers: DieResult['modifiers']): DieResult['modifiers'] {
79
- return modifiers.filter((m) => m !== 'kept' && m !== 'dropped' && m !== 'rerolled');
58
+ return !die.modifiers.includes('dropped') && !isVersusDc(die);
80
59
  }
81
60
 
82
61
  /**
@@ -105,24 +84,22 @@ export function applyRecursiveReroll(
105
84
  let current = original;
106
85
  let iterations = 0;
107
86
 
87
+ // Mutate flags in place — the same DieResult objects are shared with
88
+ // the RollPart tree, and both views must reflect reroll state.
108
89
  while (matchesCondition(current.result, operator, value)) {
109
90
  if (iterations >= env.maxRerollIterations) {
110
91
  throw rerollLimitError(env.maxRerollIterations);
111
92
  }
112
93
 
113
- result.push({
114
- ...current,
115
- modifiers: [...stripSlotFlags(current.modifiers), 'rerolled', 'dropped'],
116
- });
94
+ current.modifiers = rewriteFlags(current.modifiers, REROLL_SLOT_FLAGS, 'rerolled', 'dropped');
95
+ result.push(current);
117
96
 
118
97
  current = rollReplacement(current.sides, rng, env);
119
98
  iterations += 1;
120
99
  }
121
100
 
122
- result.push({
123
- ...current,
124
- modifiers: [...stripSlotFlags(current.modifiers), 'kept'],
125
- });
101
+ current.modifiers = rewriteFlags(current.modifiers, REROLL_SLOT_FLAGS, 'kept');
102
+ result.push(current);
126
103
  }
127
104
 
128
105
  return result;
@@ -148,24 +125,19 @@ export function applyRerollOnce(
148
125
  continue;
149
126
  }
150
127
 
128
+ // Mutate flags in place — see `applyRecursiveReroll`.
151
129
  if (!matchesCondition(original.result, operator, value)) {
152
- result.push({
153
- ...original,
154
- modifiers: [...stripSlotFlags(original.modifiers), 'kept'],
155
- });
130
+ original.modifiers = rewriteFlags(original.modifiers, REROLL_SLOT_FLAGS, 'kept');
131
+ result.push(original);
156
132
  continue;
157
133
  }
158
134
 
159
- result.push({
160
- ...original,
161
- modifiers: [...stripSlotFlags(original.modifiers), 'rerolled', 'dropped'],
162
- });
135
+ original.modifiers = rewriteFlags(original.modifiers, REROLL_SLOT_FLAGS, 'rerolled', 'dropped');
136
+ result.push(original);
163
137
 
164
138
  const replacement = rollReplacement(original.sides, rng, env);
165
- result.push({
166
- ...replacement,
167
- modifiers: [...stripSlotFlags(replacement.modifiers), 'kept'],
168
- });
139
+ replacement.modifiers = rewriteFlags(replacement.modifiers, REROLL_SLOT_FLAGS, 'kept');
140
+ result.push(replacement);
169
141
  }
170
142
 
171
143
  return result;
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Sort modifier implementation.
3
+ *
4
+ * Sort is purely cosmetic — it reorders the dice produced by its target in
5
+ * ascending or descending order of `result` without touching any flag
6
+ * (`kept`, `dropped`, `critical`, `fumble`, `rerolled`, `meta`) or the total.
7
+ * Dropped dice stay in the pool and sort alongside kept dice so readers can
8
+ * see where the dropped value landed in the ordered sequence.
9
+ *
10
+ * DC dice are the exception: they hold their positions rather than sorting,
11
+ * so a `vs` comparison's DC faces never appear shuffled into the roll-side
12
+ * pool they are not a member of.
13
+ *
14
+ * @module evaluator/modifiers/sort
15
+ */
16
+
17
+ import type { DieResult } from '../../types.js';
18
+ import { isVersusDc } from './flags.js';
19
+
20
+ /**
21
+ * Returns a sorted copy of `dice` in the given order, preserving every die's
22
+ * original flags and metadata. Comparison is on `.result` — the raw face
23
+ * value is what the user sees in rendered output, so ordering by raw value
24
+ * matches the visual intent regardless of any `kept`/`dropped` flagging.
25
+ *
26
+ * Relies on `Array.prototype.sort` being stable — equal-valued dice retain
27
+ * their original insertion order.
28
+ */
29
+ export function sortDice(dice: DieResult[], order: 'ascending' | 'descending'): DieResult[] {
30
+ const cmp =
31
+ order === 'ascending'
32
+ ? (a: DieResult, b: DieResult) => a.result - b.result
33
+ : (a: DieResult, b: DieResult) => b.result - a.result;
34
+
35
+ const sortable = dice.filter((die) => !isVersusDc(die));
36
+ if (sortable.length === dice.length) return [...dice].sort(cmp);
37
+
38
+ // Sort only the pool members, then lay them back into the slots they came
39
+ // from, leaving every DC die exactly where it was.
40
+ sortable.sort(cmp);
41
+ let next = 0;
42
+ return dice.map((die) => (isVersusDc(die) ? die : (sortable[next++] as DieResult)));
43
+ }
@@ -15,13 +15,9 @@
15
15
  * @module evaluator/modifiers/success-count
16
16
  */
17
17
 
18
- import type { CompareOp, DieResult } from '../../types';
19
- import { matchesCondition } from './compare';
20
-
21
- export type ResolvedThreshold = {
22
- operator: CompareOp;
23
- value: number;
24
- };
18
+ import type { DieResult, ResolvedComparePoint } from '../../types.js';
19
+ import { matchesCondition } from './compare.js';
20
+ import { isVersusDc } from './flags.js';
25
21
 
26
22
  export type SuccessCountResult = {
27
23
  total: number;
@@ -31,13 +27,14 @@ export type SuccessCountResult = {
31
27
 
32
28
  export function countSuccesses(
33
29
  dice: DieResult[],
34
- threshold: ResolvedThreshold,
35
- failThreshold?: ResolvedThreshold,
30
+ threshold: ResolvedComparePoint,
31
+ failThreshold?: ResolvedComparePoint,
36
32
  ): SuccessCountResult {
37
33
  let successes = 0;
38
34
  let failures = 0;
39
35
 
40
36
  for (const die of dice) {
37
+ if (isVersusDc(die)) continue;
41
38
  if (die.modifiers.includes('dropped')) continue;
42
39
 
43
40
  if (matchesCondition(die.result, threshold.operator, threshold.value)) {
package/src/index.ts CHANGED
@@ -4,69 +4,108 @@
4
4
  * @module roll-parser
5
5
  */
6
6
 
7
- // * Error hierarchy
8
- export { RollParserError, isRollParserError } from './errors';
9
- export type { RollParserErrorCode } from './errors';
7
+ import { version } from './version.js';
10
8
 
11
- // * Lexer exports
12
- export { LexerError } from './lexer/lexer';
13
-
14
- // * Parser exports
15
- export { parse, ParseError } from './parser/parser';
9
+ export type { ErrorSpan, NotationErrorCode, RollParserErrorCode } from './errors.js';
10
+ export {
11
+ getErrorSpan,
12
+ isNotationError,
13
+ isRollParserError,
14
+ NOTATION_ERROR_CODES,
15
+ ROLL_PARSER_ERROR_CODES,
16
+ RollParserError,
17
+ } from './errors.js';
18
+ export {
19
+ DEFAULT_MAX_DICE,
20
+ DEFAULT_MAX_EXPLODE_ITERATIONS,
21
+ DEFAULT_MAX_REROLL_ITERATIONS,
22
+ EvaluatorError,
23
+ evaluate,
24
+ } from './evaluator/evaluator.js';
25
+ export { LexerError, lex } from './lexer/lexer.js';
26
+ export type { Token } from './lexer/tokens.js';
27
+ export { TokenType } from './lexer/tokens.js';
16
28
  export type {
17
29
  ASTNode,
18
30
  BinaryOpNode,
31
+ CritThreshold,
32
+ CritThresholdNode,
19
33
  DiceNode,
34
+ DieBoundNode,
20
35
  ExplodeNode,
21
36
  FateDiceNode,
22
37
  FunctionCallNode,
38
+ GroupedNode,
39
+ GroupNode,
40
+ KeepDropNode,
23
41
  LiteralNode,
24
- ModifierNode,
42
+ NodeSpan,
25
43
  RerollNode,
44
+ SortNode,
26
45
  SuccessCountNode,
27
46
  UnaryOpNode,
47
+ VariableNode,
28
48
  VersusNode,
29
- } from './parser/ast';
49
+ } from './parser/ast.js';
30
50
  export {
31
51
  isBinaryOp,
52
+ isCritThreshold,
32
53
  isDice,
54
+ isDieBound,
33
55
  isExplode,
34
56
  isFateDice,
35
57
  isFunctionCall,
58
+ isGroup,
59
+ isGrouped,
60
+ isKeepDrop,
36
61
  isLiteral,
37
- isModifier,
38
62
  isReroll,
63
+ isSort,
39
64
  isSuccessCount,
40
65
  isUnaryOp,
66
+ isVariable,
41
67
  isVersus,
42
- } from './parser/ast';
43
-
44
- // * RNG exports
45
- export type { RNG } from './rng/types';
46
- export { SeededRNG } from './rng/seeded';
47
-
48
- // * Evaluator exports
49
- export {
50
- DEFAULT_MAX_DICE,
51
- DEFAULT_MAX_EXPLODE_ITERATIONS,
52
- DEFAULT_MAX_REROLL_ITERATIONS,
53
- evaluate,
54
- EvaluatorError,
55
- } from './evaluator/evaluator';
68
+ } from './parser/ast.js';
69
+ export { MAX_PARSE_DEPTH, ParseError, parse } from './parser/parser.js';
70
+ export type { RngState } from './rng/seeded.js';
71
+ export { SeededRNG } from './rng/seeded.js';
72
+ export type { RNG } from './rng/types.js';
73
+ export type { RollOptions } from './roll.js';
74
+ export { roll } from './roll.js';
56
75
  export type {
57
76
  CompareOp,
58
77
  ComparePoint,
59
78
  DieModifier,
60
79
  DieResult,
61
80
  EvaluateOptions,
81
+ EvaluationOptions,
82
+ KeepDropSpec,
83
+ ResolvedComparePoint,
84
+ ResolvedCritThreshold,
85
+ RollPart,
86
+ RollPartBase,
87
+ RollPartType,
62
88
  RollResult,
63
- } from './types';
64
- export { DegreeOfSuccess } from './types';
65
-
66
- // * Public API
67
- export { roll } from './roll';
68
- export type { RollOptions } from './roll';
69
-
70
- import pkg from '../package.json';
89
+ } from './types.js';
90
+ export { DegreeOfSuccess } from './types.js';
71
91
 
72
- export const VERSION: string = pkg.version;
92
+ /**
93
+ * Installed roll-parser version — the exact string in `package.json`, so
94
+ * pre-releases keep their suffix (`'3.0.0-beta.0'`). Sourced from the
95
+ * generated `src/version.ts`, kept in sync with the manifest by
96
+ * `bun run generate:version` and gated by `check:version` and CI.
97
+ *
98
+ * Useful in bug reports and for feature-gating against a minimum version. The
99
+ * CLI prints it for `--version` and in the `--help` header.
100
+ *
101
+ * @example
102
+ * ```typescript
103
+ * import { VERSION } from 'roll-parser';
104
+ *
105
+ * VERSION; // e.g. '3.0.0', or '3.1.0-beta.0' on a pre-release
106
+ * VERSION.startsWith('3.'); // true
107
+ * ```
108
+ *
109
+ * @category Core
110
+ */
111
+ export const VERSION: string = version;