roll-parser 3.0.0 → 3.1.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.
- package/CHANGELOG.md +36 -1
- package/MIGRATION.md +73 -1
- package/README.md +85 -19
- package/dist/cli/format.d.ts.map +1 -1
- package/dist/cli/format.js +8 -7
- package/dist/cli/format.js.map +1 -1
- package/dist/evaluator/die.d.ts +2 -1
- package/dist/evaluator/die.d.ts.map +1 -1
- package/dist/evaluator/die.js.map +1 -1
- package/dist/evaluator/env.d.ts +31 -0
- package/dist/evaluator/env.d.ts.map +1 -1
- package/dist/evaluator/env.js.map +1 -1
- package/dist/evaluator/evaluator.d.ts.map +1 -1
- package/dist/evaluator/evaluator.js +29 -21
- package/dist/evaluator/evaluator.js.map +1 -1
- package/dist/evaluator/modifiers/crit-threshold.d.ts +57 -4
- package/dist/evaluator/modifiers/crit-threshold.d.ts.map +1 -1
- package/dist/evaluator/modifiers/crit-threshold.js +27 -8
- package/dist/evaluator/modifiers/crit-threshold.js.map +1 -1
- package/dist/evaluator/modifiers/die-bound.d.ts +5 -2
- package/dist/evaluator/modifiers/die-bound.d.ts.map +1 -1
- package/dist/evaluator/modifiers/die-bound.js +6 -3
- package/dist/evaluator/modifiers/die-bound.js.map +1 -1
- package/dist/evaluator/modifiers/explode.d.ts.map +1 -1
- package/dist/evaluator/modifiers/explode.js +7 -5
- package/dist/evaluator/modifiers/explode.js.map +1 -1
- package/dist/evaluator/modifiers/flags.d.ts +10 -0
- package/dist/evaluator/modifiers/flags.d.ts.map +1 -1
- package/dist/evaluator/modifiers/flags.js +7 -0
- package/dist/evaluator/modifiers/flags.js.map +1 -1
- package/dist/evaluator/modifiers/keep-drop.d.ts +4 -2
- package/dist/evaluator/modifiers/keep-drop.d.ts.map +1 -1
- package/dist/evaluator/modifiers/keep-drop.js +7 -7
- package/dist/evaluator/modifiers/keep-drop.js.map +1 -1
- package/dist/evaluator/modifiers/reroll.d.ts.map +1 -1
- package/dist/evaluator/modifiers/reroll.js +16 -10
- package/dist/evaluator/modifiers/reroll.js.map +1 -1
- package/dist/evaluator/modifiers/sort.d.ts +1 -1
- package/dist/evaluator/modifiers/sort.d.ts.map +1 -1
- package/dist/evaluator/modifiers/sort.js +3 -3
- package/dist/evaluator/modifiers/sort.js.map +1 -1
- package/dist/evaluator/modifiers/success-count.d.ts +5 -2
- package/dist/evaluator/modifiers/success-count.d.ts.map +1 -1
- package/dist/evaluator/modifiers/success-count.js +7 -6
- package/dist/evaluator/modifiers/success-count.js.map +1 -1
- package/dist/parser/ast.d.ts +12 -7
- package/dist/parser/ast.d.ts.map +1 -1
- package/dist/parser/ast.js.map +1 -1
- package/dist/render.d.ts +95 -0
- package/dist/render.d.ts.map +1 -0
- package/dist/render.js +227 -0
- package/dist/render.js.map +1 -0
- package/dist/types.d.ts +49 -9
- package/dist/types.d.ts.map +1 -1
- package/dist/types.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +11 -1
- package/src/cli/format.ts +15 -23
- package/src/evaluator/die.ts +2 -1
- package/src/evaluator/env.ts +32 -0
- package/src/evaluator/evaluator.ts +48 -21
- package/src/evaluator/modifiers/crit-threshold.ts +95 -10
- package/src/evaluator/modifiers/die-bound.ts +13 -4
- package/src/evaluator/modifiers/explode.ts +12 -6
- package/src/evaluator/modifiers/flags.ts +17 -0
- package/src/evaluator/modifiers/keep-drop.ts +9 -5
- package/src/evaluator/modifiers/reroll.ts +20 -11
- package/src/evaluator/modifiers/sort.ts +9 -3
- package/src/evaluator/modifiers/success-count.ts +21 -6
- package/src/parser/ast.ts +12 -7
- package/src/render.ts +392 -0
- package/src/types.ts +49 -9
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,40 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [3.1.0] - 2026-08-08
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- `roll-parser/render`, a subpath export holding `renderBreakdown(result, marks?)`, `DieMarks`, and `MARKDOWN_MARKS`. It rebuilds the `RollResult.rendered` breakdown from `RollResult.parts` with markers the caller chooses, so consumers targeting anything other than Discord markdown no longer regex-parse the baked string. Six slots — `dropped`, `success`, `failure`, `critical`, `fumble`, and `droppedGroup` — compose in a fixed order; `critical`/`fumble` read the `DieResult` booleans, which `rendered` has no marker for. With no `marks` the output is byte-identical to `rendered`, pinned by a property test. Budgeted separately at 2 kB, leaving the `index.js` and `{ roll }` budgets to the library ([#292](https://github.com/edloidas/roll-parser/issues/292))
|
|
15
|
+
|
|
16
|
+
### Changed
|
|
17
|
+
|
|
18
|
+
- The `explode`, `reroll`, and `successCount` members of `RollPart` now carry `rolls: DieResult[]`, the pool the modifier produced — mirroring what `sort` already did. Standard and penetrating explosions and both reroll forms append dice that appear nowhere under `target`, so the part tree could not previously describe its own output ([#292](https://github.com/edloidas/roll-parser/issues/292))
|
|
19
|
+
|
|
20
|
+
Reading `parts` is unaffected. Three things do change:
|
|
21
|
+
|
|
22
|
+
- Code that **constructs** one of those three parts stops compiling — `TS2322: Property 'rolls' is missing in type … but required`. Add the pool, or widen the annotation.
|
|
23
|
+
- Deep-equality assertions against those parts need the new field.
|
|
24
|
+
- `--json` and `JSON.stringify(result)` payloads grow for expressions using explode, reroll, or success counting, since the pool is serialized on the modifier as well as its target — roughly +40% to +96% depending on pool size. Expressions without those modifiers are unchanged.
|
|
25
|
+
|
|
26
|
+
- performance: the per-die `isVersusDc` check is now skipped entirely for notation that cannot carry a `'dc'` tag, `sortDice` scans before allocating instead of running an unconditional `filter`, and `countSuccesses` filters DC dice up front rather than guarding inside the loop. Sort notation had no benchmark coverage, which is why the regression shipped unseen; `4d6sd`, `10d10sd`, `100d6sa`, and `10d10sd>=6f1` now have bench cases ([#281](https://github.com/edloidas/roll-parser/issues/281))
|
|
27
|
+
|
|
28
|
+
### Fixed
|
|
29
|
+
|
|
30
|
+
- Bare `cs`/`cf` now judge the natural face (`initialResult ?? result`) rather than a value a preceding modifier rewrote, matching what the documentation always claimed. Compound explode also stopped clobbering the raw face recorded by an earlier `minN`/`maxN` clamp, which made `1d6min6!!cs` report a false critical. Explicit thresholds deliberately keep reading `result`, so the two kinds can disagree on one die ([#288](https://github.com/edloidas/roll-parser/issues/288))
|
|
31
|
+
- `cs`/`cf` thresholds now apply to dice minted by explode and reroll, which previously fell back to the built-in default rule. Each resolved rule is recorded per die and inherited by appended and replacement dice, so a further explode or reroll inherits it in turn. Overriding one side no longer moves the other: `1d6cf>5!p` keeps the `critical` that plain `1d6!p` gives the appended die ([#289](https://github.com/edloidas/roll-parser/issues/289))
|
|
32
|
+
- Chained same-kind bounds (`1d6min2min3`) no longer push duplicate `'min'`/`'max'` modifiers onto a die, and `initialResult` still comes from the first bound that moved it. The same duplication is fixed for `'success'`/`'failure'` on a group counted after its members (`{4d6>=5}>=1`) and for `'meta'` on nested meta operands (`((1d2)d4)d6`) ([#290](https://github.com/edloidas/roll-parser/issues/290))
|
|
33
|
+
- CLI `--verbose` no longer mangles a dropped group sub-roll nested inside another dropped group sub-roll. `{{1d6, 1d8}kh1, {1d10, 1d12}kh1}kh1` rendered as `({)1d10[1](, 1d12[3]})`; the marker rewrite was a regex over the flat string and stopped at the first inner `~~`. It now reads the structure and emits `({(1d10[1]), 1d12[3]})` ([#292](https://github.com/edloidas/roll-parser/issues/292))
|
|
34
|
+
- Demo site: fractional totals are rounded to four decimals — falling back to exponential where rounding would show a non-zero total as `0` — with the exact value on a `title` attribute, instead of overflowing the result card ([#285](https://github.com/edloidas/roll-parser/issues/285))
|
|
35
|
+
- Demo site: the total count-up is gated on `Number.isSafeInteger`, so an astronomical total lands without animating rather than interpolating through to `0` ([#286](https://github.com/edloidas/roll-parser/issues/286))
|
|
36
|
+
|
|
37
|
+
### Documentation
|
|
38
|
+
|
|
39
|
+
- `MIGRATION.md` gains a 3.0.0 → 3.1.0 section covering the `RollPart` field, the three ways it can surface, and the new render subpath. The file is now ordered newest-first with an index, and `README.md` points at it for every upgrade path rather than only for 2.x ([#292](https://github.com/edloidas/roll-parser/issues/292))
|
|
40
|
+
- `DieResult.critical` and `DieResult.fumble` now scope the max-face/1 default to `sides > 1`, so neither claims to fire on `d1` or Fate dice, and the `FateDiceNode` / `createFateDieResult` TSDoc no longer states the flags are never set on Fate dice — an explicit `4dFcs>0` or `4dFcf=-1` does set them ([#291](https://github.com/edloidas/roll-parser/issues/291))
|
|
41
|
+
- The README no longer implies success counting rejects every enclosing expression — it is terminal only against direct wrapping
|
|
42
|
+
- The README performance table gained a `4d6sd` row and links the published benchmark trend, and the Node badge reads from the published `engines` range
|
|
43
|
+
|
|
10
44
|
## [3.0.0] - 2026-08-06
|
|
11
45
|
|
|
12
46
|
First stable release of the v3 rewrite. This section covers what changed since
|
|
@@ -183,7 +217,8 @@ Dice mechanics (Stage 2):
|
|
|
183
217
|
- Dice count safety limit via `maxDice` option (default 10,000), enforced across the whole expression to prevent DoS via additive groups like `5000d6+5000d6` ([#19](https://github.com/edloidas/roll-parser/issues/19))
|
|
184
218
|
- Parser and evaluator correctness: duplicate `kept` modifier entries, implicit modifier count defaulting to 1 (`4d6kh` → `4d6kh1`), `critical` flag suppression when `sides === 1`, negative `--seed` CLI values ([#21](https://github.com/edloidas/roll-parser/issues/21))
|
|
185
219
|
|
|
186
|
-
[Unreleased]: https://github.com/edloidas/roll-parser/compare/v3.
|
|
220
|
+
[Unreleased]: https://github.com/edloidas/roll-parser/compare/v3.1.0...HEAD
|
|
221
|
+
[3.1.0]: https://github.com/edloidas/roll-parser/releases/tag/v3.1.0
|
|
187
222
|
[3.0.0]: https://github.com/edloidas/roll-parser/releases/tag/v3.0.0
|
|
188
223
|
[3.0.0-beta.0]: https://github.com/edloidas/roll-parser/releases/tag/v3.0.0-beta.0
|
|
189
224
|
[3.0.0-alpha.0]: https://github.com/edloidas/roll-parser/releases/tag/v3.0.0-alpha.0
|
package/MIGRATION.md
CHANGED
|
@@ -1,4 +1,76 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Migration notes
|
|
2
|
+
|
|
3
|
+
Newest first. [Upgrading from 3.0.0 to 3.1.0](#upgrading-from-300-to-310) ·
|
|
4
|
+
[Migrating from v2 to v3](#migrating-from-v2-to-v3) ·
|
|
5
|
+
[Upgrading from a v3 pre-release](#upgrading-from-a-v3-pre-release)
|
|
6
|
+
|
|
7
|
+
## Upgrading from 3.0.0 to 3.1.0
|
|
8
|
+
|
|
9
|
+
Nothing was removed and no behaviour changed: `total`, `expression`, `rendered`,
|
|
10
|
+
`rolls`, and `degree` are byte-identical to 3.0.0 for every expression. The
|
|
11
|
+
whole of this section is about one added field.
|
|
12
|
+
|
|
13
|
+
**`RollPart` gained `rolls` on three variants.** The `explode`, `reroll`, and
|
|
14
|
+
`successCount` members now carry `rolls: DieResult[]` — the pool the modifier
|
|
15
|
+
produced — joining `sort`, which always had it. They needed it: standard and
|
|
16
|
+
penetrating explosions and both reroll forms *append* dice, and those dice
|
|
17
|
+
appeared nowhere in the part tree, so a part could not describe its own output.
|
|
18
|
+
|
|
19
|
+
<!-- readme-test: skip -->
|
|
20
|
+
```typescript
|
|
21
|
+
// 3.0.0 — the explosion die is missing from the tree
|
|
22
|
+
roll('2d6!').parts.target.rolls.length; // 2, but `rendered` shows three dice
|
|
23
|
+
|
|
24
|
+
// 3.1.0 — the modifier carries the pool it actually rendered
|
|
25
|
+
roll('2d6!').parts.rolls.length; // 3
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Per the [versioning policy](README.md#versioning), adding a field to a returned
|
|
29
|
+
object is a minor. Reading `parts` is unaffected and needs no change. Three
|
|
30
|
+
things can still bite:
|
|
31
|
+
|
|
32
|
+
- **Constructing one of those parts stops compiling** — `TS2322: Property
|
|
33
|
+
'rolls' is missing in type … but required`. This is only reachable in test
|
|
34
|
+
fixtures and mocks, since no library function accepts a `RollPart`. Add the
|
|
35
|
+
pool, or widen the annotation.
|
|
36
|
+
- **Deep-equality assertions against those parts fail.** Add `rolls` to the
|
|
37
|
+
expected object, or assert on the fields you care about instead of the whole
|
|
38
|
+
part.
|
|
39
|
+
- **`--json` and `JSON.stringify(result)` payloads grow** for expressions using
|
|
40
|
+
explode, reroll, or success counting, because the pool is serialized on the
|
|
41
|
+
modifier as well as on its target — roughly +40% to +96% depending on pool
|
|
42
|
+
size. Expressions without those modifiers are unchanged. Budget for it if you
|
|
43
|
+
log, cache, or ship results over a wire.
|
|
44
|
+
|
|
45
|
+
**The CLI renders nested dropped sub-rolls correctly.** `--verbose` on a dropped
|
|
46
|
+
group sub-roll nested inside another one used to emit mismatched delimiters —
|
|
47
|
+
`{{(1d6[1]), 1d8[4]}, ({)1d10[1](, 1d12[3]})}`. It now emits
|
|
48
|
+
`{{(1d6[1]), 1d8[4]}, ({(1d10[1]), 1d12[3]})}`. Anything parsing that output
|
|
49
|
+
should be reading `--json` instead.
|
|
50
|
+
|
|
51
|
+
**New, optional: `roll-parser/render`.** A subpath export whose
|
|
52
|
+
`renderBreakdown(result, marks?)` rebuilds the breakdown from `result.parts`
|
|
53
|
+
with markers you choose, so you no longer have to regex the markdown out of
|
|
54
|
+
`rendered`. With no marks its output is byte-identical to `rendered`, which
|
|
55
|
+
makes it a drop-in starting point:
|
|
56
|
+
|
|
57
|
+
```typescript
|
|
58
|
+
import { roll } from 'roll-parser';
|
|
59
|
+
import { renderBreakdown } from 'roll-parser/render';
|
|
60
|
+
import { createMockRng } from 'roll-parser/testing';
|
|
61
|
+
|
|
62
|
+
const result = roll('4d6kh3', { rng: createMockRng([3, 6, 2, 5]) });
|
|
63
|
+
|
|
64
|
+
renderBreakdown(result) === result.rendered; // true
|
|
65
|
+
renderBreakdown(result, { dropped: (_die, text) => `(${text})` });
|
|
66
|
+
// '4d6[3, 6, (2), 5] = 14'
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
See [Custom markers](README.md#custom-markers) for the six slots and the order
|
|
70
|
+
they compose in. Nothing forces you to adopt it — `rendered` is unchanged and
|
|
71
|
+
is not deprecated.
|
|
72
|
+
|
|
73
|
+
## Migrating from v2 to v3
|
|
2
74
|
|
|
3
75
|
v3 is a full rewrite. Nothing in the 2.x API carries over — the entry points,
|
|
4
76
|
the result shape, the error behaviour, and parts of the notation all changed. To
|
package/README.md
CHANGED
|
@@ -12,8 +12,7 @@ Dice notation for tabletop RPGs, rolled into structured results.
|
|
|
12
12
|
<a href="https://www.npmjs.com/package/roll-parser"><img src="https://img.shields.io/npm/v/roll-parser?color=cb3837" alt="npm version"></a>
|
|
13
13
|
<a href="https://github.com/edloidas/roll-parser/actions/workflows/ci.yml"><img src="https://img.shields.io/github/actions/workflow/status/edloidas/roll-parser/ci.yml?branch=master&label=CI" alt="CI status"></a>
|
|
14
14
|
<a href="https://github.com/edloidas/roll-parser/blob/master/LICENSE"><img src="https://img.shields.io/npm/l/roll-parser?color=blue" alt="MIT license"></a>
|
|
15
|
-
|
|
16
|
-
<a href="https://nodejs.org/"><img src="https://img.shields.io/badge/node-%3E%3D22.12-5fa04e" alt="Node.js >= 22.12"></a>
|
|
15
|
+
<a href="https://nodejs.org/"><img src="https://img.shields.io/node/v/roll-parser?color=5fa04e" alt="Node.js >= 22.12"></a>
|
|
17
16
|
</p>
|
|
18
17
|
|
|
19
18
|
<p align="center">
|
|
@@ -98,7 +97,7 @@ roll('4d6kh3', { rng: createMockRng([3, 6, 2, 5]) }).total; // 14, every run
|
|
|
98
97
|
- [Performance](#performance)
|
|
99
98
|
- [Known limitations](#known-limitations)
|
|
100
99
|
- [Versioning](#versioning)
|
|
101
|
-
- [Upgrading
|
|
100
|
+
- [Upgrading](#upgrading)
|
|
102
101
|
- [Contributing](#contributing)
|
|
103
102
|
- [License](#license)
|
|
104
103
|
|
|
@@ -144,10 +143,11 @@ as one request:
|
|
|
144
143
|
`https://esm.sh/roll-parser` works the same way. Raw file URLs
|
|
145
144
|
(`unpkg.com/roll-parser`) also work but fetch each module separately.
|
|
146
145
|
|
|
147
|
-
### Upgrading
|
|
146
|
+
### Upgrading
|
|
148
147
|
|
|
149
|
-
|
|
150
|
-
|
|
148
|
+
[MIGRATION.md](MIGRATION.md) covers every upgrade path, newest first — 3.0.0 to
|
|
149
|
+
3.1.0, v2 to v3, and the v3 pre-releases. v3 is a complete rewrite and the 2.x
|
|
150
|
+
API is not compatible; to stay on the old line, pin `roll-parser@2.3.2`.
|
|
151
151
|
|
|
152
152
|
## Notation reference
|
|
153
153
|
|
|
@@ -220,6 +220,16 @@ Chained keep/drop modifiers do **not** nest. `4d6kh3dl1` flattens into two
|
|
|
220
220
|
specs applied independently to the same pool, with the dropped sets unioned —
|
|
221
221
|
the Roll20 rule.
|
|
222
222
|
|
|
223
|
+
`cs` / `cf` covers the whole pool, including dice that `!`, `!p`, `r`, and `ro`
|
|
224
|
+
add after it — `1d6cs<2!` and `1d6!cs<2` flag the same dice. Two cases stay
|
|
225
|
+
order-sensitive. Modifiers that rewrite a die's value rather than add one (`!!`,
|
|
226
|
+
`minN`, `maxN`) are judged on whatever that value is when the threshold runs:
|
|
227
|
+
with `[6, 6, 3]`, `1d6cs>10!!` reports no critical while `1d6!!cs>10` reports
|
|
228
|
+
one, judging the compounded 15. And because `!p`'s added dice store one less
|
|
229
|
+
than they rolled, the side you *don't* override moves with position —
|
|
230
|
+
`1d6cf>5!p` keeps the critical that plain `1d6!p` gives the added 6, where
|
|
231
|
+
`1d6!pcf>5` does not.
|
|
232
|
+
|
|
223
233
|
### Pools and checks
|
|
224
234
|
|
|
225
235
|
| Notation | Meaning |
|
|
@@ -231,8 +241,10 @@ the Roll20 rule.
|
|
|
231
241
|
| `{a+b}khN` | Single-sub-roll group: keep/drop selects across the flattened pool |
|
|
232
242
|
|
|
233
243
|
> [!IMPORTANT]
|
|
234
|
-
> Success counting is **terminal** —
|
|
235
|
-
> a parse error. Put the arithmetic inside the
|
|
244
|
+
> Success counting is **terminal** — no modifier or arithmetic applies to it
|
|
245
|
+
> directly, so `10d10>=6 + 2` is a parse error. Put the arithmetic inside the
|
|
246
|
+
> threshold, `10d10>=(4+2)`, or wrap the count in group braces to operate on its
|
|
247
|
+
> result: `{10d10>=6}+2`.
|
|
236
248
|
|
|
237
249
|
> [!WARNING]
|
|
238
250
|
> A bare `d` after a dice expression is rejected: `4d6d1` throws
|
|
@@ -342,9 +354,22 @@ describe(roll('4d6kh3 + 2', { rng: createMockRng([3, 6, 2, 5]) }).parts);
|
|
|
342
354
|
|
|
343
355
|
Invariants worth relying on: `result.parts.total === result.total`,
|
|
344
356
|
`successCount.total === successes - failures`, and every part's `rolls[]`
|
|
345
|
-
sharing references with `result.rolls`.
|
|
346
|
-
|
|
347
|
-
|
|
357
|
+
sharing references with `result.rolls`.
|
|
358
|
+
|
|
359
|
+
Four variants carry a `rolls[]` of their own, holding the pool the modifier
|
|
360
|
+
produced rather than the one its target rolled — read those instead of
|
|
361
|
+
descending into `target` when you want what was rendered:
|
|
362
|
+
|
|
363
|
+
- `sort` — the target's pool in sorted order. Chained sorts (`4d6s sd`) nest,
|
|
364
|
+
and the outermost one holds the order `rendered` shows.
|
|
365
|
+
- `explode` — the expanded pool. Standard and penetrating explosions append
|
|
366
|
+
dice that exist nowhere under `target`.
|
|
367
|
+
- `reroll` — discarded intermediates and their replacements, both appended
|
|
368
|
+
rather than substituted.
|
|
369
|
+
- `successCount` — the tallied pool.
|
|
370
|
+
|
|
371
|
+
Unlike the pool on a `dice` part, those four keep `'meta'` dice, so filter them
|
|
372
|
+
before counting or displaying.
|
|
348
373
|
|
|
349
374
|
Meta-expressions do **not** appear as nested parts. `(1d4)d6`, `4d6kh(1d2)`, and
|
|
350
375
|
`1d6!>(1d2+3)` surface only their resolved numbers in the owning part; their
|
|
@@ -369,6 +394,36 @@ success-count modifiers, but **not** keep/drop — `4d6kh3` renders as `4d6[…]
|
|
|
369
394
|
while `8d6!` renders as `8d6![…]`. Read `result.expression` when you need the
|
|
370
395
|
modifier back.
|
|
371
396
|
|
|
397
|
+
#### Custom markers
|
|
398
|
+
|
|
399
|
+
Markdown is one dialect, and `rendered` bakes it in. `roll-parser/render` is a
|
|
400
|
+
≈1.4 kB entry point that rebuilds the same breakdown from `result.parts` with
|
|
401
|
+
markers you choose — no regex round-trip through the baked string.
|
|
402
|
+
|
|
403
|
+
```typescript
|
|
404
|
+
import { renderBreakdown } from 'roll-parser/render';
|
|
405
|
+
|
|
406
|
+
const result = roll('4d6kh3', { seed: 'demo' });
|
|
407
|
+
|
|
408
|
+
renderBreakdown(result); // '4d6[1, 6, ~~1~~, 3] = 10'
|
|
409
|
+
renderBreakdown(result, {}); // '4d6[1, 6, 1, 3] = 10'
|
|
410
|
+
renderBreakdown(result, {
|
|
411
|
+
dropped: (_die, text) => `<s>${text}</s>`,
|
|
412
|
+
critical: (_die, text) => `<b>${text}</b>`,
|
|
413
|
+
}); // '4d6[1, <b>6</b>, <s>1</s>, 3] = 10'
|
|
414
|
+
```
|
|
415
|
+
|
|
416
|
+
Six slots are available — `dropped`, `success`, `failure`, `critical`,
|
|
417
|
+
`fumble`, and `droppedGroup` for a whole sub-roll struck by `{…}kh1`. An
|
|
418
|
+
omitted slot renders plain, so pass `{}` to strip markup entirely or spread
|
|
419
|
+
`MARKDOWN_MARKS` to keep the rest. `critical` and `fumble` read the
|
|
420
|
+
`DieResult` booleans, which `rendered` has no marker for at all.
|
|
421
|
+
|
|
422
|
+
Marks compose in a fixed order: `critical` then `fumble` innermost, then
|
|
423
|
+
exactly one of `dropped`, `success`, or `failure` — a dropped die is never
|
|
424
|
+
also shown as a success. With no marks the output is byte-identical to
|
|
425
|
+
`result.rendered`; a property test pins that.
|
|
426
|
+
|
|
372
427
|
## Randomness
|
|
373
428
|
|
|
374
429
|
Every die is drawn through the `RNG` interface — no roll path bypasses the RNG
|
|
@@ -891,7 +946,8 @@ precedes them. Errors go to stderr; only the result goes to stdout.
|
|
|
891
946
|
| `1d20` | 85 ns | 164 ns | **0.49 µs** (~2.0M rolls/s) |
|
|
892
947
|
| `2d6+3` | 98 ns | 215 ns | **0.77 µs** |
|
|
893
948
|
| `4d6kh3` | 122 ns | 245 ns | **1.2 µs** |
|
|
894
|
-
| `
|
|
949
|
+
| `4d6sd` | 101 ns | 211 ns | **0.84 µs** |
|
|
950
|
+
| `10d10>=6f1` | 161 ns | 336 ns | **1.6 µs** |
|
|
895
951
|
| `100d6` | 82 ns | 168 ns | **2.6 µs** |
|
|
896
952
|
|
|
897
953
|
The `roll` column pays for a fresh `SeededRNG` per call, which an injected RNG
|
|
@@ -907,13 +963,21 @@ Values are **p50**, from
|
|
|
907
963
|
(`.gc('inner')`), taken as the per-record median of four full
|
|
908
964
|
`bun run bench:json` passes, every row agreeing within 5% except
|
|
909
965
|
`lex / 4d6kh3` at 7%. Measured 2026-08-05 on Bun
|
|
910
|
-
1.3.14, Apple M2 Pro, macOS, idle and on AC power.
|
|
911
|
-
`10d10>=6f1` `roll` figures were re-measured 2026-08-06 over three passes
|
|
912
|
-
agreeing within 4%, after per-die `'dc'` tag checks entered the keep/drop and
|
|
913
|
-
success-count paths; the remaining rows re-measured within noise. Read them as
|
|
966
|
+
1.3.14, Apple M2 Pro, macOS, idle and on AC power. Read them as
|
|
914
967
|
two significant digits: another machine shifts every row, and a busy one
|
|
915
968
|
inflates the heavy cases most.
|
|
916
969
|
|
|
970
|
+
The `10d10>=6f1` `roll` figure and the `4d6sd` row come from a later
|
|
971
|
+
five-pass measurement on the same machine, after the per-die `'dc'` tag checks
|
|
972
|
+
that had entered the keep/drop, success-count, and sort paths were hoisted out
|
|
973
|
+
of those loops. That session was not idle: `evaluate / 10d10>=6f1` and
|
|
974
|
+
`evaluate / 10d10sd` each read bimodally across passes with roughly 10% between
|
|
975
|
+
the two modes, so those two are quoted as "recovered to the pre-exclusion
|
|
976
|
+
range" rather than to a point value. The rows this hoist does not touch
|
|
977
|
+
(`1d20`, `2d6+3`, `4d6kh3`, `100d6`) re-measured within noise of the figures
|
|
978
|
+
above and are left as first measured, since the earlier idle session is the
|
|
979
|
+
better data for them.
|
|
980
|
+
|
|
917
981
|
p50 rather than mean, because the mean here is effectively a GC-pause
|
|
918
982
|
histogram and swings ±40% between processes. Every bench body is JIT-primed
|
|
919
983
|
before measurement and pinned to mitata's batched sampling mode, so all cases
|
|
@@ -925,8 +989,10 @@ mis-times mid-weight cases by 10-30x.
|
|
|
925
989
|
Run `bun run bench` for the full suite, or `bench:lex` / `bench:parse` /
|
|
926
990
|
`bench:evaluate` / `bench:roll` for one stage. Cross-library numbers live in
|
|
927
991
|
the [competitor suite](https://github.com/edloidas/roll-parser/tree/master/bench/competitors)
|
|
928
|
-
(`bun run bench:competitors`).
|
|
929
|
-
|
|
992
|
+
(`bun run bench:competitors`). Every push to `master` publishes its p50s to a
|
|
993
|
+
[trend chart](https://edloidas.io/roll-parser/dev/bench/), and CI comments on
|
|
994
|
+
any commit that regresses a case past 1.75x. Bundle size is gated in CI by
|
|
995
|
+
`size-limit`; the budgets live in `package.json`.
|
|
930
996
|
|
|
931
997
|
## Known limitations
|
|
932
998
|
|
|
@@ -940,7 +1006,7 @@ budgets live in `package.json`.
|
|
|
940
1006
|
- **Threshold comparisons bind tight.** `1d6!>=5+2` parses as `(1d6!>=5)+2`;
|
|
941
1007
|
parenthesize for a computed threshold, `1d6!>=(5+2)`. Success counts bind the
|
|
942
1008
|
same way but are terminal, so `1d6>=5+2` errors outright — write
|
|
943
|
-
`1d6>=(5+2)
|
|
1009
|
+
`1d6>=(5+2)`, or `{1d6>=5}+2` to add to the count itself.
|
|
944
1010
|
- **`result.expression` substitutes meta-expressions with their resolved
|
|
945
1011
|
values,** so it does not round-trip through `parse` when they are present:
|
|
946
1012
|
`roll('(1d4)d6').expression` is `'4d6'` when the `1d4` rolled 4, and
|
package/dist/cli/format.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/cli/format.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;
|
|
1
|
+
{"version":3,"file":"format.d.ts","sourceRoot":"","sources":["../../src/cli/format.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAGH,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAe9C;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG;IAC1B,8EAA8E;IAC9E,IAAI,CAAC,EAAE,OAAO,CAAC;IACf,kEAAkE;IAClE,OAAO,CAAC,EAAE,OAAO,CAAC;CACnB,CAAC;AAEF;;;;;;;;;;;;GAYG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,GAAE,aAAkB,GAAG,MAAM,CAYpF"}
|
package/dist/cli/format.js
CHANGED
|
@@ -1,3 +1,10 @@
|
|
|
1
|
+
import { renderBreakdown } from '../render.js';
|
|
2
|
+
const TERMINAL_MARKS = {
|
|
3
|
+
dropped: (_die, text) => `(${text})`,
|
|
4
|
+
success: (_die, text) => `[${text}]`,
|
|
5
|
+
failure: (_die, text) => `{${text}}`,
|
|
6
|
+
droppedGroup: (inner) => `(${inner})`,
|
|
7
|
+
};
|
|
1
8
|
export function formatResult(result, options = {}) {
|
|
2
9
|
const { json = false, verbose = false } = options;
|
|
3
10
|
if (json) {
|
|
@@ -6,12 +13,6 @@ export function formatResult(result, options = {}) {
|
|
|
6
13
|
if (!verbose) {
|
|
7
14
|
return String(result.total);
|
|
8
15
|
}
|
|
9
|
-
return
|
|
10
|
-
}
|
|
11
|
-
function formatRendered(rendered) {
|
|
12
|
-
return rendered
|
|
13
|
-
.replace(/\*\*(-?\d+)\*\*/g, '[$1]')
|
|
14
|
-
.replace(/__(-?\d+)__/g, '{$1}')
|
|
15
|
-
.replace(/~~([^~]+)~~/g, '($1)');
|
|
16
|
+
return renderBreakdown(result, TERMINAL_MARKS);
|
|
16
17
|
}
|
|
17
18
|
//# sourceMappingURL=format.js.map
|
package/dist/cli/format.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/cli/format.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/cli/format.ts"],"names":[],"mappings":"AAQA,OAAO,EAAiB,eAAe,EAAE,MAAM,cAAc,CAAC;AAS9D,MAAM,cAAc,GAAa;IAC/B,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,GAAG;IACpC,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,GAAG;IACpC,OAAO,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,EAAE,CAAC,IAAI,IAAI,GAAG;IACpC,YAAY,EAAE,CAAC,KAAK,EAAE,EAAE,CAAC,IAAI,KAAK,GAAG;CACtC,CAAC;AAyBF,MAAM,UAAU,YAAY,CAAC,MAAkB,EAAE,OAAO,GAAkB,EAAE;IAC1E,MAAM,EAAE,IAAI,GAAG,KAAK,EAAE,OAAO,GAAG,KAAK,EAAE,GAAG,OAAO,CAAC;IAElD,IAAI,IAAI,EAAE,CAAC;QACT,OAAO,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC;IAChC,CAAC;IAED,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,MAAM,CAAC,MAAM,CAAC,KAAK,CAAC,CAAC;IAC9B,CAAC;IAED,OAAO,eAAe,CAAC,MAAM,EAAE,cAAc,CAAC,CAAC;AACjD,CAAC"}
|
package/dist/evaluator/die.d.ts
CHANGED
|
@@ -20,7 +20,8 @@ import type { DieModifier, DieResult } from '../types.js';
|
|
|
20
20
|
export declare function createDieResult(sides: number, result: number, modifiers: DieModifier[]): DieResult;
|
|
21
21
|
/**
|
|
22
22
|
* Creates a Fate/Fudge die result. Uses `sides = 0` as a sentinel — Fate dice
|
|
23
|
-
* have no max-face concept, so `critical` and `fumble`
|
|
23
|
+
* have no max-face concept, so `critical` and `fumble` start `false` and the
|
|
24
|
+
* default rule never sets them. An explicit `cs`/`cf` threshold still can.
|
|
24
25
|
*/
|
|
25
26
|
export declare function createFateDieResult(result: number, modifiers: DieModifier[]): DieResult;
|
|
26
27
|
//# sourceMappingURL=die.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"die.d.ts","sourceRoot":"","sources":["../../src/evaluator/die.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE1D;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,WAAW,EAAE,GACvB,SAAS,CAUX;AAED
|
|
1
|
+
{"version":3,"file":"die.d.ts","sourceRoot":"","sources":["../../src/evaluator/die.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE1D;;;;;;GAMG;AACH,wBAAgB,eAAe,CAC7B,KAAK,EAAE,MAAM,EACb,MAAM,EAAE,MAAM,EACd,SAAS,EAAE,WAAW,EAAE,GACvB,SAAS,CAUX;AAED;;;;GAIG;AACH,wBAAgB,mBAAmB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,WAAW,EAAE,GAAG,SAAS,CAQvF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"die.js","sourceRoot":"","sources":["../../src/evaluator/die.ts"],"names":[],"mappings":"AAqBA,MAAM,UAAU,eAAe,CAC7B,KAAa,EACb,MAAc,EACd,SAAwB;IAIxB,OAAO;QACL,KAAK;QACL,MAAM;QACN,SAAS;QACT,QAAQ,EAAE,MAAM,KAAK,KAAK,IAAI,KAAK,GAAG,CAAC;QACvC,MAAM,EAAE,MAAM,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC;KAClC,CAAC;AACJ,CAAC;
|
|
1
|
+
{"version":3,"file":"die.js","sourceRoot":"","sources":["../../src/evaluator/die.ts"],"names":[],"mappings":"AAqBA,MAAM,UAAU,eAAe,CAC7B,KAAa,EACb,MAAc,EACd,SAAwB;IAIxB,OAAO;QACL,KAAK;QACL,MAAM;QACN,SAAS;QACT,QAAQ,EAAE,MAAM,KAAK,KAAK,IAAI,KAAK,GAAG,CAAC;QACvC,MAAM,EAAE,MAAM,KAAK,CAAC,IAAI,KAAK,GAAG,CAAC;KAClC,CAAC;AACJ,CAAC;AAOD,MAAM,UAAU,mBAAmB,CAAC,MAAc,EAAE,SAAwB;IAC1E,OAAO;QACL,KAAK,EAAE,CAAC;QACR,MAAM;QACN,SAAS;QACT,QAAQ,EAAE,KAAK;QACf,MAAM,EAAE,KAAK;KACd,CAAC;AACJ,CAAC"}
|
package/dist/evaluator/env.d.ts
CHANGED
|
@@ -8,6 +8,16 @@
|
|
|
8
8
|
* @module evaluator/env
|
|
9
9
|
*/
|
|
10
10
|
import type { ASTNode } from '../parser/ast.js';
|
|
11
|
+
import type { DieResult, ResolvedCritThreshold } from '../types.js';
|
|
12
|
+
/**
|
|
13
|
+
* A resolved `cs`/`cf` pair, recorded per die so that dice minted later by
|
|
14
|
+
* explode or reroll can be judged by the rule the user declared instead of
|
|
15
|
+
* the built-in default.
|
|
16
|
+
*/
|
|
17
|
+
export type CritRule = {
|
|
18
|
+
readonly success: readonly ResolvedCritThreshold[];
|
|
19
|
+
readonly fail: readonly ResolvedCritThreshold[];
|
|
20
|
+
};
|
|
11
21
|
/**
|
|
12
22
|
* Per-evaluation shared environment (created once, shared across all branches).
|
|
13
23
|
*
|
|
@@ -32,6 +42,27 @@ export type EvalEnv = {
|
|
|
32
42
|
* left-chain check.
|
|
33
43
|
*/
|
|
34
44
|
insideVersus: boolean;
|
|
45
|
+
/**
|
|
46
|
+
* `true` once a `vs` has tagged its DC dice `'dc'`. Lets every pool
|
|
47
|
+
* operation skip its per-die `isVersusDc` check outright — the tag exists in
|
|
48
|
+
* a small minority of notation, and the call inside the loop cost 11-38% on
|
|
49
|
+
* keep/drop, success, and sort notation that can never carry one (#281).
|
|
50
|
+
*
|
|
51
|
+
* ! Monotonic: set by `evalVersus`, never cleared. `insideVersus` is not a
|
|
52
|
+
* ! model for it — that flag resets in a `finally`, and it is false exactly
|
|
53
|
+
* ! when the tagged DC dice become visible to the enclosing pool.
|
|
54
|
+
*/
|
|
55
|
+
hasVersusDc: boolean;
|
|
56
|
+
/**
|
|
57
|
+
* Crit rule per die, keyed on the `DieResult` object itself. Populated by
|
|
58
|
+
* `applyCritThresholds`; read by explode and reroll so a die they mint
|
|
59
|
+
* inherits the rule of the die it descended from.
|
|
60
|
+
*
|
|
61
|
+
* Out of band rather than a `DieResult` field because `DieResult` is public
|
|
62
|
+
* and serialized. `undefined` until the first `cs`/`cf` node — declared here
|
|
63
|
+
* so the shape stays stable for notation that has none.
|
|
64
|
+
*/
|
|
65
|
+
critRules: WeakMap<DieResult, CritRule> | undefined;
|
|
35
66
|
/**
|
|
36
67
|
* User-supplied variable map for `@name` / `@{name}` references. Always
|
|
37
68
|
* defined — `evaluate()` defaults to an empty object so lookups can be
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/evaluator/env.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;
|
|
1
|
+
{"version":3,"file":"env.d.ts","sourceRoot":"","sources":["../../src/evaluator/env.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAGH,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,kBAAkB,CAAC;AAChD,OAAO,KAAK,EAAE,SAAS,EAAE,qBAAqB,EAAE,MAAM,aAAa,CAAC;AAEpE;;;;GAIG;AACH,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,CAAC,OAAO,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACnD,QAAQ,CAAC,IAAI,EAAE,SAAS,qBAAqB,EAAE,CAAC;CACjD,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IACzB,QAAQ,CAAC,oBAAoB,EAAE,MAAM,CAAC;IACtC,QAAQ,CAAC,mBAAmB,EAAE,MAAM,CAAC;IACrC,eAAe,EAAE,MAAM,CAAC;IACxB;;;;OAIG;IACH,eAAe,EAAE,OAAO,CAAC;IACzB;;;;;OAKG;IACH,YAAY,EAAE,OAAO,CAAC;IACtB;;;;;;;;;OASG;IACH,WAAW,EAAE,OAAO,CAAC;IACrB;;;;;;;;OAQG;IACH,SAAS,EAAE,OAAO,CAAC,SAAS,EAAE,QAAQ,CAAC,GAAG,SAAS,CAAC;IACpD;;;;OAIG;IACH,QAAQ,CAAC,OAAO,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;IACnD;;;OAGG;IACH,QAAQ,CAAC,iBAAiB,EAAE,OAAO,GAAG,MAAM,CAAC;CAC9C,CAAC;AAEF;;;;;;;GAOG;AACH,wBAAgB,UAAU,CAAC,GAAG,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,CASvF;AAED,mDAAmD;AACnD,wBAAgB,SAAS,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,IAAI,CAEvE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/evaluator/env.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;
|
|
1
|
+
{"version":3,"file":"env.js","sourceRoot":"","sources":["../../src/evaluator/env.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,cAAc,EAAE,MAAM,cAAc,CAAC;AAgF9C,MAAM,UAAU,UAAU,CAAC,GAAY,EAAE,KAAa,EAAE,QAAyB;IAC/E,IAAI,GAAG,CAAC,eAAe,GAAG,KAAK,GAAG,GAAG,CAAC,OAAO,EAAE,CAAC;QAC9C,MAAM,IAAI,cAAc,CACtB,oBAAoB,GAAG,CAAC,eAAe,GAAG,KAAK,qBAAqB,GAAG,CAAC,OAAO,EAAE,EACjF,qBAAqB,EACrB,QAAQ,CACT,CAAC;IACJ,CAAC;IACD,GAAG,CAAC,eAAe,IAAI,KAAK,CAAC;AAC/B,CAAC;AAGD,MAAM,UAAU,SAAS,CAAC,GAAY,EAAE,QAAyB;IAC/D,UAAU,CAAC,GAAG,EAAE,CAAC,EAAE,QAAQ,CAAC,CAAC;AAC/B,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluator/evaluator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAiB,cAAc,EAAuC,MAAM,cAAc,CAAC;AAClG,OAAO,KAAK,EACV,OAAO,EAmBR,MAAM,kBAAkB,CAAC;AAG1B,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,KAAK,EAIV,SAAS,EACT,eAAe,EAKf,UAAU,EACX,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAK9C,OAAO,EAKL,8BAA8B,EAC/B,MAAM,wBAAwB,CAAC;
|
|
1
|
+
{"version":3,"file":"evaluator.d.ts","sourceRoot":"","sources":["../../src/evaluator/evaluator.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAiB,cAAc,EAAuC,MAAM,cAAc,CAAC;AAClG,OAAO,KAAK,EACV,OAAO,EAmBR,MAAM,kBAAkB,CAAC;AAG1B,OAAO,KAAK,EAAE,GAAG,EAAE,MAAM,iBAAiB,CAAC;AAC3C,OAAO,KAAK,EAIV,SAAS,EACT,eAAe,EAKf,UAAU,EACX,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAK9C,OAAO,EAKL,8BAA8B,EAC/B,MAAM,wBAAwB,CAAC;AAShC,OAAO,EAGL,6BAA6B,EAC9B,MAAM,uBAAuB,CAAC;AAM/B,OAAO,EAAE,cAAc,EAAE,CAAC;AAM1B;;;;;;;;;GASG;AACH,eAAO,MAAM,gBAAgB,QAAS,CAAC;AAmCvC,OAAO,EAAE,8BAA8B,EAAE,6BAA6B,EAAE,CAAC;AAMzE;;;;;;GAMG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB,eAAe,EAAE,MAAM,EAAE,CAAC;IAC1B,aAAa,EAAE,MAAM,EAAE,CAAC;IACxB;;;;;OAKG;IACH,cAAc,CAAC,EAAE;QACf,MAAM,EAAE,eAAe,CAAC;QACxB,OAAO,EAAE,MAAM,GAAG,SAAS,CAAC;QAC5B,OAAO,EAAE,MAAM,CAAC;KACjB,CAAC;CACH,CAAC;AAogDF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,GAAE,eAAoB,GAAG,UAAU,CA8D1F"}
|