Browse pages
Browser Persistence
Browser storage is the only durable store in a client-only product.
Intent#
Browser storage is the only durable store in a client-only product. It is small and shared, and it can disappear. Access runs through one adapter, keys carry versions, and shapes migrate. The user can always export what they made.
Standards#
Own storage in Infrastructure#
ruleguidanceRequirement Browser storage MUST be reached only through an Infrastructure adapter implementing an Application-declared interface.
Rationale No Domain type, component, or page touches storage, so a test substitutes an in-memory implementation of the same interface.
Version and scope every storage key#
ruleguidanceRequirement A storage key MUST use the form {product}.{area}.v{n} and be declared once as a constant beside its adapter.
Rationale The version segment increments when the stored shape changes incompatibly. A raw string literal elsewhere in the code is a second, unversioned declaration.
Example shop.cart.v1 names its product, its area, and its shape version.
Provide a migration for every changed shape#
ruleguidanceRequirement A change to a persisted shape MUST ship a migration that reads every previously released version and produces the current one.
Rationale The migration runs at startup before the store finishes loading. Data it cannot interpret is preserved under a quarantine key and reported. Deleting unreadable user data is not a recovery.
Treat reads as fallible#
ruleguidanceRequirement A storage adapter MUST return a typed result covering unavailable, absent, corrupt, and present.
Rationale Storage can be full, disabled, or partitioned by the browser. The application starts and stays usable when it is unavailable, with durable writes disabled and the user told once.
Bound what is stored#
ruleguidanceRequirement A specification persisting a growing collection MUST state its bound and its eviction rule.
Rationale Browser quotas are small and shared across the origin, so unbounded append eventually fails a write the user cannot diagnose. Large generated assets are cached by the service worker rather than written to key storage.
Provide export and import#
ruleguidanceRequirement Durable state MUST export to one versioned file that imports the same version, transforms an older version, and refuses a newer version.
Rationale The user saves the file, which is the only recovery path in a product with no server. Device-local state is excluded from the export. The file carries its own schema version. A refusal names the application version that wrote the file. A partial import of an unknown shape loses data silently.
Conventions#
Select the storage mechanism per shape#
conventionguidanceDefault Use key-value storage for small records, an indexed store for growing or multi-key collections, and the cache API for generated assets.
Replacement A consumer can replace this default with an explicit local convention.
Rationale Serializing a large collection into a single key-value entry rewrites the whole entry on every change.
Keep a stable serialization contract#
conventionguidanceDefault Give persisted records explicit property names and tolerate unknown properties on read.
Replacement A consumer can replace this default with an explicit local convention.
Rationale A newer build writing an extra field then does not break an older build still installed on another device.
Record the classification#
conventionguidanceDefault Store durable and device-local state under separate key prefixes.
Replacement A consumer can replace this default with an explicit local convention.
Rationale Export, clearing, and any future sync can then address each kind independently.
Verification#
| ID | Mark | Evidence |
|---|---|---|
| litespec/frontend/blazor/browser#own-storage-in-infrastructure | guidance | Does every storage call resolve the Application interface? |
| litespec/frontend/blazor/browser#version-and-scope-every-storage-key | guidance | Is each key a declared constant carrying product, area, and version? |
| litespec/frontend/blazor/browser#provide-a-migration-for-every-changed-shape | guidance | Does each changed shape ship a migration reading every released version? |
| litespec/frontend/blazor/browser#treat-reads-as-fallible | guidance | Does the adapter return unavailable, absent, corrupt, and present as typed outcomes? |
| litespec/frontend/blazor/browser#bound-what-is-stored | guidance | Does each persisting specification state its collection bound and eviction rule? |
| litespec/frontend/blazor/browser#provide-export-and-import | guidance | Does an export reimport to identical durable state, refusing a newer version? |
| litespec/frontend/blazor/browser#select-the-storage-mechanism-per-shape | guidance | Does each persisted shape use its declared mechanism? |
| litespec/frontend/blazor/browser#keep-a-stable-serialization-contract | guidance | Does a record with an unknown property still read? |
| litespec/frontend/blazor/browser#record-the-classification | guidance | Do durable and device-local keys use separate prefixes? |