API Reference
Root Module
local lsd = require("./lsd")lsd.store(config)
Creates and returns a new Store instance.
local store = lsd.store({
name = "player_data",
template = { ... },
})- Parameters:
config: StoreConfig<T> - Returns:
Store<T>
lsd.shutdown()
Closes all active stores and flushes pending data and telemetry. This is registered automatically with game:BindToClose.
lsd.shutdown()lsd.telemetry(callback)
Registers a telemetry sink callback that receives batched metrics and event logs every 10 seconds and on shutdown.
local disconnect = lsd.telemetry(function(batch)
for _, event in batch.events do
if event.severity == "error" then
warn(`[lsd] {event.name}`, event.fields)
end
end
end)- Parameters:
callback: (batch: TelemetryBatch) -> () - Returns:
disconnect: () -> ()
lsd.validator
Namespace containing the schema definition and validation engine. See Validator API.
Store Configuration
Passed to lsd.store(config):
| Field | Type | Default | Description |
|---|---|---|---|
name | string | Required | DataStore namespace for player profiles. |
template | T | Required | Default data structure for new players and missing keys. |
validate | Predicate<T> | Validator<T> | nil | Plain (data: T) -> boolean predicate, or a compiled schema returning (boolean, string?). |
migrations | { Migration<T> } | {} | Ordered list of versioned schema upgrades. |
on_load_error | (player, reason, message) -> () | nil | Handler called when profile loading fails after all retries. |
on_global_update | (player, update, data) -> T | nil | Handler for queued cross-server global updates. |
steal_on_locked | boolean | false | Automatically steal session lock if held by another server. |
max_load_attempts | number | 3 | Maximum number of attempts before triggering on_load_error. |
datastore_service | DataStoreService? | game:GetService("DataStoreService") | Override the DataStore service (tests, dependency injection). |
memorystore_service | MemoryStoreService? | game:GetService("MemoryStoreService") | Override the MemoryStore service (tests, dependency injection). |
Store Instance
The examples below use this store:
local store = lsd.store({
name = "player_data",
template = {
coins = 0,
inventory = {},
},
})store.load(player)
Yields while acquiring exclusive session ownership and loading the player's profile.
store.load(player)- Returns:
() - Raises: if the player is already loaded, or the store has been closed.
- Returns immediately if the player has already left the game.
store.steal(player)
Immediately steals session ownership from any remote server and loads the player's profile.
store.steal(player)store.unload(player)
Closes the player's session, clears processed updates, saves pending changes, and releases ownership.
store.unload(player)- Returns:
()
store.get(player)
Returns the player's current profile. The returned table is deeply frozen.
local data = store.get(player)- Returns:
T(deeply frozen) - Raises: if the player has no active session.
store.update(player, transform)
Deep-copies the profile and executes transform(data) on the mutable clone. If transform returns false, changes are discarded.
local success = store.update(player, function(data)
data.coins += 50
end)- Returns:
boolean(trueif updated,falseif cancelled)
store.save(player)
Requests an immediate save for the player's session without waiting for autosave.
store.save(player)- Returns:
() - Raises: if the session is closed or a transaction is active.
store.is_loaded(player)
Returns whether the player currently has an active, loaded session on this server.
local loaded = store.is_loaded(player)- Returns:
boolean
store.is_locked(user_id)
Checks whether an active distributed lock is held for the given user ID.
local locked = store.is_locked(user_id)- Returns:
boolean - Note: performs a MemoryStore read, which consumes request budget and can raise if the probe fails.
store.on_close(player, callback)
Registers a callback invoked when the player's session closes. Returns a disconnect function.
local disconnect = store.on_close(player, function(reason)
print(`Session closed: {reason}`)
end)Close Reasons: "manual" | "lock_lost" | "shutdown" | "stolen" | "save_unknown" | "transaction_unknown"
store.peek(user_id)
Reads offline data from DataStore without acquiring a lock. Applies template defaults and pending migrations.
local data = store.peek(user_id)- Returns:
T?
store.transact(participants, transform)
Executes an atomic transaction across multiple online or offline participants.
local success = store.transact({ player_a, player_b }, function(states)
states[tostring(player_a.UserId)].coins -= 10
states[tostring(player_b.UserId)].coins += 10
end)- Parameters:
participants: { Player | number | string }transform: (states: { [string]: T }) -> ...boolean?(returnfalseto cancel)
- Returns:
boolean
store.send_global_update(user_id, data)
Appends an update payload to the target player's queue.
store.send_global_update(user_id, { reward = "gift_box" })- Returns:
() - Note: only the 50 newest updates per player are kept.
store.erase(user_id)
Permanently removes all stored records, shards, and queued updates for a player. Fails if a session is currently active.
store.erase(user_id)store.list_loaded()
Returns an array of all Player instances with active sessions in this store.
local active_players = store.list_loaded()- Returns:
{ Player }
store.get_session_info(player)
Returns metadata about the active session, or nil if not loaded.
local info = store.get_session_info(player)- Returns:
{ key: string, is_dirty: boolean, is_saving: boolean, open_time: number, last_save_time: number, version: number }?
store.close()
Closes all sessions in this store and flushes pending saves.
store.close()- Returns:
() - Gives dirty sessions up to 15 seconds to finish saving before returning.
Validator API
local validator = lsd.validatorPrimitive Rules
validator.boolean: Booleans (true/false).validator.number: Finite numbers (rejectsNaNandInf).validator.integer: Finite integers (value % 1 == 0).validator.string: Valid UTF-8 strings.validator.any: Any DataStore-serializable value subtree.
Structure Rules
validator.record(fields): Strict table with fixed string fields.validator.list(item_rule): Contiguous 1-indexed array.validator.map(value_rule): Open dictionary with string keys.validator.optional(rule): Acceptsnilor the inner rule.
Composition Rules
validator.literal(expected): Exact scalar equality match.validator.enum(values): Value must match one of the items in the set.validator.either(...rules): Ordered union; matches first passing rule.validator.where(rule, predicate, [message]): Custom refinement constraint.
Compilation & Execution
validator.create(fields): Shorthand forvalidator.compile(validator.record(fields)).validator.compile(root_rule): Compiles a rule into a validator function(value: T) -> (boolean, string?).validator.check(checker, value): Safely tests a value withpcall, returning(boolean, string?).validator.assert_valid(checker, value, context): Asserts validity or throws"{context}: {message}".