Storage Service
8.12 StorageService — Scoped key-value persistence
Runs in: both worker and view. Writes from the worker are the source of truth for state that needs to survive view eviction.
Permission required: storage:read for reads, storage:write for writes.
The recommended way to persist extension data across sessions. Each extension gets its own isolated namespace backed by SQLite on the Rust side — extensions cannot read or write other extensions' data.
interface IStorageService {
get(key: string): Promise<string | null>;
set(key: string, value: string): Promise<void>;
delete(key: string): Promise<boolean>;
getAll(): Promise<Record<string, string>>;
clear(): Promise<number>;
}
Usage:
const storage = context.getService<IStorageService>('storage');
// Store a value (values are strings — JSON.stringify complex data)
await storage.set('theme', 'dark');
await storage.set('bookmarks', JSON.stringify(['https://example.com']));
// Read a value
const theme = await storage.get('theme'); // "dark"
const raw = await storage.get('bookmarks');
const bookmarks = raw ? JSON.parse(raw) : [];
// Check if a key exists
const val = await storage.get('nonexistent'); // null
// Delete a single key
const wasDeleted = await storage.delete('theme'); // true
// Get everything
const all = await storage.getAll(); // { "bookmarks": "[\"https://example.com\"]" }
// Nuclear option — delete all your extension's data
const count = await storage.clear(); // number of deleted entries
How it works under the hood:
All extensions share one extension_storage table in asyar_data.db (SQLite, WAL mode). The IPC Router derives your identity from the host-set data-extension-id attribute on the iframe element — any extensionId your extension includes in the message payload is ignored for this purpose, so you receive only your own namespace. On uninstall, Asyar deletes all rows for your extension automatically.
When to use what:
| Need | Use |
|---|---|
| Persist data across sessions (user preferences, notes, stable state) | StorageService |
| Transient data with an expiration (API responses, search indexes) | CacheService |
| Transient state within a single session | localStorage (iframe-scoped, survives view navigation) |
| App-wide settings with reactive change subscriptions | SettingsService |
Pro Tip: Values are plain strings. For objects and arrays, use
JSON.stringify/JSON.parse. Keep keys short and descriptive — there's no size limit, but the entire table is shared across all extensions.