manacost-labs / hearthstone-deckstrings
Dependency-free Hearthstone deckstring codec, validation, and clipboard export library
Package info
github.com/Manacost-Labs/deckstrings
Language:C#
pkg:composer/manacost-labs/hearthstone-deckstrings
Requires
- php: >=8.2 <8.6
Requires (Dev)
- phpstan/phpstan: ^2.1
- phpunit/phpunit: ^11.5 || ^12.0
Suggests
None
Provides
None
Conflicts
None
Replaces
None
This package is not auto-updated.
Last update: 2026-09-24 23:27:10 UTC
README
A dependency-free Hearthstone deckstring backend library for JavaScript, TypeScript, PHP, Python, and .NET. All four implementations share one public contract and the same golden fixtures, including sideboards and full clipboard exports.
Release status:
1.0.0is the first stable cross-language release and is publicly available on npm, Packagist, PyPI, and NuGet. The install commands below are the supported production path.
Install
| Ecosystem | Package | Supported runtime |
|---|---|---|
| npm | npm install @manacost-labs/deckstrings |
Node.js 22 or 24+, modern browsers |
| Composer | composer require manacost-labs/hearthstone-deckstrings |
PHP 8.2–8.5 |
| PyPI | python -m pip install manacost-deckstrings |
Python 3.10–3.14 |
| NuGet | dotnet add package ManacostLabs.Deckstrings |
netstandard2.0, .NET 8, .NET 10 |
Package versions advance together. Version 1.0.0 defines the first stable
cross-language API.
What the library owns
- version 1 deckstring encoding and decoding;
- Wild, Standard, Classic, and Twist formats;
- heroes, cards, and sideboards;
- deterministic canonical ordering;
- structured validation and stable error codes;
- parsing and formatting complete Hearthstone clipboard exports;
- defensive limits for untrusted input.
The core does not download card data, make network requests, or decide whether a deck is legal for a patch. Card names and costs can be added at the edge with an optional resolver callback.
Shared model
{
"format": 1,
"heroes": [7],
"cards": [[1, 2], [2, 2], [3, 2], [4, 1]],
"sideboardCards": [[5, 1, 90749]]
}
cardsentries are[dbfId, count];sideboardCardsentries are[dbfId, count, ownerDbfId];- heroes and cards are sorted by DBF ID;
- sideboard cards are sorted by owner and then DBF ID;
- duplicate heroes, cards, and sideboard
(ownerDbfId, dbfId)pairs are invalid.
The normative model is spec/deck.schema.json, and the wire/error/export contract is documented in spec/README.md.
JavaScript / TypeScript
import { FormatType, canonicalize, decode, encode, formatExport, parseExport, validate, } from "@manacost-labs/deckstrings"; const deck = decode("AAEBAQcBBAMBAgMAAA=="); const result = validate(deck); // { valid, errors } const canonical = canonicalize(deck); const deckstring = encode(canonical); const parsed = parseExport(`### Example\n${deckstring}`); const text = formatExport(parsed.deck, parsed.metadata);
The npm package includes ESM, CommonJS, browser ESM, UMD, and bundled TypeScript declarations.
PHP
use ManacostLabs\Deckstrings\Deckstrings; $deck = Deckstrings::decode('AAEBAQcBBAMBAgMAAA=='); $result = Deckstrings::validate($deck); $deckstring = Deckstrings::encode(Deckstrings::canonicalize($deck)); $parsed = Deckstrings::parseExport("### Example\n{$deckstring}"); $text = Deckstrings::formatExport($parsed['deck'], $parsed['metadata']);
Python
from manacost_deckstrings import ( canonicalize, decode, encode, format_export, parse_export, validate, ) deck = decode("AAEBAQcBBAMBAgMAAA==") result = validate(deck) deckstring = encode(canonicalize(deck)) parsed = parse_export(f"### Example\n{deckstring}") text = format_export(parsed["deck"], parsed["metadata"])
The Python distribution is typed and ships a py.typed marker.
C# / .NET
using ManacostLabs.Deckstrings; var deck = Deckstrings.Decode("AAEBAQcBBAMBAgMAAA=="); var result = Deckstrings.Validate(deck); var deckstring = Deckstrings.Encode(Deckstrings.Canonicalize(deck)); var parsed = Deckstrings.ParseExport($"### Example\n{deckstring}"); var text = Deckstrings.FormatExport(parsed.Deck, parsed.Metadata);
NuGet releases include XML documentation, portable PDBs, Source Link metadata,
and a separate .snupkg symbol package.
Validation and errors
validate/Validate returns ordinary user-input failures and does not throw:
{
"valid": false,
"errors": [
{
"code": "invalid_count",
"path": "cards[0][1]",
"message": "card count must be a positive integer"
}
]
}
Encoding, decoding, canonicalization, and export parsing raise an idiomatic language exception. Match its stable machine-readable code, not the human-readable message. See docs/API.md for language-specific names and the complete code list.
Card display resolver
formatExport/format_export accepts an optional callback from DBF ID to
{ name, cost? } (or the native equivalent). The callback may return null
for an unknown card. Names must be non-blank single-line strings, and costs
must be non-negative integers no larger than 2,147,483,647. The resolver is
presentation-only and never changes the encoded deckstring.
Compatibility promise
Every implementation reads fixtures/deckstrings.json, fixtures/api.json, and fixtures/exports.json directly. A behavior change is not accepted until the shared contract and all language jobs agree. Legacy deckstrings without a sideboard marker remain supported; encoders always produce canonical output.
Development
# JavaScript / TypeScript yarn install --frozen-lockfile --ignore-scripts npx playwright install chromium npm run verify # Shared schemas uv sync --project packages/python --locked --all-extras uv run --project packages/python --locked python scripts/validate_fixtures.py uv run --project packages/python --locked python scripts/check_versions.py # PHP composer install --no-scripts --no-plugins composer check # Python uv run --project packages/python --locked pytest packages/python/tests uv run --project packages/python --locked ruff check packages/python uv run --project packages/python --locked mypy packages/python/src # .NET dotnet test packages/dotnet/tests/ManacostLabs.Deckstrings.Tests/ManacostLabs.Deckstrings.Tests.csproj -c Release
See CONTRIBUTING.md, the release process, the migration guide, and the roadmap.
Credits and license
This is an independent Manacost Labs repository whose complete Git history is derived from and preserves attribution to HearthSim/hearthstone-deckstrings. Manacost Labs maintains the multi-language contract, native backend implementations, packaging, and release automation. Repository migration details are recorded in docs/REPOSITORY_MIGRATION.md.
Licensed under the ISC License.