Adds remove(video, options) to the mirror service for delete / toggle-off:
- NIP-09 kind-5 delete referencing BOTH addressable kinds
(34235:pub:root + 34236:pub:root, with k tags) so it's robust to a
since-changed orientation.
- empty-replace tombstone (same d-tag, title, NO imeta) so clients that cache
addressable events but ignore NIP-09 still see the entry cleared; uses the
short kind for portrait videos.
Published to the write/outbox relay set; unavailable without a signer.
tests/nip71-mirror-service.test.mjs: remove publishes delete (both kinds) +
tombstone; portrait => 34236 tombstone; unavailable without signer.
Mutation-verified (tombstone emission + both-kind a-tags load-bearing).
Note: full unit suite green apart from the documented order-dependent flakes
(uploadModal-reset; a transient login-modal clipboard global-state flake) — both
pass in isolation and are unrelated to this change.
Co-Authored-By: Claude Opus 4.8 <[email protected]>
Publish service for the opt-in NIP-71 mirror (kind 34235/36) to the user's
write/outbox relays, built on the Phase 0 builder. Flag still off; not yet wired
into the publish path (next increment).
- js/services/nip71MirrorService.js: createNip71MirrorService (DI) → isAvailable,
canMirror(video), publish(video, options). Signs with the active signer and
publishes to the write set (getDeletePublishRelays). Resolves the moderation
audit item: the site ALLOW_NSFW_CONTENT gate — an instance that won't surface
NSFW must not publish it outward (enforced in both publish() and canMirror(),
defense-in-depth). Private/url rules come from the builder.
tests/nip71-mirror-service.test.mjs: publishes a public video; NSFW blocked when
ALLOW_NSFW_CONTENT=false (and allowed when true); private/url-less refused;
unavailable without signer. Mutation-verified (NSFW gate load-bearing).
Co-Authored-By: Claude Opus 4.8 <[email protected]>