Platform · Storage
Choosing where the file lives
Storage is a public contract, not an implementation detail: OPFS, IndexedDB, Cache Storage and in-memory are interchangeable providers behind the same interface. Selection prefers the order you configure, but existing data outranks preference - a database created on IndexedDB does not silently vanish the day OPFS becomes available.
Fastest persistent tier. Real files in the origin private file system. Concurrent connections, no COOP/COEP.
Preference order, and what counts as acceptable
Every registered provider is a candidate. AllowNonPersistentFallback()
is what makes in-memory acceptable at all - without it, an origin with no persistent
backend fails to open rather than quietly losing data on reload.
BsqliteStorageSelectionBuilder.Create(s => s
.Prefer(BsqliteOpfsStorageProvider.ProviderName)
.Fallback(BsqliteIndexedDbStorageProvider.ProviderName)
.Fallback(BsqliteCacheStorageProvider.ProviderName)
.Fallback(BsqliteInMemoryStorageProvider.ProviderName)
.AllowNonPersistentFallback()); // without this, in-memory is not acceptable
// Default is BsqliteStorageMigrationMode.KeepExisting: data outranks preference.
// AutomaticOnOpen copies to the preferred backend, checks the image, then rebinds.Why this session landed where it did
A resolution is a record of the whole decision, not just its outcome: every candidate, its probe, and the reason it was passed over. That is what makes a support question answerable without reproducing the user's browser.
Opening this tab's database…
var session = await database.OpenAsync();
var resolution = session.Resolution;
resolution.Provider.Name; // the backend that actually opened
resolution.IsFirstChoice; // false when a fallback won
resolution.WasDecidedByExistingData; // the sticky binding overruled preference
resolution.BetterProviderAvailable?.Name; // ranked higher but holds no data
resolution.Attempts; // every candidate, with the reason it lostWhat this backend can honour
The core enforces these flags; it never special-cases OPFS against IndexedDB in
SQL. A backend that cannot honour ATTACH or
PRAGMA page_size fails in BsqliteFeatureGuards instead of
corrupting the file - see Limits.
Opening this tab's database…
var caps = session.Resolution.Provider.Capabilities;
caps.IsPersistent; // survives reload
caps.SupportsMultipleConnections;
caps.SupportsConcurrentReads;
caps.SupportsRelaxedDurability;
caps.SupportsMultiDatabaseTransactions; // gates ATTACH
caps.CanChangePageSize; // gates PRAGMA page_size
// The core reads these flags. It never special-cases a provider by name, so a new
// backend gets the same guards without touching Bsqlite.Ask every backend what it can do here
A probe reports availability, quota when the browser will say, and the raw environment facts behind the verdict. Unavailable is a result, never an exception, so selection can continue and still explain itself afterwards.
foreach (var provider in Database.Providers)
{
var probe = await provider.ProbeAsync();
probe.IsAvailable; // unavailability is a result, never an exception
probe.UnavailableReason; // why, in one sentence
probe.UsageBytes; // when the browser will say
probe.QuotaBytes;
probe.Environment; // raw facts: JSPI, getDirectory, isSecureContext...
}Ask the browser not to evict you
Persistent storage is granted per origin, not per backend. Without it, any of the persistent backends may be cleared under storage pressure - which is a product decision, so the library exposes the call rather than making it for you.
// navigator.storage.persist(). Origin-wide, not per backend: without it the browser
// may evict OPFS / IndexedDB / Cache Storage under pressure.
bool granted = await BsqlitePersistence.RequestAsync(js);The four backends
Every provider this site registers, in its preference order. Each lives in a namespace of its own and is
registered with AddStorageProvider<T>().
| Provider | Character |
|---|---|
| OPFS opfs | Fastest persistent tier. Real files in the origin private file system. Concurrent connections, no COOP/COEP. |
| IndexedDB indexeddb | Widest reach. Survives browsers that do not have OPFS. Relaxed durability available. Page size is pinned. |
| Cache Storage cache-storage | The besql-compatible fallback. Persistence cost scales with database size. |
| In-memory in-memory | Volatile. Gone on reload. Useful to try the engine without touching disk. |