Skip to content

API Reference ​

Root Module ​

luau
local lsd = require("./lsd")

lsd.store(config) ​

Creates and returns a new Store instance.

luau
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.

luau
lsd.shutdown()

lsd.telemetry(callback) ​

Registers a telemetry sink callback that receives batched metrics and event logs every 10 seconds and on shutdown.

luau
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):

FieldTypeDefaultDescription
namestringRequiredDataStore namespace for player profiles.
templateTRequiredDefault data structure for new players and missing keys.
validatePredicate<T> | Validator<T>nilPlain (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) -> ()nilHandler called when profile loading fails after all retries.
on_global_update(player, update, data) -> TnilHandler for queued cross-server global updates.
steal_on_lockedbooleanfalseAutomatically steal session lock if held by another server.
max_load_attemptsnumber3Maximum number of attempts before triggering on_load_error.
datastore_serviceDataStoreService?game:GetService("DataStoreService")Override the DataStore service (tests, dependency injection).
memorystore_serviceMemoryStoreService?game:GetService("MemoryStoreService")Override the MemoryStore service (tests, dependency injection).

Store Instance ​

The examples below use this store:

luau
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.

luau
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.

luau
store.steal(player)

store.unload(player) ​

Closes the player's session, clears processed updates, saves pending changes, and releases ownership.

luau
store.unload(player)
  • Returns: ()

store.get(player) ​

Returns the player's current profile. The returned table is deeply frozen.

luau
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.

luau
local success = store.update(player, function(data)
  data.coins += 50
end)
  • Returns: boolean (true if updated, false if cancelled)

store.save(player) ​

Requests an immediate save for the player's session without waiting for autosave.

luau
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.

luau
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.

luau
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.

luau
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.

luau
local data = store.peek(user_id)
  • Returns: T?

store.transact(participants, transform) ​

Executes an atomic transaction across multiple online or offline participants.

luau
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? (return false to cancel)
  • Returns: boolean

store.send_global_update(user_id, data) ​

Appends an update payload to the target player's queue.

luau
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.

luau
store.erase(user_id)

store.list_loaded() ​

Returns an array of all Player instances with active sessions in this store.

luau
local active_players = store.list_loaded()
  • Returns: { Player }

store.get_session_info(player) ​

Returns metadata about the active session, or nil if not loaded.

luau
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.

luau
store.close()
  • Returns: ()
  • Gives dirty sessions up to 15 seconds to finish saving before returning.

Validator API ​

luau
local validator = lsd.validator

Primitive Rules ​

  • validator.boolean: Booleans (true / false).
  • validator.number: Finite numbers (rejects NaN and Inf).
  • 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): Accepts nil or 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 for validator.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 with pcall, returning (boolean, string?).
  • validator.assert_valid(checker, value, context): Asserts validity or throws "{context}: {message}".

Last updated:

Released under the MIT License.