IndexedDB wrapper with Promise API per store, named change listeners, and cross-tab notifications via BroadcastChannel.
Module indexedDB | Source packages/front/fw/src/dom/fs/indexedDB.js | Deps broadcastChannel | Worker-safe no
Resolve
const idb = runtime.resolve('indexedDB');
// Returns: { isSupported, open, drop }
API
idb.isSupported() → boolean
idb.open(name, version, upgrade?, options?) → Promise<DB>
Opens or creates/migrates an IndexedDB database.
const db = await idb.open('myApp', 1, (db, oldVersion, newVersion) => {
// Called if version changes
if (oldVersion < 1) {
db.createObjectStore('cache');
db.createObjectStore('users', { keyPath: 'id' });
}
});
| Param | Type | Description |
|---|---|---|
name |
string |
Database name |
version |
number |
Version (integer ≥ 1) |
upgrade |
(db, oldVersion, newVersion) → void |
Migration callback (optional) |
options |
{ crossTab?, namespace? } |
Cross-tab options (optional, see below) |
Options
| Option | Type | Default | Description |
|---|---|---|---|
crossTab |
boolean |
true |
If false, disables all cross-tab broadcasting. listen events stay same-tab only. |
namespace |
string |
'' |
BroadcastChannel name prefix to isolate events between user sessions sharing the same browser. Must not contain :. |
Channel name construction:
// With namespace:
const channelName = `idb:${namespace}:${name}`; // e.g.: 'idb:user-42:myApp'
// Without namespace:
const channelName = `idb:${name}`; // e.g.: 'idb:myApp'
Validation:
crossTabmust be a boolean if provided, otherwisethrow 'indexedDB: crossTab must be a boolean'.namespacemust be a string without:, otherwisethrow 'indexedDB: namespace must be a string without ":"'.
idb.drop(name) → Promise<void>
Permanently deletes the database.
DB — database handle
db.name // → string
db.version // → number
db.storeNames // → string[]
db.close() // closes the connection and the broadcastChannel (if active)
db.store(storeName) → IDBStoreAccessor
Returns an accessor for the named store.
db.listen — change notifications
Fires after each set, del, and clear on any store.
db.listen.add('myListener', ({ type, store, key, cross }) => {
// type : 'set' | 'del' | 'clear'
// store : store name
// key : IDBValidKey | null (null for 'clear')
// cross : true if event came from another tab
console.log(type, store, key);
});
db.listen.del('myListener');
IDBStoreAccessor — CRUD methods
const s = db.store('cache');
// Read
const val = await s.get(key); // → * | undefined
const exists = await s.has(key); // → boolean
const all = await s.getAll(); // → *[]
const allKeys = await s.getAllKeys(); // → IDBValidKey[]
const count = await s.count(); // → number
// Write
const stored = await s.set(key, value); // → IDBValidKey (stored key)
await s.del(key);
await s.clear();
// Iteration
await s.each((value, key) => {
// Return false to stop iteration
if (key === 'stop') return false;
console.log(key, value);
});
All set, del, clear methods emit a notification to registered listeners.
Query filtering
getAll, getAllKeys, and count accept a query (key or IDBKeyRange) and an optional max count:
await s.getAll(IDBKeyRange.bound('a', 'z'), 10);
await s.count(IDBKeyRange.lowerBound('prefix_'));
Cross-tab change notification
Listeners receive cross: false for changes in the same tab, cross: true for changes from other tabs (via the broadcastChannel module).
Compatibility: Chrome 54+, Firefox 38+, Safari 15.4+. Silent degradation if BroadcastChannel is not available (same-tab only).
Multi-user isolation with namespace
Use the namespace option to isolate BroadcastChannel events between multiple user sessions sharing the same browser (e.g. two authenticated accounts in two tabs):
// Tab A — user 42
const dbA = await idb.open('myApp', 1, undefined, { namespace: 'user-42' });
// Channel: 'idb:user-42:myApp'
// Tab B — user 99
const dbB = await idb.open('myApp', 1, undefined, { namespace: 'user-99' });
// Channel: 'idb:user-99:myApp'
// Mutations in A trigger NO callback in B (separate channels)
Warning:
namespaceisolates only BroadcastChannel broadcasting. IndexedDB store contents share the same database ifnameis identical — data separation between users must be done at a higher level (prefix onnameor on keys).
Opt-out cross-tab with crossTab: false
// No BroadcastChannel created — same-tab events only
const db = await idb.open('myApp', 1, undefined, { crossTab: false });
Examples
Complete — config + listener
const idb = runtime.resolve('indexedDB');
// Open with migration and user namespace
const db = await idb.open('app', 2,
(db, oldVersion) => {
if (oldVersion < 1) db.createObjectStore('config');
if (oldVersion < 2) db.createObjectStore('logs');
},
{ namespace: 'user-42' }
);
const config = db.store('config');
// CRUD
await config.set('theme', 'dark');
await config.set('lang', 'fr');
const theme = await config.get('theme'); // 'dark'
const keys = await config.getAllKeys(); // ['theme', 'lang']
// Iterate
await config.each((val, key) => console.log(key, '=', val));
// Listen to changes (including cross-tab, same namespace only)
db.listen.add('sync', ({ type, store, key, cross }) => {
if (store === 'config') refreshUI();
});
db.close();
Notes
- The
broadcastChannelmodule is declared as a dependency: no nativenew BroadcastChannel(...)in this module. - If
broadcastChannel.isSupported()returnsfalse, degradation is silent (same-tab only, no error). namespaceresolves the multi-session BroadcastChannel ambiguity, but does not replace a prefix onnameor on keys for data separation in the database.
See also
- storage — localStorage / sessionStorage (synchronous, simple)
- broadcastChannel — underlying module for cross-tab broadcasting
- download