Project Awesome project awesome

sravioli/memo.wz

Memoization, caching, and persistent state management.

Package 2 stars GitHub

memo.wz

Awesome Tests Lint Coverage

Memoization, caching, and persistent state for WezTerm plugins and configuration code.

  • Session-scoped memoization cache backed by wezterm.GLOBAL
  • File-persistent key/value store with auto-load/save and async writes
  • Deterministic key generation via serialization and concatenation
  • Configurable TTL, eviction policies, and hit/miss statistics
  • Namespaced cache partitions with scoped keys
  • compute() for automatic memoization of function results

Installation

local wezterm = require "wezterm"

-- from git
local memo = wezterm.plugin.require "https://github.com/sravioli/memo.wz"

-- from a local checkout
local memo = wezterm.plugin.require("file:///" .. wezterm.config_dir .. "/plugins/memo.wz")

Type annotations

Full LuaCATS type annotations are available via wezterm-types. After installing the types, annotate the import to get autocompletion and type checking:

---@type Memo
local memo = wezterm.plugin.require "https://github.com/sravioli/memo.wz"

Usage

-- cache a computed value
memo.cache.set("theme", "tokyonight")
memo.cache.get("theme") -- "tokyonight"

-- memoize a function
local result = memo.cache.compute("expensive", function(x)
  return x * x
end, 42)

-- persistent state across WezTerm restarts
local store = memo.state.new { path = wezterm.home_dir .. "/.local/share/wezterm/my-state.json" }
store:set("last_workspace", "dev")
store:get("last_workspace") -- "dev"

Modules

The plugin exposes three sub-modules via its public API:

local memo = wezterm.plugin.require "https://github.com/sravioli/memo.wz"
memo.cache -- session-scoped memoization cache
memo.key   -- serialization and key generation utilities
memo.state -- file-persistent key/value store factory

Cache

Session-scoped memoization cache backed by wezterm.GLOBAL. TTL and stats are opt-in; when disabled the cache stores bare values with zero bookkeeping overhead.

Configuration

memo.cache.configure {
  max_entries = 1000,                -- nil = unlimited
  eviction    = "expire-first",      -- eviction policy
  ttl         = { default = 60 },    -- TTL in seconds; nil = disabled
  stats       = true,                -- track hit/miss/eviction counters
  debug       = false,               -- log debug messages
}
Field Type Default Description
max_entries integer? nil Max cache entries. nil = unlimited.
eviction string "expire-first" Eviction policy when limit reached.
ttl table? nil { default = N } in seconds. nil = off.
stats boolean false Track hit/miss/eviction statistics.
debug boolean false Log debug messages.
clock fun(): number os.time Clock function for TTL (injectable).

Pass false for ttl or max_entries to explicitly disable them.

Methods

Method Description
cache.get(key) Retrieve a cached value.
cache.set(key, value, opts?) Store a value. Optional { ttl = N } per-call TTL.
cache.has(key) Check whether a key exists and is fresh.
cache.delete(key) Delete a single entry.
cache.clear(selector?) Clear all or filtered entries.
cache.expire(key) Mark a key as expired immediately (TTL mode).
cache.is_fresh(key) Check whether an entry is still fresh.
cache.touch(key) Reset the TTL on an existing key.
cache.compute(name, fn, ...) Memoize: cache fn(...) result under a derived key.
cache.keys(selector?) Return all cache keys, optionally filtered by prefix.
cache.stats() Return { hits, misses, evictions, entries }.
cache.namespace(name) Create a prefixed cache partition.
cache.configure(opts) Merge options into the current configuration.

Selectors

clear and keys accept an optional selector table:

  • { prefix = "foo" } — match keys starting with "foo".
  • { older_than = N } — match entries older than N seconds (TTL mode).

Namespaces

local ns = memo.cache.namespace "my-plugin"
ns.set("key", "value")
ns.get("key") -- "value"

All keys are automatically prefixed with "my-plugin:". The namespace wrapper exposes the same API as memo.cache but scoped to the prefix.

Key

Deterministic cache-key generation via serialization and concatenation. Each Lua value is converted to an unambiguous string representation and the parts are joined with |. Tables are serialized recursively with sorted keys; cyclic references produce the sentinel "<cycle>".

Functions

Function Description
key.serialize(value, seen?) Serialize any Lua value into a deterministic string (cycle-safe).
key.make_cache_key(name, ...) Generate a deterministic key from name + arguments.
memo.key.serialize({ nested = { 1, 2, 3 } }) -- deterministic string
memo.key.make_cache_key("fn", "a", "b")       -- "fn|a|b"

State

File-persistent key/value store backed by wezterm.GLOBAL. Each instance owns a dedicated GLOBAL slot and an on-disk JSON file. Data is loaded from disk once per WezTerm process and optionally flushed after every mutation.

Factory

local store = memo.state.new {
  path      = "/path/to/state.json", -- required
  auto_load = true,                  -- load from disk on first access
  auto_save = true,                  -- flush to disk on every mutation
  async     = true,                  -- use wezterm.background_task if available
}
Option Type Default Description
path string Absolute path to the JSON file. Required.
auto_load boolean true Load from disk on first access.
auto_save boolean true Write to disk on every mutation.
async boolean true Use wezterm.background_task when available.

Methods

Method Description
store:get(key) Retrieve a value by key.
store:set(key, v) Store a value (no functions).
store:has(key) Check whether a key exists.
store:delete(key) Delete a single key.
store:clear() Clear all entries.
store:load() Reload state from the JSON file.
store:save() Flush current state to disk.
store:restore() Return a shallow copy of all stored data.

Examples

Memoize an expensive computation:

local result = memo.cache.compute("heavy-calc", function(n)
  -- expensive work
  return n * n
end, 42)

Cache with TTL:

memo.cache.configure { ttl = { default = 300 } }
memo.cache.set("temp", "value")           -- expires in 5 minutes
memo.cache.set("short", "value", { ttl = 10 }) -- expires in 10 seconds

Persistent state across restarts:

local store = memo.state.new {
  path = wezterm.home_dir .. "/.local/share/wezterm/my-plugin.json",
}
store:set("window_count", 3)
-- after WezTerm restart:
store:get("window_count") -- 3

License

Code is licensed under the GNU General Public License v2. Documentation is licensed under Creative Commons Attribution-NonCommercial 4.0 International.

Back to WezTerm