Same class of bug as the subscription fix, applied everywhere an idempotent,
replaceable list is published with an optimistic UI: when no relay ACKs within the
10s window (slow/flaky relays), the event was still sent and almost always
persists (it shows up after a refresh), but assertAnyRelayAccepted threw -> the UI
showed a scary error / reverted, even though it worked.
- nostrPublish.js: add shared helpers relayPublishFailuresAreAllTimeouts() and
assertAnyRelayAcceptedOrUnconfirmed() — the latter returns a summary flagged
{ unconfirmed: true } on all-timeout instead of throwing; explicit relay
rejections (and pre-publish signer/encryption errors) still throw.
- Applied to the replaceable-list publishes: subscriptions (kind 30000, refactored
to the shared helper), user mute/block list (10000), relay list (10002), DM
relay hints (10050), hashtag preferences (30015).
Left as-is for now (higher-stakes, separate follow-up): the generic content
publish path (video/edit/delete/revert/rebroadcast) in publishHelpers/client.js —
same latent issue but the video outcome-describer needs careful handling.
Tests: tests/subscription-publish-timeout.test.mjs (8) now cover both the
classifier and assertAnyRelayAcceptedOrUnconfirmed (confirmed->summary,
all-timeout->soft, rejection->throws). hashtag-preferences suite green (14).
Co-Authored-By: Claude Opus 4.8 <[email protected]>