{"id":51319367,"url":"https://github.com/echecsjs/tunx","last_synced_at":"2026-07-01T11:03:56.349Z","repository":{"id":347860811,"uuid":"1195520785","full_name":"echecsjs/tunx","owner":"echecsjs","description":"Parse and stringify SwissManager TUNX binary tournament files. Zero dependencies, strict TypeScript, full round-trip fidelity.","archived":false,"fork":false,"pushed_at":"2026-06-29T14:01:04.000Z","size":907,"stargazers_count":1,"open_issues_count":5,"forks_count":0,"subscribers_count":1,"default_branch":"main","last_synced_at":"2026-06-29T15:24:57.038Z","etag":null,"topics":["chess","parser","swissmanager","typescript"],"latest_commit_sha":null,"homepage":"https://github.com/echecsjs/tunx#readme","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/echecsjs.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-03-29T19:05:45.000Z","updated_at":"2026-06-29T14:02:11.000Z","dependencies_parsed_at":null,"dependency_job_id":"b4bd2ea4-040f-4b2d-9e05-67fdb7959cf0","html_url":"https://github.com/echecsjs/tunx","commit_stats":null,"previous_names":["mormubis/tunx","echecsjs/tunx"],"tags_count":4,"template":false,"template_full_name":null,"purl":"pkg:github/echecsjs/tunx","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/echecsjs%2Ftunx","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/echecsjs%2Ftunx/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/echecsjs%2Ftunx/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/echecsjs%2Ftunx/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/echecsjs","download_url":"https://codeload.github.com/echecsjs/tunx/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/echecsjs%2Ftunx/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35003466,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-01T02:00:05.325Z","response_time":130,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["chess","parser","swissmanager","typescript"],"created_at":"2026-07-01T11:03:55.606Z","updated_at":"2026-07-01T11:03:56.342Z","avatar_url":"https://github.com/echecsjs.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# @echecs/tunx\n\nParse [Swiss-Manager](https://swiss-manager.at/) `.TUNX` binary tournament\nfiles. Zero dependencies, strict TypeScript. Output types align with\n[`@echecs/trf`](https://www.npmjs.com/package/@echecs/trf).\n\n## Installation\n\n```bash\nnpm install @echecs/tunx\n```\n\n## Quick Start\n\n```typescript\nimport { parse } from '@echecs/tunx';\nimport { readFileSync } from 'node:fs';\n\n// Parse a TUNX file\nconst buffer = new Uint8Array(readFileSync('tournament.TUNX'));\nconst tournament = parse(buffer);\n\nif (tournament) {\n  console.log(tournament.name); // \"IV Elllobregat Open Chess Tmnt Grupo A\"\n  console.log(tournament.rounds); // 9\n  console.log(tournament.players.length); // 210\n\n  // Player data\n  const player = tournament.players[0];\n  console.log(player.name); // \"Fedoseev, Vladimir\"\n  console.log(player.rating); // 2675\n  console.log(player.fideId); // \"24130737\"\n  console.log(player.title); // \"GM\"\n  console.log(player.points); // 6.5\n  console.log(player.rank); // 1\n}\n```\n\n## API\n\n### `parse(input, options?)`\n\nDecode a TUNX binary buffer into a `Tournament` object.\n\n```typescript\nfunction parse(\n  input: Uint8Array,\n  options?: ParseOptions,\n): Tournament | undefined;\n```\n\n- Returns `undefined` for unrecoverable failures (bad magic, missing markers).\n- Calls `options.onError` before returning `undefined`.\n- Calls `options.onWarning` for recoverable issues — parsing continues.\n- Never throws.\n\n### `ParseOptions`\n\n```typescript\ninterface ParseOptions {\n  onError?: (error: ParseError) =\u003e void;\n  onWarning?: (warning: ParseWarning) =\u003e void;\n}\n```\n\n### `ParseError`\n\n```typescript\ninterface ParseError {\n  message: string;\n  offset?: number;\n}\n```\n\n### `ParseWarning`\n\n```typescript\ninterface ParseWarning {\n  message: string;\n  offset?: number;\n}\n```\n\n## Types\n\nOutput types are compatible with\n[`@echecs/trf`](https://www.npmjs.com/package/@echecs/trf).\n\n### `Tournament`\n\n```typescript\ninterface Tournament {\n  // TRF-compatible fields\n  chiefArbiter?: string;\n  city?: string;\n  deputyArbiters?: string[];\n  endDate?: string;\n  federation?: string;\n  name?: string;\n  numberOfPlayers?: number;\n  players: Player[];\n  roundDates?: string[];\n  rounds: number;\n  startDate?: string;\n  tiebreaks?: string[];\n  timeControl?: string;\n  tournamentType?: string;\n\n  // TUNX-specific extensions\n  currentRound?: number;\n  header?: Header;\n  pairings?: Pairing[][];\n  roundTimes?: string[];\n  subtitle?: string;\n  venue?: string;\n}\n```\n\n### `Player`\n\n```typescript\ninterface Player {\n  birthDate?: string;\n  federation?: string;\n  fideId?: string;\n  name: string;\n  nationalRatings?: NationalRating[];\n  pairingNumber: number;\n  points: number;\n  rank: number;\n  rating?: number;\n  results: RoundResult[];\n  sex?: Sex;\n  title?: Title;\n}\n```\n\n### `RoundResult`\n\n```typescript\ninterface RoundResult {\n  color: '-' | 'b' | 'w';\n  opponentId: number | null;\n  result: ResultCode;\n  round: number;\n}\n```\n\n### `Pairing`\n\nPer-board pairing record, grouped by round in `Tournament.pairings`.\n\n```typescript\ninterface Pairing {\n  black: number;\n  board: number;\n  result?: ResultCode;\n  white: number;\n}\n```\n\n### `Header`\n\nTUNX-specific header metadata. Available on `Tournament.header`.\n\n```typescript\ninterface Header {\n  installSignature: Uint8Array;\n  installedAt?: Date;\n  licenseHash: Uint8Array;\n  savedAt?: Date;\n  tournamentId: number;\n}\n```\n\n### `NationalRating`\n\n```typescript\ninterface NationalRating {\n  birthDate?: string;\n  classification?: string;\n  federation: string;\n  name?: string;\n  nationalId?: string;\n  origin?: string;\n  pairingNumber: number;\n  rating: number;\n  sex?: Sex;\n}\n```\n\n### `ResultCode`\n\n```typescript\ntype ResultCode =\n  | '+'\n  | '-'\n  | '0'\n  | '1'\n  | '='\n  | 'D'\n  | 'F'\n  | 'H'\n  | 'L'\n  | 'U'\n  | 'W'\n  | 'Z';\n```\n\n| Code | Meaning                   |\n| ---- | ------------------------- |\n| `1`  | Win                       |\n| `0`  | Loss                      |\n| `=`  | Draw                      |\n| `+`  | Forfeit win               |\n| `-`  | Forfeit loss              |\n| `D`  | Draw by forfeit           |\n| `F`  | Full-point bye            |\n| `H`  | Half-point bye            |\n| `L`  | Loss by forfeit (special) |\n| `W`  | Win by forfeit (special)  |\n| `Z`  | Zero-point bye / unpaired |\n| `U`  | Unplayed                  |\n\n### `Sex`\n\n```typescript\ntype Sex = 'm' | 'w';\n```\n\n### `Tiebreak`\n\nKnown tiebreak identifiers used as values in `Tournament.tiebreaks`.\n\n```typescript\ntype Tiebreak =\n  | 'average-rating'\n  | 'buchholz'\n  | 'buchholz-cut-1'\n  | 'buchholz-cut-2'\n  | 'buchholz-cut-3'\n  | 'direct-encounter'\n  | 'koya'\n  | 'median-buchholz'\n  | 'number-of-wins'\n  | 'performance-rating'\n  | 'progressive'\n  | 'sonneborn-berger';\n```\n\n### `Title`\n\n```typescript\ntype Title = 'CM' | 'FM' | 'GM' | 'IM' | 'WCM' | 'WFM' | 'WGM' | 'WIM';\n```\n\n## TUNX Format\n\nTUNX is the proprietary binary format used by\n[Swiss-Manager](https://swiss-manager.at/). The format uses little-endian\nintegers and UTF-16LE strings with U16LE length prefixes.\n\n### File Structure\n\n1. **Header** (108 bytes) — magic `93 FF 89 44`, tournament ID, license data\n2. **Metadata strings** — name, subtitle, arbiters, city, time control\n3. **Config section** (`95 FF 89 44`) — rounds, players, dates, tiebreaks\n4. **A3 sub-section** (`A3 FF 89 44`) — per-round schedule (dates, times)\n5. **Player records** (`A5 FF 89 44`) — 30 strings + 110-byte numeric block\n6. **Pairings** (`B3 FF 89 44`) — 21-byte records per pairing\n7. **D3 section** (`D3 FF 89 44`) — section offset table\n8. **E3 section** (`E3 FF 89 44`) — file terminator\n\n## License\n\nMIT\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fechecsjs%2Ftunx","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fechecsjs%2Ftunx","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fechecsjs%2Ftunx/lists"}