Project

Decisions

Decision. The package is published to npmjs as platform-achievements, from github.com/jbcom/platform-achievements, MIT licensed, first version 0.1.0.

2026-10-07: open source under the owner's npm user, as platform-achievements

Decision. The package is published to npmjs as platform-achievements, from github.com/jbcom/platform-achievements, MIT licensed, first version 0.1.0.

Why the name. It says what the package is for (achievements) and what is distinctive about it (several platforms at once), is free on npmjs, and is unscoped so it is easy to find.

The model is keyed by a flat list, not by grades

Google Play Games, Game Center and Steam have no grades, so the package knows only a flat list of achievements. A game that grades an achievement defines one per grade and builds earned from its own save data. This keeps the sync, the adapters and the export free of any one game's notions.

earned and acks have the same shape

Both are a map from key to a number: 1 for a standard unlock, steps for an incremental one. The difference between them is the whole sync, and having one shape makes outstanding a short pure function that is easy to test exhaustively.

syncAchievements takes one request object

It needs the list, what is earned, an adapter and a store. Four positional arguments of mixed kinds were easy to transpose, so it takes an object.

Adapters are structural

GameServicesPlugin and SteamClient list the methods used. No peer dependency is declared because nothing is imported; the shapes match @idleflowgames/capacitor-play-games 0.3 on Capacitor 8 and steamworks.js 0.4.

Added over the original code: validateAchievements and createZip

Console limits are facts about the platforms and are cheap to check in a unit test, so they ship with the package. The Play Console's import wants a flat ZIP; a small deterministic stored-ZIP writer avoids requiring a system zip binary.

Toolchain: Node 26, pnpm 12, TypeScript 7

Built on Node 26 and pnpm 12 with TypeScript 7 (native), every tsconfig on moduleResolution: bundler. The CommonJS build emits its own .d.cts declarations so a CommonJS consumer resolves correct types. engines.node is >=22. The first release is published locally once, with a token passed only through --userconfig; later releases publish from cd.yml by OIDC trusted publishing.

2026-10-07: support every maintained Node line

Node.js 22, 24 and 26 are supported, with major-only CI selectors and a Node 26 development default. The >=22 engine range expresses a maintained-line policy, rather than a promise about every historical patch. All shipped adapters use structural clients and need no newer Node API floor. The complete verification chain, including packed ESM/CommonJS consumers, is checked on Node 22 and 26 locally; CI also verifies Node 24. Scripts and hooks impose no exact-patch equality check.

Branch rulesets use the shared OSS policy

scripts/apply-branch-ruleset.mjs applies the three OSS rulesets idempotently, using merge commits and resolved review threads. With no arguments it targets jbcom/platform-achievements and requires CI / gate, title, Repository Policy / gate and Dependency Review / gate. Explicit repository and semicolon-separated check arguments override those defaults. It omits Copilot review and Code Quality rules because both spend AI credits. Applying rulesets is an explicit maintainer operation; CI does not run this script.