Parse, resolve, and stringify SAN (Standard Algebraic Notation) chess moves. Strict TypeScript.
npm install @echecs/san
import { parse, resolve, stringify } from '@echecs/san';
// Parse SAN syntax only — returns a SanMove (no position needed)
const sanMove = parse('Nf3');
// Parse and resolve in one call — returns a Move with from/to squares
const move = parse('Nf3', position);
// Resolve a SanMove to a Move (find the from square)
const move = resolve(sanMove, position);
// Convert a Move back to a SAN string
const san = stringify(move, position);Parses a SAN string and returns a SanMove describing the move's syntax. Does
not require a position. Throws RangeError for empty or invalid input.
Parses and resolves a SAN string against a position in one call. Equivalent to
calling parse then resolve. Throws RangeError for empty, invalid, or
illegal input.
Finds the source square for a SanMove in the given position. Throws
RangeError if no legal move matches or if the move is ambiguous.
Returns the SAN string for a Move in the given position, including
disambiguation, capture marker, check, and checkmate symbols. Throws
RangeError if no piece occupies the source square.
interface SanMove {
capture: boolean;
castle: 'kingside' | 'queenside' | undefined;
check: 'check' | 'checkmate' | undefined;
file: File | undefined;
piece: PieceType;
promotion: PromotionPieceType | undefined;
rank: Rank | undefined;
to: Square | undefined;
}file and rank are the disambiguation hints from the SAN string, not the
destination square. to is undefined for castling moves.
Move and Position are re-exported from @echecs/position:
import type { Move, Position } from '@echecs/san';All functions throw RangeError for domain violations:
| Situation | Message |
|---|---|
| Empty SAN string | Empty SAN string |
| Invalid SAN syntax | Invalid SAN: "<input>" |
| No legal move found | Describes the move |
| Ambiguous move | Lists the number of candidates |
| No piece on source square | Describes the square |
MIT