Skip to main content

Offline Storage And Migrations

Compass supports a meaningful local-first path for unauthenticated users and resilient fallback behavior for authenticated users.

Storage Boundaries

Compass uses two intentionally separate storage abstractions:

  • OfflineDataStore owns asynchronous event, task, and migration-record data. Its current implementation is IndexedDbOfflineDataStore.
  • BrowserKeyValueStore owns synchronous browser key-value state backed by localStorage or sessionStorage.

Primary files:

  • packages/web/src/common/storage/offline-data/offline-data.store.registry.ts
  • packages/web/src/common/storage/offline-data/offline-data.store.ts
  • packages/web/src/common/storage/offline-data/indexeddb-offline-data.store.ts
  • packages/web/src/common/storage/browser-key-value.store.ts

Components and hooks must use feature-level storage utilities. Native localStorage, sessionStorage, and IndexedDB access belong only in these storage implementations, migrations, or black-box test setup.

Offline Data Store Lifecycle

initializeOfflineDataStore() does this:

  1. lazily create the offline data store singleton
  2. open the underlying IndexedDB database
  3. run store-level schema upgrades
  4. run app-level data migrations
  5. run app-level external import migrations

The initialization call is idempotent and memoized by initPromise.

For tests that instantiate IndexedDbOfflineDataStore directly, call store.close() during teardown before deleting the test database. This closes the Dexie connection and resets readiness state for the next test.

IndexedDB Schema

Current database name:

  • compass-local

Current table groups:

  • events
  • tasks
  • _migrations

The IndexedDB store keeps:

  • events keyed by _id
  • tasks keyed by _id and associated to a dateKey
  • migration completion records in _migrations

Legacy Primary-Key Migration

File:

  • packages/web/src/common/storage/offline-data/legacy-primary-key.migration.ts

There is explicit support for an older task schema that used id instead of _id.

Recovery strategy:

  1. detect the Dexie upgrade error
  2. read legacy records through a legacy Dexie schema
  3. delete the old database
  4. reopen using the current schema
  5. reinsert events and tasks

Data Migrations

File:

  • packages/web/src/common/storage/migrations/migrations.ts

Data migrations:

  • transform data already inside Compass storage
  • are tracked in the _migrations table
  • fail startup if they fail

Current example:

  • task id -> _id migration

External Migrations

External migrations:

  • import data from outside the offline data store
  • are tracked in localStorage, not IndexedDB
  • are non-blocking on failure

Current examples:

  • localStorage task import
  • demo data seeding

Failure Model

Database initialization errors are surfaced to the user but do not hard-stop app boot.

Files:

  • packages/web/src/common/utils/app-init.util.ts
  • packages/web/src/index.tsx

Expected behavior:

  • app still renders
  • toast explains offline storage is unavailable
  • authenticated users can continue in remote-only mode

Event And Task Persistence

Event operations:

  • query overlapping date ranges
  • put one or many events
  • delete by event id

Task operations:

  • get tasks for a dateKey
  • replace all tasks for a date
  • upsert a single task
  • move a task between dates

All task writes should pass through normalization helpers from packages/web/src/common/types/task.types.ts.

When To Add A Migration

Add a migration when you change:

  • IndexedDB schema shape
  • local task/event field names or required defaults
  • import behavior from legacy local sources

Choose the right mechanism:

  • offline data store schema/version change for storage structure
  • data migration for data already in storage
  • external migration for imports from localStorage or other sources

Safe Editing Checklist

Before changing storage behavior:

  1. update the relevant storage abstraction
  2. add or adjust migration if existing user data could break
  3. add tests for fresh database and migrated database paths
  4. confirm startup still degrades gracefully when storage fails