roll-parser 3.3.0 → 3.4.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 +42 -1
- package/README.md +60 -32
- package/dist/cli/args.d.ts +11 -1
- package/dist/cli/args.d.ts.map +1 -1
- package/dist/cli/args.js +67 -42
- package/dist/cli/args.js.map +1 -1
- package/dist/cli/format.d.ts +8 -1
- package/dist/cli/format.d.ts.map +1 -1
- package/dist/cli/format.js +4 -3
- package/dist/cli/format.js.map +1 -1
- package/dist/cli/main.d.ts +3 -3
- package/dist/cli/main.d.ts.map +1 -1
- package/dist/cli/main.js +50 -19
- package/dist/cli/main.js.map +1 -1
- package/dist/evaluator/evaluator.d.ts +15 -3
- package/dist/evaluator/evaluator.d.ts.map +1 -1
- package/dist/evaluator/evaluator.js +134 -105
- package/dist/evaluator/evaluator.js.map +1 -1
- package/dist/evaluator/modifiers/flags.d.ts +7 -1
- package/dist/evaluator/modifiers/flags.d.ts.map +1 -1
- package/dist/evaluator/modifiers/flags.js +9 -1
- package/dist/evaluator/modifiers/flags.js.map +1 -1
- package/dist/evaluator/modifiers/keep-drop.d.ts.map +1 -1
- package/dist/evaluator/modifiers/keep-drop.js +19 -16
- package/dist/evaluator/modifiers/keep-drop.js.map +1 -1
- package/dist/lexer/lexer.d.ts +5 -0
- package/dist/lexer/lexer.d.ts.map +1 -1
- package/dist/lexer/lexer.js +3 -0
- package/dist/lexer/lexer.js.map +1 -1
- package/dist/parser/parser.d.ts +1 -0
- package/dist/parser/parser.d.ts.map +1 -1
- package/dist/parser/parser.js +23 -20
- package/dist/parser/parser.js.map +1 -1
- package/dist/render.d.ts.map +1 -1
- package/dist/render.js +2 -1
- package/dist/render.js.map +1 -1
- package/dist/rng/seeded.d.ts.map +1 -1
- package/dist/rng/seeded.js.map +1 -1
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +13 -14
- package/src/cli/args.ts +112 -40
- package/src/cli/format.ts +11 -3
- package/src/cli/main.ts +97 -22
- package/src/evaluator/evaluator.ts +189 -150
- package/src/evaluator/modifiers/flags.ts +18 -2
- package/src/evaluator/modifiers/keep-drop.ts +42 -16
- package/src/lexer/lexer.ts +8 -0
- package/src/parser/parser.ts +36 -32
- package/src/render.ts +2 -1
- package/src/rng/seeded.ts +4 -0
- package/src/version.ts +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -7,6 +7,45 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [3.4.0] - 2026-09-11
|
|
11
|
+
|
|
12
|
+
CLI release: `--json` becomes a complete record. A payload now names the seed
|
|
13
|
+
and version that produced it, so any roll — seeded or not — can be replayed from
|
|
14
|
+
its own output, and every diagnostic the CLI raises now arrives as one JSON line
|
|
15
|
+
instead of an `Error:` prefix and a caret diagram. The library, its public types
|
|
16
|
+
and its serialized shape are untouched; the `--json` success payload only gains
|
|
17
|
+
keys at the end, so existing field access is unaffected.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- `--json` output ends in `seed` and `version`, making a payload re-derivable from itself. The CLI mints its own seed with `crypto.randomUUID()` rather than letting `SeededRNG` auto-seed and discard the value, so the emitted seed is one that can be handed straight back to `--seed`; `version` rides along because the seed-to-dice mapping is only fixed within a major. Minting is unconditional, so the plain and verbose paths draw from the same source. Both keys are appended, so every existing key keeps its position ([#342](https://github.com/edloidas/roll-parser/issues/342))
|
|
22
|
+
- Every diagnostic raised after `--json` is parsed is now one JSON line on stderr, carrying the error's stable `code` and the `span` that `getErrorSpan` normalizes, plus the `notation` and `seed` that produced the failure. Machine consumers no longer have to regex the message and count caret spaces for a position the process already held. The record reports `span` rather than a flat position because the evaluator carries a start and an end. A failing unseeded roll is now reproducible too — the minted seed was declared inside the `try` and the `catch` never echoed it. Exit codes and empty-stdout-on-failure are unchanged ([#343](https://github.com/edloidas/roll-parser/issues/343))
|
|
23
|
+
|
|
24
|
+
### Changed
|
|
25
|
+
|
|
26
|
+
- `--json` now covers the two usage errors that escaped it. `Unknown option: <x>` and `Missing value for --seed` were raised at the offending token, before the flag was known, so they printed as plain text; `parseArgs` now records the first usage error and runs the loop to the end, so the flag survives alongside it. Detection stays positional: `--seed --json` binds `--json` as the seed value and `roll-parser -- --json` makes it notation, and neither emits JSON. The record is `{"error":{"message":...},"version":...}` — a usage error never reached the dice, so it carries no `code`, `span`, `notation` or `seed`. Exit code 2 is unchanged ([#351](https://github.com/edloidas/roll-parser/issues/351))
|
|
27
|
+
- A failed run whose notation was passed after `--` now ends with a hint saying the terminator makes everything after it part of the notation. It is gated on what the parser did rather than on `argv` containing a `--`: `--seed` takes any non-empty next argument, so `--seed --` binds that `--` as a seed value and no terminator ever runs. `parseArgs` carries `terminated` on its success arm to express that. The `--json` path is untouched — every diagnostic there stays one JSON line ([#344](https://github.com/edloidas/roll-parser/issues/344))
|
|
28
|
+
|
|
29
|
+
### Fixed
|
|
30
|
+
|
|
31
|
+
- `roll-parser --seed -- --help` prints the manual instead of exiting 2 with `Unknown option`. The scan that finds `--help`, `-h` and `--version` broke at the first literal `--`, but `readSeedValue` takes any non-empty next argument, so in `--seed --` that `--` is the seed value and never reaches the terminator branch of the main loop. The scan now consumes a `--seed` value the way the loop does, exempting an informational flag from the skip so `--seed --help` still resolves to help ([#364](https://github.com/edloidas/roll-parser/issues/364))
|
|
32
|
+
|
|
33
|
+
### Documentation
|
|
34
|
+
|
|
35
|
+
- `HELP_TEXT` and the README CLI section no longer teach `--` as the way to write negative notation. `roll-parser -1d6+3` works on its own — `-1d6`, `-d6`, `-dF`, `-(2d6)`, `-{2d6}` and `-@str` are all accepted as positionals — and teaching the terminator there walked users into its real semantics, where a trailing `--verbose` becomes part of the notation and the lexer rejects it as an identifier. The `--` option line now names what the terminator is actually for ([#344](https://github.com/edloidas/roll-parser/issues/344))
|
|
36
|
+
- The README `--json` sample no longer pins a `"version":"3.3.1"` literal that nothing checks, and the prose states that `version` is a bare semver with no `v` prefix ([#348](https://github.com/edloidas/roll-parser/issues/348))
|
|
37
|
+
- The README performance table is re-measured under Bun 1.4 as the per-record median of eight `bench:json` passes: `roll` runs 8-18% faster on the light expressions and flat on the heavy pools, while `lex` and `parse` land within 10% of the old figures in both directions. The measurement protocol records the non-idle session and its wider pass-to-pass spread ([#340](https://github.com/edloidas/roll-parser/issues/340))
|
|
38
|
+
|
|
39
|
+
## [3.3.1] - 2026-08-20
|
|
40
|
+
|
|
41
|
+
### Changed
|
|
42
|
+
|
|
43
|
+
- performance: the evaluator does less work per node and per die. `EvalContext`'s `expression` and `rendered` fragment arrays are single strings carried on `EvalResult`, so no branch can omit one; `rewriteFlags` is one loop instead of a closure, a filter array and a spread; a modifier's target context no longer renders the pool it discards, with every pool render routed through `renderedPool`; and `renderGroup` looks kept indices up in a `Set` rather than an array. No notation evaluates differently and no public type changed ([#336](https://github.com/edloidas/roll-parser/issues/336))
|
|
44
|
+
|
|
45
|
+
### Documentation
|
|
46
|
+
|
|
47
|
+
- `package.json` metadata is tuned for npm search: the `description` leads with queryable nouns and names the systems, `roller`, `die`, `notation`, `pathfinder` and the space-separated `dice notation` join the keywords, and seven keywords that ranked outside the top 100 for their own chip are gone. `README.md` gains a `min+brotli ≤13.5 kB` badge linking to the size breakdown ([#333](https://github.com/edloidas/roll-parser/issues/333))
|
|
48
|
+
|
|
10
49
|
## [3.3.0] - 2026-08-17
|
|
11
50
|
|
|
12
51
|
Error-message work throughout: nine parse and lex errors now name the rule they
|
|
@@ -295,7 +334,9 @@ Dice mechanics (Stage 2):
|
|
|
295
334
|
- 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))
|
|
296
335
|
- 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))
|
|
297
336
|
|
|
298
|
-
[Unreleased]: https://github.com/edloidas/roll-parser/compare/v3.
|
|
337
|
+
[Unreleased]: https://github.com/edloidas/roll-parser/compare/v3.4.0...HEAD
|
|
338
|
+
[3.4.0]: https://github.com/edloidas/roll-parser/releases/tag/v3.4.0
|
|
339
|
+
[3.3.1]: https://github.com/edloidas/roll-parser/releases/tag/v3.3.1
|
|
299
340
|
[3.3.0]: https://github.com/edloidas/roll-parser/releases/tag/v3.3.0
|
|
300
341
|
[3.2.2]: https://github.com/edloidas/roll-parser/releases/tag/v3.2.2
|
|
301
342
|
[3.2.1]: https://github.com/edloidas/roll-parser/releases/tag/v3.2.1
|
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@ Dice notation for tabletop RPGs, rolled into structured results.
|
|
|
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
15
|
<a href="https://nodejs.org/"><img src="https://img.shields.io/node/v/roll-parser?color=5fa04e" alt="Node.js >= 22.12"></a>
|
|
16
|
+
<a href="https://github.com/edloidas/roll-parser#why-roll-parser"><img src="https://img.shields.io/badge/min%2Bbrotli-%E2%89%A413.5%20kB-8b5cf6" alt="min+brotli ≤13.5 kB"></a>
|
|
16
17
|
</p>
|
|
17
18
|
|
|
18
19
|
<p align="center">
|
|
@@ -51,11 +52,11 @@ roll('4d6kh3', { rng: createMockRng([3, 6, 2, 5]) }).total; // 14, every run
|
|
|
51
52
|
|
|
52
53
|
## Why roll-parser
|
|
53
54
|
|
|
54
|
-
- **Complete notation.**
|
|
55
|
-
min/max clamps, success pools, crit thresholds,
|
|
56
|
-
degrees of success, math functions, variables,
|
|
57
|
-
Pathfinder, World of Darkness, Shadowrun,
|
|
58
|
-
Cthulhu without escape hatches. Spelled the way
|
|
55
|
+
- **Complete notation.** A dice roller that takes keep/drop, three flavours of
|
|
56
|
+
exploding dice, rerolls, min/max clamps, success pools, crit thresholds,
|
|
57
|
+
sorting, grouped rolls, PF2e degrees of success, math functions, variables,
|
|
58
|
+
computed dice — enough for D&D 5e, Pathfinder, World of Darkness, Shadowrun,
|
|
59
|
+
Fate, Savage Worlds, and Call of Cthulhu without escape hatches. Spelled the way
|
|
59
60
|
[Roll20](https://help.roll20.net/hc/en-us/articles/360037773133-Dice-Reference)
|
|
60
61
|
spells it wherever the two overlap, so the notation your table already uses
|
|
61
62
|
keeps working.
|
|
@@ -314,8 +315,9 @@ pool.total; // 5 — successes minus failures
|
|
|
314
315
|
| `successes` / `failures` | `number?` | Present only when success counting was used |
|
|
315
316
|
| `degree` / `natural` | `DegreeOfSuccess?` / `number?` | Present only for a top-level `vs` |
|
|
316
317
|
|
|
317
|
-
`Readonly` at the top level and fully JSON-serializable
|
|
318
|
-
|
|
318
|
+
`Readonly` at the top level and fully JSON-serializable. The CLI's `--json`
|
|
319
|
+
flag prints these fields and appends two of its own, `seed` and `version` —
|
|
320
|
+
see [CLI](#cli).
|
|
319
321
|
|
|
320
322
|
```typescript
|
|
321
323
|
JSON.stringify(roll('3d6', { rng: createMockRng([4, 2, 6]) }));
|
|
@@ -1002,7 +1004,7 @@ Options:
|
|
|
1002
1004
|
-v, --verbose Show detailed roll breakdown
|
|
1003
1005
|
--json Print the whole result as compact JSON (wins over --verbose)
|
|
1004
1006
|
--seed <value> Use seed for reproducible rolls
|
|
1005
|
-
--
|
|
1007
|
+
-- Notation that looks like an option — put options before it
|
|
1006
1008
|
```
|
|
1007
1009
|
|
|
1008
1010
|
```bash
|
|
@@ -1020,15 +1022,48 @@ Error: Invalid dice sides: 0
|
|
|
1020
1022
|
2d6+1d0+3
|
|
1021
1023
|
^
|
|
1022
1024
|
|
|
1023
|
-
$ roll-parser --seed demo
|
|
1025
|
+
$ roll-parser "2d6+1d0+3" --json --seed demo
|
|
1026
|
+
{"error":{"message":"Invalid dice sides: 0","code":"INVALID_DICE_SIDES","span":{"start":4,"end":7}},"notation":"2d6+1d0+3","seed":"demo",...}
|
|
1027
|
+
|
|
1028
|
+
$ roll-parser -1d6+3 --seed demo
|
|
1024
1029
|
2
|
|
1025
1030
|
```
|
|
1026
1031
|
|
|
1032
|
+
That payload is cut short; in full it ends:
|
|
1033
|
+
|
|
1034
|
+
```json
|
|
1035
|
+
{"total":8,"notation":"1d20+7 vs 15", …, "seed":"demo","version":"…"}
|
|
1036
|
+
```
|
|
1037
|
+
|
|
1027
1038
|
Verbose mode rewrites the markdown markers for plain terminals: `~~n~~` becomes
|
|
1028
1039
|
`(n)`, `**n**` becomes `[n]`, `__n__` becomes `{n}`. `--seed` takes both
|
|
1029
1040
|
`--seed value` and `--seed=value`, and accepts any non-empty value including a
|
|
1030
|
-
dash-prefixed one. `--
|
|
1031
|
-
|
|
1041
|
+
dash-prefixed one. The `--json` payload ends with two fields the library
|
|
1042
|
+
result does not carry: the `seed` that produced the roll and the `version` (a
|
|
1043
|
+
bare semver, no `v` prefix) that fixes the seed-to-dice mapping — feed that
|
|
1044
|
+
seed back through `--seed` on the same major and the dice repeat. Omitting
|
|
1045
|
+
`--seed` mints one, so an unplanned roll stays reproducible too. `--help` and
|
|
1046
|
+
`--version` win over any usage error that precedes them. Errors go to stderr;
|
|
1047
|
+
only the result goes to stdout.
|
|
1048
|
+
|
|
1049
|
+
Leading-minus notation needs no `--`: `-1d6+3`, `-d6`, `-dF`, `-(2d6)`,
|
|
1050
|
+
`-{2d6}` and `-@str` are all read as notation. `--` is the escape hatch for
|
|
1051
|
+
notation that would otherwise parse as an option, and everything after it is
|
|
1052
|
+
notation — flags included — so options go before it, never after. A failed roll
|
|
1053
|
+
whose notation swallowed one prints `Hint: options must come before "--"` after
|
|
1054
|
+
the caret diagram.
|
|
1055
|
+
|
|
1056
|
+
Once `--json` is parsed, every diagnostic is one JSON line on stderr instead
|
|
1057
|
+
of the caret diagram. `code` is the same stable `RollParserErrorCode` the
|
|
1058
|
+
library throws, and `span` is what `getErrorSpan` returns — `{ start }` for a
|
|
1059
|
+
lexer or parser error, `{ start, end }` for an evaluator one, absent when the
|
|
1060
|
+
failure carries no position. A roll error also repeats the `notation` and
|
|
1061
|
+
`seed` that produced it, so a failure replays through `--seed` exactly as a
|
|
1062
|
+
result does. Branch on `code`, not on `message`, which is free to change
|
|
1063
|
+
between releases. Usage errors are covered too — an unknown option and a
|
|
1064
|
+
missing `--seed` value carry a `message` and nothing else. Detection honours
|
|
1065
|
+
what `--json` actually binds to, so `--seed --json` reads the flag as the seed
|
|
1066
|
+
value and `roll-parser -- --json` reads it as notation; neither emits JSON.
|
|
1032
1067
|
|
|
1033
1068
|
| Exit code | Meaning |
|
|
1034
1069
|
|----------:|---------|
|
|
@@ -1040,36 +1075,29 @@ precedes them. Errors go to stderr; only the result goes to stdout.
|
|
|
1040
1075
|
|
|
1041
1076
|
| Notation | `lex` | `parse` | `roll` (end to end) |
|
|
1042
1077
|
|----------|------:|--------:|--------------------:|
|
|
1043
|
-
| `1d20` |
|
|
1044
|
-
| `2d6+3` |
|
|
1045
|
-
| `4d6kh3` |
|
|
1046
|
-
| `4d6sd` |
|
|
1047
|
-
| `10d10>=6f1` |
|
|
1048
|
-
| `100d6` |
|
|
1078
|
+
| `1d20` | 86 ns | 166 ns | **0.43 µs** (~2.3M rolls/s) |
|
|
1079
|
+
| `2d6+3` | 104 ns | 230 ns | **0.64 µs** |
|
|
1080
|
+
| `4d6kh3` | 127 ns | 262 ns | **0.98 µs** |
|
|
1081
|
+
| `4d6sd` | 112 ns | 232 ns | **0.77 µs** |
|
|
1082
|
+
| `10d10>=6f1` | 165 ns | 361 ns | **1.6 µs** |
|
|
1083
|
+
| `100d6` | 81 ns | 162 ns | **2.6 µs** |
|
|
1049
1084
|
|
|
1050
1085
|
The `roll` column pays for a fresh `SeededRNG` per call, which an injected RNG
|
|
1051
1086
|
avoids. Every roll also builds the `parts` tree; there is no opt-out and these
|
|
1052
|
-
numbers include it. A 1000-die pool costs roughly
|
|
1053
|
-
while lexing and parsing stay flat at ~
|
|
1087
|
+
numbers include it. A 1000-die pool costs roughly 56x a `1d20` (~24 µs here),
|
|
1088
|
+
while lexing and parsing stay flat at ~83 / ~164 ns.
|
|
1054
1089
|
|
|
1055
1090
|
<details>
|
|
1056
1091
|
<summary>Measurement protocol</summary>
|
|
1057
1092
|
|
|
1058
1093
|
Values are **p50**, from
|
|
1059
1094
|
[mitata](https://github.com/evanwashere/mitata) with forced per-iteration GC
|
|
1060
|
-
(`.gc('inner')`), taken as the per-record median of
|
|
1061
|
-
`bun run bench:json` passes
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
inflates the heavy cases most.
|
|
1066
|
-
|
|
1067
|
-
The `4d6sd` row and the `10d10>=6f1` `roll` figure come from a later five-pass
|
|
1068
|
-
measurement on the same machine, after the per-die `'dc'` tag checks were
|
|
1069
|
-
hoisted out of the keep/drop, success-count, and sort loops. That session was
|
|
1070
|
-
not idle and those two read bimodally with roughly 10% between the modes, so
|
|
1071
|
-
take them as a recovered range rather than point values. Every other row is as
|
|
1072
|
-
first measured.
|
|
1095
|
+
(`.gc('inner')`), taken as the per-record median of eight full
|
|
1096
|
+
`bun run bench:json` passes. Measured 2026-09-03 on Bun 1.4.0, Apple M2 Pro,
|
|
1097
|
+
macOS, on AC power but not on an idle machine — pass-to-pass spread runs 7-11%
|
|
1098
|
+
on the `lex` and `parse` rows and up to 10% on `roll`, against 5% for the idle
|
|
1099
|
+
session these numbers replace. Read them as two significant digits: another
|
|
1100
|
+
machine shifts every row, and a busy one inflates the heavy cases most.
|
|
1073
1101
|
|
|
1074
1102
|
p50 rather than mean, because the mean here is effectively a GC-pause
|
|
1075
1103
|
histogram and swings ±40% between processes. Every bench body is JIT-primed
|
package/dist/cli/args.d.ts
CHANGED
|
@@ -17,15 +17,25 @@ export type CliArgs = {
|
|
|
17
17
|
showVersion: boolean;
|
|
18
18
|
};
|
|
19
19
|
/**
|
|
20
|
-
* Result of parsing CLI arguments — either success or a usage error.
|
|
20
|
+
* Result of parsing CLI arguments — either success or a usage error. The
|
|
21
|
+
* failure arm carries `json` so a usage error renders in the format the
|
|
22
|
+
* caller asked for; the success arm carries `terminated`, true only when a
|
|
23
|
+
* `--` was consumed as the terminator rather than as a `--seed` value.
|
|
21
24
|
*/
|
|
22
25
|
export type ParseArgsResult = {
|
|
23
26
|
ok: true;
|
|
24
27
|
args: CliArgs;
|
|
28
|
+
terminated: boolean;
|
|
25
29
|
} | {
|
|
26
30
|
ok: false;
|
|
27
31
|
error: string;
|
|
32
|
+
json: boolean;
|
|
28
33
|
};
|
|
34
|
+
/**
|
|
35
|
+
* True for an argument this parser accepts as a *valid* option outside a `--`
|
|
36
|
+
* terminator — `--typo` is read as an option and rejected, and is not one here.
|
|
37
|
+
*/
|
|
38
|
+
export declare function isKnownOption(arg: string): boolean;
|
|
29
39
|
/**
|
|
30
40
|
* Parses a raw argument array into typed CLI options.
|
|
31
41
|
*
|
package/dist/cli/args.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF
|
|
1
|
+
{"version":3,"file":"args.d.ts","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;GAEG;AACH,MAAM,MAAM,OAAO,GAAG;IACpB,QAAQ,EAAE,MAAM,GAAG,SAAS,CAAC;IAC7B,OAAO,EAAE,OAAO,CAAC;IACjB,IAAI,EAAE,OAAO,CAAC;IACd,IAAI,EAAE,MAAM,GAAG,SAAS,CAAC;IACzB,QAAQ,EAAE,OAAO,CAAC;IAClB,WAAW,EAAE,OAAO,CAAC;CACtB,CAAC;AAEF;;;;;GAKG;AACH,MAAM,MAAM,eAAe,GACvB;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,CAAC;IAAC,UAAU,EAAE,OAAO,CAAA;CAAE,GAChD;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,CAAC;AA6DhD;;;GAGG;AACH,wBAAgB,aAAa,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO,CAElD;AAyDD;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,eAAe,CA0CzD"}
|
package/dist/cli/args.js
CHANGED
|
@@ -7,26 +7,77 @@ const BASE_ARGS = {
|
|
|
7
7
|
showHelp: false,
|
|
8
8
|
showVersion: false,
|
|
9
9
|
};
|
|
10
|
+
function informationalKind(arg) {
|
|
11
|
+
if (arg === '--help' || arg === '-h')
|
|
12
|
+
return 'help';
|
|
13
|
+
if (arg === '--version')
|
|
14
|
+
return 'version';
|
|
15
|
+
return undefined;
|
|
16
|
+
}
|
|
10
17
|
function findInformationalFlag(argv) {
|
|
11
18
|
let flag;
|
|
12
|
-
for (
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
if (
|
|
19
|
+
for (let i = 0; i < argv.length; i++) {
|
|
20
|
+
const arg = argv[i];
|
|
21
|
+
const kind = informationalKind(arg);
|
|
22
|
+
if (kind === 'help')
|
|
16
23
|
return 'help';
|
|
17
|
-
if (
|
|
24
|
+
if (kind === 'version')
|
|
18
25
|
flag = 'version';
|
|
26
|
+
else if (arg === TERMINATOR)
|
|
27
|
+
break;
|
|
28
|
+
else if (arg === '--seed' && informationalKind(argv[i + 1]) == null)
|
|
29
|
+
i += 1;
|
|
19
30
|
}
|
|
20
31
|
return flag;
|
|
21
32
|
}
|
|
22
33
|
function isNegativeNotation(arg) {
|
|
23
|
-
return
|
|
34
|
+
return /^[\ddD({@]/.test(arg.slice(1));
|
|
35
|
+
}
|
|
36
|
+
const KNOWN_OPTIONS = new Set(['--verbose', '-v', '--json', '--help', '-h', '--version', '--seed']);
|
|
37
|
+
export function isKnownOption(arg) {
|
|
38
|
+
return KNOWN_OPTIONS.has(arg) || arg.startsWith('--seed=');
|
|
39
|
+
}
|
|
40
|
+
function readSeedValue(arg, next) {
|
|
41
|
+
if (arg === '--seed') {
|
|
42
|
+
return next == null || next === '' ? null : { seed: next, consumed: 1 };
|
|
43
|
+
}
|
|
44
|
+
const value = arg.slice('--seed='.length);
|
|
45
|
+
return value === '' ? null : { seed: value, consumed: 0 };
|
|
46
|
+
}
|
|
47
|
+
function failWith(acc, message) {
|
|
48
|
+
acc.error ??= message;
|
|
49
|
+
}
|
|
50
|
+
function applyArg(arg, next, acc) {
|
|
51
|
+
if (arg === '--verbose' || arg === '-v') {
|
|
52
|
+
acc.verbose = true;
|
|
53
|
+
}
|
|
54
|
+
else if (arg === '--json') {
|
|
55
|
+
acc.json = true;
|
|
56
|
+
}
|
|
57
|
+
else if (arg === '--seed' || arg.startsWith('--seed=')) {
|
|
58
|
+
const read = readSeedValue(arg, next);
|
|
59
|
+
if (read == null) {
|
|
60
|
+
failWith(acc, 'Missing value for --seed');
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
acc.seed = read.seed;
|
|
64
|
+
return read.consumed;
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
else if (arg.startsWith('--') || (arg.startsWith('-') && !isNegativeNotation(arg))) {
|
|
68
|
+
failWith(acc, `Unknown option: ${arg}`);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
acc.positional.push(arg);
|
|
72
|
+
}
|
|
73
|
+
return 0;
|
|
24
74
|
}
|
|
25
75
|
export function parseArgs(argv) {
|
|
26
76
|
const informational = findInformationalFlag(argv);
|
|
27
77
|
if (informational != null) {
|
|
28
78
|
return {
|
|
29
79
|
ok: true,
|
|
80
|
+
terminated: false,
|
|
30
81
|
args: {
|
|
31
82
|
...BASE_ARGS,
|
|
32
83
|
showHelp: informational === 'help',
|
|
@@ -34,48 +85,22 @@ export function parseArgs(argv) {
|
|
|
34
85
|
},
|
|
35
86
|
};
|
|
36
87
|
}
|
|
37
|
-
|
|
38
|
-
let
|
|
39
|
-
let seed;
|
|
40
|
-
const positional = [];
|
|
88
|
+
const acc = { verbose: false, json: false, positional: [] };
|
|
89
|
+
let terminated = false;
|
|
41
90
|
for (let i = 0; i < argv.length; i++) {
|
|
42
91
|
const arg = argv[i];
|
|
43
92
|
if (arg === TERMINATOR) {
|
|
44
|
-
positional.push(...argv.slice(i + 1));
|
|
93
|
+
acc.positional.push(...argv.slice(i + 1));
|
|
94
|
+
terminated = true;
|
|
45
95
|
break;
|
|
46
96
|
}
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
else if (arg === '--seed') {
|
|
54
|
-
const next = argv[i + 1];
|
|
55
|
-
if (next == null || next === '') {
|
|
56
|
-
return { ok: false, error: 'Missing value for --seed' };
|
|
57
|
-
}
|
|
58
|
-
seed = next;
|
|
59
|
-
i++;
|
|
60
|
-
}
|
|
61
|
-
else if (arg.startsWith('--seed=')) {
|
|
62
|
-
const value = arg.slice('--seed='.length);
|
|
63
|
-
if (value === '') {
|
|
64
|
-
return { ok: false, error: 'Missing value for --seed' };
|
|
65
|
-
}
|
|
66
|
-
seed = value;
|
|
67
|
-
}
|
|
68
|
-
else if (arg.startsWith('--')) {
|
|
69
|
-
return { ok: false, error: `Unknown option: ${arg}` };
|
|
70
|
-
}
|
|
71
|
-
else if (arg.startsWith('-') && !isNegativeNotation(arg)) {
|
|
72
|
-
return { ok: false, error: `Unknown option: ${arg}` };
|
|
73
|
-
}
|
|
74
|
-
else {
|
|
75
|
-
positional.push(arg);
|
|
76
|
-
}
|
|
97
|
+
i += applyArg(arg, argv[i + 1], acc);
|
|
98
|
+
}
|
|
99
|
+
const { verbose, json, seed, error, positional } = acc;
|
|
100
|
+
if (error != null) {
|
|
101
|
+
return { ok: false, error, json };
|
|
77
102
|
}
|
|
78
103
|
const notation = positional.length > 0 ? positional.join(' ') : undefined;
|
|
79
|
-
return { ok: true, args: { ...BASE_ARGS, notation, verbose, json, seed } };
|
|
104
|
+
return { ok: true, terminated, args: { ...BASE_ARGS, notation, verbose, json, seed } };
|
|
80
105
|
}
|
|
81
106
|
//# sourceMappingURL=args.js.map
|
package/dist/cli/args.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"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"}
|
package/dist/cli/format.d.ts
CHANGED
|
@@ -14,6 +14,8 @@ export type FormatOptions = {
|
|
|
14
14
|
json?: boolean;
|
|
15
15
|
/** Show the detailed roll breakdown instead of the bare total. */
|
|
16
16
|
verbose?: boolean;
|
|
17
|
+
/** Seed that produced the result. Emitted in JSON mode only. */
|
|
18
|
+
seed: string;
|
|
17
19
|
};
|
|
18
20
|
/**
|
|
19
21
|
* Formats a roll result for terminal display.
|
|
@@ -24,9 +26,14 @@ export type FormatOptions = {
|
|
|
24
26
|
* tree — as a single-line `JSON.stringify` payload, where `degree` appears as
|
|
25
27
|
* its numeric `DegreeOfSuccess` value.
|
|
26
28
|
*
|
|
29
|
+
* The JSON payload appends two fields the library result does not carry: the
|
|
30
|
+
* `seed` that produced the result and the `version` that fixes the
|
|
31
|
+
* seed-to-dice mapping. Together they let the payload replay through `--seed`
|
|
32
|
+
* on the same major.
|
|
33
|
+
*
|
|
27
34
|
* @param result - The roll result to format
|
|
28
35
|
* @param options - Output mode; JSON wins when combined with verbose
|
|
29
36
|
* @returns Formatted string for terminal output
|
|
30
37
|
*/
|
|
31
|
-
export declare function formatResult(result: RollResult, options
|
|
38
|
+
export declare function formatResult(result: RollResult, options: FormatOptions): string;
|
|
32
39
|
//# sourceMappingURL=format.d.ts.map
|
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;AAIH,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;IAClB,gEAAgE;IAChE,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,UAAU,EAAE,OAAO,EAAE,aAAa,GAAG,MAAM,CAY/E"}
|
package/dist/cli/format.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { VERSION } from '../index.js';
|
|
1
2
|
import { renderBreakdown } from '../render.js';
|
|
2
3
|
const TERMINAL_MARKS = {
|
|
3
4
|
dropped: (_die, text) => `(${text})`,
|
|
@@ -5,10 +6,10 @@ const TERMINAL_MARKS = {
|
|
|
5
6
|
failure: (_die, text) => `{${text}}`,
|
|
6
7
|
droppedGroup: (inner) => `(${inner})`,
|
|
7
8
|
};
|
|
8
|
-
export function formatResult(result, options
|
|
9
|
-
const { json
|
|
9
|
+
export function formatResult(result, options) {
|
|
10
|
+
const { json, verbose, seed } = options;
|
|
10
11
|
if (json) {
|
|
11
|
-
return JSON.stringify(result);
|
|
12
|
+
return JSON.stringify({ ...result, seed, version: VERSION });
|
|
12
13
|
}
|
|
13
14
|
if (!verbose) {
|
|
14
15
|
return String(result.total);
|
package/dist/cli/format.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
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;
|
|
1
|
+
{"version":3,"file":"format.js","sourceRoot":"","sources":["../../src/cli/format.ts"],"names":[],"mappings":"AAQA,OAAO,EAAE,OAAO,EAAE,MAAM,aAAa,CAAC;AACtC,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;AAgCF,MAAM,UAAU,YAAY,CAAC,MAAkB,EAAE,OAAsB;IACrE,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,OAAO,CAAC;IAExC,IAAI,IAAI,EAAE,CAAC;QACT,OAAO,IAAI,CAAC,SAAS,CAAC,EAAE,GAAG,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC,CAAC;IAC/D,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/cli/main.d.ts
CHANGED
|
@@ -28,9 +28,9 @@ export declare function writeErrorContext(notation: string, error: unknown, writ
|
|
|
28
28
|
* `1` for a roll-parser error, `2` for a usage error. Anything that is not a
|
|
29
29
|
* `RollParserError` propagates so the runtime reports it with a stack.
|
|
30
30
|
*
|
|
31
|
-
* `--json` swaps the success payload
|
|
32
|
-
* stderr
|
|
33
|
-
* before
|
|
31
|
+
* `--json` swaps the success payload on stdout and every diagnostic on
|
|
32
|
+
* stderr, usage errors included. Exit codes are identical either way, so
|
|
33
|
+
* scripts can still branch on the code before reading a stream.
|
|
34
34
|
*/
|
|
35
35
|
export declare function main(env: CliEnv): number;
|
|
36
36
|
//# sourceMappingURL=main.d.ts.map
|
package/dist/cli/main.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;
|
|
1
|
+
{"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAyDH,iDAAiD;AACjD,MAAM,MAAM,OAAO,GAAG,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;AAE7C,qDAAqD;AACrD,MAAM,MAAM,MAAM,GAAG;IACnB,iFAAiF;IACjF,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,MAAM,EAAE,OAAO,CAAC;IAChB,MAAM,EAAE,OAAO,CAAC;CACjB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,OAAO,GAAG,IAAI,CAWxF;AAmDD;;;;;;;;GAQG;AACH,wBAAgB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAkDxC"}
|
package/dist/cli/main.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { getErrorSpan, isRollParserError } from '../errors.js';
|
|
1
|
+
import { getErrorSpan, isRollParserError, } from '../errors.js';
|
|
2
2
|
import { VERSION } from '../index.js';
|
|
3
3
|
import { roll } from '../roll.js';
|
|
4
|
-
import { parseArgs } from './args.js';
|
|
4
|
+
import { isKnownOption, parseArgs } from './args.js';
|
|
5
5
|
import { formatResult } from './format.js';
|
|
6
6
|
const HELP_TEXT = `roll-parser v${VERSION}
|
|
7
7
|
|
|
@@ -13,12 +13,23 @@ Options:
|
|
|
13
13
|
-v, --verbose Show detailed roll breakdown
|
|
14
14
|
--json Print the whole result as compact JSON (wins over --verbose)
|
|
15
15
|
--seed <value> Use seed for reproducible rolls
|
|
16
|
-
--
|
|
16
|
+
-- Notation that looks like an option — put options before it
|
|
17
17
|
|
|
18
18
|
JSON output:
|
|
19
|
-
Emits the complete result, including the structured "parts" tree
|
|
20
|
-
"
|
|
21
|
-
|
|
19
|
+
Emits the complete result, including the structured "parts" tree, plus the
|
|
20
|
+
"seed" that produced it and the "version" that fixes the seed-to-dice
|
|
21
|
+
mapping — rerun with --seed <seed> on the same major to replay the roll.
|
|
22
|
+
When --seed is omitted the CLI mints one. The "degree" field
|
|
23
|
+
(DegreeOfSuccess) serializes as a number: 0 critical failure, 1 failure,
|
|
24
|
+
2 success, 3 critical success.
|
|
25
|
+
|
|
26
|
+
JSON errors:
|
|
27
|
+
Every diagnostic is one JSON line on stderr:
|
|
28
|
+
{"error":{"message":...,"code":...,"span":{"start":N}},...}. A roll error
|
|
29
|
+
adds the "notation" and "seed" that produced it, so a failure replays like
|
|
30
|
+
a result does; "code" and "span" are absent when the failure carries
|
|
31
|
+
neither. Usage errors are covered too, but only where --json still reads as
|
|
32
|
+
the flag: it is a seed value in --seed --json, and notation after --.
|
|
22
33
|
|
|
23
34
|
Exit codes:
|
|
24
35
|
0 Success
|
|
@@ -30,7 +41,7 @@ Examples:
|
|
|
30
41
|
roll-parser 4d6kh3 --verbose
|
|
31
42
|
roll-parser 4d6dl1 --seed "character-str"
|
|
32
43
|
roll-parser "1d20+7 vs 25" --json
|
|
33
|
-
roll-parser
|
|
44
|
+
roll-parser -1d6+3
|
|
34
45
|
`;
|
|
35
46
|
export function writeErrorContext(notation, error, write) {
|
|
36
47
|
const span = getErrorSpan(error);
|
|
@@ -42,15 +53,31 @@ export function writeErrorContext(notation, error, write) {
|
|
|
42
53
|
write(` ${notation}\n`);
|
|
43
54
|
write(` ${' '.repeat(column)}^\n`);
|
|
44
55
|
}
|
|
56
|
+
function writeOptionHint(terminated, notation, write) {
|
|
57
|
+
if (terminated && notation.split(' ').some(isKnownOption)) {
|
|
58
|
+
write('Hint: options must come before "--"\n');
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
function writeJsonError(write, error, context) {
|
|
62
|
+
write(`${JSON.stringify({ error, ...context, version: VERSION })}\n`);
|
|
63
|
+
}
|
|
64
|
+
function failUsage(stderr, message, json) {
|
|
65
|
+
if (json) {
|
|
66
|
+
writeJsonError(stderr, { message });
|
|
67
|
+
}
|
|
68
|
+
else {
|
|
69
|
+
stderr(`Error: ${message}\n`);
|
|
70
|
+
stderr('Run "roll-parser --help" for usage.\n');
|
|
71
|
+
}
|
|
72
|
+
return 2;
|
|
73
|
+
}
|
|
45
74
|
export function main(env) {
|
|
46
75
|
const { argv, stdout, stderr } = env;
|
|
47
76
|
const parsed = parseArgs(argv);
|
|
48
77
|
if (!parsed.ok) {
|
|
49
|
-
stderr
|
|
50
|
-
stderr('Run "roll-parser --help" for usage.\n');
|
|
51
|
-
return 2;
|
|
78
|
+
return failUsage(stderr, parsed.error, parsed.json);
|
|
52
79
|
}
|
|
53
|
-
const { args } = parsed;
|
|
80
|
+
const { args, terminated } = parsed;
|
|
54
81
|
if (args.showHelp) {
|
|
55
82
|
stdout(HELP_TEXT);
|
|
56
83
|
return 0;
|
|
@@ -60,20 +87,24 @@ export function main(env) {
|
|
|
60
87
|
return 0;
|
|
61
88
|
}
|
|
62
89
|
if (args.notation == null) {
|
|
63
|
-
stderr
|
|
64
|
-
stderr('Run "roll-parser --help" for usage.\n');
|
|
65
|
-
return 2;
|
|
90
|
+
return failUsage(stderr, 'No dice notation provided.', args.json);
|
|
66
91
|
}
|
|
92
|
+
const seed = args.seed ?? crypto.randomUUID();
|
|
67
93
|
try {
|
|
68
|
-
const
|
|
69
|
-
const
|
|
70
|
-
const output = formatResult(result, { json: args.json, verbose: args.verbose });
|
|
94
|
+
const result = roll(args.notation, { seed });
|
|
95
|
+
const output = formatResult(result, { json: args.json, verbose: args.verbose, seed });
|
|
71
96
|
stdout(`${output}\n`);
|
|
72
97
|
}
|
|
73
98
|
catch (error) {
|
|
74
99
|
if (isRollParserError(error)) {
|
|
75
|
-
|
|
76
|
-
|
|
100
|
+
if (args.json) {
|
|
101
|
+
writeJsonError(stderr, { message: error.message, code: error.code, span: getErrorSpan(error) }, { notation: args.notation, seed });
|
|
102
|
+
}
|
|
103
|
+
else {
|
|
104
|
+
stderr(`Error: ${error.message}\n`);
|
|
105
|
+
writeErrorContext(args.notation, error, stderr);
|
|
106
|
+
writeOptionHint(terminated, args.notation, stderr);
|
|
107
|
+
}
|
|
77
108
|
return 1;
|
|
78
109
|
}
|
|
79
110
|
throw error;
|
package/dist/cli/main.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AAUA,OAAO,
|
|
1
|
+
{"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"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"}
|