Quick start
A working app before your first registration.
Start with a complete dashboard and demo match. No account, Warcraft III installation, or desktop client required.
npx --yes --package=github:W3Booster/app-starter w3booster-create my-app
cd my-app
npm install
npm run devOpen http://localhost:5173/. You should see two players and a running game clock. The starter includes complete rendering, error states, a transparent overlay, and demo scenarios. Follow the first-app tutorial to connect real data.
Add the SDK to an existing application
Create a private app in W3Booster first, then run these commands from your project directory. The following integration sketch assumes your own rendering functions.
npm install @w3booster/sdk
npx w3booster-settings init app_your_id --endpoint https://api.w3booster.comimport { w3boosterApp } from './w3booster.generated';
const runtime = w3boosterApp.createRuntime({ retry: true });
runtime.lifecycle.subscribe(snapshot => {
renderConnection(snapshot.status, snapshot.error);
renderMatch(snapshot.state, snapshot.settings);
renderHostActions(snapshot.host);
}, { signal: runtime.signal });
await runtime.start();
// Component or page teardown:
await runtime.stop();runtime.start() resolves after the current connection has synchronized state. runtime.stop() aborts runtime.signal and removes subscriptions scoped to that lifetime. The checked-in binding carries the exact client ID, scopes, settings schema, defaults, and revision; application code does not choose a WebSocket, carry credentials, apply patches, or implement reconnection.
Develop without W3Booster
Demo mode loads a representative fixture on demand and keeps demo code out of normal production startup.
const runtime = w3boosterApp.createRuntime({ demo: true });
await runtime.start();Pass demo: { interval: 0, settings: { … } } for a deterministic fixture, or provide a complete demo state for focused UI tests.
Application model
One app. Up to three surfaces.
Every W3Booster app is a remotely hosted web application. Configure only the surfaces the product needs; hosting it yourself or through W3Booster does not change the SDK.
An interactive dashboard or workspace opened inside the client.
A transparent surface composed into the user’s stable OBS browser source.
A transparent surface aligned over Warcraft III by the desktop host.
- Enable Developer ModeUse the account menu in W3Booster, then open Apps → Developer.
- Create a private appAdd its name, requested scopes, and at least one surface URL. W3Booster generates the public client ID. Localhost is allowed for development; deploy to HTTPS for publication.
- Build and testUse demo data for UI work and an owner-only local session for real match data.
- Invite testersPrivate invitation codes grant discovery; each tester still installs and enables the app.
Local development
Use real data without deploying.
Run Vite, Angular, or any web server on localhost. In your app’s developer page, choose Test locally and add the local URLs for the surfaces you are working on.
http://localhost:5173/Owner only · expires after 12 hoursThe session installs and enables the app only for its owner and temporarily replaces only the chosen surfaces. Published metadata and other users do not change. Return to the published version at any time.
// Normal application code stays environment-free.
const runtime = w3boosterApp.createRuntime();
// Only platform developers normally force a backend.
const local = w3boosterApp.createRuntime({
backend: 'local'
});Cloud is the default. W3Booster may add ?backend=local or ?backend=cloud to a platform launch, and the SDK honors it automatically. Use backend: 'auto' only when intentionally trying local before cloud.
Frontend lifecycle
Render connection, freshness, and state together.
runtime.lifecycle is the preferred database-bound UI boundary. Its snapshot atomically contains the client, status, state, isSynchronized, resolved settings, host capabilities, and the current connection error. It publishes immediately and after every transition.
const runtime = w3boosterApp.createRuntime({ retry: true });
runtime.lifecycle.subscribe(snapshot => {
render({
status: snapshot.status,
state: snapshot.state,
fresh: snapshot.isSynchronized,
settings: snapshot.settings,
host: snapshot.host,
error: snapshot.error,
retry: snapshot.retry
});
}, { signal: runtime.signal });
await runtime.start();| Method | Resolves when | Use it for |
|---|---|---|
runtime.start() | A fresh synchronized state exists by default | Canonical database-bound frontend startup |
client.open() | The transport is connected | Advanced transport-only startup; connect() is deprecated |
whenReady() | Any hydrated state exists, including preserved reconnect state | Work that can use temporarily stale state |
whenSynchronized() | A fresh snapshot for the current connection exists | Rendering or actions that require a current baseline |
Automatic reconnect preserves hydrated state for visual continuity but sets isSynchronized to false until a fresh snapshot arrives. During initial retry, snapshot.retry exposes the attempt, configured limit, last transient error, and next delay so a UI can show progress without parsing error strings. State subscriptions and watchers are reevaluated for freshness-only transitions even when the immutable state identity is unchanged. State is recursively immutable and structurally shared, so unchanged branches preserve identity.
State and scopes
One hydrated, capability-aware match model.
The platform filters data before serialization. A scope expresses which branch an app may read; it is not a subscription tier. state.capabilities is the final runtime answer after the app’s granted scopes, account plan, and match context have been applied. Individual optional values can still be absent when the live source does not provide them.
Available to Free and paid accounts when scoped and present.
Paid / ObserverPaid access during normal play; also available in observer match state.
| Scope | State branch | Availability | Includes |
|---|---|---|---|
match:read | match | All plans | Lifecycle, time, map, mode, realm, observer/replay flags |
players:read | players[] | All plans | Identity, race, team, color, position |
stats:read | players[].stats | All plans | Rank, league, wins, losses, main account |
heroes:read | players[].heroes[] | Paid / Observer | Level, XP, health, mana, abilities, inventory |
upgrades:read | players[].upgrades | Paid / Observer | Completed, active, and researching upgrades |
resources:read | players[].resources | All plans | Gold, lumber, supply, worker supply |
controlgroups:read | players[].controlgroups | All plans | Front units and group sizes |
| No scope | gameContext | All plans | HUD scale, chat state, team-color preference |
runtime.client.state.subscribe(state => {
if (!state) return;
const resources = state.capabilities.includes('resources')
? state.players[0]?.resources
: undefined;
renderResources(resources);
});Complete API data model
Everything W3Booster delivers.
This is the full hydrated MatchState<TSettings> exposed by @w3booster/sdk. The SDK validates it at runtime, freezes it recursively, and maintains it from snapshots and patches.
The field has no W3Booster subscription gate. Its scope and live source data may still make it optional.
The API removes the branch and its capability for a Free account during normal player matches. Observer match state bypasses the plan gate.
Optional because of match type, source availability, privacy, surface, or app configuration—not because it is necessarily paid.
MatchState<TSettings>
| Field | Type | Availability | Description |
|---|---|---|---|
capabilities | readonly Capability[] | Always | The data branches actually available now: match, players, stats, heroes, upgrades, resources, controlgroups, and overlay. |
match | Match | All plans | Current match metadata. Without match:read, the root remains present as a neutral no-match value. |
players | readonly Player[] | All plans | One entry per player when at least one player-related scope is available; otherwise an empty array. |
gameContext | GameContext | All plans | Always-delivered game context; HUD scale defaults to 1. |
application? | ApplicationState<TSettings> | App launch | The current application identity, surface, development state, and delivered settings. Generated runtimes expose the separately resolved settings value. |
match:readMatch
| Field | Type | Required | Description |
|---|---|---|---|
id | string | Yes | Stable match identity; empty only while status is none. |
status | 'starting' | 'running' | 'finished' | 'none' | Yes | Current match lifecycle state. |
gameTime | number | Yes | Elapsed in-game time in whole seconds, excluding paused time. |
mode | string | Yes | Normalized game mode such as 1v1, 2v2, or 4ffa. |
map? | string | No | Human-readable, display-ready map name. |
realm? | string | No | Match service or realm, for example W3Champions or Battle.net context. |
paused? | boolean | No | Whether the current game clock is paused. |
isReplay? | boolean | No | Whether W3Booster is reading a replay. |
isReforged? | boolean | No | Selects Reforged versus Classic presentation assets. |
isObserver? | boolean | No | Whether the active match state is an observer context. This is the API-side bypass for PRO-only match branches. |
broadcasterPlayerId? | string | No | Public player ID associated with the broadcaster. |
realBroadcasterPlayerId? | string | No | Underlying broadcaster player ID when presentation identity differs. |
startedAt? | string | No | ISO-8601 match start timestamp. |
endedAt? | string | No | Authoritative ISO-8601 completion timestamp when supplied by the platform. |
result? | { playerId: string; outcome: 'won' | 'lost' } | No | Recorder-confirmed outcome for the actual local player. Uses match:read; absent when unknown, observing, or replaying. See result availability and examples. |
players:read + related scopesPlayer
| Field | Type | Scope / plan | Description |
|---|---|---|---|
id | string | Any player branch | Stable identity within the match. |
name? | string | players:read · All plans | Display name, subject to protected-match redaction. |
race? | Race | players:read · All plans | random, human, orc, undead, or night-elf. |
team? | number | players:read · All plans | Zero-based Warcraft team ID. |
colorId? | number | players:read · All plans | Native Warcraft player-color index. |
startPosition? | { x: number; y: number } | players:read · All plans | Warcraft map coordinates, useful for ordering and relative placement—not screen pixels. |
isAI? | boolean | players:read · All plans | Whether this slot is computer-controlled. |
mainAccount? | MainAccount | stats:read · All plans | Resolved main-account identity when one exists. |
stats? | PlayerStatsCollection | stats:read · All plans | Solo, team, 4v4, and FFA ranking collections. |
resources? | Resources | resources:read · All plans | Normalized economy and supply values. |
controlgroups? | Record<string, ControlGroup> | controlgroups:read · All plans | Control-group number to its visible front unit and size. |
heroes? | readonly Hero[] | heroes:read · Paid / Observer | Full hero state. Omitted and removed from capabilities when the plan gate applies. |
upgrades? | UpgradeState | upgrades:read · Paid / Observer | Completed, active, and researching upgrade state. |
Player 2, race random, and no mainAccount. This privacy filtering happens on the trusted server before the app stream is serialized.overlay:read is retired. OverlayRuntimeState remains a deprecated compatibility alias. Shared context is always delivered; Match Vision’s score belongs to its application data. The old matchScore() and matchScoreOrZero() selectors are deprecated compatibility helpers.
stats:readPlayerStats
| Field | Type | Description |
|---|---|---|
wins | number | Recorded wins. |
losses | number | Recorded losses. |
winRate | number | Display-ready percentage from 0–100. |
rank? | number | Leaderboard rank. |
league? | string | number | Service-provided league or division. |
level? | number | Service-provided ladder level. |
PlayerStatsCollection may contain solo?, team?, team4?, and ffa?. Use preferredStats() from @w3booster/sdk/standard-game to select the best entry for the match mode.
stats:readMainAccount
| Field | Type | Description |
|---|---|---|
name | string | Resolved main-account name. |
country? | string | Normalized country identifier; resolve its flag with @w3booster/sdk/assets. |
mainRace? | Race | Main-account race when known. |
resources:readResources
| Field | Type | Description |
|---|---|---|
gold | number | Current gold. |
lumber | number | Current lumber. |
supply | number | Used supply. |
supplyCap | number | Current supply cap. |
workerSupply? | number | Supply committed to workers. |
controlgroups:readControlGroup
| Field | Type | Description |
|---|---|---|
frontunit | string | Rawcode of the group’s front unit or building. |
size | number | Number of units/buildings assigned to the group. |
heroes:readHero, HeroAbility, and ValuePool
| Object | Field | Type | Description |
|---|---|---|---|
Hero | id | string | Standard-game hero rawcode used for metadata and artwork lookup. |
name | string | Human-readable hero name. | |
level | number | Current derived hero level. | |
experience? | number | Total hero experience. | |
hitpoints? | ValuePool | Current and maximum hit points. | |
mana? | ValuePool | Current and maximum mana. | |
abilities? | readonly HeroAbility[] | Learned abilities and activation timing. | |
inventory? | readonly string[] | Item rawcodes in slot order. | |
HeroAbility | id | string | Stable identity within the owning hero. |
name | string | Standard-game ability rawcode. | |
level | number | Learned ability level. | |
lastActivation? | number | Milliseconds on the match game-time clock; use the cooldown helper instead of interpreting it directly. | |
ValuePool | current | number | Current value. |
max | number | Maximum value. |
upgrades:readUpgradeState
| Field | Type | Description |
|---|---|---|
upgrades | readonly CompletedUpgrade[] | Completed research and upgrade levels. |
active | readonly ActiveUpgrade[] | Currently relevant active upgrades. |
researching | readonly ResearchingUpgrade[] | Research currently in progress. |
| Upgrade field | Type | Description |
|---|---|---|
name | string | Canonical four-character standard-game upgrade rawcode. |
level | number | Explicit normalized upgrade level. |
gametime | number | Unix timestamp in milliseconds when W3Booster observed the upgrade. |
researchStart? | string | ISO-8601 timestamp on researching upgrades. |
researchFinish? | string | ISO-8601 estimated completion timestamp. |
GameContext
| Field | Type | Description |
|---|---|---|
chatbarOpen? | boolean | Whether the in-game chat bar is open. |
hudScale | number | CSS multiplier normalized from 0.5–1.0; defaults to 1. |
teamColors? | boolean | Native versus simplified team-color preference. |
ApplicationState<TSettings>
| Field | Type | Description |
|---|---|---|
data? | JsonObject | Read-only data belonging to this app. Match Vision alone receives its score in data.matchScore. |
clientId | string | The current app’s public immutable identifier. |
settings | DeepReadonly<TSettings> | The authenticated settings payload for this app only. Generated bindings type it as recursively partial; use runtime.lifecycle.settings for defaults-completed settings. |
surface? | 'application' | 'streamOverlay' | 'ingameOverlay' | The surface W3Booster launched. |
development? | boolean | Whether the owner’s temporary local override is active. |
Schema-dependent A settings field can declare requiresPlan: 'pro'. That prevents Basic users from changing the setting; it is an app-settings control, not a data entitlement. The resolved default or previously saved value can still be delivered.
? fields are conditional because no match is active, the source did not provide the value, the app lacks that scope, the current surface does not use it, or server-side privacy filtering applies. Only the hero and upgrade branches have a platform data-plan gate today.Events and selectors
Observe the view model you need.
Use state subscriptions for complete rendering, watch() for one derived value, and domain events for meaningful changes after the initial snapshot.
const client = runtime.client;
client.state.watch(
state => state?.match.gameTime ?? null,
seconds => drawClock(seconds),
{ signal: runtime.signal }
);
client.on('match.started', ({ match }) => showMatch(match), { signal: runtime.signal });
client.on('player.resources.changed', ({ player, resources }) => {
updateEconomy(player.id, resources);
}, { signal: runtime.signal });
client.on('hero.inventory.changed', ({ player, inventory }) => {
updateInventory(player.id, inventory);
}, { signal: runtime.signal });
client.subscribeMatchLifecycle(({ phase, initial, match, observedAt }) => {
updateHistory({ phase, initial, match, observedAt });
}, { signal: runtime.signal, includeCurrentFinished: true });The initial snapshot emits state.ready and state.changed. It does not synthesize match.started for a match that was already running. Use subscribeMatchLifecycle() when a feature needs that initial active match and later starts/ends through one exactly-once stream. By default an initially finished match is only a baseline; history and audit features can opt into one initial ended observation with includeCurrentFinished: true. Lifecycle observedAt is client observation time; prefer authoritative match.startedAt and match.endedAt when present.
match.result can arrive after the first finished snapshot and match.ended event. Observe state updates, keep missing outcomes unknown, and deduplicate persisted actions by match.id across hydration, reconnects, and app windows. The SDK does not maintain a session score. Read the result guide.
Pure selectors
import {
broadcasterPlayer,
groupPlayersByTeam,
headToHeadPair,
playerDisplayIdentity,
playerResources,
playerResourcesOrZero,
gameContext,
playerRelationship
} from '@w3booster/sdk/selectors';
const broadcaster = broadcasterPlayer(state.match, state.players);
const teams = groupPlayersByTeam(state.players);
const headToHead = headToHeadPair(state.players); // typed pair, or null
const identity = playerDisplayIdentity(broadcaster, {
stripBattleTagDiscriminator: true
});
const context = gameContext(state); // no scope required; hudScale defaults to 1
const resources = playerResources(broadcaster); // undefined when unavailable
const displayResources = playerResourcesOrZero(broadcaster);Selectors are framework-free, preserve immutable input identity where useful, and never mutate state. Team grouping accepts lightweight records with team: null; tuple and grouping helpers preserve input subtypes so applications do not need casts or local regrouping. Availability-preserving selectors return undefined; choose an explicit OrZero helper only when zero is the intended presentation fallback. The namespace also includes broadcaster-first teams, relationships, inventory, upgrades, and shared game context.
Standard-game data and assets
Import only the Warcraft knowledge you use.
Rules, icons, and cooldown metadata have separate entry points. This keeps lightweight apps small while preserving a combined objects namespace for compatibility.
import * as game from '@w3booster/sdk/standard-game';
import * as standardGameIcons from '@w3booster/sdk/standard-game/icons';
import * as cooldowns from '@w3booster/sdk/standard-game/cooldowns';
const assets = standardGameIcons.createAssetResolver();
const heroIcon = assets.hero(state.match, hero);
const countryFlag = assets.countryFlag(player.mainAccount?.country);
const cooldown = cooldowns.abilityCooldown(ability, state.match.gameTime);
const clock = game.formatGameTime(state.match.gameTime);
const progress = game.heroExperienceState(hero.experience);
const raceKey = game.raceInfo(player.race).localizationKey;
const teams = game.orderMatchTeams(state.players, state.match, { reverse: false });The standard-game namespace covers locale-neutral race IDs and localization keys, modes, player colors, statistics selection, game-time formatting, day/night state, hero progression, health/mana ratios, upkeep, mode-aware presentation-team ordering, and presentation colors. Team ordering keeps the broadcaster first on player and team-observer surfaces, uses map position for 1v1 observer/replay surfaces, and preserves FFA sides. The launch-aware asset resolver binds Warcraft icons and country flags to one validated asset origin. Your app owns translated race copy plus sprite and CSS layout policy. Artwork is not bundled with npm.
Typed settings
Define settings once. Keep UI and types together.
Build the schema in W3Booster and let the client render consistent controls. The SDK can generate a checked-in TypeScript binding with the exact schema, defaults, scopes, client ID, and a managed application runtime.
npx w3booster-settings init app_your_id --endpoint https://api.w3booster.com
npm run w3booster:sync
npm run w3booster:checkCommit src/w3booster.generated.ts. This keeps editor types and offline builds deterministic while making schema changes visible in code review.
import { w3boosterApp } from './w3booster.generated';
const runtime = w3boosterApp.createRuntime({ retry: true });
runtime.lifecycle.subscribe(snapshot => {
renderMatch(snapshot.state, { fresh: snapshot.isSynchronized });
renderSettings(snapshot.settings);
renderHostActions(snapshot.host);
}, { signal: runtime.signal });
await runtime.start();
await runtime.stop();The managed runtime resolves partial user settings over database defaults and publishes settings atomically with state and host capabilities. It also sends the generated definition revision during connection; APPLICATION_DEFINITION_MISMATCH means the binding must be synchronized and the app redeployed. Generated client, runtime, snapshot, and connect-option aliases preserve typed additive overlay extensions. Lower-level tools remain available from @w3booster/sdk/settings.
Host actions
Ask the workspace. Await the result.
Embedded app surfaces can open or close windows, persist typed settings, report their height, and send supported host commands. Generic app storage keeps application data isolated; apps own their scoring and other feature logic. Every asynchronous action waits for a host acknowledgement.
import { canUseHostCapability } from '@w3booster/sdk';
client.host.lifecycle.subscribe(host => {
setCompactEnabled(canUseHostCapability(host, 'window:open'));
});
await client.host.openWindow({
path: '?view=compact',
width: 520,
height: 620
});
const saved = await client.host.setSetting(
'observer.layout',
'wide',
{ signal, timeout: 3000 }
);Use host.can(capability) for imperative code or the reactive host lifecycle for UI. Capability discovery distinguishes pending, known, legacy, and unavailable hosts. Setting writes are serialized globally so overlapping parent and child paths preserve request order. Action cancellation raises AbortError; rejected execution raises HostActionError; missing or timed-out hosts raise ConnectionError.
Framework integration
Use the store your framework already understands.
The SDK has no frontend framework dependency. @w3booster/sdk/store creates stable derived stores and observer-style subscribables for Angular, RxJS, Vue, Svelte, or plain JavaScript; a small React subpath implements the complete useSyncExternalStore contract.
@w3booster/sdk/storeconst status = createSelectorStore(
runtime.lifecycle,
snapshot => snapshot.status
);
status.subscribe(renderStatus, { signal });
@w3booster/sdk/reactconst store = createReactStore(client.lifecycle, {
getServerSnapshot: () => ({
status: 'idle', state: null,
isSynchronized: false, error: null, retry: null
})
});
const snapshot = useSyncExternalStore(
store.subscribe,
store.getSnapshot,
store.getServerSnapshot
);
toSignal()const lifecycle = toSignal(
createSubscribable(runtime.lifecycle),
{ initialValue: runtime.lifecycle.get() }
);
Errors and diagnostics
Separate startup failure from runtime issues.
Catch permanent startup errors around start(). Subscribe to structured issue events for recoverable connection, protocol, recorder, and listener problems that should not turn healthy match data into a failed UI.
import {
classifyW3BoosterError,
createClient
} from '@w3booster/sdk';
const client = createClient({ clientId: 'your_app_id' });
client.on('issue', issue => {
console.warn(issue.source, issue.recoverable, issue.error);
});
try {
await client.start();
} catch (error) {
const info = classifyW3BoosterError(error);
switch (info.kind) {
case 'permission': showOpenFromW3Booster(); break;
case 'connection':
if (info.code === 'APPLICATION_DEFINITION_MISMATCH') showRedeployRequired();
else showOffline(info.code);
break;
case 'abort': break;
default: report(info.error);
}
}Open the installed, enabled app from W3Booster or renew its local session.
Inspect stable codes such as UNAVAILABLE, CONFIGURATION, APPLICATION_DEFINITION_MISMATCH, STARTUP_TIMEOUT, or STATE_TIMEOUT. A startup timeout covers transport opening, retry backoff, and readiness; a state timeout means an established client did not receive the requested state. Definition mismatches are permanent until the binding is regenerated and redeployed.
Use the code and details. Recoverable invalid data resynchronizes; permanent incompatibility closes the stream.
The authenticated workspace received an action but rejected its execution.
Publish
Private first. Reviewed before discovery.
- Complete the store listingAdd a clear description, surfaces, screenshots, source or homepage links, and requested scopes.
- Test every surfaceCheck application, stream, and in-game behavior; disabled states; reconnects; and missing optional data.
- Request reviewW3Booster reviews the record, hosted behavior, scope use, and user experience.
- Make it publicAfter approval, users can discover and install it from the App Store. Metadata edits do not require another review.
Installation and enabling are separate platform states. Disabling preserves the app in the user’s library; uninstalling removes the grant. Revocation closes affected streams immediately.
Package reference
Focused entry points, one compatibility policy.
@w3booster/sdkClient, lifecycle, hydrated data model, events, host facade, errors.
@w3booster/sdk/selectorsPure match, player, team, resource, game-context, and identity derivations.
@w3booster/sdk/appGenerated application bindings and managed application runtimes.
@w3booster/sdk/settingsSchema validation, defaults, resolution, and binding generation.
@w3booster/sdk/standard-gameLightweight Warcraft III rules and presentation helpers.
…/standard-game/iconsClassic/Reforged asset URLs and match-aware resolvers.
…/standard-game/cooldownsAbility cooldown metadata and state derivation.
@w3booster/sdk/assetsHosted shared-asset and country-flag URL helpers.
@w3booster/sdk/reactDependency-free adapters for React external stores and selectors.
@w3booster/sdk/storeFramework-neutral selector stores, memoized selectors, and observer-style subscribables.
@w3booster/sdk/testingDemo transports and custom transport types for tests.
@w3booster/sdk/compositor authenticates the stable browser source and loads enabled overlay apps. Ordinary applications do not import it and never receive browser-source credentials.