@echecs/pgn 3.4.0 → 3.5.2

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,3 +7,143 @@ and this project adheres to
7
7
  [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
8
8
 
9
9
  ## [Unreleased]
10
+
11
+ ## [3.5.2] - 2026-03-14
12
+
13
+ ### Fixed
14
+
15
+ - Benchmark: `benko.pgn` moved to multi-game group and compared with the correct
16
+ `parseGames` API — previously called `parseGame` (single-game), causing all
17
+ comparison parsers to error
18
+ - Benchmark: `comments.pgn` BOM (`U+FEFF`) now stripped before parsing, enabling
19
+ comparison against `@mliebelt/pgn-parser` and `pgn-parser`
20
+ - Benchmark: fixture exclusion reasons documented in `BENCHMARK_RESULTS.md` and
21
+ in bench source comments
22
+
23
+ ## [3.5.1] - 2026-03-14
24
+
25
+ ### Changed
26
+
27
+ - README: document `stream()` API with signature and Node.js usage example
28
+ - README: update type names (`Moves` → `MoveList`) and clarify `Move.from`
29
+ disambiguation and move tuple slot semantics
30
+ - Updated benchmark results for v3.5.0 SAN rule restructure
31
+
32
+ ## [3.5.0] - 2026-03-14
33
+
34
+ ### Added
35
+
36
+ - `stream(input: AsyncIterable<string>): AsyncGenerator<PGN>` — new named export
37
+ for incremental, memory-efficient parsing of large PGN databases
38
+
39
+ ### Changed
40
+
41
+ - `Move.from` widened from `File | Rank` to `Disambiguation`
42
+ (`Square | File | Rank`) to correctly type fully-disambiguated moves (e.g.
43
+ `Qd1xe4` → `from: "d1"`)
44
+ - `type Moves` renamed to `MoveList`; new
45
+ `MovePair = [number, Move | undefined, Move?]` tuple
46
+ - `type Variation` simplified to `MoveList[]`
47
+
48
+ ### Performance
49
+
50
+ - Restructured `SAN` grammar rule to eliminate post-match regex on every move;
51
+ closes remaining ~1.1–1.2x gap vs `pgn-parser` on move-heavy fixtures
52
+
53
+ ## [3.4.0] - 2026-02-21
54
+
55
+ ### Changed
56
+
57
+ - Rewrote README following `@echecs/elo` library style with badges, Why, Quick
58
+ Start, Usage, and Contributing sections
59
+ - Updated AGENTS.md to reflect Peggy migration and remove stale nearley/moo
60
+ references
61
+
62
+ ### Added
63
+
64
+ - Features section in README highlighting RAV and NAG support with a parser
65
+ comparison table
66
+ - Performance section in README with benchmark results table
67
+ - Codecov badge to README
68
+
69
+ ### Removed
70
+
71
+ - `docs.yml` workflow (no hosted docs in this project)
72
+
73
+ ## [3.3.0] - 2026-02-21
74
+
75
+ ### Added
76
+
77
+ - Peggy PEG parser replacing nearley/moo for O(n) linear-time parsing —
78
+ delivering up to 10× throughput improvement on large PGN files
79
+ - Comparative benchmark (`comparison.bench.ts`) measuring `@echecs/pgn` against
80
+ `@mliebelt/pgn-parser` and `chess.js`
81
+
82
+ ### Performance
83
+
84
+ - Replaced `pickBy` with direct property assignment in SAN action block,
85
+ reducing allocations per move
86
+ - Added length-check guards before NAG and comment processing in MOVE action,
87
+ skipping unnecessary work for moves without annotations
88
+ - Removed `delete` mutations and reduced allocations in `pairMoves`, avoiding V8
89
+ hidden-class transitions
90
+
91
+ ### Removed
92
+
93
+ - Stale `tokenizer.ts` debug script
94
+
95
+ ## [3.2.1] - 2026-02-20
96
+
97
+ ### Fixed
98
+
99
+ - Sort `multiGameFixtures` keys in comparison benchmark to satisfy the
100
+ `sort-keys` lint rule
101
+
102
+ ## [3.2.0] - 2026-02-20
103
+
104
+ ### Added
105
+
106
+ - Comparative PGN parser benchmark (`comparison.bench.ts`) for cross-library
107
+ performance tracking
108
+
109
+ ### Performance
110
+
111
+ - Reduced Earley parser overhead via grammar and caching optimizations
112
+
113
+ ## [3.1.3] - 2025-03-27
114
+
115
+ ### Fixed
116
+
117
+ - Removed accidental production dependency introduced in 3.1.2
118
+
119
+ ## [3.1.2] - 2025-03-01
120
+
121
+ ### Fixed
122
+
123
+ - Increased per-test timeout to accommodate `long.pgn` (~3 500 games) on slow CI
124
+ runners
125
+
126
+ ## [3.1.1] - 2025-03-01
127
+
128
+ ### Fixed
129
+
130
+ - Corrected `.js` extension on relative imports in test files (NodeNext
131
+ resolution)
132
+
133
+ ## [3.1.0] - 2025-03-01
134
+
135
+ ### Added
136
+
137
+ - moo tokenizer for faster lexing
138
+ - New grammar supporting the full PGN specification including RAV (recursive
139
+ annotated variations) and NAG (numeric annotation glyphs)
140
+
141
+ [unreleased]: https://github.com/mormubis/pgn/compare/v3.4.0...HEAD
142
+ [3.4.0]: https://github.com/mormubis/pgn/compare/v3.3.0...v3.4.0
143
+ [3.3.0]: https://github.com/mormubis/pgn/compare/v3.2.1...v3.3.0
144
+ [3.2.1]: https://github.com/mormubis/pgn/compare/v3.2.0...v3.2.1
145
+ [3.2.0]: https://github.com/mormubis/pgn/compare/v3.1.3...v3.2.0
146
+ [3.1.3]: https://github.com/mormubis/pgn/compare/v3.1.2...v3.1.3
147
+ [3.1.2]: https://github.com/mormubis/pgn/compare/v3.1.1...v3.1.2
148
+ [3.1.1]: https://github.com/mormubis/pgn/compare/v3.1.0...v3.1.1
149
+ [3.1.0]: https://github.com/mormubis/pgn/releases/tag/v3.1.0
package/README.md CHANGED
@@ -31,8 +31,9 @@ opening book, or game viewer, you need more:
31
31
  - **NAG support** — symbolic (`!`, `?`, `!!`, `??`, `!?`, `?!`) and numeric
32
32
  (`$1`–`$255`) annotations are surfaced as an `annotations` array. Essential
33
33
  for Lichess and ChessBase exports.
34
- - **Multi-game files** — parse entire PGN databases in one call. Tested on files
35
- with 3 500+ games.
34
+ - **Multi-game files** — parse entire PGN databases in one call, or stream them
35
+ game-by-game with `stream()` for memory-efficient processing of large files.
36
+ Tested on files with 3 500+ games.
36
37
  - **Fast** — built on a [Peggy](https://peggyjs.org/) PEG parser. Throughput is
37
38
  within 1.1–1.2x of the fastest parsers on npm, which do far less work per move
38
39
  (see [BENCHMARK_RESULTS.md](./BENCHMARK_RESULTS.md)).
@@ -67,19 +68,41 @@ console.log(games[0].moves[0]);
67
68
 
68
69
  ## Usage
69
70
 
70
- `parse()` takes a PGN string and returns an array of game objects — one per game
71
- in the file.
71
+ ### `parse()`
72
+
73
+ Takes a PGN string and returns an array of game objects — one per game in the
74
+ file.
72
75
 
73
76
  ```typescript
74
77
  parse(input: string): PGN[]
75
78
  ```
76
79
 
80
+ ### `stream()`
81
+
82
+ Takes any `AsyncIterable<string>` and yields one `PGN` object per game. Memory
83
+ usage stays proportional to one game at a time, making it suitable for large
84
+ databases read from disk or a network stream.
85
+
86
+ ```typescript
87
+ stream(input: AsyncIterable<string>): AsyncGenerator<PGN>
88
+ ```
89
+
90
+ ```typescript
91
+ import { createReadStream } from 'node:fs';
92
+ import { stream } from '@echecs/pgn';
93
+
94
+ const chunks = createReadStream('database.pgn', { encoding: 'utf8' });
95
+ for await (const game of stream(chunks)) {
96
+ console.log(game.meta.White, 'vs', game.meta.Black);
97
+ }
98
+ ```
99
+
77
100
  ### PGN object
78
101
 
79
102
  ```typescript
80
103
  {
81
- meta: Meta, // tag pairs (Event, Site, Date, White, Black, …)
82
- moves: Moves, // paired move list
104
+ meta: Meta, // tag pairs (Event, Site, Date, White, Black, …)
105
+ moves: MoveList, // paired move list
83
106
  result: 1 | 0 | 0.5 | '?'
84
107
  }
85
108
  ```
@@ -90,7 +113,7 @@ parse(input: string): PGN[]
90
113
  {
91
114
  piece: 'P' | 'R' | 'N' | 'B' | 'Q' | 'K', // always present
92
115
  to: string, // destination square, e.g. "e4"
93
- from?: string, // disambiguation, e.g. "e" or "e2"
116
+ from?: string, // disambiguation: file "e", rank "2", or square "e2"
94
117
  capture?: true,
95
118
  castling?: true,
96
119
  check?: true,
@@ -98,12 +121,13 @@ parse(input: string): PGN[]
98
121
  promotion?: 'R' | 'N' | 'B' | 'Q',
99
122
  annotations?: string[], // e.g. ["!", "$14"]
100
123
  comment?: string,
101
- variants?: Moves[], // recursive annotation variations
124
+ variants?: MoveList[], // recursive annotation variations
102
125
  }
103
126
  ```
104
127
 
105
- Moves are grouped into tuples: `[moveNumber, whiteMove, blackMove]`. If the last
106
- move of a game or variation was made by white, `blackMove` is `undefined`.
128
+ Moves are grouped into tuples: `[moveNumber, whiteMove, blackMove]`. Both move
129
+ slots can be `undefined` `whiteMove` when a variation begins on black's turn,
130
+ `blackMove` when the game or variation ends on white's move.
107
131
 
108
132
  ### Annotations and comments
109
133