fix(forms): guard TemplateResourceForm's kind-30142 publish (#72) #73

Merged
laoc merged 1 commit from fix/template-resource-form-publish-guards into dev 2026-07-31 09:23:51 +00:00
Owner

Closes #72. Found by TestOER, measured at e0aa0aba.

Two changes, both the pattern the rest of the app already uses after #62/#64:

  • created_at: nextCreatedAt(resourceEvent) in edit mode — without it a replacement can tie with what it replaces, and on a tie nostr-idb keeps the OLD one deterministically.
  • eventStore.add moved to after a successful publish, via cachePublishedEvent. publishEvent has no failure path (unlike publishEventOptimistic it never removes or un-caches), so an add placed before it has nothing to undo it. Kind 30142 is cacheable, so that left a version existing on no relay cached at its address.

A total publish failure now surfaces as an error instead of navigating to the new naddr as though it had worked.

Verification

474 files / 5231 tests pass, exit 0    clean tree on e0aa0aba
pnpm check 0 errors                    pnpm lint clean

Not covered by a new test — stated plainly

Driving this component's submit needs decodeFormNaddr, parseFormTemplate, addressLoader, the event factory, publishEvent and buildTemplateResourceSubmission all mocked. The two behaviours are already unit-tested on the helper (replaceableUpdates), but that this component calls them is unproven in CI. TestOER's T1/T2 at this commit is what should confirm it.

Closes #72. Found by TestOER, measured at `e0aa0aba`. Two changes, both the pattern the rest of the app already uses after #62/#64: - `created_at: nextCreatedAt(resourceEvent)` in edit mode — without it a replacement can tie with what it replaces, and on a tie nostr-idb keeps the **OLD** one deterministically. - `eventStore.add` moved to **after** a successful publish, via `cachePublishedEvent`. `publishEvent` has no failure path (unlike `publishEventOptimistic` it never removes or un-caches), so an add placed before it has nothing to undo it. Kind 30142 is cacheable, so that left a version existing on no relay cached at its address. A total publish failure now surfaces as an error instead of navigating to the new naddr as though it had worked. ## Verification ``` 474 files / 5231 tests pass, exit 0 clean tree on e0aa0aba pnpm check 0 errors pnpm lint clean ``` ## Not covered by a new test — stated plainly Driving this component's submit needs `decodeFormNaddr`, `parseFormTemplate`, `addressLoader`, the event factory, `publishEvent` and `buildTemplateResourceSubmission` all mocked. The two behaviours are already unit-tested on the helper (`replaceableUpdates`), but **that this component calls them is unproven in CI**. TestOER's T1/T2 at this commit is what should confirm it.
Carried both bugs #62 and #64 fixed elsewhere. Measured at e0aa0aba by
TestOER: a same-second edit is lost while the app reports success (memory
V2 / IDB V1, then memory EMPTY after reload), and a failed publish leaves
a phantom in IDB that survives a reload with the relay down.

Two changes, both the pattern the rest of the app already uses:

- created_at: nextCreatedAt(resourceEvent) in edit mode. Without it a
  replacement can tie with the version it replaces, and on a tie nostr-idb
  keeps the OLD one deterministically.
- eventStore.add moved to AFTER a successful publish, via
  cachePublishedEvent. publishEvent has no failure path — unlike
  publishEventOptimistic it never removes or un-caches — so an add placed
  before it has nothing to undo it when no relay accepts. Kind 30142 is
  cacheable, so that left a version existing on no relay cached at its
  address, and a cache hit ends the address loader before any relay is
  asked.

A total publish failure now surfaces as an error instead of navigating to
the new naddr as though it had worked. Hardcoded German matches the file
convention (zero paraglide imports, 'Bitte anmelden.' /
'Veröffentlichung fehlgeschlagen' already inline).

Why #64's sweep missed this: that sweep looked for publish sites with NO
eventStore.add. This one has one, on the wrong side of the publish —
grepping for an absence cannot find a misordering. TestOER's framing.

Reachability traced, not assumed: the route passes editNaddr to this
component and the wizard's own edit fetch is explicitly skipped when
templateNaddr is set, so this is the designed edit path for any resource
authored from a kind-30168 form template.

Verification: 474 files / 5231 tests pass, exit 0, at a clean tree on
e0aa0aba. lint clean, pnpm check 0 errors.

NOT covered by a new test. Driving this component's submit needs
decodeFormNaddr, parseFormTemplate, addressLoader, the event factory,
publishEvent and buildTemplateResourceSubmission all mocked; the two
behaviours themselves are already unit-tested on the helper
(replaceableUpdates), but that this component calls them is unproven in
CI. The browser measurement that found it (T1/T2) is what should confirm
it.

Closes #72
laoc merged commit 1393efe82b into dev 2026-07-31 09:23:51 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
edufeed/edufeed-app!73
No description provided.