/** * Durable-state stores: host election tracking ({@link HostTracker}), the * per-user app save blob ({@link SaveStateStore}), and typed avatar state * ({@link AvatarStateStore}). These wrap the GraphQL surfaces with local * caches and typed codecs — independent types from your replication state, * as durable and realtime payloads rarely share a layout. */ import { type StateCodec } from './codec.js'; import type { WorldSessionContext } from './session.js'; /** Options for {@link attachHostTracker}. */ export interface HostTrackerConfig { /** * The authenticated user's id (or a getter), used to compute * {@link HostTracker.isHost}. Without it only `hostUserId` is tracked. */ myUserId?: string | (() => string | null); /** Heartbeat cadence in ms (also keeps you host-eligible). Defaults to 3000. */ intervalMs?: number; /** Send one heartbeat immediately on attach. Defaults to true. */ heartbeatImmediately?: boolean; } /** * The SDK-managed **host election tracker**: heartbeats on the session * ticker (keeping this client host-eligible), caches the elected host, and * fires {@link onHostChanged} on transitions. Election is informational — * gate authoritative writes with `is_host` invoke policies server-side. * Transient heartbeat failures keep the last known host. */ export declare class HostTracker { private readonly ctx; private readonly config; private hostUserIdValue; private readonly listeners; constructor(ctx: WorldSessionContext, config?: HostTrackerConfig); /** The elected host's user id (null until the first successful beat). */ get hostUserId(): string | null; /** Whether the configured user is the elected host. */ get isHost(): boolean; /** Fired when the elected host changes (including the first election). @returns off. */ onHostChanged(listener: (hostUserId: string | null) => void): () => void; /** Send one heartbeat now and apply the result. */ beat(): Promise; } /** Attach a {@link HostTracker}. Prefer the `host` config key. */ export declare function attachHostTracker(ctx: WorldSessionContext, config?: HostTrackerConfig): HostTracker; /** Options for {@link attachSaveState}. */ export interface SaveStateConfig { /** Codec for the save blob. Defaults to JSON. */ codec?: StateCodec; /** * Debounced autosave: when set, a dirty value persists automatically at * most once per this many ms (on the session ticker). Defaults to off. */ autosaveMs?: number | false; /** Clock override for tests. Defaults to `Date.now`. */ now?: () => number; } /** * The SDK-managed **save state**: a typed local cache over the per-user * per-app `client.state` blob. `load()` hydrates it, `set()` updates it (and * autosave persists it when configured), `save()` persists on demand. * The type is intentionally independent of your replication state — durable * saves and 5 Hz poses are different data. */ export declare class SaveStateStore { private readonly ctx; private current; private dirtyFlag; private saving; private lastSavedAtValue; private readonly codec; private readonly now; constructor(ctx: WorldSessionContext, config?: SaveStateConfig); /** The cached typed save (null before {@link load}/{@link set}). */ get value(): T | null; /** Whether the cache has unsaved changes. */ get dirty(): boolean; /** Local time of the last successful save. */ get lastSavedAt(): number | null; /** Fetch and decode the server copy into the cache (null when none). */ load(): Promise; /** Update the cached save and mark it dirty (autosave persists it). */ set(value: T): void; /** Merge a partial update into the cached save (object saves only). */ patch(patch: Partial): void; /** Persist the cached save now. No-op when nothing is cached. */ save(): Promise; } /** Attach a {@link SaveStateStore}. Prefer the `save` config key. */ export declare function attachSaveState(ctx: WorldSessionContext, config?: SaveStateConfig): SaveStateStore; /** Options for {@link attachAvatarState}. */ export interface AvatarStateConfig { /** The avatar to bind. Omit to bind the caller's first avatar on load. */ avatarId?: string; /** Codec for the public (anyone-readable) avatar state. Defaults to JSON. */ publicCodec?: StateCodec; /** Codec for the private (owner-only) avatar state. Defaults to JSON. */ privateCodec?: StateCodec; /** Codec for the per-app avatar state. Defaults to JSON. */ appCodec?: StateCodec; } /** * The SDK-managed **avatar state**: typed, cached views of one avatar's * public / private / per-app state blobs, each with its own codec (three * independent types — public profiles, private inventory-ish data, and * app-specific progress rarely share a shape). */ export declare class AvatarStateStore { private readonly ctx; private avatarIdValue; private publicValue; private privateValue; private appValue; private readonly publicCodec; private readonly privateCodec; private readonly appCodec; constructor(ctx: WorldSessionContext, config?: AvatarStateConfig); /** The bound avatar id (null before {@link load} resolves a default). */ get avatarId(): string | null; /** The cached decoded public state. */ get publicState(): TPublic | null; /** The cached decoded private state (owner-only). */ get privateState(): TPrivate | null; /** The cached decoded per-app state. */ get appState(): TApp | null; /** * Hydrate the cache: resolves the avatar (the caller's first when no * `avatarId` was configured), decodes its public/private state, and * fetches this app's avatar state. */ load(): Promise; /** Write the public and/or private state (typed) and update the cache. */ setIdentityState(input: { publicState?: TPublic; privateState?: TPrivate; }): Promise; /** Write this app's avatar state (typed) and update the cache. */ setAppState(value: TApp): Promise; private requireAvatar; private decodeWith; } /** Attach an {@link AvatarStateStore}. Prefer the `avatar` config key. */ export declare function attachAvatarState(ctx: WorldSessionContext, config?: AvatarStateConfig): AvatarStateStore; //# sourceMappingURL=durable.d.ts.map