14 KiB
09 · Data Model & Persistence
This section defines the persistent entities of Astrolabe and how they relate. It is the authoritative data contract: an implementer recreating the app should store equivalent records with these fields and meanings. Types are given abstractly (string, number, boolean, ISO-timestamp string, string[], object, "JSON value") so they map onto any stack. "JSON value" means any valid JSON shape — object, array, string, number, boolean, or null.
All data lives entirely in the browser. There is no server, account, or sync. Records survive page reload and remain available offline (see Application Shell & Navigation). To move data between browsers or devices, use Import & Export.
A. Snippet
A Snippet is a saved Vega-Lite specification together with its metadata. Snippets are the primary user-authored entity, listed and managed in the Snippet Library.
| Field | Type | Meaning |
|---|---|---|
id |
string | Unique, stable identifier for the snippet. |
version |
number | Schema version of this record, used for read-time migration (see Schema versioning below). |
name |
string | Human-readable title shown in the library. |
created |
ISO-timestamp string | When the snippet was first created. |
modified |
ISO-timestamp string | When the snippet was last saved. |
spec |
JSON value | The published Vega-Lite spec. May be an object or a string. This is the version rendered and shared by default. |
draftSpec |
JSON value | The working draft Vega-Lite spec being edited. May be an object or a string. |
comment |
string | Free-form user note about the snippet. |
tags |
string[] | User-assigned labels for filtering and organization. |
datasetRefs |
string[] | Names of Datasets referenced by this spec (see relationships below). |
meta |
object | Free-form, extensible metadata bag for app- or feature-specific data. |
Dual spec / draftSpec model
A snippet carries two specs at once. draftSpec is the editable working copy; spec is the last published copy. Editing affects only draftSpec until the user publishes, at which point draftSpec is promoted to spec. This separation backs the draft/published workflow described in Spec Editor & Draft/Published Workflow — it lets users experiment freely while keeping a known-good published version, and drives indicators for unpublished changes.
datasetRefs
datasetRefs records the names of datasets the spec depends on. It is the link used to display a snippet's linked datasets and, conversely, to find which snippets use a given dataset (see Cross-entity relationships). It mirrors the dataset names referenced by the draft spec — the version the user is editing — and is recomputed on every change to the draft (auto-save, the Extract-to-Dataset rewrite, revert) and on publish. Tracking the draft means a snippet's linked datasets reflect what the editor currently shows, not only the last published version; recomputation only ever runs on a valid (parseable) spec, so a half-typed draft never disturbs the links.
B. Dataset
A Dataset is a named, reusable data source that snippets can reference by name instead of inlining data. Datasets are managed in the Datasets manager and support multiple formats and two source kinds.
| Field | Type | Meaning |
|---|---|---|
id |
number | Unique numeric identifier. |
version |
number | Schema version of this record, used for read-time migration (see Schema versioning below). |
name |
string | Unique, human-readable name; the key snippets reference via datasetRefs. |
data |
JSON value | The payload, shaped by format: raw CSV/TSV text, or the parsed JSON/TopoJSON value. For source = url this is the fetched snapshot, or null before the first successful fetch. |
format |
string | One of json, csv, tsv, topojson. |
source |
string | One of inline (data pasted into the record) or url (data fetched once from a remote address and snapshotted into the record). |
url |
string (url only) | The remote address a url dataset was fetched from, retained so it can be re-fetched ("Refresh"). Absent for inline datasets. |
fetchedAt |
ISO-timestamp or null | For url datasets: when the snapshot was last fetched, or null if never fetched. Absent for inline datasets. |
comment |
string | Free-form user note about the dataset. |
rowCount |
number or null | Number of data rows, or null when unknown/not applicable. |
columnCount |
number or null | Number of columns, or null when unknown/not applicable. |
columns |
string[] | Column names, in order. |
columnTypes |
array of { name, type } |
Per-column inferred type. name is the column; type is one of number, string, date, boolean. |
size |
number | Approximate payload size in bytes. |
created |
ISO-timestamp string | When the dataset was first added. |
modified |
ISO-timestamp string | When the dataset was last changed. |
The rowCount, columnCount, columns, columnTypes, and size fields are derived summaries computed when data is added or updated — including when a url dataset is fetched or refreshed; a fetched URL snapshot profiles exactly like inline data. They support previews and type display without re-parsing the full payload.
Schema versioning
Both Snippet and Dataset records carry a numeric version recording the shape of that individual record. When a record is read from storage it is migrated up to the current shape before the app uses it; new writes always store the current version. A record written before versioning existed (no version field) is treated as version 1. This is distinct from the storage container's own layout version, and from the Import & Export envelope version (which describes the file format, not a record). Records exported via Import & Export include their version.
The current Dataset version is 2. The v1→v2 migration reflects the URL-snapshot model: a v1 url dataset stored its address in data, so migration moves that address into the new url field and clears data to null — the record becomes an unfetched reference that renders against its live URL until the user refreshes it, at which point the fetched snapshot is stored and profiled.
C. UserSettings
UserSettings holds persisted user preferences as a single structured record. The semantics and UX of each option are covered in Settings; the shape below is the storage contract.
| Field | Type | Meaning |
|---|---|---|
version |
number | Schema version of the settings record, used for migration. |
editor.fontSize |
number | Editor font size. |
editor.theme |
string | Editor color theme identifier. |
editor.minimap |
boolean | Whether the editor minimap is shown. |
editor.wordWrap |
string | on or off. |
editor.lineNumbers |
string | on or off. |
editor.tabSize |
number | Spaces per indentation level. |
performance.renderDebounce |
number | Delay (ms) before re-rendering the preview after edits. |
ui.theme |
string | App theme: light or dark. |
ui.previewFitMode |
string | Preview sizing: default, width, height, or full. |
formatting.dateFormat |
string | Date display mode: smart, iso, or custom. |
formatting.customDateFormat |
string | Pattern used when dateFormat = custom. |
A reference shape:
UserSettings = { version, editor: { fontSize, theme, minimap, wordWrap, lineNumbers, tabSize }, performance: { renderDebounce }, ui: { theme, previewFitMode }, formatting: { dateFormat, customDateFormat } }
D. App / UI preferences (persisted separately)
Some preferences persist independently of UserSettings so they can update frequently without rewriting the settings record. They are stored locally and restored on load.
- Snippet sort preference — how the Snippet Library list is ordered.
sortByis one ofname,modified,created;sortOrderisascordesc. Default ismodified/desc(most recently changed first). - Panel layout — the resizable three-panel arrangement: per-pane widths and per-pane visibility (which panels are shown or hidden). Restored so the workspace reopens as the user left it.
E. Persistence & limits
| Tier | What it holds | Capacity & behavior |
|---|---|---|
| Snippet store | All Snippet records | Local, with a practical budget of about 5 MB. A storage monitor tracks usage and surfaces warnings as the budget fills (see Snippet Library). |
| Dataset store | All Dataset records | Local, in a separate, much higher-capacity store, suited to larger payloads. |
| Settings & preferences | UserSettings plus the app/UI preferences in (D) | Local, small. |
Everything stays in the browser — no server or account is involved. All tiers survive reload and function offline. Because capacity is finite and per-browser, Import & Export is the supported path for backup and for moving data between browsers or devices.
F. Cross-entity relationships
Snippets and datasets are linked bidirectionally by dataset name: snippet.datasetRefs holds dataset names, and each such name matches a dataset.name.
- From a snippet,
datasetRefsyields its linked datasets. - From a dataset, scanning snippets for its
nameindatasetRefsyields the snippets that reference it.
This name-based link is what the Snippet Library and Datasets surfaces use to show linkage in both directions. The actual resolution of a referenced dataset into spec data at render time is covered in Live Preview.