@yipe/dice 0.11.0 → 0.12.1

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 (65) hide show
  1. package/README.md +282 -8
  2. package/dist/builder/ac.d.ts +44 -1
  3. package/dist/builder/ac.d.ts.map +1 -1
  4. package/dist/builder/arguments.d.ts +6 -0
  5. package/dist/builder/arguments.d.ts.map +1 -0
  6. package/dist/builder/ast.d.ts +36 -7
  7. package/dist/builder/ast.d.ts.map +1 -1
  8. package/dist/builder/attack.d.ts +88 -5
  9. package/dist/builder/attack.d.ts.map +1 -1
  10. package/dist/builder/dc.d.ts +13 -0
  11. package/dist/builder/dc.d.ts.map +1 -1
  12. package/dist/builder/example.d.ts +11 -15
  13. package/dist/builder/example.d.ts.map +1 -1
  14. package/dist/builder/expression.d.ts +72 -0
  15. package/dist/builder/expression.d.ts.map +1 -0
  16. package/dist/builder/factory.d.ts +2 -3
  17. package/dist/builder/factory.d.ts.map +1 -1
  18. package/dist/builder/index.cjs +3657 -1373
  19. package/dist/builder/index.cjs.map +1 -1
  20. package/dist/builder/index.js +3650 -1374
  21. package/dist/builder/index.js.map +1 -1
  22. package/dist/builder/nodes.d.ts +7 -1
  23. package/dist/builder/nodes.d.ts.map +1 -1
  24. package/dist/builder/prob.d.ts +9 -0
  25. package/dist/builder/prob.d.ts.map +1 -1
  26. package/dist/builder/roll.d.ts +151 -21
  27. package/dist/builder/roll.d.ts.map +1 -1
  28. package/dist/builder/save.d.ts +6 -1
  29. package/dist/builder/save.d.ts.map +1 -1
  30. package/dist/builder/types.d.ts +18 -0
  31. package/dist/builder/types.d.ts.map +1 -1
  32. package/dist/common/bounce.d.ts +24 -11
  33. package/dist/common/bounce.d.ts.map +1 -1
  34. package/dist/common/lru-cache.d.ts +29 -1
  35. package/dist/common/lru-cache.d.ts.map +1 -1
  36. package/dist/index.cjs +1219 -421
  37. package/dist/index.cjs.map +1 -1
  38. package/dist/index.js +1219 -421
  39. package/dist/index.js.map +1 -1
  40. package/dist/parser/dice.d.ts +59 -16
  41. package/dist/parser/dice.d.ts.map +1 -1
  42. package/dist/parser/parser.d.ts +1 -5
  43. package/dist/parser/parser.d.ts.map +1 -1
  44. package/dist/parser/rollType.d.ts +4 -4
  45. package/dist/parser/scaleDice.d.ts +14 -0
  46. package/dist/parser/scaleDice.d.ts.map +1 -0
  47. package/dist/pmf/mixture.d.ts +17 -3
  48. package/dist/pmf/mixture.d.ts.map +1 -1
  49. package/dist/pmf/pmf.d.ts +118 -33
  50. package/dist/pmf/pmf.d.ts.map +1 -1
  51. package/dist/pmf/query.d.ts +25 -12
  52. package/dist/pmf/query.d.ts.map +1 -1
  53. package/dist/turn/effects.d.ts +114 -0
  54. package/dist/turn/effects.d.ts.map +1 -0
  55. package/dist/turn/index.d.ts +3 -1
  56. package/dist/turn/index.d.ts.map +1 -1
  57. package/dist/turn/plan.d.ts +101 -21
  58. package/dist/turn/plan.d.ts.map +1 -1
  59. package/dist/turn/state.d.ts +14 -8
  60. package/dist/turn/state.d.ts.map +1 -1
  61. package/dist/turn/turn.d.ts +127 -26
  62. package/dist/turn/turn.d.ts.map +1 -1
  63. package/dist/turn/types.d.ts +154 -17
  64. package/dist/turn/types.d.ts.map +1 -1
  65. package/package.json +1 -1
package/README.md CHANGED
@@ -179,6 +179,121 @@ RollBuilder (d20, d6, roll(), etc.)
179
179
  └─ .toPMF() ──► PMF
180
180
  ```
181
181
 
182
+ **Crits double the damage dice, never the flats.** An `onHit` payload with no `onCrit` override
183
+ crits with its dice doubled: `roll(2, d6).plus(5)` and the string `"2d6+5"` both crit as `4d6 + 5`.
184
+ A pool doubles inside, then pools — `roll(2, d6).plus(3).keepHighestAll(2, 1)` crits as
185
+ `roll(4, d6).plus(3).keepHighestAll(2, 1)` (mean 18.93), not as the whole pool rolled twice (22.74).
186
+ Rider damage doubles the same way; a bare `PMF` has no dice, so it is added as-is. `onCrit(...)` and
187
+ `noCrit()` stay explicit, and a damage string containing an attack or save check (`AC`/`DC`) throws
188
+ when used as a payload that doubles (`onHit`, `doubleDice()`); as a rider it is added as-is on a
189
+ crit, like the attack or save builder it stands for.
190
+ An attack string with no crit clause crits too: `"(d20 + 8 AC 16) * (2d6)"` rolls its natural 20 as
191
+ `4d6`, like `d20.plus(8).ac(16).onHit(roll(2, d6))`; a `crit (…)` clause still wins. A term after
192
+ the payload joined by `+`, `*`, `**`, `/` or `//` is part of the payload and never applies to a
193
+ miss (the grammar reads left to right). `+` adds to every landed hit and crit, and to every
194
+ other outcome that carries a payload (miss damage, a potent-cantrip half, a save's failure or half),
195
+ one whose payload rolled 0 included, like the builder's `plus`: `(d20 + 5 AC 12) * (1d4 - 1) + 1d6` is
196
+ `onHit(roll(1, d4).minus(1).plus(d6))` (mean 3.8). `*`, `**`, `/` and `//` act on the payload's
197
+ value, so a hit that deals 0 still deals 0. `(d20 + 5 AC 15) * (1d8) + 1d6` crits as `2d8 + 2d6`,
198
+ like `onHit(roll(1, d8).plus(roll(1, d6)))`, and keeps its hit, crit and miss labels. After a
199
+ `crit (…)` clause the term is added to the crit as written, and after a `miss (…)` clause to the
200
+ miss damage too. The crit rate reads the check's natural die, its one d20 wherever it sits in the
201
+ sum (with no d20, its largest die), through bonus to-hit dice, advantage (`d20 > d20`, `d20!`,
202
+ `2kh1d20`), disadvantage (`2kl1(1d20)`), elven accuracy (`3kh1(1d20)`) and halfling luck (`hd20`,
203
+ `d20 reroll 1`), for `crit` and `xcrit N` alike: `(d20 > d20 + 5 + 1d4 AC 15) * (2d6)` crits at
204
+ 39/400, like `d20.withAdvantage().plus(5).plus(d4)`, `(1d4 + d20 + 5 AC 15)` crits on the d20, not
205
+ the d4, and `(d20 + d100 AC 60)` on the d20, not the d100 (61/2000). A max or min against another
206
+ die or a number crits where the natural 20 is the value kept: `(d20 > d4 + 5 AC 10)` on every
207
+ natural 20, `(d20 < 15 + 5 AC 10)` never. An `&` mix crits where one of its sides rolls its natural
208
+ 20, at that side's share of the mix, in either order and with the AC gate on either side:
209
+ `(d4 & d20 AC 5) * (1d6) crit (2d6)` and `(d4 & (d20 AC 5)) * (1d6)` both crit at 1/24. A die on
210
+ the AC side is the target's roll, never the natural roll. A check with no single natural die
211
+ (`2d20`, `d20 + d20`, `d20 + d20 + d100`, `2kh2(1d20)`, advantage over a total like `(d20 + 1d4)!`,
212
+ `d20 + 5 > d20`, a reroll, repeat, keep or double advantage of a mix with a smaller die in it, like
213
+ `(d20 & d4) reroll 1`, `1(d20 & d4)`, `2kh1(d20 & d4)` or `(d20 & d4)!!`) throws `crit rate cannot
214
+ be computed exactly …` when it would crit, and so does an `xcrit N` wider than the die. A check with
215
+ no die at all (`(15 AC 12) * (1d6)`, `(25 AC d20) * (1d6)`) has no natural roll, so it never crits,
216
+ and a `crit (…)` or `xcrit N` clause on it is inert: `(15 AC 12) * (1d6) crit (2d6)` means 3.5, like
217
+ `roll.flat(15).ac(12).onHit(d6)`, which emits that string.
218
+
219
+ **`&` shapes that are refused.** An `&` mix weights each side by its count of outcomes, so these
220
+ shapes have no single reading and throw rather than return a number that depends on how they are
221
+ spelled:
222
+
223
+ - `&` with dice on either side inside a payload that doubles on a crit: an attack string with no
224
+ crit clause (`(d20 + 5 AC 15) * (1d6 & 3)`, or a trailing `+ (1d4 & 2)`), `onHit("1d6 & 3")`, a
225
+ rider's auto-crit and `doubleDice()`. Doubling the dice also changes each side's share of the
226
+ mix (`1d6 & 3` → `2d6 & 3` moves the flat's share from 1/7 to 1/37), so this throws
227
+ `AmbiguousCritDoublingError`. Give the crit explicitly: `… * (1d6 & 3) crit (2d6 & 3)` parses.
228
+ - `&` with an attack already split into crit, miss or save outcomes (`((d20 AC 5) * (1d6)) & d4`,
229
+ `(d20 + 5 AC 15) * (1d6) & 3`), `&` of a saving throw with anything but another saving throw
230
+ (`d4 & (d20 DC 12)`), and a crit, save, pc or miss clause right after an `&` mix
231
+ (`(d20 + 5 AC 15) & (1d6) crit (2d6)`). Mix the checks before the payload instead:
232
+ `((d20 AC 10) & (d20 AC 15)) * (1d6)`.
233
+ - A reroll, repeat, keep or double advantage of a mix with a smaller die in it, when it would crit
234
+ (listed above). `(1(d20 & d4) AC 5) * (1d6) crit (2d6)` and `(2kh1(d20 & d4) AC 15) * (1d6) crit
235
+ (2d6)` were exact in 0.11.0 (119/48 and 2093/1152) and now throw. Spell the second as
236
+ `((d20 & d4)! AC 15)`, which gives the same exact value.
237
+
238
+ A parsed attack still differs from the builder in these cases:
239
+
240
+ - `parse()` has no natural-1 miss and no natural-20 hit: `(d20+30 AC 5) * (1d8)` means 4.725, the
241
+ builder 4.5.
242
+ - A payload the doubling rewrite cannot read (`d4d6`, a nested check) is added to the crit as-is;
243
+ the builder's `onHit` throws.
244
+ - A term joined by an op that also changes a miss (`~+`, `-`, `>`, `<`, `=`, `reroll`, `!`) loses
245
+ the attack's crit and miss labels, so riders never see its crit. So do a clause after a trailing
246
+ term (`… * (2d6) + 3 miss (1d6)`) and a repeat wrapper around an attack string
247
+ (`2((d20 + 5 AC 15) * (2d6))`). An `&` there throws (see above).
248
+
249
+ **Crits on keeps.** A keep-highest-of-1 payload, meaning "roll it N times, keep the best", doubles
250
+ its dice inside each trial on a crit. For example, `roll(2,d6).keepHighest(2,1).plus(3)` crits as
251
+ `2kh1(4d6) + 3` (18.9334, the same as the pooled `keepHighestAll(2,1)`), and the parsed
252
+ `2kh1(2d6)+3` crits as `2kh1(4d6)+3`. Other shapes have more than one reasonable doubled meaning,
253
+ so `doubleDice()`/`scaleDice()` throws an Error rather than guessing. The throw names the shape and
254
+ asks for an explicit crit. The shapes that throw are: a per-die keep with K >= 2
255
+ (`roll(4,d6).keepHighest(4,3)`, which used to crit at 88.98); any keepLowest (`3kl1`, `2kl1`); a
256
+ `bestOf()` that is a keep, or becomes one when doubled (`roll(4,d6).bestOf(3)`,
257
+ `roll(2,d6).bestOf(3)`); a non-d20 die rolled with advantage, disadvantage or elven accuracy
258
+ (`d6.withAdvantage()`, which used to crit at the hit's mean); a parsed `NkhK(...)` with K >= 2 or
259
+ any `NklK(...)`, including when nested (`4kh3d6`, `3kh2(2d6+1)`, `2kh1(4kh3(1d6))`); a parsed
260
+ min of two dice terms (`d6 < d6`); and a parsed `&` mix with dice on either side (`1d6 & 3`). This
261
+ covers an attack's auto-crit (`resolve()` and `toExpression()` throw), a rider's auto-crit, and an
262
+ attack string with no crit clause
263
+ (`(d20+5 AC 12) * (4kh3(1d6))` now fails to parse). You can fix any of these with `onCrit(...)`, a
264
+ rider's `critDamage`, a `crit (...)` clause, or `noCrit()`. The attack check's own d20 advantage is
265
+ never doubled and is unaffected. Three things still do not throw: `keepHighestAll`/`keepLowestAll`
266
+ pools, which double inside and then pool as before; a flat cap or floor like `2d6 < 9` or `3>d6`;
267
+ and `diceMatchInfo()`, whose crit descriptor is `null` for such an attack (its hit side is unchanged).
268
+
269
+ **Builder semantics.** `roll(N, X)` is N independent copies of X, so `roll(2, d6.keepHighest(2, 1))`
270
+ is two best-of-two d6 (161/18), and a group of zero dice rolls nothing (`roll(0, d6).plus(3)` is 3).
271
+ A roll type applies to each die of its group: `roll(2, d6).withAdvantage()` is two advantaged d6.
272
+ `explode(k)` needs a finite cap k, sides must be finite and not negative (`roll(2, 0)` is no die),
273
+ `scaleResult()` needs a finite numerator and a non-zero finite denominator, and `ac()`, `dc()`,
274
+ `critOn()`, `minimumDamageDie()`, `rerollDamage()` and a `withCheck()` result need finite numbers;
275
+ each throws, naming the argument. Per-die keeps have one reading each: `roll(1, d).keepHighest(T, K)`
276
+ keeps K of T dice, `roll(N, d).keepHighest(N, K)` keeps K of the N dice, and
277
+ `roll(N, d).keepHighest(T, 1)` is the best of T rolls of the whole group (`keepLowest(T, 1)` with
278
+ T ≠ N the worst); any other keep on several dice throws `AmbiguousKeepError`, so use
279
+ `keepHighestAll`/`keepLowestAll` or a keep on one die. `minus(X)` subtracts X's flat along with its
280
+ dice. `half()`, `scaleResult()` and `maxOf()` keep their transform under `plus()`/`minus()`.
281
+ A check's natural roll is its d20 wherever it sits in the sum (with no d20, its largest die);
282
+ `withAdvantage()`/`withDisadvantage()`/`withElvenAccuracy()` apply to that d20 whatever the call
283
+ order; a natural roll of more than one die (`roll(2, d20).ac(15)`) throws; a check with no die
284
+ (`flat(15).ac(12)`) compares its total with the target and never crits; a natural 20 always crits,
285
+ including under `alwaysHits()` and `alwaysCrits()`. A parsed string cannot be a check
286
+ (`d("d20+5").ac(15)` throws `ParsedCheckError`); spell it with the builder or as a full attack string.
287
+ `rerollDamage(k)` rerolls a face when its kept value, after any `minimumDamageDie` floor, is below
288
+ a fresh die's expected value, and never lowers a payload's own `reroll` or `minimum`.
289
+
290
+ **Strings from builders.** `toExpression()` prints a string that `parse()` reads back to the
291
+ builder's own distribution: a term after the first is parenthesised when it is an expression of its
292
+ own, `~+` joins a term to a running total that can be 0, and an attack always carries its crit clause
293
+ (`noCrit()` prints `xcrit0 (<hit payload>)`, which crits on no natural face). Explode, pool-wide explode, `scaleResult(…, "round")`,
294
+ fractional scale factors, `plusSeparateDamage()` and `halfOnMiss()` have no spelling and throw.
295
+ Strings have no natural-1 miss or natural-20 hit.
296
+
182
297
  ### Core Class Flow
183
298
 
184
299
  ```
@@ -317,6 +432,49 @@ const query = new DiceQuery(pmf);
317
432
  console.log("DPR:", query.mean());
318
433
  ```
319
434
 
435
+ #### Grammar
436
+
437
+ Spaces are ignored and letters may be any case. Every binary operator has the **same precedence and
438
+ associates left to right**: `1d6 + 2 * 3` is `(1d6 + 2) * 3`. Parenthesise to group.
439
+
440
+ | Syntax | Meaning |
441
+ | --- | --- |
442
+ | `7`, `d6`, `3d6`, `hd20` | A number, a die, a sum of dice, a die whose 1 is rerolled once (halfling luck, `d20 reroll 1`). |
443
+ | `N(X)`, `(X)d6` | N independent copies of X, summed. The count may be rolled: `(1d4)d6`. A count of 0 is 0 (`0d6`, `(1d4 - 1)d6` has P(0) = 1/4); a count that can be negative throws. |
444
+ | `NkhK(X)`, `NklK(X)` | The sum of the K highest (lowest) of N independent copies of X, exact for any X: `4kh3d6`, `2kl1(1d20)`, `4kh1(2d20)`. `2kl1(2d6)` keeps the lower of two 2d6 *sums*. |
445
+ | `X + Y` | Adds Y where the total so far is not 0, so a miss (0) stays 0. After an attack's or a save's payload it adds to every outcome that carries a payload (a landed hit or crit, a miss clause's damage, a potent-cantrip half, a save's failure or half), one that deals 0 included: `(d20 + 5 AC 15) * (1d4 - 1) + 1d6`. Inside a check total (left of `AC`/`DC`) `+` always adds, so `d20 - 5 + 1d4 AC 1` adds the d4 on a natural 5 too. |
446
+ | `X ~+ Y` | Always adds. |
447
+ | `X - Y`, `-X` | Subtracts. A leading `-` negates the argument after it, repeat included: `-2d6` is `-(2d6)`, `1d6 + -3` is `1d6 - 3`, `-1d8 + 1d6` has mean -1. There is no unary `+`. |
448
+ | `X * Y` | Y where X is not 0, else 0: the hit gate of `(check) * (damage)`. |
449
+ | `X ** Y` | The product. |
450
+ | `X / Y`, `X // Y` | Division rounding up, rounding down (toward -∞). A divisor that can be 0 throws. |
451
+ | `X > Y`, `X < Y` | The max, the min: `d20 > d20` is advantage, `3>d6` a floor of 3. |
452
+ | `X!` | The max of two independent copies of X. |
453
+ | `X = Y` | 1 where X equals Y, else 0. |
454
+ | `X & Y` | A mix weighted by each side's count of outcomes (see the refused shapes above). |
455
+ | `X reroll R` | Rolls X, and on a result in the face set R rolls X again and keeps the second roll. |
456
+ | `X AC T`, `X DC T` | An attack check (X where X ≥ T, else 0) and a saving throw (0 on a save, 1 on a failure). A total of exactly 0 that meets a T of 0 or less lands, though it reads 0 like a miss: `(d20 - 5 AC 0) * (1d6)` hits on a natural 5 and crits on a 20, like `d20.minus(5).ac(0).onHit(roll(1, d6))`. |
457
+ | `… crit (Y)`, `… xcritN (Y)`, `… miss (Y)`, `… save half`, `… pc` | Outcome clauses after an attack's or a save's payload. `xcrit0 (Y)` never crits: every landing is a hit. |
458
+
459
+ **Rerolls.** `reroll N` rerolls the face N only; `reroll dN` rerolls every face from 1 to N; the
460
+ builder's `.reroll(N)` rerolls every face up to N, like `reroll dN`. On a d6 they differ from N = 2
461
+ on: `d6 reroll 2` is 15/4, `d6 reroll d2` and `d6.reroll(2)` are 25/6. `reroll d0` rerolls
462
+ nothing. A reroll applies to the whole value on its left, so `2d6 reroll 1` rerolls the *total*,
463
+ which is never 1 (mean 7); spell a per-die reroll as `2(d6 reroll 1)` (47/6), and several of them as
464
+ `1(d8 reroll 1) + 2(d6 reroll 1)` (613/48). Each result keeps its own probability, so a reroll of a
465
+ sum, a max or a floor is exact: `2d6 reroll 2` is 257/36, `(d20 > d20) reroll 1` is 221713/16000.
466
+ The reroll decides on the raw face and a minimum applies after: `3>(d6 reroll 1)` (what
467
+ `roll(1, d6).reroll(1).minimum(3)` prints) is 25/6.
468
+
469
+ **Labels.** An attack labels its outcomes `hit`, `crit`, `missNone` and `missDamage`. A save labels a
470
+ failed save `saveFail` (whatever the payload rolls, 0 included), a halved success `saveHalf`, and a
471
+ success with no `save half` `missNone`, like the builder's `onSaveFailure()`. A landed hit whose
472
+ payload deals 0 is still `hit` (a 0-damage crit is `crit`), like the builder, and so is an AC
473
+ check's landed total of exactly 0; only a miss is `missNone`.
474
+
475
+ A `d0` has no faces: it is only a face set (`reroll d0`); rolled on its own it throws, as does a
476
+ string the grammar cannot read, always as a `DiceParseError`.
477
+
320
478
  ### Error Handling
321
479
 
322
480
  `parse()` throws a `DiceParseError` (a subclass of `Error`) for invalid input.
@@ -335,14 +493,15 @@ try {
335
493
  ```
336
494
 
337
495
  For UI code that parses on every keystroke, `tryParse()` returns an empty PMF
338
- instead of throwing, and accepts a bare integer — which the grammar rejects, but
339
- a half-typed damage field is one for a keystroke or two:
496
+ instead of throwing, and also reads an integer with a leading `+`, which the
497
+ grammar rejects — a half-typed damage field is one for a keystroke or two:
340
498
 
341
499
  ```ts
342
500
  import { tryParse } from "@yipe/dice";
343
501
 
344
502
  tryParse("1d6 + 2").mean(); // 5.5
345
- tryParse("-3").mean(); // -3 — signed integers, which the grammar rejects
503
+ tryParse("+7").mean(); // 7 — a leading `+`, which the grammar rejects
504
+ tryParse("-3").mean(); // -3 — the same as parse("-3")
346
505
  tryParse("0x10").mass(); // 0 — decimal only
347
506
  tryParse("1d").mass(); // 0 — empty PMF
348
507
  ```
@@ -400,7 +559,8 @@ chance of 0.015 instead of 0.1225.
400
559
  |---|---|---|
401
560
  | `onFirstHit` | once, on the first attack that lands — doubled if it crit | Sneak Attack |
402
561
  | `onAnyCrit` | once, if any attack crit | Divine Smite |
403
- | `onAnyMiss` | once, if any attack missed | Unerring Accuracy, Lucky |
562
+ | `onAnyMiss` | once, if any attack missed; runs after every attack | a reroll no grant reaches |
563
+ | `onFirstMiss` | once, on the first attack that missed; runs right after it | Unerring Accuracy, Lucky |
404
564
  | `onEveryHit` | once per attack that lands | Hunter's Mark, Hex, Rage |
405
565
  | `otherwise` | when the rider before it did *not* | flurry of blows if you didn't smite |
406
566
 
@@ -434,6 +594,10 @@ turn([sword, sword]).onEveryHit(flat(2)); // Rage
434
594
  turn([dagger, dagger]).onAnyCrit(roll(4, d8)).otherwise([unarmed, unarmed]); // smite, or flurry
435
595
  ```
436
596
 
597
+ A list of attacks deals their exact summed damage, but it is one payload, not attacks that each
598
+ land: it is not a watchable attack. Naming it in `of` throws `not-an-attack`, and it never joins a
599
+ later rider's default `of`. To watch each strike, give each strike its own rider.
600
+
437
601
  #### The hard build
438
602
 
439
603
  A goliath rogue/monk/paladin, every trigger at once:
@@ -444,7 +608,7 @@ const goliath = turn([dagger, dagger])
444
608
  .onFirstHit(d10) // fire's burn
445
609
  .onAnyCrit(roll(2, d8)) // divine smite
446
610
  .otherwise([unarmed, unarmed]) // flurry of blows, if the smite didn't happen
447
- .onEveryHit(d6); // hunter's mark
611
+ .onEveryHit(d6); // hunter's mark, on the daggers
448
612
 
449
613
  goliath.mean(); // 39.5903
450
614
  goliath.toQuery().damageAttributionChartModel();
@@ -453,7 +617,8 @@ goliath.toQuery().damageAttributionChartModel();
453
617
  Two things that would be easy to get wrong are handled for you. Riders sharing a trigger resolve
454
618
  **jointly** — sneak attack and fire's burn fire together or not at all, which shows up in the spread
455
619
  even though it never moves the mean. And `otherwise()` binds to the rider immediately before it, so
456
- the smite and the flurry are two branches of one decision and can never both land.
620
+ the smite and the flurry are two branches of one decision and can never both land. The mark
621
+ watches the two daggers: the flurry is a list rider, which no rider watches.
457
622
 
458
623
  #### Asking questions
459
624
 
@@ -467,6 +632,104 @@ t.toQuery().probTotalAtLeast(20); // 0.5062 — P(20+ damage)
467
632
  t.toQuery().percentiles([0.25, 0.5, 0.75]); // [15, 20, 24]
468
633
  ```
469
634
 
635
+ #### Once per turn: keep the better damage roll
636
+
637
+ `onFirstHit` also takes a **transform** instead of damage. `keepBestDamage()` rolls the attack's own
638
+ base payload a second time and keeps the better total — once per turn, on the first attack that
639
+ lands. No dice are restated, and one call covers every attack in the turn:
640
+
641
+ ```ts
642
+ import { keepBestDamage } from "@yipe/dice/builder";
643
+
644
+ const attack = d20.plus(5).ac(12).onHit(roll(2, d6).plus(3));
645
+
646
+ turn([attack, attack]).mean(); // 14.7000
647
+ turn([attack, attack]).onFirstHit(keepBestDamage()).mean(); // 15.9849
648
+ ```
649
+
650
+ What it rerolls is the **base payload**: a crit transforms the doubled crit dice, while
651
+ `plusSeparateDamage` channels, `every-hit` riders and the miss branch are never rerolled. Spending
652
+ on the first landing is a policy, and not the best one — a player who sees a high roll holds the
653
+ reroll — so the number is a lower bound. `keepBestDamage().ifBelow({ hit, crit })` spends only when
654
+ the base payload total (dice plus the payload's own flat bonus, excluding separate-damage channels)
655
+ is below the threshold for that mode. When no later attack it watches can still land — the last
656
+ one, or an earlier one whose watched reroll can no longer fire — it spends on any landing.
657
+ `ifBelow({ hit: 10, crit: 18 })` reaches the optimum on the turn above, and since the threshold
658
+ reads the payload's own values it works on a parsed string or a bare `PMF` too.
659
+ On a tie the original roll is kept; the total is the same, so that only matters to a dice-match
660
+ trigger reading the same attack.
661
+ `fireProbability(id)` reports P(spent). A second transform over any of the same attacks throws
662
+ `duplicate-substitute`, and a transform passed to any other verb throws `unsupported-trigger`.
663
+
664
+ #### Conditions: advantage, disadvantage and crits granted by earlier attacks
665
+
666
+ The same verbs take a **grant** — a modifier with a lifetime — for the attack rolls after it:
667
+
668
+ ```ts
669
+ import { advantage, disadvantage, turn } from "@yipe/dice/builder";
670
+
671
+ const sword = d20.plus(5).ac(12).onHit(roll(1, d8).plus(3));
672
+
673
+ // A hit gives the next attack advantage. The next attack uses it up even on a miss, and passes it
674
+ // on if it lands.
675
+ turn([sword, sword]).onEveryHit(advantage().untilNextAttack()).mean(); // 12.202125
676
+ turn([sword, sword, sword]).onEveryHit(advantage().untilNextAttack()).mean(); // 19.192196
677
+
678
+ // A hit gives advantage for the rest of the turn if the target fails a save; each landing tries again.
679
+ turn([axe, axe]).onEveryHit(advantage().untilEndOfTurn(), { save: d20.plus(2).dc(15) });
680
+
681
+ // Once per turn, on the first hit, 40% of the time: advantage, and every later hit is a crit.
682
+ turn([fist, fist, fist]).onFirstHit(advantage().critOnHit().untilEndOfTurn(), { chance: 0.4 });
683
+
684
+ // One save, two grants: advantage for the melee attacks, disadvantage for the ranged ones.
685
+ turn().attack(axe, { tag: "melee" }).attack(bow, { tag: "ranged" })
686
+ .onEveryHit([advantage().untilEndOfTurn().to("melee"),
687
+ disadvantage().untilEndOfTurn().to("ranged")], { of: ["melee"], save: dc });
688
+ ```
689
+
690
+ `advantage()`, `disadvantage()` and `critOnHit()` are not grants until they get a lifetime, so
691
+ `onEveryHit(advantage())` does not compile. A grant combines with the reading attack's own roll
692
+ type by cancellation — advantage and disadvantage together roll flat — and a net advantage rolls
693
+ three dice for an attack built with `threeDiceAdvantage()`. `critOnHit` makes every landing a crit;
694
+ a natural 1 still misses. `.to(…)` takes ids or tags; left out, every later attack roll reads the
695
+ grant, rerolls and bonus attacks included. An `onAnyMiss` reroll resolves after every attack, so one
696
+ that would read a grant, or apply one, throws `unsupported-trigger`: use `onFirstMiss`, which
697
+ resolves right after the miss and reads the grants in force there.
698
+
699
+ `chance` or `save` gates the grants only: damage in the same call (`[d8, advantage()…]`) lands
700
+ whatever the save does. `onEveryHit` rolls the save again on each landing until it takes, and
701
+ `onFirstHit` rolls it once. An application whose grants are all `untilEndOfTurn` and all already
702
+ in force rolls no further save; one with any `untilNextAttack` grant always rolls.
703
+ `onSave` applies other grants on the success branch of the same roll:
704
+ `{ chance: 0.4, onSave: advantage().untilNextAttack() }`. `fireProbability(id)` reports P(the grants
705
+ were applied where a later attack reads them). In plain data it is `TurnSpec.conditions`, with the
706
+ `save` already turned into its `chance`.
707
+
708
+ #### Rerolls, sweeping AC, and naming attacks
709
+
710
+ A reroll is an attack-shaped rider. `onFirstMiss(attack)` resolves right after the attack that
711
+ missed, so anything that reads order — a `first-hit` rider's crit mode, a granted advantage — sees
712
+ it where it happened. A rider or transform added *after* an `onAnyMiss` / `onFirstMiss` reroll
713
+ watches it without being told:
714
+
715
+ ```ts
716
+ turn([sword, sword])
717
+ .onFirstMiss(sword) // reroll the first miss
718
+ .onFirstHit(roll(1, d10)) // watches both attacks and the reroll
719
+ ```
720
+
721
+ `vsAC(ac)` rebuilds every attack with an AC — including rerolls and bonus attacks carried by
722
+ riders — for a DPR-by-AC sweep, leaving saves untouched:
723
+
724
+ ```ts
725
+ const base = turn([sword, sword]).onFirstHit(roll(3, d6));
726
+ [12, 14, 16, 18].map((ac) => base.vsAC(ac).mean());
727
+ ```
728
+
729
+ `attack(source, { tag })` and `attacks(n, source, { tag })` name a group of attacks, and an `of`
730
+ entry that is not an id expands to every attack with that tag — so reordering a turn cannot
731
+ silently retarget a rider the way a positional `attack 2` can.
732
+
470
733
  #### Ids, errors, and plain data
471
734
 
472
735
  Nothing above needs an `id`: attacks and riders get `attack 1`, `rider 2`, … in declaration order,
@@ -482,12 +745,17 @@ paladin.riderIds; // ["smite"]
482
745
 
483
746
  Every construction path validates immediately and throws a `TurnSpecError` whose `code` —
484
747
  `unknown-id`, `cycle`, `not-an-attack`, `duplicate-id`, `self-reference`, `unused-crit-damage`,
485
- `too-many-groups` — maps
748
+ `too-many-groups`, `no-dice-descriptor`, `duplicate-substitute`, `attack-after-rider`,
749
+ `no-rebindable-source`, `unsupported-trigger`, `unsupported-policy`, `too-many-flags` — maps
486
750
  straight onto a UI field state. A bad `of` fails at the call that introduced it, not later at
487
751
  `.mean()`.
488
752
 
489
753
  Each `onX` method takes an optional `{ id, of, critDamage }`, where `of` picks which attacks the
490
- rider watches and defaults to all of them. All of them are sugar over `rider()`, which takes the
754
+ rider watches. Left out, it is filled in at that call: the attacks declared so far, plus any reroll
755
+ declared so far. So declare attacks first — `.attack()` after such a rider throws
756
+ `attack-after-rider` rather than silently leaving the new attack out. `Turn.from` fills an omitted
757
+ `of` the same way — every attack plus the rerolls, for a rider the rerolls listed before it — so
758
+ both spellings of a turn watch the same sources. All of them are sugar over `rider()`, which takes the
491
759
  trigger as plain data — and `Trigger` is JSON-safe, so a UI can persist one and hand it straight
492
760
  back:
493
761
 
@@ -529,6 +797,12 @@ console.table(query.toChartSeries());
529
797
  └─────────┴────┴──────────┘
530
798
  ```
531
799
 
800
+ Percentiles and `quantile()` land on the exact bin, so a d20's median is 10. `missChance()` is the
801
+ probability that at least one attack misses; for "every attack misses" use
802
+ `probExactlyK(["missNone", "missDamage"], n)`. `turn()`, `Turn.from()` and `resolve()` keep every
803
+ reachable damage value (their `eps` defaults to 0). `setCachingEnabled(false)` turns off and
804
+ empties every internal cache; PMFs returned from a cache have frozen bins.
805
+
532
806
  ## 🧪 Running Examples
533
807
 
534
808
  This repository includes example scripts:
@@ -1,10 +1,26 @@
1
1
  import { PMF } from "../pmf/pmf.js";
2
2
  import { AttackBuilder } from "./attack.js";
3
3
  import { AlwaysCritBuilder, RollBuilder } from "./roll.js";
4
+ import type { RollConfig, RollType } from "./types.js";
4
5
  export interface AttackConfig {
5
6
  ac: number;
6
7
  critThreshold: number;
8
+ advantageDice: 2 | 3;
7
9
  }
10
+ /**
11
+ * Combines a source's own roll type with granted advantage/disadvantage flags by cancellation —
12
+ * `{advantage, disadvantage}` from any combination of source and grants resolve to `advantage` /
13
+ * `disadvantage` / `flat`. `advantageDice` governs how many dice a NET advantage rolls, whether
14
+ * the advantage came from `rollType` itself or from `flags`; an own `elven accuracy` roll type
15
+ * always rolls three. Pure; independently testable against the full 4×2×4 table.
16
+ */
17
+ export declare function combine(rollType: RollType, advantageDice: 2 | 3, flags: {
18
+ advantage: boolean;
19
+ disadvantage: boolean;
20
+ }): {
21
+ rollType: RollType;
22
+ dice: number;
23
+ };
8
24
  export declare class ACBuilder extends RollBuilder {
9
25
  readonly attackConfig: AttackConfig;
10
26
  constructor(baseRoll: RollBuilder, ac: number, attackConfig?: AttackConfig);
@@ -16,11 +32,38 @@ export declare class ACBuilder extends RollBuilder {
16
32
  onHit(count: number, die: RollBuilder, modifier: number): AttackBuilder;
17
33
  onHit(count: number, sides: number, modifier: number): AttackBuilder;
18
34
  get critThreshold(): number;
35
+ /**
36
+ * Three-dice advantage is an attacker property, not a roll type. Setting it now applies
37
+ * whenever the NET result later resolves to advantage — whether this source already has
38
+ * advantage baked in, or advantage is granted afterward (via `withCheck`).
39
+ */
40
+ threeDiceAdvantage(): ACBuilder;
41
+ /**
42
+ * The dice configs with `advantageDice` mechanically folded in (`elven accuracy` in place of
43
+ * `advantage` when the net roll should use 3 dice). Shared by every consumer that needs the
44
+ * REAL resolvable dice rather than the raw stored config: {@link getRootDieConfig} (read by
45
+ * `rollType`/`resolveRootD20`) and {@link alwaysCrits} (whose target class has no override).
46
+ */
47
+ private resolvedConfigs;
48
+ getRootDieConfig(): RollConfig | undefined;
19
49
  cacheKey(): string | null;
50
+ /** Sets the crit threshold: a natural roll at or above it is a crit. */
20
51
  critOn(threshold: number): ACBuilder;
21
52
  alwaysCrits(): AlwaysCritBuilder;
53
+ /**
54
+ * `(<to-hit total> AC <ac>)`: the natural roll as {@link toPMF} reads it (one root die, rolled with
55
+ * the resolved roll type, so three-dice advantage prints `d20 > d20 > d20`), its flats and every
56
+ * bonus die.
57
+ */
22
58
  toExpression(): string;
59
+ /**
60
+ * The raw to-hit PMF: the check's total where it reaches the AC and 0 where it does not, with no
61
+ * natural-1 miss or natural-20 hit (`d20.plus(20).ac(15).toPMF()` never shows 0, although the
62
+ * attack misses on a natural 1). The attack outcome — natural rules, crits — comes from
63
+ * `onHit(...).resolve()`.
64
+ */
23
65
  toPMF(eps?: number): PMF;
24
- copy(): ACBuilder;
66
+ /** Accepts a replacement AC, so `vsAC` can rebind the check without rebuilding the roll. */
67
+ copy(ac?: number): ACBuilder;
25
68
  }
26
69
  //# sourceMappingURL=ac.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ac.d.ts","sourceRoot":"","sources":["../../src/builder/ac.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,OAAO,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAExD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;CACvB;AACD,qBAAa,SAAU,SAAQ,WAAW;IACxC,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IAEpC,YAAY,QAAQ,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,YAAY,EAQzE;IAMD,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAAC;IAClC,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAAC;IAClC,KAAK,CAAC,GAAG,EAAE,WAAW,GAAG,aAAa,CAAC;IACvC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,aAAa,CAAC;IACtD,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,aAAa,CAAC;IACnD,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,GAAG,aAAa,CAAC;IACxE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,aAAa,CAAC;IAMrE,IAAI,aAAa,IAAI,MAAM,CAE1B;IAEQ,QAAQ,IAAI,MAAM,GAAG,IAAI,CAGjC;IAGD,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAMnC;IAED,WAAW,IAAI,iBAAiB,CAS/B;IAGQ,YAAY,IAAI,MAAM,CAM9B;IAEQ,KAAK,CAAC,GAAG,GAAE,MAAU,GAAG,GAAG,CAsBnC;IAEQ,IAAI,IAAI,SAAS,CAOzB;CACF"}
1
+ {"version":3,"file":"ac.d.ts","sourceRoot":"","sources":["../../src/builder/ac.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,YAAY,CAAC;AACjC,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAIzC,OAAO,EAAE,iBAAiB,EAAoB,WAAW,EAAE,MAAM,QAAQ,CAAC;AAC1E,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEpD,MAAM,WAAW,YAAY;IAC3B,EAAE,EAAE,MAAM,CAAC;IACX,aAAa,EAAE,MAAM,CAAC;IAItB,aAAa,EAAE,CAAC,GAAG,CAAC,CAAC;CACtB;AAED;;;;;;GAMG;AACH,wBAAgB,OAAO,CACrB,QAAQ,EAAE,QAAQ,EAClB,aAAa,EAAE,CAAC,GAAG,CAAC,EACpB,KAAK,EAAE;IAAE,SAAS,EAAE,OAAO,CAAC;IAAC,YAAY,EAAE,OAAO,CAAA;CAAE,GACnD;IAAE,QAAQ,EAAE,QAAQ,CAAC;IAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAUtC;AAED,qBAAa,SAAU,SAAQ,WAAW;IACxC,QAAQ,CAAC,YAAY,EAAE,YAAY,CAAC;IAEpC,YAAY,QAAQ,EAAE,WAAW,EAAE,EAAE,EAAE,MAAM,EAAE,YAAY,CAAC,EAAE,YAAY,EAQzE;IAED,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAAC;IAClC,KAAK,CAAC,GAAG,EAAE,MAAM,GAAG,aAAa,CAAC;IAClC,KAAK,CAAC,GAAG,EAAE,WAAW,GAAG,aAAa,CAAC;IACvC,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,GAAG,aAAa,CAAC;IACtD,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,aAAa,CAAC;IACnD,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,GAAG,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,GAAG,aAAa,CAAC;IACxE,KAAK,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,aAAa,CAAC;IAMrE,IAAI,aAAa,IAAI,MAAM,CAE1B;IAED;;;;OAIG;IACH,kBAAkB,IAAI,SAAS,CAM9B;IAED;;;;;OAKG;IACH,OAAO,CAAC,eAAe;IAiBd,gBAAgB,IAAI,UAAU,GAAG,SAAS,CAIlD;IAEQ,QAAQ,IAAI,MAAM,GAAG,IAAI,CAKjC;IAED,wEAAwE;IACxE,MAAM,CAAC,SAAS,EAAE,MAAM,GAAG,SAAS,CAOnC;IAED,WAAW,IAAI,iBAAiB,CAU/B;IAED;;;;OAIG;IACM,YAAY,IAAI,MAAM,CAE9B;IAED;;;;;OAKG;IACM,KAAK,CAAC,GAAG,GAAE,MAAU,GAAG,GAAG,CAsBnC;IAED,4FAA4F;IACnF,IAAI,CAAC,EAAE,CAAC,EAAE,MAAM,GAAG,SAAS,CAQpC;CACF"}
@@ -0,0 +1,6 @@
1
+ /**
2
+ * Throws for a non-finite builder argument, naming it. NaN reads "got NaN" unless the call site has
3
+ * its own, earlier NaN message. Internal: not re-exported from the package.
4
+ */
5
+ export declare function requireFinite(value: number, what: string): void;
6
+ //# sourceMappingURL=arguments.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arguments.d.ts","sourceRoot":"","sources":["../../src/builder/arguments.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAE/D"}
@@ -1,8 +1,32 @@
1
1
  import { PMF } from "../.js";
2
2
  import type { DieNode, ExpressionNode } from "./nodes.js";
3
- import type { RollBuilder } from "./roll.js";
3
+ import { type RollBuilder } from "./roll.js";
4
4
  import type { RollConfig, RollType } from "./types.js";
5
+ /** Clears the single-die and d20-lift PMF caches (reached through `clearRollCache`). */
6
+ export declare function clearDieCaches(): void;
5
7
  export declare function dieNodeFromConfig(cfg: RollConfig): DieNode;
8
+ /**
9
+ * Thrown for a per-die `keepHighest(T, K)`/`keepLowest(T, K)` on a group of N > 1 dice whose
10
+ * reading is not unique: "keep K of T rolls of the whole N-dice sum" and "keep K of T single dice"
11
+ * give different numbers, and nothing in the call says which is meant.
12
+ */
13
+ export declare class AmbiguousKeepError extends Error {
14
+ constructor(message: string);
15
+ }
16
+ type KeepSpec = NonNullable<RollConfig["keep"]>;
17
+ /**
18
+ * How a per-die keep on a group of `count` d`sides` reads:
19
+ *
20
+ * - `"dice"`: keep K of T single dice. Used for one die (`d6.keepHighest(3, 2)`), for K >= 2 of
21
+ * the group's own N dice (`roll(4, d6).keepHighest(4, 3)`), and for `bestOf(k)`.
22
+ * - `"trials"`: roll the whole N-dice group T times and keep K of those sums. Used for K = 1:
23
+ * `roll(2, d6).keepHighest(2, 1)` is the better of two 2d6, and `keepLowest(T, 1)` with T != N
24
+ * is the worse of T rolls of the group.
25
+ *
26
+ * Every other shape on N > 1 dice (K >= 2 with T != N, and `keepLowest(N, 1)`, whose single-die
27
+ * and whole-group readings differ) throws an {@link AmbiguousKeepError} naming both spellings.
28
+ */
29
+ export declare function perDieKeepReading(count: number, sides: number, keep: KeepSpec, synthesizedBestOf?: boolean): "dice" | "trials";
6
30
  export declare function astFromRollConfigs(configs: readonly RollConfig[]): ExpressionNode | undefined;
7
31
  export declare function resolve(node: ExpressionNode, eps?: number): PMF;
8
32
  export declare function pmfFromRollBuilder(rb: RollBuilder, eps?: number): PMF;
@@ -26,14 +50,19 @@ export declare function pmfFromRollBuilder(rb: RollBuilder, eps?: number): PMF;
26
50
  */
27
51
  export declare function resolveD20Roll(die: DieNode, rollType: RollType | undefined): PMF;
28
52
  /**
29
- * Resolve a check builder's root die (the to-hit/save d20, or whatever die it wraps), honoring
30
- * that die's reroll/minimum/explode, then lift by its `rollType`. The single entry point every
31
- * attack/save check builder ({@link ACBuilder}, {@link AttackBuilder}, {@link AlwaysHitBuilder},
32
- * {@link AlwaysCritBuilder}, `DCBuilder`, save `resolveProbabilities`) should use instead of
33
- * reaching for `d20RollPMF(rollType, baseReroll > 0)` directly — that 2-argument summary silently
34
- * drops `minimum`/`explode` on the root config.
53
+ * Resolve a check builder's natural roll (its d20, or with no d20 its largest die: see
54
+ * {@link naturalRollIndex}), honoring that die's reroll/minimum/explode, then lift by its
55
+ * `rollType`. The single entry point every attack/save check builder ({@link ACBuilder},
56
+ * {@link AttackBuilder}, {@link AlwaysHitBuilder}, {@link AlwaysCritBuilder}, `DCBuilder`, save
57
+ * `resolveProbabilities`) uses for the natural roll; the other dice are bonus dice.
58
+ *
59
+ * A check with no die has no natural roll: this returns a certain 0, so its total is its flat
60
+ * modifier plus any bonus dice and no natural-1/natural-20 rule can apply. A natural roll of more
61
+ * than one die (`roll(2, d20)`, or two equal top dice like `d20.plus(d20)`) has no single natural
62
+ * 1 or 20 and throws.
35
63
  */
36
64
  export declare function resolveRootD20(check: RollBuilder): PMF;
37
65
  export declare function resolveSingleDie(die: DieNode, eps?: number): PMF;
38
66
  export declare function getASTSignature(node: ExpressionNode): string;
67
+ export {};
39
68
  //# sourceMappingURL=ast.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../../src/builder/ast.ts"],"names":[],"mappings":"AAAA,OAAO,EAAY,GAAG,EAAE,MAAM,KAAK,CAAC;AAGpC,OAAO,KAAK,EAIV,OAAO,EACP,cAAc,EAIf,MAAM,SAAS,CAAC;AACjB,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AAC1C,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAOpD,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAW1D;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,SAAS,UAAU,EAAE,GAC7B,cAAc,GAAG,SAAS,CAwI5B;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,cAAc,EAAE,GAAG,GAAE,MAAmB,GAAG,GAAG,CAgG3E;AAED,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,WAAW,EACf,GAAG,GAAE,MAAmB,GACvB,GAAG,CAGL;AAID;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,GAAG,SAAS,GAAG,GAAG,CA8BhF;AAED;;;;;;;GAOG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,CAOtD;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,GAAE,MAAmB,GAAG,GAAG,CAsE5E;AAqTD,wBAAgB,eAAe,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAsD5D"}
1
+ {"version":3,"file":"ast.d.ts","sourceRoot":"","sources":["../../src/builder/ast.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,KAAK,CAAC;AAG1B,OAAO,KAAK,EAIV,OAAO,EACP,cAAc,EAIf,MAAM,SAAS,CAAC;AACjB,OAAO,EAAoB,KAAK,WAAW,EAAE,MAAM,QAAQ,CAAC;AAC5D,OAAO,KAAK,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAOpD,wFAAwF;AACxF,wBAAgB,cAAc,IAAI,IAAI,CAGrC;AAED,wBAAgB,iBAAiB,CAAC,GAAG,EAAE,UAAU,GAAG,OAAO,CAa1D;AAED;;;;GAIG;AACH,qBAAa,kBAAmB,SAAQ,KAAK;IAC3C,YAAY,OAAO,EAAE,MAAM,EAI1B;CACF;AAED,KAAK,QAAQ,GAAG,WAAW,CAAC,UAAU,CAAC,MAAM,CAAC,CAAC,CAAC;AAEhD;;;;;;;;;;;GAWG;AACH,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,MAAM,EACb,KAAK,EAAE,MAAM,EACb,IAAI,EAAE,QAAQ,EACd,iBAAiB,UAAQ,GACxB,MAAM,GAAG,QAAQ,CAgBnB;AAED,wBAAgB,kBAAkB,CAChC,OAAO,EAAE,SAAS,UAAU,EAAE,GAC7B,cAAc,GAAG,SAAS,CAsF5B;AAED,wBAAgB,OAAO,CAAC,IAAI,EAAE,cAAc,EAAE,GAAG,GAAE,MAAmB,GAAG,GAAG,CAmG3E;AAED,wBAAgB,kBAAkB,CAChC,EAAE,EAAE,WAAW,EACf,GAAG,GAAE,MAAmB,GACvB,GAAG,CAGL;AAID;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,OAAO,EAAE,QAAQ,EAAE,QAAQ,GAAG,SAAS,GAAG,GAAG,CA8BhF;AAED;;;;;;;;;;;GAWG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,WAAW,GAAG,GAAG,CAgBtD;AAED,wBAAgB,gBAAgB,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,GAAE,MAAmB,GAAG,GAAG,CA2E5E;AA6UD,wBAAgB,eAAe,CAAC,IAAI,EAAE,cAAc,GAAG,MAAM,CAwD5D"}