Changelog
Changelog
All notable changes to this project will be documented in this file.
0.8.0 - 2026-09-11
- Added
getItem()legacy imports with a custom deserializer and optional schema validation. Imports run only when the destination is physically missing, persist the validated value without expiration, and remove the legacy key after a successful write. - Added
getExpiration()to inspect stored deadlines without cleanup andsetExpiration()to change or remove expiration without supplying the value. - Changed writes without expiration options to preserve the current unexpired deadline, including
updateItem()and React setters. Pass{ expiresAt: null }to remove expiration explicitly. - Added
getItemResult()with distinct outcomes for successful reads, missing and expired entries, unsupported data, parse errors, and validation errors. Successful results distinguish storednullfrom a missing value. - Added opt-in
{ reactiveExpiration: true }for subscriptions and React hooks. Expiration timers notify listeners and update React values without deleting entries, with rechecks on window focus and page visibility changes. - Expanded documentation for migration, expiration controls, detailed reads, and reactive expiration, and added a changelog page.
Upgrade notes
- Previously, writing without expiration options removed an existing deadline. To retain that behavior, pass
{ expiresAt: null }tosetItem(),updateItem(), or a React setter. Pass a newttlto restart the lifetime instead of preserving the original deadline. - Reactive expiration remains opt-in. Timer notifications may be delayed, do not remove stored entries, and can be followed by another
expireevent during lazy cleanup. - Legacy imports are available through
getItem()only.getItemResult()does not import legacy data; backend failures, thrown schema errors, and unsupported asynchronous validation still throw.
0.7.0 - 2026-09-11
- Added array storage keys through the exported
StorageKeytype across storage methods, subscriptions, and React hooks. Equivalent arrays address the same entry without depending on array identity. - Added
keys()to enumerate non-expired keys in one scan. Bothkey()andkeys()restore array keys as fresh arrays of their original string segments. - Added development warnings when a namespace prefix contains its separator, indicating possible overlap with broader namespaces.
- Fixed package imports in browser environments without a global
process, with smoke checks for both ESM and CommonJS builds. - Hardened stored-entry validation and rejected non-finite expiration timestamps, including invalid dates and overflowing TTLs.
- Fixed Standard Schema handling for successful results with
issues: undefinedand asynchronous validators returning promises or thenables. Unsupported asynchronous validation now consumes rejections before throwing a synchronous error. - Improved in-memory key enumeration by caching ordered keys until insertions or deletions change them.
- Expanded documentation for React usage, namespace overlap, storage durability, migration, and synchronous helper behavior.
Upgrade notes
key()now returnsStorageKey | nullinstead ofstring | null. Update callers that assume every enumerated key is a string.UltraStorageno longer intersects the nativeStoragetype; useUltraStoragefor application instances.- Existing string keys and
__us/__gsentries remain supported. Avoid authoring string keys beginning with the reserved array-key prefix\u0000us:a:; canonical encoded strings address the same entries as their corresponding arrays. - Subscription events retain a string
change.key; array keys use their opaque serialized form. - Invalid or non-finite
ttlandexpiresAtvalues now throwTypeError. Stored envelopes must have a value, version1, and an expiry ofnullor a finite number to be recognized.
0.6.0 - 2026-09-10
-
Renamed the package from
greatstoragetoultrastorageand the exportedGreatStoragetype toUltraStorage. -
Added key-level subscriptions through
subscribe(key, listener), including same-page updates across instances sharing a backend and native cross-tab storage events. -
Made the
UltraStorageinterface a strict superset of the nativeStorageinterface. -
New entries use the internal storage marker
__us. Existing__gsentries remain supported without migration. -
Added optional React 18/19 bindings through
ultrastorage/react:useStorageandcreateStorageHook, with typed fallbacks, schema reads, functional updates, and expiration options. -
Added cached, side-effect-free internal snapshots and SSR/hydration support. Core reads retain their existing fresh-object and lazy-cleanup behavior.
-
Deferred default
localStorageaccess until the first operation, making instance construction safe on the server. Backend failures now occur on use rather than construction; no memory fallback is introduced. -
Added adapter rendering, hydration, subscription, failure, and type coverage.
vp run test:coverageenforces 100% runtime coverage. -
Added documentation at https://ultrastorage.dev.
Upgrade notes
- Replace the
greatstoragedependency and import paths withultrastorage, includinggreatstorage/corewithultrastorage/core. Rename type imports fromGreatStoragetoUltraStorage. - Existing
__gsentries remain readable. Older versions cannot read newly written__usentries, so update applications that share the same storage together. - React remains optional. Install React 18 or 19 only when using
ultrastorage/react. - Default storage is resolved on first use, so constructing an instance on the server is safe; operations still require an available storage backend.
0.5.0 - 2026-03-10
ultrastorage 0.5.0 focuses on better bundle control for custom serializers and safer development ergonomics.
Highlights
- Added a new
ultrastorage/coreentry point for applications that provide their own serializer and want to avoid bundlingdevalue. - Added development-only warnings for two easy-to-miss cases:
- writing
null, which reads back the same as a missing key viagetItem() - writing values with an expiry that is already in the past
- writing
- Expanded test coverage for the new core entry point and the warning behavior.
- Improved the README to document the new entry point and clarify the public API.
Why ultrastorage/core exists
The default ultrastorage entry point still includes devalue so rich JavaScript values work out of the box. If you already use a custom serializer such as superjson, ultrastorage/core lets you keep the same storage API while making the serializer explicit and avoiding the extra default serialization dependency in your bundle.
Upgrade notes
- No breaking changes in the default
ultrastorageentry point. ultrastorage/coreis new in this release. ItscreateStorage()requires aserializer.- The new warnings only run outside production builds and do not change runtime behavior.