roll-parser 3.3.1 → 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 CHANGED
@@ -7,6 +7,35 @@ 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
+
10
39
  ## [3.3.1] - 2026-08-20
11
40
 
12
41
  ### Changed
@@ -305,7 +334,8 @@ Dice mechanics (Stage 2):
305
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))
306
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))
307
336
 
308
- [Unreleased]: https://github.com/edloidas/roll-parser/compare/v3.3.1...HEAD
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
309
339
  [3.3.1]: https://github.com/edloidas/roll-parser/releases/tag/v3.3.1
310
340
  [3.3.0]: https://github.com/edloidas/roll-parser/releases/tag/v3.3.0
311
341
  [3.2.2]: https://github.com/edloidas/roll-parser/releases/tag/v3.2.2
package/README.md CHANGED
@@ -315,8 +315,9 @@ pool.total; // 5 — successes minus failures
315
315
  | `successes` / `failures` | `number?` | Present only when success counting was used |
316
316
  | `degree` / `natural` | `DegreeOfSuccess?` / `number?` | Present only for a top-level `vs` |
317
317
 
318
- `Readonly` at the top level and fully JSON-serializable this is exactly what
319
- the CLI's `--json` flag prints.
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).
320
321
 
321
322
  ```typescript
322
323
  JSON.stringify(roll('3d6', { rng: createMockRng([4, 2, 6]) }));
@@ -1003,7 +1004,7 @@ Options:
1003
1004
  -v, --verbose Show detailed roll breakdown
1004
1005
  --json Print the whole result as compact JSON (wins over --verbose)
1005
1006
  --seed <value> Use seed for reproducible rolls
1006
- -- Treat every following argument as notation
1007
+ -- Notation that looks like an option — put options before it
1007
1008
  ```
1008
1009
 
1009
1010
  ```bash
@@ -1021,15 +1022,48 @@ Error: Invalid dice sides: 0
1021
1022
  2d6+1d0+3
1022
1023
  ^
1023
1024
 
1024
- $ roll-parser --seed demo -- -1d6+3
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
1025
1029
  2
1026
1030
  ```
1027
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
+
1028
1038
  Verbose mode rewrites the markdown markers for plain terminals: `~~n~~` becomes
1029
1039
  `(n)`, `**n**` becomes `[n]`, `__n__` becomes `{n}`. `--seed` takes both
1030
1040
  `--seed value` and `--seed=value`, and accepts any non-empty value including a
1031
- dash-prefixed one. `--help` and `--version` win over any usage error that
1032
- precedes them. Errors go to stderr; only the result goes to stdout.
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.
1033
1067
 
1034
1068
  | Exit code | Meaning |
1035
1069
  |----------:|---------|
@@ -1041,36 +1075,29 @@ precedes them. Errors go to stderr; only the result goes to stdout.
1041
1075
 
1042
1076
  | Notation | `lex` | `parse` | `roll` (end to end) |
1043
1077
  |----------|------:|--------:|--------------------:|
1044
- | `1d20` | 85 ns | 164 ns | **0.49 µs** (~2.0M rolls/s) |
1045
- | `2d6+3` | 98 ns | 215 ns | **0.77 µs** |
1046
- | `4d6kh3` | 122 ns | 245 ns | **1.2 µs** |
1047
- | `4d6sd` | 101 ns | 211 ns | **0.84 µs** |
1048
- | `10d10>=6f1` | 161 ns | 336 ns | **1.6 µs** |
1049
- | `100d6` | 82 ns | 168 ns | **2.6 µs** |
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** |
1050
1084
 
1051
1085
  The `roll` column pays for a fresh `SeededRNG` per call, which an injected RNG
1052
1086
  avoids. Every roll also builds the `parts` tree; there is no opt-out and these
1053
- numbers include it. A 1000-die pool costs roughly 47x a `1d20` (~23 µs here),
1054
- while lexing and parsing stay flat at ~84 / ~168 ns.
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.
1055
1089
 
1056
1090
  <details>
1057
1091
  <summary>Measurement protocol</summary>
1058
1092
 
1059
1093
  Values are **p50**, from
1060
1094
  [mitata](https://github.com/evanwashere/mitata) with forced per-iteration GC
1061
- (`.gc('inner')`), taken as the per-record median of four full
1062
- `bun run bench:json` passes, every row agreeing within 5% except
1063
- `lex / 4d6kh3` at 7%. Measured 2026-08-05 on Bun
1064
- 1.3.14, Apple M2 Pro, macOS, idle and on AC power. Read them as
1065
- two significant digits: another machine shifts every row, and a busy one
1066
- inflates the heavy cases most.
1067
-
1068
- The `4d6sd` row and the `10d10>=6f1` `roll` figure come from a later five-pass
1069
- measurement on the same machine, after the per-die `'dc'` tag checks were
1070
- hoisted out of the keep/drop, success-count, and sort loops. That session was
1071
- not idle and those two read bimodally with roughly 10% between the modes, so
1072
- take them as a recovered range rather than point values. Every other row is as
1073
- 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.
1074
1101
 
1075
1102
  p50 rather than mean, because the mean here is effectively a GC-pause
1076
1103
  histogram and swings ±40% between processes. Every bench body is JIT-primed
@@ -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
  *
@@ -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;;GAEG;AACH,MAAM,MAAM,eAAe,GAAG;IAAE,EAAE,EAAE,IAAI,CAAC;IAAC,IAAI,EAAE,OAAO,CAAA;CAAE,GAAG;IAAE,EAAE,EAAE,KAAK,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AA0CzF;;;;;GAKG;AACH,wBAAgB,SAAS,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,eAAe,CAyDzD"}
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 (const arg of argv) {
13
- if (arg === TERMINATOR)
14
- break;
15
- if (arg === '--help' || arg === '-h')
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 (arg === '--version')
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 arg.length > 1 && /^[\ddD({@]/.test(arg.slice(1));
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
- let verbose = false;
38
- let json = false;
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
- else if (arg === '--verbose' || arg === '-v') {
48
- verbose = true;
49
- }
50
- else if (arg === '--json') {
51
- json = true;
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
@@ -1 +1 @@
1
- {"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"args.js","sourceRoot":"","sources":["../../src/cli/args.ts"],"names":[],"mappings":"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"}
@@ -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?: FormatOptions): string;
38
+ export declare function formatResult(result: RollResult, options: FormatOptions): string;
32
39
  //# sourceMappingURL=format.d.ts.map
@@ -1 +1 @@
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"}
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"}
@@ -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 = false, verbose = false } = options;
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);
@@ -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;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"}
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"}
@@ -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 only diagnostics stay plain text on
32
- * stderr and the exit codes are identical, so scripts can branch on the code
33
- * before parsing stdout.
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
@@ -1 +1 @@
1
- {"version":3,"file":"main.d.ts","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAsCH,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;AAED;;;;;;;;GAQG;AACH,wBAAgB,IAAI,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CA2CxC"}
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
- -- Treat every following argument as notation
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. The
20
- "degree" field (DegreeOfSuccess) serializes as a number: 0 critical failure,
21
- 1 failure, 2 success, 3 critical success. Errors stay plain text on stderr.
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 -- -1d6+3
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(`Error: ${parsed.error}\n`);
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('Error: No dice notation provided.\n');
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 options = args.seed != null ? { seed: args.seed } : {};
69
- const result = roll(args.notation, options);
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
- stderr(`Error: ${error.message}\n`);
76
- writeErrorContext(args.notation, error, stderr);
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;
@@ -1 +1 @@
1
- {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"main.js","sourceRoot":"","sources":["../../src/cli/main.ts"],"names":[],"mappings":"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"}
@@ -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;AAElG,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;AAWhC,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;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB;;;;;;;;;;;OAWG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;;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;AAmoDF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,GAAE,eAAoB,GAAG,UAAU,CAgE1F"}
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;AAElG,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;AAWhC,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;;;;;;;GAOG;AACH,MAAM,MAAM,WAAW,GAAG;IACxB,KAAK,EAAE,SAAS,EAAE,CAAC;IACnB;;;;;;;;;;;OAWG;IACH,cAAc,CAAC,EAAE,OAAO,CAAC;IACzB;;;;;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;AAkpDF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AACH,wBAAgB,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,GAAG,EAAE,GAAG,EAAE,OAAO,GAAE,eAAoB,GAAG,UAAU,CAgE1F"}