Thank you for being patient! We're working hard on resolving the issue
Row keys are the stable identifiers for rows. Use them when a label is too human-facing or too likely to change.
Most app code uses a row key in three places:
| Kind | Example | Use |
|---|---|---|
| User row | r_0123456789abcdef0123456789abcdef | A normal server-backed row |
| Lookup key | :revenue | A user-managed stable name |
| Reserved key | :~weather | A builtin / canonical-provider row (see below) |
| Virtual row | `virtual(r_0123456789abcdef0123456789abcdef | logs-0)` |
| System row | system(header) | Framework-owned structure |
| Pending row | pending(0123456789abcdef0123456789abcdef) | A local row that has not been persisted yet |
The SDK accepts a few legacy forms when parsing, but these are the canonical forms you should expect to see in new code and serialized data.
import { RowKey } from "@tento-lona/sheets";
const userRow = RowKey.parse("r_0123456789abcdef0123456789abcdef");
const lookup = RowKey.lookup("revenue");
const builtin = RowKey.reserved("weather");
const system = RowKey.system("header");
RowKey.parse() is the right choice when you already have a string. The
factory methods are clearer when you are constructing a key in code.
RowKey is canonically exported from @tento-lona/sheets. The SDK
(@tento-lona) re-exports it for convenience, so both import paths work;
prefer @tento-lona/sheets when writing library-layer code.
Lookup keys and reserved keys are both name-based, but they serve different purposes:
:name is user-managed:~name is framework-managed ("reserved" / "canonical-provider" key)Use a lookup key when you want a row to be referenced by a meaningful name such
as :revenue or :resting-heart-rate.
Use a reserved key when you want a framework-owned canonical row such as
:~weather or another integration-backed row that the app owns.
:~)Reserved keys are special: they point at sheet-invariant canonical rows.
The same :~weather:{locationId} row is identical whether you open it from
your home sheet or your vacation sheet — the cells come from a single
canonical store, not per-sheet sourceCellData.
This matters for two reasons:
Client see the same
canonical cells. Fetches are coalesced by SheetsAccessor's shared
canonicalCells store.CanonicalBackend intercepts reads for :~-keyed
rows and routes them to a provider-aware fetch path (weather sync,
Google Calendar sync, Garmin sync, etc.) instead of the normal
row-by-id path.Common canonical prefixes:
| Prefix pattern | What it names |
|---|---|
:~weather:{locationId} | Daily weather for a location |
:~google:{authId}:calendar:{calendarId} | A Google Calendar |
:~garmin:{metric} | A Garmin-sourced metric (sleep, resting HR, …) |
:~whoop:{metric} | A Whoop-sourced metric |
You generally don't mint these keys yourself — the SDK builds them from auth bindings and provider metadata. You just open the row:
const weather = sheet.row({ lookupKey: "[weather@lona.so]:~weather:2550002" });
See Row Roles & Facades for facade access to
these rows (e.g. weather?.as(WEATHER_FACADE)).
Labels are for people. Keys are for code.
const row = sheet.row({ lookupKey: ":revenue" });
A RowKey can be resolved into a RowLocalId, which is the in-memory identity
used by trees, maps, and UI state:
const key = RowKey.lookup("revenue");
const rowId = key.resolve();
See Row Identity for when to use RowKey versus
RowLocalId.
RowKey, RowLocalId, RowId