# 08 · Import & Export Astrolabe lets a user back up or transfer their entire workspace as a single JSON file, and bring data back in by importing such a file. Both actions are triggered from header controls labelled **Import** and **Export**. Import always merges with existing data; it never replaces what is already stored. ## Export Export produces one downloadable JSON file containing every snippet (see *Snippet Library*) and every dataset (see *Datasets*), wrapped in an envelope carrying format metadata. - **Trigger**: the **Export** header control runs the export immediately (no intermediate dialog). - **Contents**: all snippets and all datasets currently stored, plus envelope metadata. - **Empty workspace**: if there are no snippets, the user is informed ("No snippets to export") and no file is downloaded — even if datasets exist. - **Filename**: `astrolabe-project-YYYY-MM-DD.json`, where the date is today's date (export day). - **Feedback**: on success a toast reports the counts, e.g. "Exported 4 snippets and 2 datasets" (the dataset clause is omitted when there are no datasets; singular/plural wording adapts to the counts). ### Export envelope shape The downloaded file is a single JSON object: an envelope with a format `version`, an export timestamp, an exporter tag, and the two data arrays. ```json { "version": "1.0", "exportedAt": "2026-06-03T12:00:00.000Z", "exportedBy": "Astrolabe", "snippets": [ /* full snippet objects (see Data Model) */ ], "datasets": [ /* full dataset objects (see Data Model) */ ] } ``` - `version` — export format version (currently `"1.0"`). - `exportedAt` — ISO 8601 timestamp of the export. - `exportedBy` — fixed identifier `"Astrolabe"`. - `snippets` / `datasets` — arrays of complete records as defined in *Data Model*, each including its record `version` field. (This is the per-record schema version, not the envelope `version` above.) ## Import Import lets the user pick a JSON file from their device; its contents are normalized, merged into the current workspace, and saved. - **Trigger**: the **Import** header control opens a file picker restricted to JSON files. After a file is chosen (or the picker cancelled) the control is ready to be used again immediately. ### Accepted inputs The importer recognizes several shapes so that both Astrolabe exports and looser snippet files work: - **Astrolabe export envelope** — an object with a `version` and a `snippets` array; an optional `datasets` array is imported too. - **Bare array of snippets** — a top-level JSON array is treated as a list of snippets (no datasets). - **Single snippet object** — any other object is treated as one snippet. - **Older / foreign snippet shapes** — snippets that do not match the current model are normalized onto it: - Alternative field names are mapped: `content` → spec, `draft` → draft spec, `createdAt` → creation timestamp. - Missing timestamps are generated at import time (creation and modification set to now, or derived from the source timestamp when present). - Missing identifiers, names, comments, tags, dataset references, metadata, and record `version` are filled with defaults (a missing `version` is treated as the earliest shape and migrated up on read — see *Data Model*). - Such normalized imports are tagged `"imported"` so the user can find them. A snippet is treated as already in current Astrolabe format when it carries an ISO-style creation timestamp; in that case its existing fields (id, name, timestamps, spec, draft spec, comment, tags, dataset references, metadata) are preserved as-is, with sensible fallbacks for any missing field. ### Merge behavior - Imported snippets are **appended** to the existing library; nothing is overwritten or removed. - **ID collisions** (an incoming snippet whose id already exists) are resolved by assigning the incoming snippet a fresh unique id; the original snippet keeps its id. - Datasets are imported **before** snippets so that snippet dataset references can resolve. ### Dataset conflicts When an imported dataset's name already exists in the library, it is auto-renamed to a unique name rather than overwriting the existing one (see *Datasets*). - A numeric suffix is appended to the original name; further suffixes are added until the name is unique. - The renamed datasets are reported to the user via a warning toast listing each `original -> new` rename. - If a single dataset fails to import, it is skipped and the rest of the import continues. ### Storage limit handling Snippet storage has an approximate 5 MB budget (see *Snippet Library* storage monitor). - If the incoming snippets would push total snippet storage over the budget, the user is warned about the overage amount, but the app still attempts to save the import. - If the save ultimately fails because the storage quota is exceeded, the user is told to delete some snippets and try again, and no partial snippet import is committed. - The storage check applies to snippets; datasets are stored separately and saved during the dataset phase above. ### Feedback - **Success**: a toast reports how many snippets (and datasets, when any) were imported, e.g. "Imported 4 snippets and 2 datasets". - **Renames**: when datasets were renamed, the success message is shown as a warning toast that also lists the renames. - **Empty file**: if no snippets are found in the file, the user is informed ("No snippets found in file") and nothing is imported. - **Quota failure**: a clear error advising the user to delete snippets and retry. - **Invalid file**: a non-JSON or unparseable file produces a clear error ("Failed to import. Please check that the file is valid JSON."); an unreadable file produces a read error. In all error cases the existing workspace is left unchanged.