Types
What Ledger exports for strict mode.
Everything here is exported off the top level module, so Ledger.Op, Ledger.Store and so on.
Op
type Op = {
Id: string,
Kind: string,
[any]: any,
}One change. Id and Kind are Ledger's, everything else is yours.
Id, Kind and OnceAt are reserved. Passing any of them in Fields warns and gets overwritten.
Once is yours to set, and it's what makes the op apply at most one time on that key. See
Once.
Every method that can fail answers (value?, Reason?) or (boolean, Reason?). Nothing in Ledger
throws into a Future, so Wait never comes back empty on you. See
Reasons.
Reducer
type Reducer<S> = (State: S, Op: Op) -> S?See Writing a reducer.
Reason
type Reason =
"Refused" | "Busy" | "Spent" | "Unresolved" | "Closed"
| "Backlog" | "Full" | "Invalid" | "Behind"Compare against Ledger.Reason.Refused and friends rather than the literals. See
Reasons.
KeyLike
type KeyLike = number | stringA UserId on a player store, a key string on an entity store. Ledger checks which one the store wants and throws with what it expected.
KeysMode
type KeysMode = "Player" | "String"Config
type Config<D> = {
read Name: string,
read Reducer: (State: D, Op: Op) -> unknown,
read Default: D,
read Balance: string?,
read Migrations: { Migration }?,
read Keys: KeysMode?,
read OnLoadFailed: ((Player: Player, Why: Reason) -> boolean)?,
}What Ledger.New takes. See Ledger.New.
Migration
type Migration =
((State: any) -> any)
| { Apply: (State: any) -> any, Compatible: boolean? }A plain function, or a table when you need Compatible. See
Migrations.
TxLeg
type TxLeg = {
Store: Store<any>?,
UserId: number?,
Key: string?,
Kind: string,
Fields: { [any]: any }?,
}One leg of a transaction. Store defaults to the one you called Tx on. Use UserId for a player
store and Key for a string keyed one.
HoldOptions
type HoldOptions = {
Hold: number?,
To: KeyLike?,
}The options table Store:Reserve takes last. Hold is how long to keep the
units, in seconds, and defaults to 15 minutes. To names the key the units are going to, which is
what Grant needs. See Reservations.
HistoryEntry
type HistoryEntry = {
Version: string,
At: number,
Deleted: boolean,
}At is Unix seconds. Version is what you hand to PeekVersion.
Future
What every method that touches the datastore hands back. The work starts at the call, :Wait()
parks your thread until it's done.
local Job = Store:Peek(UserId)
DoSomethingElse()
local State = Job:Wait()See Future for timeouts, error handling, and why a failed read gives you
nil.
Observer
What Session:Observe() and Store:Stale() hand back. Subscribe for every change that goes
through, and chain with Map, Filter and Changed.
Session:Observe():Subscribe(function(State)
UpdateHud(Player, State)
end)See Observer.
Typing your own state
Write the state type yourself and let the store carry it:
export type Profile = {
Gold: number,
Items: { string },
}
local function Reducer(State: Profile, Op: Ledger.Op): Profile?
-- ...
end
local Store: Ledger.Store<Profile> = Ledger.New({
Name = "PlayerData",
Default = { Gold = 100, Items = {} } :: Profile,
Reducer = Reducer,
})Session:Get() then gives you a Profile, and the reserved fields stay out of your type. They're
there at runtime, your reducer passes them through with table.clone, and you don't have to declare
them.