Files
amethyst/.claude/skills/nostr-expert/references/event-factory.md
T
Claude 60edd473c7 Refreshes .claude/ skill library: fixes stale refs, adds 4 new skills
- Updates CLAUDE.md tech stack to current versions (Compose 1.10.3, Kotlin 2.3.20).
- Reframes kotlin-multiplatform iOS as mature; adds secp256k1-kmp 0.23.0 references.
- Updates desktop-expert Main.kt references (code grew from ~270 to 1341 lines and
  NavigationRail moved to ui/deck/SinglePaneLayout.kt); replaces obsolete
  "hardcoded ctrl = true" anti-pattern note with accurate isMacOS branching.
- Removes compose-desktop.md (superseded by desktop-expert/).
- Adds nostr-expert references: nip19-bech32, event-factory, crypto-and-encryption,
  large-cache. Adds kotlin-expert/common-utilities, compose-expert/rich-text-parsing,
  android-expert/image-loading.
- New skills: account-state (Account + LocalCache), relay-client (subscriptions,
  filter assemblers, preloaders), feed-patterns (FeedFilter + FeedViewModel family),
  auth-signers (NostrSigner across internal / NIP-46 / NIP-55).
2026-04-21 21:00:45 +00:00

3.4 KiB

EventFactory: Parsing JSON into Typed Events

quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/utils/EventFactory.kt is the single dispatch point that turns a parsed (id, pubKey, createdAt, kind, tags, content, sig) tuple into the correct Event subclass.

What It Does

EventFactory is a giant when over kind that maps integer kind values to concrete event classes. If a kind isn't recognized, it falls back to the generic base Event (so unknown kinds still round-trip). Every NIP that defines a new kind registers its class here.

Typical shape:

object EventFactory {
    fun create(
        id: HexKey,
        pubKey: HexKey,
        createdAt: Long,
        kind: Int,
        tags: TagArray,
        content: String,
        sig: HexKey,
    ): Event = when (kind) {
        MetadataEvent.KIND        -> MetadataEvent(id, pubKey, createdAt, tags, content, sig)
        TextNoteEvent.KIND        -> TextNoteEvent(id, pubKey, createdAt, tags, content, sig)
        ContactListEvent.KIND     -> ContactListEvent(id, pubKey, createdAt, tags, content, sig)
        ReactionEvent.KIND        -> ReactionEvent(id, pubKey, createdAt, tags, content, sig)
        // …hundreds more…
        else                      -> Event(id, pubKey, createdAt, kind, tags, content, sig)
    }
}

Callers are normally upstream of this: Event.fromJson(...) / EventMapper.fromJson(...) / the relay client's message parser. You rarely call EventFactory directly — you consume typed events it produces.

Registering a New Event Kind

Adding a NIP is roughly:

  1. Create the event class under quartz/src/commonMain/kotlin/com/vitorpamplona/quartz/nipXX…/ alongside the NIP package.
  2. Subclass the right base:
    • Event — regular events (stored forever).
    • BaseReplaceableEvent — kinds 10000-19999, 0, 3.
    • BaseAddressableEvent — kinds 30000-39999 (identified by kind:pubkey:d-tag).
    • Ephemeral events extend Event but have kind 20000-29999.
  3. Define companion object { const val KIND = <n> }.
  4. If the event has tag builders, define a TagArrayBuilder<YourEvent> DSL in a TagArrayBuilder extension — see nostr-expert/references/tag-patterns.md.
  5. Add a branch to EventFactory.create(...) so JSON parsing produces your typed class.
  6. If the event is addressable, ensure it exposes a stable dTag() and address().
  7. Add tests under quartz/src/commonTest/....

Why a Monolithic when?

  • Zero overhead: compiled to a dense lookup. No reflection, no registry map.
  • Exhaustive browsing: every known kind lives at one search location. grep KIND = 1234 quartz/... finds everything.
  • Obvious migration path: adding a kind means adding a case; removing a kind is a grep-and-delete.

The tradeoff is the file is large and every new kind edits the same file — expect merge conflicts in PRs that touch it, and resolve by keeping both branches.

Supporting Utilities

  • EventAssembler.kt (crypto/) — higher-level helper that takes a signer and a kind + tags + content and produces a fully signed event (id + sig populated).
  • EventTemplate.kt (signers/) — unsigned-event holder, useful in signer flows.
  • Event.fromJson(...) / Event.toJson() — JSON round-trip using OptimizedJsonMapper (Jackson on jvmAndroid).
  • event-hierarchy.md — class hierarchy, Kind ranges
  • nip-catalog.md — which kind maps to which NIP
  • tag-patterns.mdTagArrayBuilder DSL for writing tags cleanly