Skip to main content

Building a Wrapper

A wrapper is the host application that loads the game iframe and bridges communication between the game, the casino, the RGS and tracking systems. The building blocks live in the @kalamba/sdk/wrapper entry point.

Entry point

import {
KalambaSdkWrapper,
SdkConfigManager,
type CompatibilityOptions,
type CompatibilityEntry,
type CompatibilityCheckResult,
} from '@kalamba/sdk/wrapper'
  • KalambaSdkWrapper — the wrapper core: message routing, state, free rounds, compatibility checks.
  • SdkConfigManager — abstract base class providing SDK configuration; subclass it to supply your own UI configuration.
  • CompatibilityOptions, CompatibilityEntry, CompatibilityCheckResult — types for version compatibility.

Minimal setup

Subclass SdkConfigManager and implement getConfig(): Promise<SdkConfig['ui']>. The base constructor fills this.config with defaults; getConfig returns the final UI configuration used once the game opens.

import { KalambaSdkWrapper, SdkConfigManager } from '@kalamba/sdk/wrapper'
import type { SdkConfig } from '@kalamba/sdk'

class MyConfigManager extends SdkConfigManager {
async getConfig(): Promise<SdkConfig['ui']> {
// e.g. fetch operator settings and merge over this.config.ui
return this.config.ui
}
}

const iframe = document.querySelector<HTMLIFrameElement>('#game')!

const wrapper = new KalambaSdkWrapper({
messagePort: iframe.contentWindow!,
plugins: {
casino: [MyCasinoPlugin],
rgs: MyRgsPlugin,
telemetry: [{ plugin: MyTelemetryPlugin, trackers: [MyTracker] }],
},
sdkConfigManager: MyConfigManager,
compatibility: {
version: WRAPPER_VERSION,
manifest: [{ minSdk: '3.0.0' }],
onCheck: (result, proceed) => proceed(),
},
})

Note that sdkConfigManager and all plugin entries are classes, not instances — the wrapper instantiates them itself, passing in its config, SDK config and state.

Plugins

The plugins option has this shape:

{
casino: CasinoPluginClass[] // zero or more casino plugin classes
rgs: RgsPluginClass // exactly one RGS plugin class
telemetry: { // zero or more telemetry entries
plugin: TelemetryPluginClass
trackers: TrackerClass[]
}[]
}

The SDK only defines the plugin interface; it does not ship implementations. Every wrapper provides its own plugins for casino integration, RGS and tracking. See Plugins for how to write them.

State and configuration

  • wrapper.store — a zustand vanilla store holding WrapperState. Use store.getState(), store.setState() and store.subscribe().
  • wrapper.state — a proxy over the store for direct property access: reading wrapper.state.balance calls getState(), assigning wrapper.state.updateBalance = false calls setState().
  • wrapper.config — a proxy over the WrapperConfig (game name/version, showErrors, showFreeRounds, showPromoPanel, showRealityCheck, showBars, skipErrors). It is populated from the game's configure message.
  • wrapper.sdkConfig — the SdkConfig held by the config manager.

WrapperState fields:

type WrapperState = {
isSdkConfigured: boolean
balance: number
bet: { base: number; multiplier: number }
updateBalance: boolean
openGameResponse?: OpenGameResponse['contract']
lastPlayResponse?: PlayResponse['contract']
freeRoundId?: string
playBlockers: Promise<void>[]
}

Messaging

wrapper.on(message, listener, options?) subscribes to any message (optionally domain-prefixed, e.g. 'sdk:configure', 'rgs:playResponse', or unprefixed to match any domain) and returns an unsubscribe function. wrapper.send(message, payload?) broadcasts a wrapper-domain message to all plugins and posts it to the game via messagePort.

Payload injection

wrapper.injectPayload(source, payloadProvider) registers extra data merged into every outgoing play request. The provider is either a static object or a (possibly async) function receiving the play payload:

const unregister = wrapper.injectPayload('mySource', playPayload => ({
operatorData: { token: getToken() },
}))
// later
unregister()

Registering the same source again replaces the previous provider. The wrapper uses this mechanism itself for free rounds (freeRoundId), and passes injectPayload to casino plugins.

Free rounds

The wrapper handles free rounds contained in openGame/play responses automatically: handleFreeRounds(freeRound) dispatches based on status (ACTIVE activates, PENDING sends an offer, FINISHED completes and restores bets), and activateFreeRounds(freeRound) locks bets to the free round configuration and injects the freeRoundId into play requests. See Free Rounds.

Wake lock

requestWakeLock() / releaseWakeLock() wrap the Screen Wake Lock API. The wrapper acquires the lock on playCycleStart, releases it on playCycleEnd, and re-acquires it when the page becomes visible again. Failures are silently ignored on unsupported browsers.

Version compatibility

Each wrapper release declares which SDK versions it supports. When the host serves wrapper releases from versioned URLs, an incompatible wrapper can redirect the game to an older, still-compatible wrapper version — or show an error where no redirect is possible.

The wrapper is configured with CompatibilityOptions:

interface CompatibilityEntry {
wrapper?: string // wrapper version; absent on unreleased entries
minSdk: string
eol?: string // ISO date after which this wrapper version is no longer supported
}

interface CompatibilityOptions {
version: string
manifest: CompatibilityEntry[] // first entry is the current wrapper
onCheck: (result: CompatibilityCheckResult, proceed: () => void) => void
}

When the game sends configure, the wrapper compares the game's sdkVersion (semver) against the first manifest entry:

  • No manifest entry, no sdkVersion, or minSdk: 'newest'{ status: 'compatible' }.
  • sdkVersion >= minSdk{ status: 'compatible', sdkVersion, eol } (eol lets you warn about an approaching end-of-life).
  • Otherwise → { status: 'incompatible', sdkVersion, fallback }, where fallback is the newest older manifest entry whose minSdk the game satisfies and whose eol has not passed. fallback may be undefined if none qualifies.

Your onCheck callback decides what to do: call proceed() to send wrapperConfigured and let the game continue, redirect to fallback.wrapper, or show an error.