Skip to content

API Types

iKryptonic edited this page May 3, 2026 · 10 revisions

API: Types

Types.luau exports the framework's strict-mode annotation surface: entity types, FSM types, scheduler types, persistence types, logger types, and the structural Orchestrator interface.

Important

Docs audit note (2026-05): This page was re-audited against FSM/Orchestrator/Core/Types.luau. When this page and runtime behavior differ, trust Types.luau for type annotations and the runtime module docs for behavior.


🎯 Overview

--!strict
local Types = require(game:GetService("ReplicatedStorage").RBXStateMachine.Types)

local function attach(entity: Types.BaseEntity, fsm: Types.BaseStateMachine)
    entity:Log({ Level = "INFO", Message = "attached" })
    fsm:Start({ State = "Idle" })
end

Types.luau is about annotation, not runtime construction. Many return values are still any? at runtime and require casts after guards.


πŸ§ͺ Audit highlights / surprising gaps

A few exported types intentionally or accidentally lag the runtime surface:

  1. Types.Orchestrator still includes legacy StartServiceManagerAPI and omits newer runtime helpers like StartServiceManager(), GetServiceManager(), and UnregisterEventBus().
  2. Types.LoggerInstance includes Enabled: boolean, but the current Logger.new() implementation only populates Source and History.
  3. Signal methods return an internal Connection shape, but that Connection type is local-only and not exported by name.

Keep those differences in mind when using Types with --!strict.


🧾 Foundation types

LogLevel

export type LogLevel = "INFO" | "WARN" | "ERROR" | "DEBUG"

BehaviorTreeStatus

export type BehaviorTreeStatus = "Success" | "Failure" | "Running"

Signal

export type Signal = {
    Connect: (self: any, handler: (...any) -> ()) -> any,
    Once: (self: any, handler: (...any) -> ()) -> any,
    Fire: (self: any, ...any) -> (),
    Wait: (self: any) -> ...any,
    Destroy: (self: any) -> (),
}

Disposable

export type Disposable = Instance | RBXScriptConnection | { Destroy: (any) -> () } | () -> ()

Used by entity:Manage(...) and fsm:Manage(...).


πŸ›οΈ Schema + entity types

PropertyDef

export type PropertyDef = {
    Type: string,
    Description: string?,
    Replicate: boolean?,
    Persist: boolean?,
}

BaseEntity

export type BaseEntity = {
    Name: string,
    Instance: Instance,
    IsValid: boolean,
    OwnerId: string?,

    StateUpdated: RBXScriptSignal,
    Destroyed: RBXScriptSignal,

    Log: (self: BaseEntity, params: { Level: LogLevel, Message: string }) -> (),
    DefineSchema: (self: BaseEntity, schema: { [string]: PropertyDef }) -> (),
    SetContext: (self: BaseEntity, key: any, value: any) -> (),
    Manage: (self: BaseEntity, object: any) -> any,
    UpdateEntity: (self: BaseEntity, lockingCallerId: string?) -> boolean,
    ApplyChanges: (self: BaseEntity, changes: { [string]: any }) -> (),
    GetValidProperties: (self: BaseEntity) -> { [string]: PropertyDef },
    Serialize: (self: BaseEntity) -> { [string]: any },
    Deserialize: (self: BaseEntity, data: { [string]: any }) -> (),
    AcquireLock: (self: BaseEntity, callerId: string) -> boolean,
    ReleaseLock: (self: BaseEntity, callerId: string) -> boolean,
    Destroy: (self: BaseEntity) -> (),
}

Annotation example

local function touchEntity(entity: Types.BaseEntity)
    if entity.IsValid then
        entity:UpdateEntity()
    end
end

🧠 FSM types

Transition

export type Transition = {
    TargetState: string,
    Condition: (fsm: any, dt: number) -> boolean,
}

StateObject

export type StateObject = {
    Machine: BaseStateMachine?,
    OnEnter: (any, ...any) -> (),
    OnHeartbeat: ((any, number) -> ())?,
    OnLeave: ((any) -> ())?,
    Transitions: { Transition }?,
}

SubStateConfig

export type SubStateConfig = {
    InitialState: string,
    Transitions: {
        OnCompleted: string,
        OnFailed: string,
        OnCancelled: string?,
    },
    StoreReference: string?,
}

State

export type State = ((any, ...any) -> (() -> ())?) | StateObject

BaseStateMachine

export type BaseStateMachine = {
    Id: string,
    Name: string,
    Context: { [any]: any },
    IsActive: boolean,
    State: string?,
    StateDuration: number,
    TotalDuration: number,
    WaitSpan: number,
    TransitionCount: number,
    Priority: number,

    Completed: RBXScriptSignal,
    Failed: RBXScriptSignal,
    Cancelled: RBXScriptSignal,
    StateChanged: RBXScriptSignal,

    Start: (self: BaseStateMachine, params: { State: string, Args: {any}? }) -> (),
    ChangeState: (self: BaseStateMachine, params: { Name: string, Args: {any}? }) -> (),
    Finish: (self: BaseStateMachine) -> (),
    Fail: (self: BaseStateMachine, reason: string) -> (),
    Cancel: (self: BaseStateMachine) -> (),
    Destroy: (self: BaseStateMachine) -> (),
    Manage: (self: BaseStateMachine, object: Disposable) -> Disposable,
    AddSubMachine: (self: BaseStateMachine, name: string, subMachineClass: any, config: SubStateConfig) -> (),
    AddState: (self: BaseStateMachine, name: string, state: State, validOutcomes: {string}?) -> (),
    Log: (self: BaseStateMachine, params: { Level: LogLevel, Message: string, Data: any? }) -> (),
    OnCleanup: (self: BaseStateMachine) -> (),
    RegisterStates: (self: BaseStateMachine) -> (),
}

Annotation example

local function cancelIfRunning(fsm: Types.BaseStateMachine)
    if fsm.IsActive then
        fsm:Cancel()
    end
end

⚑ Scheduler types

Task

export type Task = {
    Name: string,
    Action: () -> (),
    NextRun: number,
    Delay: number,
    IsRecurringTask: boolean,
    Priority: number,
    Event: string,
    RunCount: number,
    TotalRunTime: number,
    MaxRunTime: number,
    CreationTime: number,
    Reset: () -> (),
}

ScheduleTaskParams

export type ScheduleTaskParams = {
    TaskName: string,
    TaskAction: () -> (),
    TaskExecutionDelay: number?,
    IsRecurringTask: boolean?,
    Priority: number?,
    Event: string?,
}

Scheduler

export type Scheduler = {
    Settings: any,
    History: {any},
    LastFrameStats: { FrameTime: number, TaskCount: number, Budget: number },

    Initialize: (self: Scheduler, settings: any?) -> Scheduler,
    Clear: (self: Scheduler) -> (),
    Schedule: (self: Scheduler, params: ScheduleTaskParams) -> Task?,
    Deschedule: (self: Scheduler, name: string) -> (),
    GetTask: (self: Scheduler, name: string) -> Task?,
    GetTaskCount: (self: Scheduler) -> number,
    ExecuteTask: (self: Scheduler, taskOrName: string | Task) -> (),
    GetSyncData: (self: Scheduler) -> any,
    ResetTask: (self: Scheduler, name: string) -> (),
    GenerateKey: (self: Scheduler) -> string,
    Start: (self: Scheduler) -> (),
}

πŸ’Ύ Persistence + DataStore types

RetryConfig

export type RetryConfig = {
    enabled: boolean?,
    retries: number?,
    baseDelay: number?,
    jitter: number?,
}

DataStore

export type DataStore = {
    CachingTime: number,
    RetryConfig: RetryConfig,

    SetRetryConfig: (self: DataStore, config: RetryConfig?) -> (),
    EnableRetry: (self: DataStore, enabled: boolean) -> (),
    SetCachingTime: (self: DataStore, TimeInSeconds: number) -> (),
    GetDBName: (self: DataStore) -> string,
    GetAsync: (self: DataStore, Key: string) -> (boolean, any, string?),
    SetAsync: (self: DataStore, Key: string, value: any) -> (boolean, nil, string?),
    UpdateAsync: (self: DataStore, Key: string, transformFunction: (any) -> any) -> (boolean, any, string?),
    IncrementAsync: (self: DataStore, Key: string, delta: number) -> (boolean, number?, string?),
    RemoveAsync: (self: DataStore, Key: string) -> (boolean, any, string?),
    GetSortedAsync: ((self: DataStore, ascending: boolean, pagesize: number, minValue: number?, maxValue: number?) -> (boolean, any, string?))?,
    OnUpdate: any,
}

DataStoreHandler

export type DataStoreHandler = {
    ResetAccessCount: () -> (),
    get: (databaseName: string, scope: string?, orderedBool: boolean?) -> DataStore,
}

PersistenceConfig

export type PersistenceConfig = {
    DataStoreName: string,
    Scope: string?,
    KeyPrefix: string?,
    DataStoreHandler: any,
    EnableRetry: boolean?,
    RetryConfig: any?,
}

EntityPersistence

export type EntityPersistence = {
    Save: (self: EntityPersistence, entity: any, key: string, metadata: { [string]: any }?) -> (boolean, string?),
    Load: (self: EntityPersistence, entity: any, key: string) -> (boolean, { [string]: any }?, string?),
    Delete: (self: EntityPersistence, key: string) -> (boolean, string?),
    Update: (self: EntityPersistence, key: string, mutateFn: (data: { [string]: any }) -> { [string]: any }) -> (boolean, string?),
}

πŸͺ΅ Logger types

LogEntry

export type LogEntry = {
    Timestamp: number,
    OperationId: string?,
    Level: LogLevel,
    Message: string,
    Data: any?,
}

LoggerInstance

export type LoggerInstance = {
    Source: string,
    Enabled: boolean,
    History: {LogEntry},
    Log: (self: LoggerInstance, params: { Level: LogLevel, Message: string, OperationId: string?, Data: any? }) -> (),
    GetHistory: (self: LoggerInstance) -> {LogEntry},
}

LoggerStatic

export type LoggerStatic = {
    new: (params: { Name: string? }) -> LoggerInstance,
}

🏭 Factory types

EntityFactory

export type EntityFactory = {
    Register: (entityName: string, entityClassDefinition: any) -> (),
    get: (entityName: string, params: { [any]: any }?) -> any,
}

StateMachineInstance

export type StateMachineInstance = {
    Id: string,
    Name: string,
    State: string,
    Context: {[any]: any},
    WaitSpan: number,
    TransitionCount: number,

    Start: (self: StateMachineInstance, params: { State: string, Args: {any}? }) -> (),
    ChangeState: (self: StateMachineInstance, params: { Name: string, Args: {any}? }) -> (),
    AddState: (self: StateMachineInstance, name: string, state: any, validOutcomes: {string}?) -> (),
    AddSubMachine: (self: StateMachineInstance, name: string, subMachineClass: any, config: any) -> (),
    Manage: (self: StateMachineInstance, object: Disposable) -> (),
    Finish: (self: StateMachineInstance) -> (),
    Fail: (self: StateMachineInstance, reason: string) -> (),
    Cancel: (self: StateMachineInstance) -> (),
    Destroy: (self: StateMachineInstance) -> (),
}

StateMachineClass

export type StateMachineClass = {
    Extend: (definition: {[string]: any}) -> StateMachineClass,
    new: (params: {[string]: any}) -> StateMachineInstance,
    [any]: any,
}

StateMachineFactory

export type StateMachineFactory = {
    Register: (stateMachineName: string, class: StateMachineClass) -> (),
    get: (stateMachineName: string) -> StateMachineClass,
}

πŸ•ΈοΈ Orchestrator structural type

Exact export in Types.luau:

export type Orchestrator = {
    Logger: any,
    History: {any},

    RegisterComponents: (self: Orchestrator) -> (),
    CreateEntity: (params: { EntityClass: any, EntityId: string, Context: {any}?, Persistent: boolean?, PersistenceKey: string? }) -> any?,
    CreateStateMachine: (params: { StateMachineClass: any, StateMachineId: string, Context: {any}? }) -> any?,
    RetryStateMachine: (stateMachineId: string) -> (),
    GetStateMachine: (stateMachineId: string) -> any?,
    GetEntity: (entityId: string) -> any?,
    CancelStateMachine: (stateMachineId: string) -> boolean,
    DeleteEntity: (entityId: string) -> (),
    DeleteAllEntities: () -> (),
    CancelAll: () -> (),
    GetEntities: () -> { [string]: any },
    GetStateMachines: () -> { [string]: any },
    InitClientListeners: () -> (),
    StartServiceManagerAPI: () -> (),
    HandleReplication: (entityId: string, changes: {[string]: any}, schema: {[string]: any}) -> (),
    RegisterEventBus: (name: string) -> any,
    GetEventBus: (name: string) -> any?,
    FireEventBus: (name: string, ...any) -> (),
    AwaitEventBus: (name: string, timeout: number?) -> any?,
}

Strict-mode tip

  • cast newer runtime helpers separately when you know they exist, because they are not part of the exported structural type yet

πŸš€ Example annotation patterns

Example 1: Entity + FSM helper

--!strict
local Types = require(path.to.Types)

local function bindEntityFSM(entity: Types.BaseEntity, fsm: Types.BaseStateMachine)
    entity:Manage(fsm.Completed:Connect(function()
        entity:Log({ Level = "INFO", Message = "FSM completed" })
    end))
end

Example 2: Typed scheduler params

local params: Types.ScheduleTaskParams = {
    TaskName = "RebuildIndex",
    TaskAction = function() rebuildIndex() end,
    TaskExecutionDelay = 5,
    IsRecurringTask = true,
    Priority = 2,
    Event = "Heartbeat",
}

Example 3: Retry config annotation

local retry: Types.RetryConfig = {
    enabled = true,
    retries = 3,
    baseDelay = 1,
    jitter = 0.2,
}

⚠️ Edge cases and pitfalls

  1. Structural types are not runtime guarantees. A type can expose a field that the runtime implementation does not currently populate.

  2. Some runtime helpers are missing from Types.Orchestrator. Extend locally or cast when using newer helpers.

  3. Most factory/orchestrator returns are still any?. Guard or cast after existence checks.

  4. Signal connection type is implicit. If you need a named connection type in your own module, define a local structural alias.


πŸ”— See also

🏠 Home


πŸš€ Getting Started


πŸ—οΈ Architecture


πŸ”§ API Reference


πŸ“š Guides


βš™οΈ Advanced


πŸ› οΈ Development


πŸ“– Reference

Clone this wiki locally