feat(useStorage): add an onError hook for failed writes - #767
feat(useStorage): add an onError hook for failed writes#767sridhar-3009 wants to merge 1 commit into
Conversation
johnleider
left a comment
There was a problem hiding this comment.
Thanks for the batch of PRs this weekend — #766 merged as-is, and your #764 surfaced a naming-precedence inconsistency we're fixing in a follow-up. This one is a genuinely useful addition and the implementation is clean (purely additive, unknown over any, the option flows through the plugin with no extra wiring, good changeset copy). Three things before it can land:
-
Base branch. This adds public API (
feat+minorchangeset), which targetsdevin our branch model —masteris the patch train and merging there would push a minor out with the next patch release. Please rebase ontodev. -
Coverage vs. claim. The hook only wraps
writeStored. The read path is arguably the more common real failure and stays silent: thereadStoredcatch (corrupt/unparseable stored JSON) still only hits the logger,adapter?.getItemsits outside that try so aSecurityErroron read throws straight to the caller, andremove()/clear()/the storage-eventremoveItemhave no handling at all. Either extendonErrorto the read/remove paths, or narrow the option's doc comment and changeset to "write errors" so the API doesn't overpromise — happy with whichever you prefer, extending gets my vote. -
Docs.
apps/docs/src/pages/composables/plugins/use-storage.mdhas sections forprefixandttl; a new public option needs one too, or it ships invisible.
One non-blocking note: elsewhere in v0 (useImage, Avatar/Image components) onError is a handler the composable returns for you to bind, whereas here it's a callback you supply — same name, inverted direction. If you have a better name in you (onWriteError?), now is the cheap time; otherwise this sets the precedent and that's a defensible call too.
Closes #756.
createStorage's deep-watcher write path catches adapter errors — a fulllocalStoragequota, aSecurityErrorin a restricted context, an adapter-level failure — and routes them only to the internal logger (writeStoredcatch →logger.error('[v0:storage] Failed to write key …')) without rethrowing. Writes are fire-and-forget deep-watcher side effects, so consumers have no programmatic way to learn a write failed:storage.setcannot reject, and no error state is exposed. The user keeps editing, believing their data is persisted, while it silently is not.Proposal implemented
An opt-in
onErrorhook oncreateStorage/createStoragePlugin, per the issue's own proposal:Called with the underlying error and the prefixed storage key whenever
writeStoredthrows. The existing internal log call is untouched and still fires alongside it — this is purely additive, not a replacement.I went with the callback approach over an
errorref on the storage context, per the issue's own reasoning: a callback composes better with per-consumer handling (e.g. routing straight into a toast/notification system) and matches the adapter-injection style already used elsewhere in the package (useLogger,useLocale).Tests
onErroris called with the error and prefixed key when a write throws.onErroris not called when a write succeeds.67 existing + new tests for this composable pass, plus the full 4787-test non-browser suite.