A buyer cannot choose a ticket wave without its id, and nothing public
carried one: `PublicEventExtra` is `EventExtraBase`, which did not
include `ticket_waves`, and the NIP-52 tags deliberately publish only
the active wave with no identifier (#61). So `GET /events/{id}` and
`/events/public` gave a client everything except the one field it needs
to say which tier it wants — and the purchase endpoint refuses when
several waves are open.
Moves `ticket_waves` from `EventExtra` to `EventExtraBase`, leaving
`promo_codes` as the only organizer-private field in `extra`.
A wave holds an id, title, date window, currency, price, remaining stock
and the fiat flags — the sales information a buyer needs in order to
choose. The only thing publishing it reveals is the upcoming price
schedule, which is precisely what #61 recorded as the cost of settling
on flat Nostr tags; over REST it is a gain rather than a leak.
Prerequisite for wave support in the webapp, which otherwise cannot
offer a wave picker to anyone but the organizer.
Two tests pin the projection: the public extra carries waves and never
promo codes, and a serialised `PublicEvent` shows the same.