Lua SDK
Lua client for the beachcomber daemon. Communicates over a Unix domain socket using newline-delimited JSON. Published on LuaRocks.
Designed for Neovim plugin authors (via vim.uv) but also works in standalone Lua scripts via luasocket.
Requirements
- Lua 5.1+ or LuaJIT (Neovim-compatible)
- Inside Neovim: no extra dependencies (
vim.uv/vim.loopused automatically) - Outside Neovim: luasocket 3.0+
Installation
LuaRocks
luarocks install --local libbeachcomber
Manual (Neovim)
Add sdks/lua to your package.path, or copy the beachcomber/ directory somewhere on your runtimepath.
Quick start
local comb = require('beachcomber')
-- Connect (auto-detects vim.uv or luasocket)
local client, err = comb.connect()
if not client then
error("beachcomber: " .. err)
end
-- get a single field
local result = client:get('git.branch', '/my/repo')
if result:is_hit() then
print(result.data) -- "main"
print(result.age_ms) -- 1234
print(result.stale) -- false
end
-- get a full provider (returns object)
local r = client:get('git', '/my/repo')
if r:is_hit() then
print(r:get_str('branch')) -- "main"
end
-- poke (force recompute)
client:poke('git', '/my/repo')
-- persistent context — path applies to all subsequent queries
client:set_context('/my/repo')
local r2 = client:get('git.branch') -- uses context path
-- list providers
local providers = client:list()
for _, p in ipairs(providers) do
print(p.name, p.global, table.concat(p.fields, ', '))
end
-- daemon status
local s = client:status()
print(s.cache_entries)
client:close()
Custom socket path
local client = comb.connect({ socket_path = '/run/user/1000/beachcomber/sock' })
API reference
comb.connect([opts]) → Client | nil, error
Connect to the daemon and return a Client.
| Option | Type | Description |
|---|---|---|
socket_path | string | Override automatic socket discovery |
backend | module | Override the socket backend (advanced) |
Returns nil, error_message on failure.
Client:get(key [, path]) → Result | nil, error
Read a cached value. key is "provider" or "provider.field". path overrides any connection context.
Client:poke(key [, path]) → true | nil, error
Force the daemon to recompute key.
Client:set_context(path) → true | nil, error
Set the default working-directory path for this connection.
Client:list() → table[] | nil, error
Return an array of provider descriptors: { name = "git", global = false, fields = {"branch", "dirty", ...} }.
Client:status() → table | nil, error
Return the daemon's internal status (scheduler queue depth, cache entries, etc.).
Client:close()
Close the underlying socket.
Result
| Field | Type | Description |
|---|---|---|
data | any | Cached value; nil on a miss |
age_ms | number | Age of the cached value in milliseconds |
stale | boolean | True when past TTL but no fresh value yet |
| Method | Returns | Description |
|---|---|---|
result:is_hit() | boolean | True when data ~= nil |
result:get_str(field) | string|nil, error | Get a string field from object data |
Socket path discovery
The SDK looks for the daemon socket in this order:
$XDG_RUNTIME_DIR/beachcomber/sock(ifXDG_RUNTIME_DIRis set and the path exists)$TMPDIR/beachcomber-<uid>/sock/tmp/beachcomber-<uid>/sock
Neovim example
-- In your statusline plugin or lualine component:
local ok, comb = pcall(require, 'beachcomber')
if not ok then return '' end
local client = comb.connect()
if not client then return '' end
-- Keep a persistent client across calls for best performance
vim.api.nvim_create_autocmd('VimLeavePre', {
callback = function() client:close() end,
})
local function git_branch()
local result, err = client:get('git.branch', vim.fn.getcwd())
if not result or not result:is_hit() then return '' end
return ' ' .. result.data
end
All socket I/O in the vim.uv backend is synchronous so git_branch() can be called directly from a statusline evaluation without scheduling callbacks.