Install
Download the SDK, link it from your game.
Two steps: grab the zip, point your game at it. No registry, no network at runtime — the build is reproducible and the types come along for the ride.
1 — Download the SDK#
Unzip it into your project — anywhere your build can reference. A common
location is your-game/vendor/gamee-sdk/. At the top level you’ll find:
gamee-sdk/
├── index.js # ESM entry
├── index.cjs # CJS entry
├── index.d.ts # ESM type declarations
├── index.d.cts # CJS type declarations
└── gamee-sdk.iife.js # IIFE bundle (window.Gamee)
The SDK ships three formats from one build so you can drop into Vite, Webpack,
Rollup, esbuild, Bun, Deno, plain Node require, or a single <script> tag —
no peer dependencies in any case.
Need an older release? Every tagged version is browsable at gitlab.gamee.io/gamee/gamee-sdk/-/tags — pick a tag and grab the
build/folder from that tree.
2 — Link it from your game#
Pick the import style that matches your project.
Option A — package.json (file: dependency)#
For bundler projects (Vite, Webpack, Rollup, esbuild, Bun, Deno) and Node-style
require.
// your-game/package.json
{
"dependencies": {
"@gamee/sdk": "file:./vendor/gamee-sdk"
}
}
npm install
import { gamee } from '@gamee/sdk';
await gamee.init({ capabilities: ['saveState'] });
Works with npm, pnpm, yarn. Re-run npm install after every SDK refresh.
Commit vendor/gamee-sdk/ so the build is reproducible without network access.
Need a fresh, isolated instance (tests, multiple games on one page)?
import { createSdk } from '@gamee/sdk';
const sdk = createSdk({ allowedOrigins: ['https://games.gameeapp.com'] });
await sdk.init({ capabilities: [] });
CommonJS works too:
const { gamee } = require('@gamee/sdk');
gamee.init({ capabilities: [] }).then(() => gamee.gameReady());
Option B — <script> tag (window.Gamee, no build)#
For prototypes, level editors, or games that have no build step. Serve
gamee-sdk.iife.js from your own static host; the bundle attaches a Gamee
global to window:
<!doctype html>
<html>
<head>
<script src="/vendor/gamee-sdk/gamee-sdk.iife.js"></script>
</head>
<body>
<canvas id="game"></canvas>
<script>
// `Gamee` is the same surface as the package.
// Use `Gamee.gamee` (singleton) or `Gamee.createSdk(...)`.
Gamee.gamee.init({ capabilities: [] }).then(() => {
Gamee.gamee.gameReady();
});
</script>
</body>
</html>
The global is
Gamee(capital G). The lowercaseGamee.gameeis the default singleton;Gamee.createSdkandGamee.GameeErrorlive on the same namespace.
Working from a local monorepo checkout#
If you’d rather track master directly, clone the monorepo and link straight at
the source — no zip in between.
git clone https://gitlab.gamee.io/gamee/gamee-sdk.git
The SDK source lives under packages/sdk/ (the rest of the repo — the
playground, the docs site, the test utils — isn’t needed at runtime). Point
your game’s package.json at it:
// your-game/package.json
{
"dependencies": {
"@gamee/sdk": "file:../gamee-sdk/packages/sdk"
}
}
Run npm run sdk:build in the monorepo after every pull so packages/sdk/dist/
is fresh, then re-run npm install in the game.
What about npm?#
# Future. Does not work today.
npm install @gamee/sdk
# pnpm add @gamee/sdk
# yarn add @gamee/sdk
Once published, the same approach will also unlock the ESM-from-CDN form for no-build setups:
<script type="module">
// Future. Does not work today.
import { gamee } from 'https://esm.sh/@gamee/sdk';
</script>
TypeScript#
The SDK is written in TypeScript and ships its own .d.ts and .d.cts files.
No separate @types/... install is needed — types resolve automatically as
soon as your editor sees node_modules/@gamee/sdk/dist/index.d.ts (or the
.d.cts for CJS projects).
Recommended tsconfig.json#
The SDK targets ES2020 and ships ESM by default. The minimum settings:
{
"compilerOptions": {
"target": "ES2020",
"module": "ESNext",
// "bundler" works for Vite/esbuild/Webpack/Rollup. Use "node16" if you
// run TypeScript directly under Node.js without a bundler.
"moduleResolution": "bundler",
"strict": true,
"esModuleInterop": true,
"skipLibCheck": true,
"lib": ["ES2020", "DOM", "DOM.Iterable"],
},
}
If moduleResolution is set to the legacy "node", switch to "bundler" or
"node16" — otherwise the type bundler may not pick up the package’s
exports map and you’ll get spurious “cannot find module” errors.
Importing types#
Every public type is re-exported from the package root. Pull them in alongside the runtime imports:
import { gamee, createSdk, GameeError } from '@gamee/sdk';
import type {
// Lifecycle
SdkOptions,
InitOptions,
GameInitResponse,
GameOverData,
// Domain
Capability,
Platform,
Environment,
GameContext,
MissionData,
PurchaseDetails,
PurchaseResult,
RewardedLoadResult,
RewardedResult,
// Events
GameeEvents,
GameeEventName,
// Errors
GameeErrorCode,
// Bridge (for tests / custom transports)
PlatformBridge,
BridgeListener,
} from '@gamee/sdk';
Typing your own handlers#
Event payloads are derived from the GameeEvents map, so handlers infer their
argument shape automatically:
// `payload` is inferred as `{ gameSeed?: string; resetState?: boolean }`
gamee.on('start', (payload) => {
if (payload.resetState) clearLocalProgress();
startRun(payload.gameSeed);
});
// Unknown event names fail to compile:
gamee.on('not-a-real-event', () => {}); // ❌ TS2769
If you want to extract a payload type by name:
import type { GameeEvents } from '@gamee/sdk';
type StartPayload = GameeEvents['start'];
// → { gameSeed?: string; resetState?: boolean }
Narrowing the unknown event fields#
Two events carry unknown so the SDK doesn’t lock you into a platform shape:
avatarUpdate—{ avatar: unknown }miningEventUpdate—{ miningEvent: unknown }
Cast in the handler with whatever type matches your platform’s contract:
interface AvatarPayload {
url: string;
rarity: 'common' | 'rare';
}
gamee.on('avatarUpdate', ({ avatar }) => {
const a = avatar as AvatarPayload;
ui.setAvatar(a.url);
});
Save state and init data are strings#
initResponse.saveState and initResponse.initData are typed as
string | undefined — the platform hands you serialized JSON. Parse defensively:
interface Save {
level: number;
gold: number;
}
const ctx = await gamee.init({ capabilities: ['saveState'] });
const save: Save | null = ctx.saveState ? (JSON.parse(ctx.saveState) as Save) : null;
The corresponding write side accepts unknown and stringifies for you:
gamee.gameSaveState({ level: 4, gold: 200 } satisfies Save);
Typing errors#
GameeError has a typed code field. Exhaustive switch is the idiomatic
recovery pattern:
import { GameeError, type GameeErrorCode } from '@gamee/sdk';
function handle(err: unknown): void {
if (!(err instanceof GameeError)) throw err;
const code: GameeErrorCode = err.code;
switch (code) {
case 'CAPABILITY_MISSING':
/* … */ break;
case 'BRIDGE_TIMEOUT':
/* … */ break;
case 'BRIDGE_ERROR':
/* … */ break;
case 'BRIDGE_OVERFLOW':
/* … */ break;
case 'VALIDATION':
/* … */ break;
case 'INVALID_STATE':
/* … */ break;
case 'NOT_SUPPORTED':
/* … */ break;
}
}
TypeScript with the IIFE script tag#
The IIFE bundle attaches window.Gamee at runtime. To get types in a
TypeScript project that also uses the script-tag form, declare the global:
// src/types/gamee.d.ts
import type * as GameeNs from '@gamee/sdk';
declare global {
interface Window {
Gamee: typeof GameeNs;
}
const Gamee: typeof GameeNs;
}
export {};
You still need @gamee/sdk installed (option A) for the type imports — but the
runtime entirely comes from the <script> tag.
Verifying the install#
A 5-line health check you can paste into any new game:
import { gamee, SDK_VERSION, ALL_CAPABILITIES } from '@gamee/sdk';
console.log('SDK', SDK_VERSION, 'platform:', gamee.platform);
console.log('declarable capabilities:', ALL_CAPABILITIES);
If gamee.platform is 'web' and you are running outside the GAMEE platform,
that’s expected — see the architecture page for what changes
on iOS and Android, and Utilities for the full introspection API.