Thank you for being patient! We're working hard on resolving the issue
The SDK supports reactive patterns for subscribing to sheet changes and keeping your application in sync.
SheetHandle manages the cache of cell data for a sheet. Before reading
cells, populate the cache for the date range you need:
import { NaiveDate } from "@tento-chrono";
const sheet = await client.open(sheetKey);
const range = new NaiveDate.Range(
NaiveDate.fromYmd1(2026, 1, 1),
NaiveDate.fromYmd1(2026, 3, 31),
);
await sheet.cells.fetch(range);
This loads cell values for all rows within the specified range. Subsequent reads are synchronous from cache.
Subscribe to any cell data change in the sheet. The callback receives the set of row IDs whose data changed:
const unsubscribe = sheet.cells.onChange((staleRowIds) => {
for (const rowId of staleRowIds) {
console.log(`Row ${rowId} data changed`);
// Re-read cells or refresh your UI
}
});
// Later:
unsubscribe();
For rows that sync from external providers (calendars, weather), use
row.cell(date) for progressive loading with three callback phases:
const row = sheet.row({ label: "Weather" });
const cell = row.cell(NaiveDate.Partial.fromDateAndType(
"day",
NaiveDate.fromYmd1(2026, 2, 4),
));
// Show a loading state while data is being fetched
cell.onLoading(() => {
showShimmer();
});
// Render data when it arrives (may re-fire on sync updates)
cell.onData((data) => {
render(data);
});
// Clean up when done
cell.dispose();
CellHandle is also awaitable for simple use cases — it resolves when
sync completes:
const data = await row.cell(NaiveDate.Partial.fromDateAndType(
"day",
NaiveDate.fromYmd1(2026, 2, 4),
));
console.log(data); // CellEntry | null
Rows connected to external data sources (calendars, weather) sync
asynchronously. Sync progress is tracked per-row and displayed in the
sheet legend as a loading indicator. The onData callback fires
whenever data changes — including intermediate updates during sync — so
your UI stays current without needing to track sync state directly.
For plugins that only need final data (after all sync completes), use
onDataFull:
cell.onDataFull((data) => {
// Only fires when sync is fully complete
renderFinal(data);
});
Some providers sync in stages. Weather, for example, uses three stages:
| Stage | Description |
|---|---|
viewport | Data for the currently visible date range |
near | ±3 months around today |
full | Full historical (1 year) + forecast (300 days) |
Each stage produces a separate sync key with an @stage suffix. The
sheet polls each key independently and clears the loading indicator
only when all stages reach a terminal state.
The backend deduplicates sync polls at two levels:
The sheet legend shows a loading indicator when a row has active sync.
This is driven by RowHandle.onSyncStateChanged, which fires whenever
setSyncState is called — including from the poll loop. When all sync
keys complete, setSyncState(null) clears the indicator.
After fetching, read cells synchronously:
await sheet.cells.fetch(range);
// Synchronous read from cache
const row = sheet.row({ label: "Weight" });
const cells = sheet.cells.getRange(row.id);
for (const cell of cells) {
console.log(cell.date, cell.data);
}