npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

eafc-db-kit

v1.1.0

Published

Read and write FIFA databases with XML descriptors and EA FC squads and career saves using TypeScript.

Readme

eafc-db-kit

A TypeScript API for reading and writing FIFA databases with external XML descriptors, plus EA FC squads, career saves, and databases through JSON-compatible objects. EA FC descriptors and database templates are bundled in templates/. Reading opens inputs in read-only mode; writing creates a new output unless replacement is explicitly requested. The library does not start a server.

Installation

Requires Node.js 24 or later. This package uses ES modules.

npm install eafc-db-kit

The npm package includes compiled JavaScript, TypeScript declarations, and the bundled templates/. Consumers do not need TypeScript or a build step. Template paths are resolved relative to the installed module, independently of the working directory.

Development

From a source checkout:

npm ci
npm run build
npm run check

The build produces ESM JavaScript and TypeScript declarations in dist/. dist/ is not committed: the prepare script builds it for local installs, Git installs, and packaging. Generated code has no comments. See the publishing guide for package verification and npm release commands.

Choose a service

| Workflow | Service | Required mapping | | --- | --- | --- | | Standalone FIFA .db | FifaDatabaseService | Matching XML supplied on every read/write | | EA FC squad, career, or bundled database | EaFcFileService | Bundled version/database, or existing generic metadata options |

import { FifaDatabaseService, EaFcFileService } from 'eafc-db-kit';

const fifa = new FifaDatabaseService();
const database = await fifa.read('C:/FIFA/custom.db', 'C:/FIFA/custom.xml', { tables: ['teams'] });

const eaFc = new EaFcFileService();
const squad = await eaFc.read('C:/saves/file.squad', { version: 'FC 27', tables: ['players'] });

The FIFA service validates XML coverage for every binary table and field, edits records, and writes a standalone .db using the original layout. It requires a supported DB signature at offset zero and does not infer the game edition. See the FIFA database guide for reading, editing, writing, JSON, supported layouts, and errors, and services for the source organization and API comparison. Existing top-level EA FC functions remain available.

Read and write a FIFA database

import { FifaDatabaseService } from 'eafc-db-kit';

const fifa = new FifaDatabaseService();
const source = 'C:/FIFA/fifa_ng_db.db';
const xml = 'C:/FIFA/fifa_ng_db-meta.xml';
const data = await fifa.read(source, xml, { tables: ['teams'] });
const teams = data.databases[0].tables.find(table => table.name === 'teams');
const club = teams?.records.find(record => record.teamid === 1);
if (!club) throw new Error('Team 1 was not found.');
club.teamname = 'Arsenal Test';

await fifa.write(source, xml, 'C:/FIFA/fifa_ng_db-edited.db', data);

The XML is opened read-only and stays unchanged. Existing outputs require overwrite: true; replacing the XML descriptor is rejected. The runnable example writes a new database and reopens it to verify the edited team name.

Read a squad or career save

import { readEaFile, SUPPORTED_VERSIONS } from 'eafc-db-kit';

console.log(SUPPORTED_VERSIONS);

const data = await readEaFile('C:/saves/file.squad', {
  version: 'FC 26',
});

const players = data.databases[0]?.tables.find(table => table.name === 'players');
console.log(players?.records[0]);

Career saves use the same API:

const career = await readEaFile('C:/saves/CmMgrC20261009', {
  version: 'FC 26',
  tables: ['players', 'teams', 'teamplayerlinks'],
});

The save paths above are examples. Extensionless files are supported. version selects the XML descriptor explicitly; the library does not infer the game version from the filename. source.kind only classifies the filename and can be overridden with kind: 'career' or kind: 'squad'.

FC 25, FC 26, and FC 27 autocomplete and record types

All three versions include generated types for every table and field across their main, FUT, and localization XMLs. Passing a literal version infers the matching version and database schema:

| Version | Tables | Fields | Type guide and complete catalogs | | --- | ---: | ---: | --- | | FC 25 | 454 | 17,760 | FC 25 types | | FC 26 | 525 | 19,818 | FC 26 types | | FC 27 | 499 | 17,474 | FC 27 types |

import { readEaFile, type Fc27Record } from 'eafc-db-kit';

type Player = Fc27Record<'players'>;
const save = await readEaFile('C:/saves/file.squad', { version: 'FC 27', tables: ['players'] });
const players = save.databases.flatMap(database => database.tables)
  .find(table => table.name === 'players');
const player = players?.records.find(record => record.playerid === 158023);
console.log(player?.overallrating, player?.role9);

The guides cover database-specific types, metadata discovery, value representations, and generation commands. Raw reads and custom descriptors retain the generic API.

FC 27 includes validated main, FUT, and English localization templates, generated types, and read/write checks against a real squad. Career containers are tested with FC 27 databases and synthetic career sections; a real FC 27 career save is still needed to confirm that version's career-specific layout. See the FC 27 guide.

Available versions and databases

| version | database | Selected XML inside templates/ | | --- | --- | --- | | 'FC 25', 'FC 26', or 'FC 27' | 'main' (default) | <version>/fifa_ng_db-meta.xml | | 'FC 25', 'FC 26', or 'FC 27' | 'fut' | <version>/FUT/cards_ng_db-meta.xml | | 'FC 25', 'FC 26', or 'FC 27' | 'localization' | <version>/loc/eng_us-meta.xml |

The package exports immutable SUPPORTED_VERSIONS and TEMPLATE_DATABASES arrays and their corresponding EaFcVersion and TemplateDatabase types. Unknown versions or databases produce INVALID_OPTIONS.

Use the default main database for squads and career saves. Other database types can be selected explicitly:

const fut = await readEaFile('C:/saves/FutSquads20261009', {
  version: 'FC 26',
  database: 'fut',
});

Read bundled databases

import { readTemplateDatabase } from 'eafc-db-kit';

const teams = await readTemplateDatabase({
  version: 'FC 26',
  tables: ['teams'],
});

const localization = await readTemplateDatabase({
  version: 'FC 25',
  database: 'localization',
});

These calls load matching database and XML files from the package. No external installation paths or environment variables are required.

Filter tables

tables accepts full XML names or binary short codes. Omitting it reads all tables. Databases without the requested tables return tables: [], while tableCount retains the original directory count. Unknown table names do not produce errors. Filtering reduces decoding work and memory usage.

Read bytes and reuse descriptors

import { readFile } from 'node:fs/promises';
import { loadTemplateMetadata, parseEaFile } from 'eafc-db-kit';

const metadata = await loadTemplateMetadata('FC 26');
const bytes = await readFile('C:/saves/file.squad');
const data = parseEaFile(bytes, { metadata, fileName: 'file.squad' });

parseEaFile is synchronous and performs no I/O. readEaFile and readTemplateDatabase load files asynchronously, but decoding runs on the calling thread. Applications can use workers for large files.

For custom descriptors, use parseMetadataXml(xml) and pass the result as metadata, without version. External XML paths are not accepted by the file-reading API. Without version or metadata, records use short codes and raw integer values, accompanied by MISSING_METADATA warnings.

Read career standings, table zones, fixtures, competitions, and player statistics

Career saves also keep the live league tables, the season schedule with results, the competition tree, and season statistics for every player outside the databases. leagueteamlinks does not contain the live table and teamplayerlinks does not contain season statistics.

import { readCareerData, rankStandings, standingZones } from 'eafc-db-kit';

const career = await readCareerData('C:/saves/CmMgrC20261009');
const table = rankStandings(career.standings, 1530);
const zones = standingZones(career, 1530);
const nextMatches = career.fixtures.filter(fixture => !fixture.played);
const topScorers = career.playerStats
  .filter(stats => stats.competitionId === 1528)
  .sort((a, b) => b.goals - a.goals);

See career data for linking groups to career_users.primarycompobjid, the result contract, and the binary layout.

Edit and write a file

import { readEaFile, writeEaFile } from 'eafc-db-kit';

const source = 'C:/saves/file.squad';
const data = await readEaFile(source, { version: 'FC 26', tables: ['teams'] });
const teams = data.databases.flatMap(database => database.tables)
  .find(table => table.name === 'teams');
const team = teams?.records.find(record => record.teamid === 1);
if (!team) throw new Error('Team 1 was not found.');

team.teamname = 'Arsenal FC';

const result = await writeEaFile(source, 'C:/saves/edited.squad', data);
console.log(result.path, result.byteLength);

Career saves use the same write API. Changes can include record edits, additions, deletions, and the container's inGameName. Filtered reads are supported: unselected tables and databases are retained from the source binary. The write API uses field names and integer minima already present in data, so no additional version option is needed.

The original binary is required even when data comes from edited JSON. Standalone databases can grow or shrink. Databases inside saves retain their original byte allocations and offsets; edits that exceed an allocation fail before any destination is published. Changed databases have their database and table CRCs recalculated. New UTF-8 symbols receive a rebuilt Huffman tree.

Existing paths are rejected by default. overwrite: true explicitly allows replacement, including writing back to the source path. Publication uses a temporary file in the destination directory. Re-read the output before applying a later edit because counts, offsets, and sizes in the input snapshot remain unchanged.

See the write guide for record insertion and deletion, binary serialization, JSON import, integrity handling, validation rules, filesystem behavior, and compatibility limits.

Transfer a player

import { eaFcFileService } from 'eafc-db-kit';

const source = './Squads';
const file = await eaFcFileService.read(source, { version: 'FC 27' });
const transfer = file.transferPlayer(231747, 1, { fromTeamId: 243 });
await eaFcFileService.write(source, './Squads-edited', file);
console.log(transfer);

Use file.transferPlayer(playerId, teamId) when the player has a single club membership. fromTeamId selects one when several club links exist. Read all tables by omitting tables. The operation updates club links, shirt numbers, lineups, roles, and loans in memory while preserving national memberships. Save through the usual writer.

The shortcut supports EA FC 25, 26, and 27 squads/databases and compatible FIFA .db/XML pairs. Career transfers are not supported. See player transfers for options, validation, JSON import, and services separated by game version.

Create a player

const result = file.createPlayer({
  firstName: 'João', lastName: 'Silva', teamId: 1, nationality: 54,
  position: 'GK', baseRating: 75,
});

Creation allocates an unused ID by default and uses deterministic values suited to all 28 supported positions. Read all tables, then save with the usual writer. Use { autoGenerateId: false } with playerId for a manual ID. Randomization is optional; seeds, accepted fields, numeric ranges, explicit overrides, and a dryRun preview are supported. Names, created-player registration, club/national links, active mirrors, and reserve sheets are handled together. Career creation is excluded.

See player creation for full examples, table coverage, position profiles, and version-specific services.

Create a club

const club = file.createClub({
  name: 'Novo FC', leagueId: 13,
  colors: ['#123456', '#FFFFFF', '#FFCC00'],
});

Supply one to three mandatory colors as hex strings or RGB objects. Club IDs and related kit, formation, league, stadium, and manager records are generated automatically, with manual overrides and a dryRun preview available. Read all tables and save using the usual writer. The club starts without players; use createPlayer or transferPlayer with club.teamId to build its roster.

The shortcut supports squads and mapped standalone databases. External crests/textures and competition schedules require the game's own tooling; EA FC 25 kits need external textures. See club creation for defaults, manual kits, table coverage, version-specific services, and limits.

JSON output and export

Results contain serializable objects and arrays. For moderate output sizes:

const json = JSON.stringify(data, null, 2);

For large results, use incremental serialization:

import { createWriteStream } from 'node:fs';
import { Readable } from 'node:stream';
import { pipeline } from 'node:stream/promises';
import { eaFileJsonChunks, readTemplateDatabase } from 'eafc-db-kit';

const data = await readTemplateDatabase({ version: 'FC 26' });
await pipeline(
  Readable.from(eaFileJsonChunks(data)),
  createWriteStream('export.json', { flags: 'wx', encoding: 'utf8' }),
);

The generator avoids creating one string for the entire result. The input binary and decoded objects still remain in memory. Filter tables when only part of the database is needed.

From a source checkout, the executable example also accepts a version:

node examples/read-file.mjs `
  "templates/FC 26/fifa_ng_db.db" `
  --version "FC 26" teams --output export.json

Options: --version, --database, and --output. Without --output, JSON is written to stdout. Without a version, the example performs a raw read. Export creates a new file and rejects existing paths. The program exits after reading.

From a source checkout, import an edited JSON snapshot and create a binary file:

npm run write -- "C:/saves/file.squad" edited.json "C:/saves/edited.squad"

The write example also accepts --overwrite and --save-integrity invalidate|preserve. It prints { "path": "...", "byteLength": ... } after a successful write. JSON must follow the library's output contract; an array of records by itself is insufficient.

Public API and errors

| Function | Return value | Purpose | | --- | --- | --- | | FifaDatabaseService / fifaDatabaseService | Service class / instance | Explicit FIFA database/XML workflow | | EaFcFileService / eaFcFileService | Service class / instance | Existing EA FC workflows and typed templates | | readFifaDatabase(dbPath, xmlPath, options?) | Promise<FifaDatabaseFile> | Read a standalone database with complete XML mappings | | parseFifaDatabase(bytes, xmlText, options?) | FifaDatabaseFile | Decode a standalone database with XML text | | writeFifaDatabase(dbPath, xmlPath, outputPath, data, options?) | Promise<WriteFileResult> | Validate the pair and publish an edited database | | serializeFifaDatabase(bytes, xmlText, data, options?) | Uint8Array | Encode FIFA database edits without I/O | | readEaFile(path, options?) | Promise<EaFile> | Read a save or database using version | | readTemplateDatabase(options) | Promise<EaFile> | Read a bundled database | | loadTemplateMetadata(version, database?) | Promise<MetadataSchema> | Load a reusable local descriptor | | parseEaFile(bytes, options?) | EaFile | Decode a Uint8Array or Buffer | | serializeEaFile(sourceBytes, file, options?) | Uint8Array | Apply edits to source bytes without I/O | | writeEaFile(sourcePath, outputPath, file, options?) | Promise<WriteFileResult> | Serialize and publish a binary file | | parseMetadataXml(xml) | MetadataSchema | Parse custom descriptor XML | | eaFileJsonChunks(file) | Generator<string> | Generate compact JSON in chunks | | readCareerData(path, options?) | Promise<CareerData> | Read career competitions, standings, fixtures, and player statistics | | parseCareerData(bytes, options?) | CareerData | Decode career sections without I/O | | rankStandings(standings, groupId) | RankedStanding[] | Rank one group by points, wins, goal difference, and goals | | standingZones(career, groupId) | StandingZone[] | Champion, promotion, relegation, and continental places of one group | | file.transferPlayer(playerId, teamId, options?) | TransferPlayerResult | Transfer a player between clubs in a squad or database in memory | | withFileOperations(snapshot, profile?) | Snapshot with FileOperations | Attach shortcuts to a data snapshot | | PlayerTransferService | Service class | Apply a transfer directly to a snapshot with a selected game profile | | file.createPlayer(input, options?) | CreatePlayerResult | Create a player and related records in a squad or database | | file.createClub(input, options?) | CreateClubResult | Create a club, kits, and related records in a squad or database | | PlayerCreationService | Service class | Create a player directly on a snapshot with a selected game profile |

ReadFileOptions includes version, database, metadata, tables, fileName, kind, and limits. version and metadata are mutually exclusive. database requires version. ReadTemplateOptions requires version. ParseOptions accepts metadata without loading XML files.

The EA FC SerializeOptions includes limits and saveIntegrity, defaulting to 'invalidate'. WriteFileOptions also includes overwrite, defaulting to false. EA FC write options do not accept descriptor paths or a version; descriptors come from the read result. The FIFA write functions instead require the original .db and XML path/text explicitly; their options are described in the FIFA guide.

import { EaFileError, readEaFile } from 'eafc-db-kit';

try {
  const data = await readEaFile('C:/saves/file.squad', { version: 'FC 26' });
  console.log(data.source);
} catch (error) {
  if (error instanceof EaFileError) console.error(error.toJSON());
  else throw error;
}

Errors include code, an English message, and a byte offset, or null when no offset applies. Read offsets are absolute positions in the source. Codes: INVALID_OPTIONS, FILE_READ_FAILED, FILE_WRITE_FAILED, INVALID_WRITE, INVALID_METADATA, INVALID_FORMAT, TRUNCATED_FILE, UNSUPPORTED_FIELD_TYPE, INVALID_COMPRESSED_STRING, LIMIT_EXCEEDED, INVALID_TRANSFER, INVALID_PLAYER_CREATION, and UNSUPPORTED_OPERATION.

| Default limit | Value | | --- | ---: | | maxFileBytes | 268,435,456 bytes (256 MiB) | | maxDatabases | 64 | | maxTablesPerDatabase | 4,096 | | maxTotalRecords | 1,000,000 | | maxDecodedValues | 64,000,000 | | maxDecodedStringBytes | 67,108,864 bytes (64 MiB) |

Override limits with limits: { maxTotalRecords: 2_000_000 }. Values must be positive safe integers. Defaults are exported as DEFAULT_LIMITS.

Project structure

  • src/domain/: contracts, version catalog, errors, and decoding budgets.
  • src/application/: file reads and writes, template selection, decoding, and serialization.
  • src/infrastructure/binary/: databases, tables, fields, bits, Huffman coding, and CRCs.
  • src/infrastructure/career/: career sections, competition tree, standings, fixtures, and player statistics.
  • src/infrastructure/metadata/: XML parsing and descriptor indexes.
  • src/infrastructure/templates/: bundled template path resolution.
  • src/infrastructure/filesystem/: read-only source access and atomic output publication.
  • src/services/fifa/: standalone FIFA database/XML reads, writes, and service facade.
  • src/services/ea-fc/: EA FC service facade preserving existing application APIs.
  • src/services/file-operations/: shortcuts attached to decoded files.
  • src/services/player-transfer/: club transfers with separate version adapters and shared roster/sheet services.
  • src/services/player-creation/: player creation with separate version adapters, numeric profiles, identity, names, and roster services.
  • templates/: FC 25, FC 26, and FC 27 descriptors and databases.
  • test/: fixtures and integration tests using bundled templates.

The library has no mutable global state or external program dependency. TypeScript enables strict, noUncheckedIndexedAccess, and exactOptionalPropertyTypes. Development references: TypeScript configuration and Node.js test runner.

Further documentation: career data, writing, JSON structure, binary format, templates, and validation.

License

MIT.