From 2eb97372ed1f042089747b657977a7c39521843f Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Mon, 28 Sep 2026 10:44:29 -0700 Subject: [PATCH 1/2] feat!: rename announce events to Start/Update/End/Live Mirror the wire (ANNOUNCE_START/UPDATE/END) across every surface: - Rust moq_net::announce::Event: Announced/Updated/Retracted become Start/Update/End. - JS Announce.Event kind: "announced"/"updated"/"retracted" become "start"/"update"/"end". - moq-ffi MoqAnnounceEvent and the Go, Kotlin, Dart, and Python aliases: AnnounceEventStart/Update/End/Live. Swift cases: .start/.update/.end/.live. - moq-c: moq_announce_update is moq_announce_event, and the kind values are MOQ_ANNOUNCE_KIND_START/UPDATE/END/LIVE. Semantics and wire encodings are unchanged. Co-Authored-By: Claude Opus 5.5 --- dart/moq/README.md | 2 +- dart/moq/lib/src/aliases.dart | 10 +-- dart/moq/test/moq_test.dart | 24 +++--- dart/moq_ffi/lib/src/moq.dart | 38 +++++----- demo/web/src/index.ts | 2 +- demo/web/src/stats.ts | 2 +- doc/concept/moq-lite.md | 6 +- doc/lib/c/index.md | 2 +- doc/lib/dart/index.md | 4 +- doc/lib/go/index.md | 6 +- doc/lib/js/net.md | 6 +- doc/lib/kt/index.md | 4 +- doc/lib/py/index.md | 6 +- doc/lib/rs/index.md | 2 +- doc/lib/rs/moq-net.md | 4 +- doc/lib/swift/index.md | 4 +- go/wrapper/README.md | 2 +- go/wrapper/example_test.go | 2 +- go/wrapper/moq_test.go | 6 +- go/wrapper/reconnect_test.go | 2 +- go/wrapper/types.go | 16 ++-- js/moq-boy/src/element.tsx | 4 +- js/net/examples/discovery.ts | 2 +- js/net/src/announced.test.ts | 28 +++---- js/net/src/announced.ts | 6 +- js/net/src/connection/forward.test.ts | 14 ++-- js/net/src/connection/forward.ts | 2 +- js/net/src/connection/pool.ts | 4 +- js/net/src/connection/reload.test.ts | 2 +- js/net/src/connection/reload.ts | 4 +- js/net/src/connection/scope.test.ts | 2 +- js/net/src/ietf/subscriber.test.ts | 54 +++++++------- js/net/src/ietf/subscriber.ts | 8 +- js/net/src/integration.test.ts | 22 +++--- js/net/src/lite/subscriber.test.ts | 74 +++++++++---------- js/net/src/lite/subscriber.ts | 8 +- js/net/src/origin.test.ts | 60 +++++++-------- js/net/src/origin.ts | 6 +- js/room/src/room.test.ts | 2 +- js/room/src/room.ts | 2 +- js/watch/src/broadcast.ts | 2 +- kt/README.md | 2 +- .../kotlin/dev/moq/Aliases.kt | 10 +-- .../kotlin/dev/moq/SmokeTest.kt | 10 +-- py/moq-rs/README.md | 6 +- py/moq-rs/docs/index.md | 8 +- py/moq-rs/examples/announced.py | 4 +- py/moq-rs/moq/__init__.py | 12 +-- py/moq-rs/moq/origin.py | 10 +-- py/moq-rs/tests/test_local.py | 14 ++-- py/moq-rs/tests/test_server.py | 10 +-- quest/m1/ffi-shape/net.md | 2 +- rs/hang/examples/subscribe.rs | 2 +- rs/moq-bench/src/connection.rs | 8 +- rs/moq-boy/src/input.rs | 6 +- rs/moq-c/README.md | 2 +- rs/moq-c/src/api.rs | 18 ++--- rs/moq-c/src/origin.rs | 12 +-- rs/moq-c/src/test.rs | 20 ++--- rs/moq-cli/src/complete.rs | 4 +- rs/moq-cli/src/ls.rs | 6 +- rs/moq-ffi/src/origin.rs | 14 ++-- rs/moq-ffi/src/test.rs | 14 ++-- rs/moq-net/benches/origin.rs | 2 +- rs/moq-net/src/ietf/publisher.rs | 10 +-- rs/moq-net/src/lite/publisher.rs | 12 +-- rs/moq-net/src/model/origin.rs | 48 ++++++------ rs/moq-net/tests/announce_to_serve.rs | 12 +-- rs/moq-net/tests/caught_up.rs | 2 +- rs/moq-net/tests/goaway.rs | 2 +- rs/moq-relay/src/cluster.rs | 14 ++-- rs/moq-relay/src/internal.rs | 6 +- rs/moq-relay/src/nodes.rs | 5 +- rs/moq-relay/src/web.rs | 2 +- rs/moq-relay/tests/auth_lifetime.rs | 6 +- rs/moq-relay/tests/cluster_unknown.rs | 6 +- rs/moq-relay/tests/drills.rs | 6 +- rs/moq-relay/tests/embed.rs | 6 +- rs/moq-relay/tests/goaway_cluster.rs | 6 +- rs/moq-relay/tests/hidden_cluster.rs | 2 +- rs/moq-relay/tests/runtime_uring.rs | 6 +- rs/moq-relay/tests/runtime_workers.rs | 6 +- rs/moq-relay/tests/smoke.rs | 8 +- rs/moq-room/src/room.rs | 4 +- rs/moq-rtc/src/lib.rs | 6 +- rs/moq-stats/src/aggregate.rs | 10 +-- rs/moq-stats/src/consume.rs | 6 +- rs/moq-stats/src/produce.rs | 6 +- rs/moq-tokio/examples/clock.rs | 4 +- rs/moq-tokio/src/origin.rs | 6 +- rs/moq-tokio/src/server.rs | 6 +- rs/moq-tokio/tests/backend.rs | 6 +- rs/moq-tokio/tests/broadcast.rs | 8 +- rs/moq-tokio/tests/max_age.rs | 2 +- swift/README.md | 6 +- swift/Sources/Moq/Aliases.swift | 2 +- swift/Tests/MoqTests/SmokeTests.swift | 10 +-- 97 files changed, 428 insertions(+), 471 deletions(-) diff --git a/dart/moq/README.md b/dart/moq/README.md index 34f3c8768d..99e8b8335c 100644 --- a/dart/moq/README.md +++ b/dart/moq/README.md @@ -9,7 +9,7 @@ final connection = await Moq.connect('https://relay.example.com'); await for (final event in connection.announcements( options: const AnnounceOptions(prefix: 'live/', filter: '*/camera'), )) { - if (event is AnnounceEventAnnounced) { + if (event is AnnounceEventStart) { // Prefix stays origin-relative; captures reports wildcard matches. print(event.announce.prefix); print(event.announce.captures); diff --git a/dart/moq/lib/src/aliases.dart b/dart/moq/lib/src/aliases.dart index 72fc1bd8bb..742fbe54f4 100644 --- a/dart/moq/lib/src/aliases.dart +++ b/dart/moq/lib/src/aliases.dart @@ -52,18 +52,18 @@ typedef AnnouncedBroadcast = MoqAnnouncedBroadcast; /// A route over a prefix: its origin-relative path, wildcard captures, and route metadata. typedef Announce = MoqAnnounce; -/// What an [AnnounceConsumer] yields: [AnnounceEventAnnounced], -/// [AnnounceEventUpdated], [AnnounceEventRetracted], or [AnnounceEventLive]. +/// What an [AnnounceConsumer] yields: [AnnounceEventStart], +/// [AnnounceEventUpdate], [AnnounceEventEnd], or [AnnounceEventLive]. typedef AnnounceEvent = MoqAnnounceEvent; /// A route now covers the prefix; the stream had none there. -typedef AnnounceEventAnnounced = AnnouncedMoqAnnounceEvent; +typedef AnnounceEventStart = StartMoqAnnounceEvent; /// The route covering the prefix changed hops or cost. -typedef AnnounceEventUpdated = UpdatedMoqAnnounceEvent; +typedef AnnounceEventUpdate = UpdateMoqAnnounceEvent; /// No route covers the prefix any more; carries its last route. -typedef AnnounceEventRetracted = RetractedMoqAnnounceEvent; +typedef AnnounceEventEnd = EndMoqAnnounceEvent; /// Every route live at subscribe time has been delivered; what follows is live changes. typedef AnnounceEventLive = LiveMoqAnnounceEvent; diff --git a/dart/moq/test/moq_test.dart b/dart/moq/test/moq_test.dart index eb4a3747f7..b3a5ff4e83 100644 --- a/dart/moq/test/moq_test.dart +++ b/dart/moq/test/moq_test.dart @@ -39,14 +39,13 @@ void main() { expect(client.bandwidth(), isA()); final announcement = client.announcements().firstWhere( - (event) => event is AnnounceEventAnnounced, + (event) => event is AnnounceEventStart, ); final broadcast = relay.createBroadcast(path: 'live'); final track = broadcast.publishTrack(name: 'events', info: null); broadcast.announce(route: MoqRoute()); final announced = - (await announcement.timeout(timeout) as AnnounceEventAnnounced) - .announce; + (await announcement.timeout(timeout) as AnnounceEventStart).announce; expect(announced.prefix, 'live'); final requested = await client @@ -106,14 +105,13 @@ void main() { final serverSession = await accepted; final announcement = client.announcements().firstWhere( - (event) => event is AnnounceEventAnnounced, + (event) => event is AnnounceEventStart, ); final broadcast = server.createBroadcast('live'); final track = broadcast.publishTrack(name: 'events', info: null); broadcast.announce(route: MoqRoute()); final announced = - (await announcement.timeout(timeout) as AnnounceEventAnnounced) - .announce; + (await announcement.timeout(timeout) as AnnounceEventStart).announce; expect(announced.prefix, 'live'); client.close(); @@ -184,20 +182,20 @@ void main() { broadcast.announce(route: MoqRoute()); final announced = consumer.announced(config: MoqAnnounceConfig()); final first = await nextRoute(announced); - expect(first, isA()); - expect((first as AnnounceEventAnnounced).announce.prefix, 'live'); + expect(first, isA()); + expect((first as AnnounceEventStart).announce.prefix, 'live'); broadcast.unannounce(); final retracted = await nextRoute(announced); - expect(retracted, isA()); - expect((retracted as AnnounceEventRetracted).announce.prefix, 'live'); + expect(retracted, isA()); + expect((retracted as AnnounceEventEnd).announce.prefix, 'live'); await expectLater( consumer.requestBroadcast(path: 'live').timeout(timeout), throwsA(anything), ); broadcast.announce(route: MoqRoute()); - expect(await nextRoute(announced), isA()); + expect(await nextRoute(announced), isA()); await consumer.requestBroadcast(path: 'live').timeout(timeout); announced.cancel(); announced.dispose(); @@ -211,7 +209,7 @@ void main() { final broadcast = origin.createBroadcast(path: 'room/alice/chat'); broadcast.announce(route: MoqRoute()); - final update = await nextRoute(announced) as AnnounceEventAnnounced; + final update = await nextRoute(announced) as AnnounceEventStart; expect(update.announce.prefix, 'room/alice/chat'); expect(update.announce.captures, ['alice']); }); @@ -231,7 +229,7 @@ void main() { final announced = consumer.announced(config: MoqAnnounceConfig()); final first = await announced.next().timeout(timeout); - expect((first as AnnounceEventAnnounced).announce.prefix, 'cam'); + expect((first as AnnounceEventStart).announce.prefix, 'cam'); expect(await announced.next().timeout(timeout), isA()); announced.cancel(); announced.dispose(); diff --git a/dart/moq_ffi/lib/src/moq.dart b/dart/moq_ffi/lib/src/moq.dart index fdedc8dd97..41e2aab5bf 100644 --- a/dart/moq_ffi/lib/src/moq.dart +++ b/dart/moq_ffi/lib/src/moq.dart @@ -3917,19 +3917,19 @@ class FfiConverterMoqAnnounceEvent { final subview = Uint8List.view(buf.buffer, buf.offsetInBytes + 4); switch (index) { case 1: - final lifted = AnnouncedMoqAnnounceEvent.read(subview); + final lifted = StartMoqAnnounceEvent.read(subview); return LiftRetVal( lifted.value, lifted.bytesRead - subview.offsetInBytes + 4, ); case 2: - final lifted = UpdatedMoqAnnounceEvent.read(subview); + final lifted = UpdateMoqAnnounceEvent.read(subview); return LiftRetVal( lifted.value, lifted.bytesRead - subview.offsetInBytes + 4, ); case 3: - final lifted = RetractedMoqAnnounceEvent.read(subview); + final lifted = EndMoqAnnounceEvent.read(subview); return LiftRetVal( lifted.value, lifted.bytesRead - subview.offsetInBytes + 4, @@ -3961,18 +3961,18 @@ class FfiConverterMoqAnnounceEvent { } } -class AnnouncedMoqAnnounceEvent extends MoqAnnounceEvent { +class StartMoqAnnounceEvent extends MoqAnnounceEvent { final MoqAnnounce announce; - AnnouncedMoqAnnounceEvent(MoqAnnounce this.announce); - AnnouncedMoqAnnounceEvent._(MoqAnnounce this.announce); - static LiftRetVal read(Uint8List buf) { + StartMoqAnnounceEvent(MoqAnnounce this.announce); + StartMoqAnnounceEvent._(MoqAnnounce this.announce); + static LiftRetVal read(Uint8List buf) { int new_offset = buf.offsetInBytes; final announce_lifted = FfiConverterMoqAnnounce.read( Uint8List.view(buf.buffer, new_offset), ); final announce = announce_lifted.value; new_offset += announce_lifted.bytesRead; - return LiftRetVal(AnnouncedMoqAnnounceEvent._(announce), new_offset); + return LiftRetVal(StartMoqAnnounceEvent._(announce), new_offset); } @override @@ -3999,18 +3999,18 @@ class AnnouncedMoqAnnounceEvent extends MoqAnnounceEvent { } } -class UpdatedMoqAnnounceEvent extends MoqAnnounceEvent { +class UpdateMoqAnnounceEvent extends MoqAnnounceEvent { final MoqAnnounce announce; - UpdatedMoqAnnounceEvent(MoqAnnounce this.announce); - UpdatedMoqAnnounceEvent._(MoqAnnounce this.announce); - static LiftRetVal read(Uint8List buf) { + UpdateMoqAnnounceEvent(MoqAnnounce this.announce); + UpdateMoqAnnounceEvent._(MoqAnnounce this.announce); + static LiftRetVal read(Uint8List buf) { int new_offset = buf.offsetInBytes; final announce_lifted = FfiConverterMoqAnnounce.read( Uint8List.view(buf.buffer, new_offset), ); final announce = announce_lifted.value; new_offset += announce_lifted.bytesRead; - return LiftRetVal(UpdatedMoqAnnounceEvent._(announce), new_offset); + return LiftRetVal(UpdateMoqAnnounceEvent._(announce), new_offset); } @override @@ -4037,18 +4037,18 @@ class UpdatedMoqAnnounceEvent extends MoqAnnounceEvent { } } -class RetractedMoqAnnounceEvent extends MoqAnnounceEvent { +class EndMoqAnnounceEvent extends MoqAnnounceEvent { final MoqAnnounce announce; - RetractedMoqAnnounceEvent(MoqAnnounce this.announce); - RetractedMoqAnnounceEvent._(MoqAnnounce this.announce); - static LiftRetVal read(Uint8List buf) { + EndMoqAnnounceEvent(MoqAnnounce this.announce); + EndMoqAnnounceEvent._(MoqAnnounce this.announce); + static LiftRetVal read(Uint8List buf) { int new_offset = buf.offsetInBytes; final announce_lifted = FfiConverterMoqAnnounce.read( Uint8List.view(buf.buffer, new_offset), ); final announce = announce_lifted.value; new_offset += announce_lifted.bytesRead; - return LiftRetVal(RetractedMoqAnnounceEvent._(announce), new_offset); + return LiftRetVal(EndMoqAnnounceEvent._(announce), new_offset); } @override @@ -12844,7 +12844,7 @@ void _checkApiChecksums() { throw UniffiInternalError.panicked("UniFFI API checksum mismatch"); } if (uniffi_moq_ffi_checksum_method_moqannouncedbroadcast_available() != - 64570) { + 17719) { throw UniffiInternalError.panicked("UniFFI API checksum mismatch"); } if (uniffi_moq_ffi_checksum_method_moqannouncedbroadcast_cancel() != 63175) { diff --git a/demo/web/src/index.ts b/demo/web/src/index.ts index dd0352f0c6..2fb7a7a8b6 100644 --- a/demo/web/src/index.ts +++ b/demo/web/src/index.ts @@ -195,7 +195,7 @@ discovery.run((effect) => { // Only catalog-backed broadcasts are watchable streams; this skips the relay's // `.stats` broadcast (see the stats dashboard demo for that one). if (!path.endsWith(".hang") && !path.endsWith(".msf")) continue; - if (entry.kind === "retracted") live.delete(path); + if (entry.kind === "end") live.delete(path); else live.add(path); broadcasts.set([...live].sort()); } diff --git a/demo/web/src/stats.ts b/demo/web/src/stats.ts index ca1a937d0c..f3d77c370f 100644 --- a/demo/web/src/stats.ts +++ b/demo/web/src/stats.ts @@ -136,7 +136,7 @@ discovery.run((effect) => { const node = Net.Path.stripPrefix(prefix, path); if (!node) continue; - if (entry.kind !== "retracted") { + if (entry.kind !== "end") { if (subs.has(node)) continue; const ne = new Signals.Effect(); subs.set(node, ne); diff --git a/doc/concept/moq-lite.md b/doc/concept/moq-lite.md index 1f3f163efd..994e1fb264 100644 --- a/doc/concept/moq-lite.md +++ b/doc/concept/moq-lite.md @@ -137,10 +137,10 @@ prefixes it is told about. A subscriber watching under a root sees advertisements named relative to that root. The pattern scope filters which prefixes are visible without changing a route's prefix. Announce events carry the covered path, captures, and what -happened to it: Rust `announce::Event::{Announced, Updated, Retracted}`, each +happened to it: Rust `announce::Event::{Start, Update, End}`, each holding an `announce::Announce { prefix, captures: Option>, route }`, -and TypeScript `Announce.Event`, whose `kind` is `"announced"`, `"updated"`, or -`"retracted"` alongside the same `Announce.Announce` fields. An update is a +and TypeScript `Announce.Event`, whose `kind` is `"start"`, `"update"`, or +`"end"` alongside the same `Announce.Announce` fields. An update is a reprice in place. Captures are present when the announced prefix pins every wildcard in the most-specific matching scope member. The Rust consumer is a `Stream` and the TypeScript one an async iterable. Both, and every binding over diff --git a/doc/lib/c/index.md b/doc/lib/c/index.md index 4b963cd8a8..3f2aaaa533 100644 --- a/doc/lib/c/index.md +++ b/doc/lib/c/index.md @@ -51,7 +51,7 @@ beside `pkgconfig/`. - **Server.** `moq_server_listen` binds before it returns (a bad address or certificate fails there) and hands each incoming session to `on_request` as a request handle. Read `moq_session_request_path` and `_query` to route and authenticate, then `moq_session_request_accept` (a session handle, with origins like `moq_session_connect`) or `moq_session_request_reject` with an HTTP-style code (401 and 403 become the protocol's unauthorized close). An accepted session reports `1` once SETUP completes and never reconnects. `moq_server_addr` reports an ephemeral port and `moq_server_fingerprints` the hashes a client pins for a `tls_generate` certificate. `moq_server_close` stops listening; its terminal callback fires once the sockets are released. - **Demand.** A watcher on a published track (`moq_publish_track_demand`, `moq_publish_media_demand`, `moq_encode_video_demand`, `moq_encode_audio_demand`) calls `on_demand` with `MOQ_DEMAND_USED` or `MOQ_DEMAND_UNUSED` right away and again on every change, so an encoder on a battery-powered device runs only while someone is watching. The first call is the current state, so a track that went unused before the watcher existed still reports it. `moq_publish_demand_cancel` stops it; the terminal callback still fires. A container has no single demand and is refused. Demand follows the last real subscriber: an origin that served the track drops its source copy on the unused edge, so a cache linger never delays it. Only a relay keeps what it already delivered warm for 30 seconds, and a returning subscriber is served that cache only once the publisher confirms it is still current. - **Requests.** `moq_publish_dynamic` serves subscriptions to tracks the broadcast never declared: each arrives as a request handle, read its name with `moq_track_request_name`, then `moq_track_request_accept` (a raw track handle), `moq_track_request_video` / `_audio` (the media handle `moq_publish_video` / `_audio` return), or `moq_track_request_abort` with an application code the subscriber sees. Without a live handler an unknown name is refused. `moq_publish_track_dynamic` does the same for fetches of groups a track no longer has cached, delivered as `moq_group_request_*` (`sequence`, `priority`, `frame_start`); `moq_group_request_accept` starts the producer at `frame_start` so written frames keep their group indices. Register it with `moq_track_request_dynamic` before accepting a track that was itself requested by a fetch, so that pending group survives the transition. Both handlers stop with `moq_publish_dynamic_cancel`. -- **Everything the bindings can do** ([list](/lib/#what-every-binding-can-do)): media publish and consume with the catalog managed for you, raw pixels and PCM with the codec inside (`moq_encode_video`, `moq_encode_audio`, and the `moq_decode_*` mirrors), raw tracks with timestamps and datagrams, JSON and binary data tracks (snapshot or stream, each advertised in the catalog for as long as it lives), group fetch, catalog sections, shared video properties, and stalled hints. The three advertising operations are `moq_origin_create_broadcast` (unannounced producer, invisible to everyone), `moq_publish_announce` / `moq_publish_unannounce` (exact-path advertisement), `moq_publish_close` (ends the broadcast for good and releases its handle), and `moq_origin_dynamic` (a claim over a path prefix and everything beneath it; `""` for everything). A route is a capability, not an inventory. `moq_origin_announced` takes a `moq_announce_config` (NULL or zeroed for everything): a literal prefix and an optional relative pattern filter; each `moq_announce_update` has a `kind` (`MOQ_ANNOUNCE_KIND_ANNOUNCED`, `_UPDATED`, `_RETRACTED`, or `_LIVE` once every route live when the listener started has been delivered, with no prefix or captures); `prefix` stays relative to the origin, while `captures` reports what each wildcard matched when `has_captures` is true. Paths with a `.`-prefixed segment below the prefix are [hidden](/concept/moq-lite#hidden-broadcasts); set `hidden` or name the dot segment in `prefix` to list them. +- **Everything the bindings can do** ([list](/lib/#what-every-binding-can-do)): media publish and consume with the catalog managed for you, raw pixels and PCM with the codec inside (`moq_encode_video`, `moq_encode_audio`, and the `moq_decode_*` mirrors), raw tracks with timestamps and datagrams, JSON and binary data tracks (snapshot or stream, each advertised in the catalog for as long as it lives), group fetch, catalog sections, shared video properties, and stalled hints. The three advertising operations are `moq_origin_create_broadcast` (unannounced producer, invisible to everyone), `moq_publish_announce` / `moq_publish_unannounce` (exact-path advertisement), `moq_publish_close` (ends the broadcast for good and releases its handle), and `moq_origin_dynamic` (a claim over a path prefix and everything beneath it; `""` for everything). A route is a capability, not an inventory. `moq_origin_announced` takes a `moq_announce_config` (NULL or zeroed for everything): a literal prefix and an optional relative pattern filter; each `moq_announce_event` has a `kind` (`MOQ_ANNOUNCE_KIND_START`, `_UPDATE`, `_END`, or `_LIVE` once every route live when the listener started has been delivered, with no prefix or captures); `prefix` stays relative to the origin, while `captures` reports what each wildcard matched when `has_captures` is true. Paths with a `.`-prefixed segment below the prefix are [hidden](/concept/moq-lite#hidden-broadcasts); set `hidden` or name the dot segment in `prefix` to list them. ```c moq_client_config config; diff --git a/doc/lib/dart/index.md b/doc/lib/dart/index.md index 6ea2cf8af7..55dec882de 100644 --- a/doc/lib/dart/index.md +++ b/doc/lib/dart/index.md @@ -30,7 +30,7 @@ final moq = await Moq.connect('https://relay.example.com'); moq.announcements( options: const AnnounceOptions(prefix: 'live/', filter: '*/camera'), ).listen((event) { - if (event is AnnounceEventAnnounced) { + if (event is AnnounceEventStart) { print(event.announce.prefix); print(event.announce.captures); } else if (event is AnnounceEventLive) { @@ -76,7 +76,7 @@ every path beneath it (`''` for everything; Dart spells the origin method claim should stay advertised, and reject the requests you will not serve. A route is a capability, not an inventory. `announcements(options:)` takes a literal prefix plus an optional relative pattern and yields `AnnounceEvent`s: -`AnnounceEventAnnounced`, `AnnounceEventUpdated`, or `AnnounceEventRetracted` +`AnnounceEventStart`, `AnnounceEventUpdate`, or `AnnounceEventEnd` carrying an `Announce`, whose `prefix` stays origin-relative and whose `captures` reports the wildcard matches, or `AnnounceEventLive` once every route live at subscribe time has been delivered. Paths with diff --git a/doc/lib/go/index.md b/doc/lib/go/index.md index 9b6014fe07..cc30775649 100644 --- a/doc/lib/go/index.md +++ b/doc/lib/go/index.md @@ -37,9 +37,9 @@ for event, err := range announced.All(ctx) { if moq.IsShutdown(err) { break } log.Fatal(err) } - ann, ok := event.(moq.AnnounceEventAnnounced) + ann, ok := event.(moq.AnnounceEventStart) if !ok { - continue // AnnounceEventUpdated, AnnounceEventRetracted, or AnnounceEventLive + continue // AnnounceEventUpdate, AnnounceEventEnd, or AnnounceEventLive } // Prefix stays origin-relative; Captures reports what each wildcard matched. fmt.Printf("captures: %v\n", ann.Announce.Captures) @@ -86,7 +86,7 @@ path beneath it (`""` for everything). Hold the returned `OriginDynamic` while the claim should stay advertised, and reject the requests you will not serve. A route is a capability, not an inventory. `Announced(options)` combines a literal prefix with an optional relative pattern and yields an `AnnounceEvent`: -`AnnounceEventAnnounced`, `AnnounceEventUpdated`, or `AnnounceEventRetracted` +`AnnounceEventStart`, `AnnounceEventUpdate`, or `AnnounceEventEnd` carrying an `Announce`, whose `Prefix` stays relative to the origin and whose `Captures` reports the wildcard matches, or `AnnounceEventLive` once every route live at subscribe time has been delivered. Break on `AnnounceEventLive` to list diff --git a/doc/lib/js/net.md b/doc/lib/js/net.md index 350314dee4..25a2370efa 100644 --- a/doc/lib/js/net.md +++ b/doc/lib/js/net.md @@ -50,7 +50,7 @@ for (;;) { - **Origins** hold the broadcasts, not the connection: closing a session unannounces them but leaves them created for the next one. `origin.request(path)` resolves an announced local broadcast with no round trip, so a page that watches what it publishes reads its own copy, unless a cheaper route announces the same path. A refusal from the most specific route ends the request: `request.closed` settles with the handler's error and no broader route is asked. Create, populate, then `announce()` for an exact path; use `dynamic(prefix, route)` when the set of paths is not known: an exact-path subscribe before the tracks exist is refused, and nobody, local or remote, can see or reach a broadcast until it announces. - **Connections** race WebTransport against WebSocket. `new Connection({ url })` pools one connection per relay URL and reconnects with backoff, which the elements use. Supplying WebTransport/WebSocket options, discovery, delay, or a caller-owned origin selects a private loop; explicit `share: true` refuses those options. `closed` settles when the handle is released (`null` on a clean close); the failure that stopped retrying the current URL is `error`, and a new URL recovers the same handle. A connection owns one send-rate sampler and one `Bandwidth.Allocator`; publishers reserve against it so their encoder targets sum to the estimate instead of each matching it. - **Bandwidth** (`Bandwidth.Allocator`) divides the connection's send-rate estimate by track priority, max-min fair within a tier. An idle track claims nothing. The receive side is untouched. -- **Discovery** by any pattern scope (`origin.announced(scope)`, such as `room/*/chat`; default everything). A route event's `kind` says whether it was announced, updated, or retracted; its `prefix` is the covered prefix relative to the origin, and `captures` reports what the scope's wildcards matched when the prefix pins them. One `{ kind: "live" }` marker, with no prefix, follows the routes live at subscribe time. The consumer is an async iterable. `origin.broadcasts(scope)` is a live `Getter>` of the same covered prefixes for UIs that need the current set. A borrowed `Connection.origin` also exposes `dynamic(prefix, route)` for serving paths on demand. +- **Discovery** by any pattern scope (`origin.announced(scope)`, such as `room/*/chat`; default everything). A route event's `kind` says whether the route started, updated, or ended; its `prefix` is the covered prefix relative to the origin, and `captures` reports what the scope's wildcards matched when the prefix pins them. One `{ kind: "live" }` marker, with no prefix, follows the routes live at subscribe time. The consumer is an async iterable. `origin.broadcasts(scope)` is a live `Getter>` of the same covered prefixes for UIs that need the current set. A borrowed `Connection.origin` also exposes `dynamic(prefix, route)` for serving paths on demand. - **Publisher retention** is optional `Track.Info.maxAge`: omission sets no limit, while zero keeps the live edge. No implicit five-second window is applied. - **Subscriptions** carry a priority, a `Time.Milli` max age, and optional `groups` bounds. Groups arrive out of order and are read frame by frame, with `Error.TooFarBehind` when a reader asks for a frame the group never held and `Error.GroupTooLarge` when a write exceeds the cache budget and aborts the group. - **Track ends**: `close()` ends a track at its live edge, while `finishAt(n)` declares the exclusive end ahead of it and still accepts the groups below. A subscriber reads the end with `final()` or awaits `finished()`. A remote track ends only once every group below its end has arrived or was dropped; one reset before its header arrived is skipped after the subscription's max age on moq-lite (one second without one), or after one second on IETF. @@ -100,8 +100,8 @@ Three operations, on an origin: route is always a prefix on every wire. A route is a capability, not an inventory. `origin.announced(scope)` yields -`Announce.Event` values. `kind` is `"announced"`, `"updated"` (a reprice in -place), or `"retracted"`, each also carrying an `Announce.Announce`: `prefix` +`Announce.Event` values. `kind` is `"start"`, `"update"` (a reprice in +place), or `"end"`, each also carrying an `Announce.Announce`: `prefix` is the covered prefix relative to the origin, `captures` is one pattern per scope wildcard when the prefix pins a complete match (otherwise `undefined`), and `route` carries hops and cost (on a retraction, its last values). A single diff --git a/doc/lib/kt/index.md b/doc/lib/kt/index.md index 9d5bb2d24c..2668c37f22 100644 --- a/doc/lib/kt/index.md +++ b/doc/lib/kt/index.md @@ -26,7 +26,7 @@ import dev.moq.* // Subscribe. The Flow is live, so run it in its own coroutine. Moq.connect("https://relay.example.com", tlsRoots = listOf("ca.pem")).use { moq -> moq.announcements(AnnounceConfig(prefix = "live/", filter = "*/camera")).collect { event -> - if (event !is AnnounceEventAnnounced) return@collect // Updated, Retracted, or Live + if (event !is AnnounceEventStart) return@collect // Update, End, or Live // Prefixes stay origin-relative; captures reports what each wildcard matched. println(event.announce.captures) val broadcast = moq.requestBroadcast(event.announce.prefix) @@ -66,7 +66,7 @@ path beneath it (`""` for everything). Hold the returned `OriginDynamic` while the claim should stay advertised, and reject the requests you will not serve. A route is a capability, not an inventory. `announcements(config)` takes a literal prefix plus an optional relative pattern and yields `AnnounceEvent`s: -`AnnounceEventAnnounced`, `AnnounceEventUpdated`, or `AnnounceEventRetracted` +`AnnounceEventStart`, `AnnounceEventUpdate`, or `AnnounceEventEnd` carrying an `Announce`, whose `prefix` stays origin-relative and whose `captures` reports the wildcard matches, or `AnnounceEventLive` once every route live at subscribe time has been delivered. Paths with diff --git a/doc/lib/py/index.md b/doc/lib/py/index.md index 938735ac98..7eb3fc1390 100644 --- a/doc/lib/py/index.md +++ b/doc/lib/py/index.md @@ -24,8 +24,8 @@ async def main(): async with moq.Client("https://cdn.moq.dev/anon") as client: # The filter is relative to the literal prefix; prefixes stay origin-relative. async for event in client.announced("live/", filter="*/camera"): - if not isinstance(event, moq.AnnounceEventAnnounced): - continue # AnnounceEventUpdated, AnnounceEventRetracted, or AnnounceEventLive + if not isinstance(event, moq.AnnounceEventStart): + continue # AnnounceEventUpdate, AnnounceEventEnd, or AnnounceEventLive announcement = event.announce print(announcement.captures) # what * matched, or None for a partial overlap broadcast = await client.request_broadcast(announcement.prefix) @@ -86,7 +86,7 @@ an unannounced producer, invisible to everyone; `broadcast.announce(route)` / advertised, and reject the requests you will not serve. A route is a capability, not an inventory. `announced(prefix, filter=...)` combines a literal root with an optional relative pattern and yields `AnnounceEvent`s: -`AnnounceEventAnnounced`, `AnnounceEventUpdated`, or `AnnounceEventRetracted` +`AnnounceEventStart`, `AnnounceEventUpdate`, or `AnnounceEventEnd` carrying an `Announce` as `.announce`, whose `.prefix` stays relative to the origin and whose `.captures` reports what the pattern wildcards matched, or `AnnounceEventLive` once every route live at subscribe time has been delivered. diff --git a/doc/lib/rs/index.md b/doc/lib/rs/index.md index 2a00ac6713..37feac3fab 100644 --- a/doc/lib/rs/index.md +++ b/doc/lib/rs/index.md @@ -56,7 +56,7 @@ let consumer = origin.consume(); let mut announced = consumer.announced(); while let Some(event) = announced.next().await { // Skip retractions, and `Live`, which marks the end of what was already live. - let moq_net::announce::Event::Announced(update) = event else { continue }; + let moq_net::announce::Event::Start(update) = event else { continue }; let broadcast = consumer.request_broadcast(&update.prefix).await?; let catalog = broadcast .track(hang::Catalog::DEFAULT_NAME)? diff --git a/doc/lib/rs/moq-net.md b/doc/lib/rs/moq-net.md index e76c0e236b..7c7c6aa5c7 100644 --- a/doc/lib/rs/moq-net.md +++ b/doc/lib/rs/moq-net.md @@ -133,8 +133,8 @@ overlaps that scope; exact creates and requests must match it, so a broad route can advertise the wire-compatible prefix while excluded requests are refused locally. A disjoint route is `Unauthorized`. -`origin.consume().announced()` yields `announce::Event`s: `Announced`, -`Updated` (a reprice in place), or `Retracted`, each holding an +`origin.consume().announced()` yields `announce::Event`s: `Start`, +`Update` (a reprice in place), or `End`, each holding an `announce::Announce` with `prefix`, the covered prefix relative to the consumer's root; `captures`, what the most specific matching scope member's wildcards stood for when the prefix pins them; and `route`, its hops and cost diff --git a/doc/lib/swift/index.md b/doc/lib/swift/index.md index 51f1d8071e..ca38796654 100644 --- a/doc/lib/swift/index.md +++ b/doc/lib/swift/index.md @@ -29,7 +29,7 @@ let client = Client() let session = try await client.connect(to: "https://relay.example.com") for try await event in try session.consume.announced(prefix: "live/", filter: "*/camera") { - guard case .announced(let announcement) = event else { continue } // .updated, .retracted, or .live + guard case .start(let announcement) = event else { continue } // .update, .end, or .live // Prefixes stay origin-relative; captures reports what each wildcard matched. print(announcement.captures ?? []) let broadcast = try await session.consume.requestBroadcast(path: announcement.prefix) @@ -69,7 +69,7 @@ beneath it (`""` for everything). Hold the returned `OriginDynamic` while the claim should stay advertised, and reject the requests you will not serve. A route is a capability, not an inventory. `announced(prefix:filter:)` combines a literal root with an optional relative pattern and yields `AnnounceEvent`s: -`.announced`, `.updated`, or `.retracted` carrying an `Announce`, whose `prefix` +`.start`, `.update`, or `.end` carrying an `Announce`, whose `prefix` stays relative to the origin and whose `captures` reports what the wildcards matched, or `.live` once every route live at subscribe time has been delivered. Paths with a `.`-prefixed segment below the prefix are [hidden](/concept/moq-lite#hidden-broadcasts) unless diff --git a/go/wrapper/README.md b/go/wrapper/README.md index c16b6e860f..a723411f70 100644 --- a/go/wrapper/README.md +++ b/go/wrapper/README.md @@ -48,7 +48,7 @@ for event, err := range announced.All(ctx) { log.Fatal(err) } switch event := event.(type) { - case moq.AnnounceEventAnnounced: + case moq.AnnounceEventStart: // Prefix stays origin-relative; Captures reports wildcard matches. fmt.Println("got broadcast", event.Announce.Prefix) case moq.AnnounceEventLive: diff --git a/go/wrapper/example_test.go b/go/wrapper/example_test.go index ebc927f33b..2c1987856b 100644 --- a/go/wrapper/example_test.go +++ b/go/wrapper/example_test.go @@ -34,7 +34,7 @@ func ExampleClient_Announced() { log.Fatal(err) } switch event := event.(type) { - case moq.AnnounceEventAnnounced: + case moq.AnnounceEventStart: fmt.Println("broadcast:", event.Announce.Prefix) case moq.AnnounceEventLive: return diff --git a/go/wrapper/moq_test.go b/go/wrapper/moq_test.go index f903b6fd86..781e6c5b98 100644 --- a/go/wrapper/moq_test.go +++ b/go/wrapper/moq_test.go @@ -1316,7 +1316,7 @@ func TestBroadcastIsReachableOnlyWhileAnnounced(t *testing.T) { t.Fatal(err) } event := nextRoute(t, ctx, announced) - if retracted, ok := event.(moq.AnnounceEventRetracted); !ok || retracted.Announce.Prefix != "live" { + if retracted, ok := event.(moq.AnnounceEventEnd); !ok || retracted.Announce.Prefix != "live" { t.Fatalf("unannounce: event=%+v", event) } if _, err := consumer.RequestBroadcast(ctx, "live"); err == nil { @@ -1448,7 +1448,7 @@ func TestAnnouncedYieldsLiveOnceCaughtUp(t *testing.T) { if err != nil { t.Fatal(err) } - if a, ok := event.(moq.AnnounceEventAnnounced); ok { + if a, ok := event.(moq.AnnounceEventStart); ok { listed = append(listed, a.Announce.Prefix) } if _, ok := event.(moq.AnnounceEventLive); ok { @@ -1483,7 +1483,7 @@ func nextAnnounced(t *testing.T, ctx context.Context, announced *moq.AnnounceCon t.Helper() event := nextRoute(t, ctx, announced) - announcedEvent, ok := event.(moq.AnnounceEventAnnounced) + announcedEvent, ok := event.(moq.AnnounceEventStart) if !ok { t.Fatalf("expected an announcement, got %+v", event) } diff --git a/go/wrapper/reconnect_test.go b/go/wrapper/reconnect_test.go index bc8fcdfc3f..89378ebda3 100644 --- a/go/wrapper/reconnect_test.go +++ b/go/wrapper/reconnect_test.go @@ -116,7 +116,7 @@ func awaitAnnouncement(t *testing.T, ctx context.Context, announced *moq.Announc if event == nil { t.Fatalf("announcement stream ended before %q", path) } - if ann, ok := event.(moq.AnnounceEventAnnounced); ok && ann.Announce.Prefix == path { + if ann, ok := event.(moq.AnnounceEventStart); ok && ann.Announce.Prefix == path { return } } diff --git a/go/wrapper/types.go b/go/wrapper/types.go index 0e71ad3cd8..b351d369d0 100644 --- a/go/wrapper/types.go +++ b/go/wrapper/types.go @@ -50,15 +50,15 @@ type ( // Route serving it. It carries no broadcast; resolve a path with // [OriginConsumer.RequestBroadcast]. Announce = ffi.MoqAnnounce - // AnnounceEvent is what an AnnounceConsumer yields: AnnounceEventAnnounced, - // AnnounceEventUpdated, AnnounceEventRetracted, or AnnounceEventLive. + // AnnounceEvent is what an AnnounceConsumer yields: AnnounceEventStart, + // AnnounceEventUpdate, AnnounceEventEnd, or AnnounceEventLive. AnnounceEvent = ffi.MoqAnnounceEvent - // AnnounceEventAnnounced reports a route now covering a prefix that had none. - AnnounceEventAnnounced = ffi.MoqAnnounceEventAnnounced - // AnnounceEventUpdated reports the route covering a prefix changing hops or cost. - AnnounceEventUpdated = ffi.MoqAnnounceEventUpdated - // AnnounceEventRetracted reports that no route covers a prefix any more, carrying its last route. - AnnounceEventRetracted = ffi.MoqAnnounceEventRetracted + // AnnounceEventStart reports a route now covering a prefix that had none. + AnnounceEventStart = ffi.MoqAnnounceEventStart + // AnnounceEventUpdate reports the route covering a prefix changing hops or cost. + AnnounceEventUpdate = ffi.MoqAnnounceEventUpdate + // AnnounceEventEnd reports that no route covers a prefix any more, carrying its last route. + AnnounceEventEnd = ffi.MoqAnnounceEventEnd // AnnounceEventLive reports that every route live at subscribe time has been // delivered; what follows is live changes. Yielded once. AnnounceEventLive = ffi.MoqAnnounceEventLive diff --git a/js/moq-boy/src/element.tsx b/js/moq-boy/src/element.tsx index bde64da8a5..66e78d9751 100644 --- a/js/moq-boy/src/element.tsx +++ b/js/moq-boy/src/element.tsx @@ -144,7 +144,7 @@ export default class MoqBoy extends HTMLElement { if (!capture?.isLiteral) continue; const id = capture.text; - if (entry.kind !== "retracted" && !this.#sessions.has(id)) { + if (entry.kind !== "end" && !this.#sessions.has(id)) { const config: GameConfig = { sessionId: id, connection: this.connection, @@ -156,7 +156,7 @@ export default class MoqBoy extends HTMLElement { const game = new Game(config); this.#sessions.set(id, game); this.games.set(new Map(this.#sessions)); - } else if (entry.kind === "retracted") { + } else if (entry.kind === "end") { const game = this.#sessions.get(id); if (game) { game.close(); diff --git a/js/net/examples/discovery.ts b/js/net/examples/discovery.ts index 12f29cf198..66e3d380a8 100644 --- a/js/net/examples/discovery.ts +++ b/js/net/examples/discovery.ts @@ -14,7 +14,7 @@ async function main() { console.log("Caught up: everything live has been listed"); continue; } - if (announcement.kind === "retracted") continue; + if (announcement.kind === "end") continue; console.log("New stream available:", announcement.prefix); // Subscribe to new streams diff --git a/js/net/src/announced.test.ts b/js/net/src/announced.test.ts index 90cac2bba7..2ff05b0b50 100644 --- a/js/net/src/announced.test.ts +++ b/js/net/src/announced.test.ts @@ -19,11 +19,11 @@ test("next streams every appended event in order", async () => { const consumer = producer.consume(); const route = Route.default; - producer.append({ prefix: p("a"), captures: undefined, kind: "announced", route }); - producer.append({ prefix: p("a"), captures: undefined, kind: "retracted", route }); + producer.append({ prefix: p("a"), captures: undefined, kind: "start", route }); + producer.append({ prefix: p("a"), captures: undefined, kind: "end", route }); - expect(await consumer.next()).toEqual({ prefix: p("a"), captures: undefined, kind: "announced", route }); - expect(await consumer.next()).toEqual({ prefix: p("a"), captures: undefined, kind: "retracted", route }); + expect(await consumer.next()).toEqual({ prefix: p("a"), captures: undefined, kind: "start", route }); + expect(await consumer.next()).toEqual({ prefix: p("a"), captures: undefined, kind: "end", route }); }); test("the consumer is an async iterable of the same events", async () => { @@ -31,12 +31,12 @@ test("the consumer is an async iterable of the same events", async () => { const consumer = producer.consume(); const route = Route.default; - producer.append({ prefix: p("a"), captures: undefined, kind: "announced", route }); - producer.append({ prefix: p("a"), captures: undefined, kind: "retracted", route }); + producer.append({ prefix: p("a"), captures: undefined, kind: "start", route }); + producer.append({ prefix: p("a"), captures: undefined, kind: "end", route }); const events = consumer[Symbol.asyncIterator](); - expect((await events.next()).value?.kind).toBe("announced"); - expect((await events.next()).value?.kind).toBe("retracted"); + expect((await events.next()).value?.kind).toBe("start"); + expect((await events.next()).value?.kind).toBe("end"); // A close drops what was queued and ends the iteration. producer.close(); expect((await events.next()).done).toBe(true); @@ -50,11 +50,11 @@ test("a same-name re-announce is a distinct update", async () => { // than collapsing it. Deciding what a repeat means belongs to the session layer, which resolves // a restart into either nothing (a route change) or an end + start (a new publisher). const route = Route.default; - producer.append({ prefix: p("a"), captures: undefined, kind: "announced", route }); - producer.append({ prefix: p("a"), captures: undefined, kind: "announced", route }); + producer.append({ prefix: p("a"), captures: undefined, kind: "start", route }); + producer.append({ prefix: p("a"), captures: undefined, kind: "start", route }); - expect(await consumer.next()).toEqual({ prefix: p("a"), captures: undefined, kind: "announced", route }); - expect(await consumer.next()).toEqual({ prefix: p("a"), captures: undefined, kind: "announced", route }); + expect(await consumer.next()).toEqual({ prefix: p("a"), captures: undefined, kind: "start", route }); + expect(await consumer.next()).toEqual({ prefix: p("a"), captures: undefined, kind: "start", route }); }); test("the live marker is delivered once", async () => { @@ -64,9 +64,9 @@ test("the live marker is delivered once", async () => { // A stream spanning sessions forwards each one's marker; only the first is news. const route = Route.default; producer.append({ kind: "live" }); - producer.append({ prefix: p("a"), captures: undefined, kind: "announced", route }); + producer.append({ prefix: p("a"), captures: undefined, kind: "start", route }); producer.append({ kind: "live" }); - producer.append({ prefix: p("b"), captures: undefined, kind: "announced", route }); + producer.append({ prefix: p("b"), captures: undefined, kind: "start", route }); expect(await consumer.next()).toEqual({ kind: "live" }); expect(await consumer.next()).toMatchObject({ prefix: p("a") }); diff --git a/js/net/src/announced.ts b/js/net/src/announced.ts index 2bd5f82351..3630c290c8 100644 --- a/js/net/src/announced.ts +++ b/js/net/src/announced.ts @@ -32,15 +32,15 @@ export interface Announce { /** * What an announcement stream yields. * - * `announced`: a route now covers the prefix. `updated`: the route covering it changed - * hops or cost, in place. `retracted`: no route covers it any more. `live`: every route + * `start`: a route now covers the prefix. `update`: the route covering it changed + * hops or cost, in place. `end`: no route covers it any more. `live`: every route * live at subscribe time has been delivered, including those a connected peer was still * sending, so what follows is live changes; yielded at most once, and a caller listing * what is live stops there. * * @public */ -export type Event = ({ kind: "announced" | "updated" | "retracted" } & Announce) | { kind: "live" }; +export type Event = ({ kind: "start" | "update" | "end" } & Announce) | { kind: "live" }; /** * Options for an announcement stream. diff --git a/js/net/src/connection/forward.test.ts b/js/net/src/connection/forward.test.ts index 3b436e7fef..844a50718a 100644 --- a/js/net/src/connection/forward.test.ts +++ b/js/net/src/connection/forward.test.ts @@ -75,7 +75,7 @@ test("a discovery failure under a live session downgrades the origin", async () forwardAnnounced(session.session, origin); // The relay announces a broadcast, which lands in the table. - session.announces.append({ prefix: path, captures: undefined, kind: "announced", route: Route.default }); + session.announces.append({ prefix: path, captures: undefined, kind: "start", route: Route.default }); await settle(); expect(origin.discovery.peek()).toBe(true); expect(wireOf(origin).routes(path)).toBe(true); @@ -116,7 +116,7 @@ test("a request outlives the discovery failure that fed it", async () => { forwardAnnounced(session.session, origin); // Announced, so the table routes it and no blind answer is needed. - session.announces.append({ prefix: path, captures: undefined, kind: "announced", route: Route.default }); + session.announces.append({ prefix: path, captures: undefined, kind: "start", route: Route.default }); await settle(); const request = origin.request(path); @@ -202,8 +202,8 @@ test("the origin's live marker waits for the session's initial set", async () => forwardAnnounced(session.session, origin); const announced = origin.announced(); - session.announces.append({ prefix: Path.from("a"), captures: undefined, kind: "announced", route: Route.default }); - expect(await announced.next()).toMatchObject({ prefix: Path.from("a"), kind: "announced" }); + session.announces.append({ prefix: Path.from("a"), captures: undefined, kind: "start", route: Route.default }); + expect(await announced.next()).toMatchObject({ prefix: Path.from("a"), kind: "start" }); // The peer has not said its initial set is complete. const next = announced.next(); @@ -214,7 +214,7 @@ test("the origin's live marker waits for the session's initial set", async () => // A stream opened after the set landed does not wait on it. const later = origin.announced(); - expect(await later.next()).toMatchObject({ prefix: Path.from("a"), kind: "announced" }); + expect(await later.next()).toMatchObject({ prefix: Path.from("a"), kind: "start" }); expect(await later.next()).toEqual({ kind: "live" }); announced.close(); @@ -281,7 +281,7 @@ test("a scoped session filters announcements and blind requests under its root", session.announces.append({ prefix: Path.from(prefix), captures: undefined, - kind: "announced", + kind: "start", route: Route.default, }); } @@ -318,7 +318,7 @@ test("one failed scoped interest leaves the other routes live until the session for (const name of ["a", "b"]) { sources .get(`${name}/**`) - ?.append({ prefix: Path.from(name), captures: undefined, kind: "announced", route: Route.default }); + ?.append({ prefix: Path.from(name), captures: undefined, kind: "start", route: Route.default }); } await settle(); expect(origin.broadcasts().peek().size).toBe(2); diff --git a/js/net/src/connection/forward.ts b/js/net/src/connection/forward.ts index efe11a69e6..50206b8e44 100644 --- a/js/net/src/connection/forward.ts +++ b/js/net/src/connection/forward.ts @@ -73,7 +73,7 @@ export function forwardAnnounced(conn: Established, origin: OriginProducer): voi } if (!originWire.accepts(event.prefix)) continue; - if (event.kind !== "retracted") { + if (event.kind !== "end") { const existing = inserted.get(event.prefix); if (existing) { existing.update(event.route); diff --git a/js/net/src/connection/pool.ts b/js/net/src/connection/pool.ts index d485a694dd..3b35c13468 100644 --- a/js/net/src/connection/pool.ts +++ b/js/net/src/connection/pool.ts @@ -296,7 +296,7 @@ export class Connection { for (;;) { const entry = await effect.race(upstream.next()); if (!entry) break; - if (entry.kind === "retracted") active.delete(entry.prefix); + if (entry.kind === "end") active.delete(entry.prefix); else if (entry.kind !== "live") active.set(entry.prefix, entry); // The stream delivers the marker once; a later session's is dropped. producer.append(entry); @@ -304,7 +304,7 @@ export class Connection { } finally { if (!closed) { for (const entry of active.values()) { - producer.append({ ...entry, kind: "retracted" }); + producer.append({ ...entry, kind: "end" }); } } } diff --git a/js/net/src/connection/reload.test.ts b/js/net/src/connection/reload.test.ts index 4cfa497405..6230c5baa5 100644 --- a/js/net/src/connection/reload.test.ts +++ b/js/net/src/connection/reload.test.ts @@ -570,7 +570,7 @@ test("closing an announce consumer during upstream teardown does not append retr upstream.append({ prefix: Path.from("alice/camera.hang"), captures: undefined, - kind: "announced", + kind: "start", route: Route.default, }); await consumer.next(); diff --git a/js/net/src/connection/reload.ts b/js/net/src/connection/reload.ts index 21d9c4e54e..d05f98454e 100644 --- a/js/net/src/connection/reload.ts +++ b/js/net/src/connection/reload.ts @@ -473,7 +473,7 @@ export class Reload { for (;;) { const entry = await effect.race(upstream.next()); if (!entry) break; - if (entry.kind === "retracted") active.delete(entry.prefix); + if (entry.kind === "end") active.delete(entry.prefix); else if (entry.kind !== "live") active.set(entry.prefix, entry); // The stream delivers the marker once; a later session's is dropped. producer.append(entry); @@ -485,7 +485,7 @@ export class Reload { // watcher tears down instead of clinging to the dead route. if (consumer.closed.peek() === undefined) { for (const entry of active.values()) { - producer.append({ ...entry, kind: "retracted" }); + producer.append({ ...entry, kind: "end" }); } } } diff --git a/js/net/src/connection/scope.test.ts b/js/net/src/connection/scope.test.ts index 3fc2df36be..61da0e24b7 100644 --- a/js/net/src/connection/scope.test.ts +++ b/js/net/src/connection/scope.test.ts @@ -33,7 +33,7 @@ for (const protocol of [Lite.ALPN_07_WIP, Ietf.ALPN.DRAFT_19]) { const received: string[] = []; for (let index = 0; index < 3; index++) { const update = await withTimeout(announced.next(), 1000, "scoped announcement did not arrive"); - if (update?.kind !== "announced") throw new Error(`expected an announcement, got ${update?.kind}`); + if (update?.kind !== "start") throw new Error(`expected an announcement, got ${update?.kind}`); received.push(update.prefix); } expect(received.sort()).toEqual(["client/other/live", "client/room/.hidden", "client/room/live"]); diff --git a/js/net/src/ietf/subscriber.test.ts b/js/net/src/ietf/subscriber.test.ts index 65bf252454..d70649a79d 100644 --- a/js/net/src/ietf/subscriber.test.ts +++ b/js/net/src/ietf/subscriber.test.ts @@ -97,7 +97,7 @@ test("an unsolicited announcement lands", async () => { const next = await nextRoute(announced); expect(next?.prefix).toBe(Path.from("surprise")); - expect(next?.kind).toBe("announced"); + expect(next?.kind).toBe("start"); // The handler holds the request open until the peer drops it, and withdraws the // namespace on the way out. @@ -107,7 +107,7 @@ test("an unsolicited announcement lands", async () => { await handler; expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("surprise"), - kind: "retracted", + kind: "end", }); }); @@ -143,7 +143,7 @@ async function syncInline(stream: Stream, announced: announce.Consumer, cluster? await inlineNamespace(stream, Path.from("sentinel"), cluster); expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("sentinel"), - kind: "announced", + kind: "start", }); } @@ -169,7 +169,7 @@ test("an announcement survives the first of its two sources ending", async () => new PublishNamespace({ requestId: 0n, trackNamespace: Path.from("both") }), request, ); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("both"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("both"), kind: "start" }); await inlineNamespace(subscription, Path.from("both")); await syncInline(subscription, announced); @@ -205,7 +205,7 @@ test("an announcement ends once its last source does", async () => { new PublishNamespace({ requestId: 0n, trackNamespace: Path.from("both") }), request, ); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("both"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("both"), kind: "start" }); await inlineNamespace(subscription, Path.from("both")); await syncInline(subscription, announced); @@ -219,7 +219,7 @@ test("an announcement ends once its last source does", async () => { await subscription.writer.u53(SubscribeNamespaceEntryDone.id); await new SubscribeNamespaceEntryDone({ suffix: Path.from("both") }).encode(subscription.writer, VERSION); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("both"), kind: "retracted" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("both"), kind: "end" }); }); /** @@ -240,13 +240,13 @@ test("a subscription that dies releases what it advertised", async () => { await acceptSubscribeNamespace(pair.client); await inlineNamespace(streamA, Path.from("orphan")); - expect(await doomed.next()).toMatchObject({ prefix: Path.from("orphan"), kind: "announced" }); - expect(await survivor.next()).toMatchObject({ prefix: Path.from("orphan"), kind: "announced" }); + expect(await doomed.next()).toMatchObject({ prefix: Path.from("orphan"), kind: "start" }); + expect(await survivor.next()).toMatchObject({ prefix: Path.from("orphan"), kind: "start" }); // The stream that advertised it goes away without a NAMESPACE_DONE. streamA.writer.close(); - expect(await survivor.next()).toMatchObject({ prefix: Path.from("orphan"), kind: "retracted" }); + expect(await survivor.next()).toMatchObject({ prefix: Path.from("orphan"), kind: "end" }); }); /** @@ -268,7 +268,7 @@ test("a duplicate legacy publish_namespace is still refused", async () => { new PublishNamespace({ requestId: 0n, trackNamespace: Path.from("twice") }), first, ); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("twice"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("twice"), kind: "start" }); // The same namespace again, on its own request. const second = await Stream.open(pair.server, { version: Version.DRAFT_15 }); @@ -283,7 +283,7 @@ test("a duplicate legacy publish_namespace is still refused", async () => { peer.close(); await handler; - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("twice"), kind: "retracted" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("twice"), kind: "end" }); }); /** @@ -342,7 +342,7 @@ test("concurrent legacy publish_namespace requests take one reference", async () second, ); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("raced"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("raced"), kind: "start" }); await two; // Only one reference was taken, so the surviving request ending retracts the path. @@ -351,7 +351,7 @@ test("concurrent legacy publish_namespace requests take one reference", async () peer.close(); await one; - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("raced"), kind: "retracted" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("raced"), kind: "end" }); }); /** The Hop IDs a cluster-negotiated session declared, ours first. */ @@ -390,10 +390,10 @@ test("an inline NAMESPACE that starts looping back is retracted", async () => { const subscription = await acceptSubscribeNamespace(pair.client); await inlineNamespace(subscription, Path.from("theirs"), { hops: [PEER], cost: 0n }); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("theirs"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("theirs"), kind: "start" }); await inlineNamespace(subscription, Path.from("theirs"), { hops: [SELF, PEER], cost: 0n }); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("theirs"), kind: "retracted" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("theirs"), kind: "end" }); }); /** @@ -412,14 +412,14 @@ test("a repeated NAMESPACE reprices in place", async () => { await inlineNamespace(subscription, Path.from("theirs"), { hops: [PEER], cost: 4n }); expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("theirs"), - kind: "announced", + kind: "start", route: { hops: [PEER], cost: { warm: 4n, cold: 4n } }, }); await inlineNamespace(subscription, Path.from("theirs"), { hops: [PEER], cost: 0n }); expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("theirs"), - kind: "updated", + kind: "update", route: { hops: [PEER], cost: { warm: 0n, cold: 0n } }, }); }); @@ -543,7 +543,7 @@ test("a PUBLISH_NAMESPACE update that starts looping back is detached", async () }), request, ); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("theirs"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("theirs"), kind: "start" }); const peer = await nextStream(pair.client); if (!peer) throw new Error("no PUBLISH_NAMESPACE stream"); @@ -557,7 +557,7 @@ test("a PUBLISH_NAMESPACE update that starts looping back is detached", async () VERSION, ); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("theirs"), kind: "retracted" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("theirs"), kind: "end" }); expect(await peer.reader.u53()).toBe(RequestOk.id); await RequestOk.decode(peer.reader, VERSION); @@ -567,7 +567,7 @@ test("a PUBLISH_NAMESPACE update that starts looping back is detached", async () peer.writer, VERSION, ); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("theirs"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("theirs"), kind: "start" }); expect(await peer.reader.u53()).toBe(RequestOk.id); await RequestOk.decode(peer.reader, VERSION); @@ -599,7 +599,7 @@ test("a PUBLISH_NAMESPACE repricing is acknowledged in place", async () => { ); expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("theirs"), - kind: "announced", + kind: "start", route: { hops: [PEER], cost: { warm: 4n, cold: 4n } }, }); @@ -614,14 +614,14 @@ test("a PUBLISH_NAMESPACE repricing is acknowledged in place", async () => { await RequestOk.decode(peer.reader, VERSION); expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("theirs"), - kind: "updated", + kind: "update", route: { hops: [PEER], cost: { warm: 0n, cold: 0n } }, }); // Still announced: the stream ending is what retracts it. peer.close(); await handler; - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("theirs"), kind: "retracted" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("theirs"), kind: "end" }); }); /** @@ -646,7 +646,7 @@ test("a PUBLISH_NAMESPACE update that changes the publisher is refused", async ( }), request, ); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("theirs"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("theirs"), kind: "start" }); const peer = await nextStream(pair.client); if (!peer) throw new Error("no PUBLISH_NAMESPACE stream"); @@ -663,7 +663,7 @@ test("a PUBLISH_NAMESPACE update that changes the publisher is refused", async ( // The refusal closed the stream, which withdrew the advertisement. await handler; - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("theirs"), kind: "retracted" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("theirs"), kind: "end" }); }); /** @@ -685,7 +685,7 @@ test("a repeated PUBLISH_NAMESPACE is a protocol violation", async () => { }); const request = await Stream.open(pair.server, { version: VERSION }); const handler = subscriber.runPublishNamespace(advert, request); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("theirs"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("theirs"), kind: "start" }); const peer = await nextStream(pair.client); if (!peer) throw new Error("no PUBLISH_NAMESPACE stream"); @@ -693,7 +693,7 @@ test("a repeated PUBLISH_NAMESPACE is a protocol violation", async () => { await advert.encode(peer.writer, VERSION); await expect(handler).rejects.toThrow(ProtocolViolation); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("theirs"), kind: "retracted" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("theirs"), kind: "end" }); }); /** diff --git a/js/net/src/ietf/subscriber.ts b/js/net/src/ietf/subscriber.ts index dd75dc752e..32fd425509 100644 --- a/js/net/src/ietf/subscriber.ts +++ b/js/net/src/ietf/subscriber.ts @@ -204,7 +204,7 @@ export class Subscriber { announced.append({ prefix: active, captures: scopeCaptures(scope, active), - kind: "announced", + kind: "start", route: info.route, }); } @@ -234,7 +234,7 @@ export class Subscriber { for (const [consumer, filter] of this.#announcedConsumers) { if (!sees(filter, path)) continue; const scope = filter.scope; - consumer.append({ prefix: path, captures: scopeCaptures(scope, path), kind: "announced", route }); + consumer.append({ prefix: path, captures: scopeCaptures(scope, path), kind: "start", route }); } } @@ -251,7 +251,7 @@ export class Subscriber { for (const [consumer, filter] of this.#announcedConsumers) { if (!sees(filter, path)) continue; const scope = filter.scope; - consumer.append({ prefix: path, captures: scopeCaptures(scope, path), kind: "updated", route }); + consumer.append({ prefix: path, captures: scopeCaptures(scope, path), kind: "update", route }); } } @@ -280,7 +280,7 @@ export class Subscriber { consumer.append({ prefix: path, captures: scopeCaptures(scope, path), - kind: "retracted", + kind: "end", route: existing.route, }); } catch { diff --git a/js/net/src/integration.test.ts b/js/net/src/integration.test.ts index b634435d30..8ab334cc1b 100644 --- a/js/net/src/integration.test.ts +++ b/js/net/src/integration.test.ts @@ -104,14 +104,14 @@ async function runPublishSubscribeFlow(protocol: string, version?: number) { const entry = await nextRoute(announced); if (!entry) throw new Error("expected entry"); expect(entry.prefix).toBe("test" as Path.Valid); - expect(entry.kind).toBe("announced"); + expect(entry.kind).toBe("start"); // Scoped discovery only echoes the suffix on the wire, but presents the whole path. const prefixed = client.announced(Path.Pattern.subtree(Path.from("root"))); const prefixedEntry = await nextRoute(prefixed); if (!prefixedEntry) throw new Error("expected prefixed entry"); expect(prefixedEntry.prefix).toBe("root/child" as Path.Valid); - expect(prefixedEntry.kind).toBe("announced"); + expect(prefixedEntry.kind).toBe("start"); // Client consumes the broadcast and subscribes to a track const remote = wireOf(client).consume(Path.from("test")); @@ -388,28 +388,28 @@ test("integration: lite draft-06 announce lifecycle", async () => { let entry = await nextRoute(announced); if (!entry) throw new Error("expected announce"); expect(entry.prefix).toBe("first" as Path.Valid); - expect(entry.kind).toBe("announced"); + expect(entry.kind).toBe("start"); // A live announce. const second = publish(origin, Path.from("second")); entry = await nextRoute(announced); if (!entry) throw new Error("expected announce"); expect(entry.prefix).toBe("second" as Path.Valid); - expect(entry.kind).toBe("announced"); + expect(entry.kind).toBe("start"); // Unannounce: retracted by announce id on the wire. second.close(); entry = await nextRoute(announced); if (!entry) throw new Error("expected unannounce"); expect(entry.prefix).toBe("second" as Path.Valid); - expect(entry.kind).toBe("retracted"); + expect(entry.kind).toBe("end"); // Re-announce the same path: a fresh announce assigning a fresh id. const secondAgain = publish(origin, Path.from("second")); entry = await nextRoute(announced); if (!entry) throw new Error("expected re-announce"); expect(entry.prefix).toBe("second" as Path.Valid); - expect(entry.kind).toBe("announced"); + expect(entry.kind).toBe("start"); // Cleanup first.close(); @@ -1913,7 +1913,7 @@ async function runOriginFlow(protocol: string, version?: number) { // The announcement lands in the client's origin. const reader = clientOrigin.consume(); const announced = reader.announced(); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("test"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("test"), kind: "start" }); // Consuming through the origin reaches the wire. const remote = await routed(reader, Path.from("test")); @@ -1923,7 +1923,7 @@ async function runOriginFlow(protocol: string, version?: number) { // Unpublishing retracts the entry over the wire and out of the origin. broadcast.close(); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("test"), kind: "retracted" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("test"), kind: "end" }); await until(() => !wireOf(reader).routes(Path.from("test"))); await serving; @@ -2274,7 +2274,7 @@ test("create then announce is discoverable on the wire", async () => { broadcast.announce(); const entry = await pending; expect(entry?.prefix).toBe("later" as Path.Valid); - expect(entry?.kind).toBe("announced"); + expect(entry?.kind).toBe("start"); announced.close(); broadcast.close(); @@ -2304,7 +2304,7 @@ test("a handle serves a request under live/** over the wire", async () => { const announced = client.announced(); const entry = await nextRoute(announced); expect(entry?.prefix).toBe("live" as Path.Valid); - expect(entry?.kind).toBe("announced"); + expect(entry?.kind).toBe("start"); const remote = wireOf(client).consume(Path.from("live/cam")); const track = remote.track("chat").subscribe().ordered(); @@ -2438,7 +2438,7 @@ async function caughtUp(protocol: string, version: number | undefined, paths: st const event = await withTimeout(announced.next(), 2000, `${protocol || version}: never caught up`); if (!event) throw new Error("announcements ended"); if (event.kind === "live") break; - if (event.kind !== "announced") throw new Error(`only announcements before the marker: got ${event.kind}`); + if (event.kind !== "start") throw new Error(`only announcements before the marker: got ${event.kind}`); seen.push(event.prefix); } diff --git a/js/net/src/lite/subscriber.test.ts b/js/net/src/lite/subscriber.test.ts index 1b8c1331ed..b7d76e5c3a 100644 --- a/js/net/src/lite/subscriber.test.ts +++ b/js/net/src/lite/subscriber.test.ts @@ -162,7 +162,7 @@ test("a max-length chain plus withheld responder is dropped", async () => { ); expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), - kind: "announced", + kind: "start", route: { hops: [PUBLISHER_A, UNKNOWN_HOP] }, }); @@ -185,7 +185,7 @@ test("an unidentified responder keeps hop 0 on a nonempty chain", async () => { ); expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), - kind: "announced", + kind: "start", route: { hops: [PUBLISHER_A, UNKNOWN_HOP] }, }); @@ -204,7 +204,7 @@ test("a received empty hop list is filled with hop 0 and marked anonymous", asyn ); expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), - kind: "announced", + kind: "start", route: { hops: [UNKNOWN_HOP] }, }); @@ -227,7 +227,7 @@ test("a received chain with hop 0 is marked anonymous", async () => { ); expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), - kind: "announced", + kind: "start", }); announced.close(); @@ -247,7 +247,7 @@ test("a restart from the same publisher is a route change, not a republish", asy Version.DRAFT_06, ), ); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "start" }); // Same publisher over an identical route. In-flight subscriptions resume across it, // and there is no metadata to forward, so the subscriber must not surface a restart. @@ -256,19 +256,19 @@ test("a restart from the same publisher is a route change, not a republish", asy // A different publisher took the path: nothing carries over, so this one does surface, // as an end before the start. Reaching it proves the identical reroute above emitted nothing. await send((w) => encodeAnnounceBroadcast(w, { status: "restart", id: 0n, hops: [PUBLISHER_B] }, Version.DRAFT_06)); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "retracted" }); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "end" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "start" }); // A third publisher takes over. The replacement above has to leave its own publisher on // record, or this one reads as a first announcement and skips the end. await send((w) => encodeAnnounceBroadcast(w, { status: "restart", id: 0n, hops: [PUBLISHER_C] }, Version.DRAFT_06)); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "retracted" }); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "end" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "start" }); // And the new owner's own reroute is still transparent. await send((w) => encodeAnnounceBroadcast(w, { status: "restart", id: 0n, hops: [PUBLISHER_C] }, Version.DRAFT_06)); await send((w) => encodeAnnounceBroadcast(w, { status: "endedId", id: 0n }, Version.DRAFT_06)); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "retracted" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "end" }); announced.close(); subscriber.close(); @@ -289,7 +289,7 @@ test("a restart that re-prices the same publisher emits the new route", async () ); expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), - kind: "announced", + kind: "start", route: { hops: [PUBLISHER_A, PEER], cost: { warm: 0n, cold: 0n } }, }); @@ -302,7 +302,7 @@ test("a restart that re-prices the same publisher emits the new route", async () ); expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), - kind: "updated", + kind: "update", route: { hops: [PUBLISHER_A, PEER], cost: { warm: 4n, cold: 4n } }, }); @@ -320,13 +320,13 @@ test("a lite-05 duplicate announce follows the same restart rule", async () => { encodeAnnounceBroadcast(w, { status: "active", suffix: Path.from("room"), hops }, Version.DRAFT_05); await send(active([PUBLISHER_A])); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "start" }); // On lite-05 a restart travels as a duplicate ANNOUNCE rather than its own message. await send(active([PUBLISHER_A])); await send(active([PUBLISHER_B])); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "retracted" }); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "end" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "start" }); announced.close(); subscriber.close(); @@ -346,12 +346,12 @@ test("a restart from an unidentified publisher replaces rather than reroutes", a await send((w) => encodeAnnounceBroadcast(w, { status: "active", suffix: Path.from("room"), hops: [] }, Version.DRAFT_06), ); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "start" }); // Nobody is named on either side, so this is not provably the same content. await send((w) => encodeAnnounceBroadcast(w, { status: "restart", id: 0n, hops: [] }, Version.DRAFT_06)); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "retracted" }); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "end" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "start" }); announced.close(); subscriber.close(); @@ -512,11 +512,11 @@ test("a restart replaces an announce that was skipped as a reflected loop", asyn // The id stays live, so the peer may restart it into a route that is usable here. await send((w) => encodeAnnounceBroadcast(w, { status: "restart", id: 0n, hops: [PUBLISHER_A] }, Version.DRAFT_06)); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "start" }); // Retiring the id ends what the restart attached, and nothing else. await send((w) => encodeAnnounceBroadcast(w, { status: "endedId", id: 0n }, Version.DRAFT_06)); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "retracted" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "end" }); announced.close(); subscriber.close(); @@ -546,13 +546,13 @@ test("retiring an id whose announce was skipped ends nothing", async () => { Version.DRAFT_06, ), ); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("lobby"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("lobby"), kind: "start" }); // Retire the skipped one, then the live one. The first must surface nothing, so the // only end a consumer sees is "lobby". await send((w) => encodeAnnounceBroadcast(w, { status: "endedId", id: 0n }, Version.DRAFT_06)); await send((w) => encodeAnnounceBroadcast(w, { status: "endedId", id: 1n }, Version.DRAFT_06)); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("lobby"), kind: "retracted" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("lobby"), kind: "end" }); announced.close(); subscriber.close(); @@ -566,10 +566,10 @@ test("a draft-02 initial announcement can still be retracted", async () => { // ANNOUNCE_INIT carries the initial set. These are advertisements like any other, so // the peer may retract one later and the consumer has to hear about it. await send((w) => new AnnounceInit([Path.from("room")]).encode(w, Version.DRAFT_02)); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "start" }); await send((w) => encodeAnnounceBroadcast(w, { status: "ended", suffix: Path.from("room") }, Version.DRAFT_02)); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "retracted" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "end" }); announced.close(); subscriber.close(); @@ -591,7 +591,7 @@ test("a duplicate start is reported even when its own route reflects", async () Version.DRAFT_06, ), ); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "start" }); // A second start for that path, carrying a route that loops back through us. Skipping // it must not pre-empt the violation: the peer sent two starts with no end between @@ -647,7 +647,7 @@ test("a violation on a very long path still ends the session", async () => { const long = Path.from("x".repeat(2000)); const active: AnnounceBroadcast = { status: "active", suffix: long, hops: [PUBLISHER_A] }; await send((w) => encodeAnnounceBroadcast(w, active, Version.DRAFT_06)); - expect(await nextRoute(announced)).toMatchObject({ prefix: long, kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: long, kind: "start" }); await send((w) => encodeAnnounceBroadcast(w, active, Version.DRAFT_06)); await expect(nextRoute(announced)).rejects.toThrow("duplicate announce"); @@ -668,7 +668,7 @@ test("a draft-04 duplicate start is a violation, not a restart", async () => { const active: AnnounceBroadcast = { status: "active", suffix: Path.from("room"), hops: [PUBLISHER_A] }; await send((w) => encodeAnnounceBroadcast(w, active, Version.DRAFT_04)); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "start" }); await send((w) => encodeAnnounceBroadcast(w, active, Version.DRAFT_04)); @@ -687,7 +687,7 @@ test("a draft-05 duplicate start is still a restart", async () => { const active: AnnounceBroadcast = { status: "active", suffix: Path.from("room"), hops: [PUBLISHER_A] }; await send((w) => encodeAnnounceBroadcast(w, active, Version.DRAFT_05)); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "start" }); // Same publisher over a new route: transparent, and emphatically not an error. await send((w) => encodeAnnounceBroadcast(w, active, Version.DRAFT_05)); @@ -695,8 +695,8 @@ test("a draft-05 duplicate start is still a restart", async () => { // A different publisher does surface, which is what proves the reroute above passed. const replaced: AnnounceBroadcast = { status: "active", suffix: Path.from("room"), hops: [PUBLISHER_B] }; await send((w) => encodeAnnounceBroadcast(w, replaced, Version.DRAFT_05)); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "retracted" }); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "end" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "start" }); announced.close(); subscriber.close(); @@ -718,9 +718,9 @@ test("the count lands at the last initial start", async () => { await send(start("a")); await send(start("b")); - expect(await announced.next()).toMatchObject({ prefix: Path.from("a"), kind: "announced" }); + expect(await announced.next()).toMatchObject({ prefix: Path.from("a"), kind: "start" }); expect(await announced.next()).toEqual({ kind: "live" }); - expect(await announced.next()).toMatchObject({ prefix: Path.from("b"), kind: "announced" }); + expect(await announced.next()).toMatchObject({ prefix: Path.from("b"), kind: "start" }); announced.close(); subscriber.close(); @@ -737,7 +737,7 @@ test("a count of zero is live before any announcement", async () => { await send((w) => encodeAnnounceBroadcast(w, { status: "active", suffix: Path.from("a"), hops: [] }, Version.DRAFT_05), ); - expect(await announced.next()).toMatchObject({ prefix: Path.from("a"), kind: "announced" }); + expect(await announced.next()).toMatchObject({ prefix: Path.from("a"), kind: "start" }); announced.close(); subscriber.close(); @@ -749,8 +749,8 @@ test("a draft-02 initial set is live after its ANNOUNCE_INIT", async () => { await settle(); await send((w) => new AnnounceInit([Path.from("a"), Path.from("b")]).encode(w, Version.DRAFT_02)); - expect(await announced.next()).toMatchObject({ prefix: Path.from("a"), kind: "announced" }); - expect(await announced.next()).toMatchObject({ prefix: Path.from("b"), kind: "announced" }); + expect(await announced.next()).toMatchObject({ prefix: Path.from("a"), kind: "start" }); + expect(await announced.next()).toMatchObject({ prefix: Path.from("b"), kind: "start" }); expect(await announced.next()).toEqual({ kind: "live" }); announced.close(); @@ -767,8 +767,8 @@ test("a draft-04 initial set is live once the stream goes quiet", async () => { encodeAnnounceBroadcast(w, { status: "active", suffix: Path.from(suffix), hops: [] }, Version.DRAFT_04); await send(start("a")); await send(start("b")); - expect(await announced.next()).toMatchObject({ prefix: Path.from("a"), kind: "announced" }); - expect(await announced.next()).toMatchObject({ prefix: Path.from("b"), kind: "announced" }); + expect(await announced.next()).toMatchObject({ prefix: Path.from("a"), kind: "start" }); + expect(await announced.next()).toMatchObject({ prefix: Path.from("b"), kind: "start" }); expect(await announced.next()).toEqual({ kind: "live" }); announced.close(); diff --git a/js/net/src/lite/subscriber.ts b/js/net/src/lite/subscriber.ts index e8bf9da7f0..0a32bd0afe 100644 --- a/js/net/src/lite/subscriber.ts +++ b/js/net/src/lite/subscriber.ts @@ -274,7 +274,7 @@ export class Subscriber { advertised.set(path, { publisher: undefined, live, route, captures }); if (!live) continue; console.debug(`announced: broadcast=${path} active=true`); - announced.append({ prefix: path, captures, kind: "announced", route }); + announced.append({ prefix: path, captures, kind: "start", route }); } announced.append({ kind: "live" }); break; @@ -385,7 +385,7 @@ export class Subscriber { announced.append({ prefix: path, captures: previous.captures, - kind: "retracted", + kind: "end", route: previous.route, }); }; @@ -459,7 +459,7 @@ export class Subscriber { if (!routesEqual(previous.route, route)) { advertised.set(path, { publisher, live: true, route, captures }); console.debug(`announced: broadcast=${path} rerouted`); - announced.append({ prefix: path, captures, kind: "updated", route }); + announced.append({ prefix: path, captures, kind: "update", route }); } else { console.debug(`announced: broadcast=${path} rerouted`); } @@ -477,7 +477,7 @@ export class Subscriber { advertised.set(path, { publisher, live: true, route, captures }); console.debug(`announced: broadcast=${path} active=true`); - announced.append({ prefix: path, captures, kind: "announced", route }); + announced.append({ prefix: path, captures, kind: "start", route }); } announced.close(); diff --git a/js/net/src/origin.test.ts b/js/net/src/origin.test.ts index ba6bc5204d..88e5c5a80a 100644 --- a/js/net/src/origin.test.ts +++ b/js/net/src/origin.test.ts @@ -324,7 +324,7 @@ test("an empty origin is live at once, and the marker never repeats", async () = const landed = wireOf(origin).replaying(Path.empty()); landed(); const alice = publish(origin, Path.from("alice")); - expect(await announced.next()).toMatchObject({ prefix: Path.from("alice"), kind: "announced" }); + expect(await announced.next()).toMatchObject({ prefix: Path.from("alice"), kind: "start" }); expect(await pending(announced.next())).toBe(true); alice.close(); @@ -341,8 +341,8 @@ test("live follows the replayed routes", async () => { for (;;) { const event = await announced.next(); if (event?.kind === "live") break; - expect(event?.kind).toBe("announced"); - if (event?.kind === "announced") seen.push(event.prefix); + expect(event?.kind).toBe("start"); + if (event?.kind === "start") seen.push(event.prefix); } expect(seen.sort()).toEqual(["b", "c"]); @@ -359,7 +359,7 @@ test("a replaying session withholds live until it lands", async () => { // Routes a session lands arrive before the marker. serve(origin, Path.from("room/alice"), () => new BroadcastProducer().consume()); - expect(await announced.next()).toMatchObject({ prefix: Path.from("room/alice"), kind: "announced" }); + expect(await announced.next()).toMatchObject({ prefix: Path.from("room/alice"), kind: "start" }); // Every session replaying when the stream opened has to land, and a release is idempotent. first(); @@ -414,16 +414,16 @@ test("announced streams the table under a scope with origin-relative paths", asy const announced = consumer.announced(Path.Pattern.subtree(Path.from("room"))); // The initial state arrives first, named from the origin rather than the scope. - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room/a"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room/a"), kind: "start" }); // Additions under the scope stream in; paths outside it are invisible. const b = publish(origin, Path.from("room/b")); publish(origin, Path.from("lobby/c")); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room/b"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room/b"), kind: "start" }); // Removals retract. b.close(); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room/b"), kind: "retracted" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room/b"), kind: "end" }); // The stream ends when the origin closes. origin.close(); @@ -472,10 +472,10 @@ test("a remote entry resolves by path and retracts on dispose", async () => { // Announced streams include remote entries. const announced = consumer.announced(); - expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "announced", route: Route.default }); + expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "start", route: Route.default }); dispose(); - expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "retracted" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "end" }); expect(wireOf(consumer).routes(path)).toBe(false); announced.close(); @@ -493,10 +493,10 @@ test("announced keeps a broader covering route at its prefix", async () => { // The scope filters the route without changing the prefix it claims. const scope = Path.Pattern.subtree(Path.from("room/alice")); const announced = consumer.announced(scope); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "start" }); dispose(); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "retracted" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("room"), kind: "end" }); announced.close(); upstream.close(); @@ -524,7 +524,7 @@ test("a local publish shadows a remote entry", async () => { // One path, one announcement, even though both tables route it. const announced = consumer.announced(); - expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "announced", route: Route.default }); + expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "start", route: Route.default }); // Dropping the local publish falls back to the remote entry without a retraction. local.close(); @@ -648,12 +648,12 @@ test("disposing the newest remote route promotes the fallback", async () => { const disposeNewer = serve(origin, path, provider(newer)); const announced = consumer.announced(); - expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "start" }); // The newer session dies: consumers see a retract then the promoted fallback. disposeNewer(); - expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "retracted" }); - expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "end" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "start" }); const handle = await routed(consumer, path); const track = handle?.track("chat").subscribe(); @@ -721,7 +721,7 @@ test("requests never appear in announced or the table", async () => { const announced = consumer.announced(); publish(origin, Path.from("real")); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("real"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("real"), kind: "start" }); announced.close(); request.close(); @@ -735,12 +735,12 @@ test("a republish retracts then re-announces the path", async () => { publish(origin, path); const announced = consumer.announced(); - expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "start" }); // A new broadcast takes the path: consumers must let go of the superseded one. publish(origin, path); - expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "retracted" }); - expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "end" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "start" }); announced.close(); origin.close(); @@ -1104,7 +1104,7 @@ test("createBroadcast is invisible to everyone until announce", async () => { expect(wireOf(consumer).advertised.peek()?.has(path)).toBe(true); expect(await nextRoute(announced)).toMatchObject({ prefix: path, - kind: "announced", + kind: "start", route: { hops: [], cost: { warm: 4n, cold: 4n } }, }); const first = request.active.peek(); @@ -1114,18 +1114,18 @@ test("createBroadcast is invisible to everyone until announce", async () => { broadcast.unannounce(); expect(wireOf(consumer).routes(path)).toBe(false); expect(wireOf(consumer).advertised.peek()?.has(path)).toBe(false); - expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "retracted" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "end" }); expect(request.active.peek()).toBeUndefined(); // Back on the air through a fresh handle. broadcast.announce(); - expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "start" }); const again = request.active.peek(); expect(again).toBeDefined(); expect(again).not.toBe(first); broadcast.close(); - expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "retracted" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "end" }); request.close(); announced.close(); origin.close(); @@ -1151,8 +1151,8 @@ test("a request prefers a cheaper received route over an announced local broadca // Re-priced below it, the local broadcast wins at once, and the remote front it replaced closes. local.announce({ cost: 0n }); - expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "retracted" }); - expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "announced", route: Route.default }); + expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "end" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: path, kind: "start", route: Route.default }); expect(request.active.peek()).not.toBe(remote); expect(remote?.closed.peek()).not.toBeUndefined(); @@ -1198,7 +1198,7 @@ test("a handle serves a request under live", async () => { const consumer = origin.consume(); const handle = origin.dynamic(Path.from("live")); - expect(await nextRoute(consumer.announced())).toMatchObject({ prefix: Path.from("live"), kind: "announced" }); + expect(await nextRoute(consumer.announced())).toMatchObject({ prefix: Path.from("live"), kind: "start" }); const waiting = handle.requested().next(); const request = consumer.request(Path.from("live/cam")); @@ -1225,11 +1225,11 @@ test("a re-priced route is delivered as an update", async () => { const origin = new Producer(); const handle = origin.dynamic(Path.from("live")); const announced = origin.consume().announced(); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("live"), kind: "announced" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("live"), kind: "start" }); handle.update({ cost: 5n }); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("live"), kind: "updated" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("live"), kind: "update" }); handle.close(); - expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("live"), kind: "retracted" }); + expect(await nextRoute(announced)).toMatchObject({ prefix: Path.from("live"), kind: "end" }); announced.close(); origin.close(); }); @@ -1586,7 +1586,7 @@ test("a rooted producer shares the table and enforces its pattern union", async expect([...nested.broadcasts().peek().keys()]).toEqual([Path.from("alice")]); expect(() => nested.createBroadcast(Path.from("alice/deep"))).toThrow("outside the origin scope"); const announced = nested.announced(); - expect(await announced.next()).toMatchObject({ kind: "announced", prefix: Path.from("alice") }); + expect(await announced.next()).toMatchObject({ kind: "start", prefix: Path.from("alice") }); announced.close(); request.close(); broadcast.close(); diff --git a/js/net/src/origin.ts b/js/net/src/origin.ts index 87758ec648..d036160bb3 100644 --- a/js/net/src/origin.ts +++ b/js/net/src/origin.ts @@ -1427,7 +1427,7 @@ export class Consumer { producer.append({ prefix: path, captures: snap.captures, - kind: "retracted", + kind: "end", route: snap.route, }); } @@ -1437,11 +1437,11 @@ export class Consumer { producer.append({ prefix: path, captures: snap.captures, - kind: "announced", + kind: "start", route: snap.route, }); } else if (!routesEqual(prev.route, snap.route)) { - producer.append({ prefix: path, captures: snap.captures, kind: "updated", route: snap.route }); + producer.append({ prefix: path, captures: snap.captures, kind: "update", route: snap.route }); } } active = next; diff --git a/js/room/src/room.test.ts b/js/room/src/room.test.ts index a0f6c51f4c..b583ad2207 100644 --- a/js/room/src/room.test.ts +++ b/js/room/src/room.test.ts @@ -20,7 +20,7 @@ test("room restores the announce prefix and reconciles local identity changes", let update: Net.Announce.Event | undefined = { prefix: Net.Path.from("room-a/bob/camera.hang"), captures: [Net.Path.Pattern.literal(Net.Path.from("bob/camera.hang"))], - kind: "announced", + kind: "start", route: { hops: [], cost: { warm: 0n, cold: 0n } }, }; const stream = { diff --git a/js/room/src/room.ts b/js/room/src/room.ts index 0ab7b92229..dd8fcb0f2b 100644 --- a/js/room/src/room.ts +++ b/js/room/src/room.ts @@ -96,7 +96,7 @@ export class Room { const local = this.identity.peek(); if (local && parsed.identity === local) continue; - if (update.kind !== "retracted") { + if (update.kind !== "end") { this.#add(parsed.identity, parsed.kind, Moq.Path.from(covered)); } else { this.#remove(parsed.identity, parsed.kind); diff --git a/js/watch/src/broadcast.ts b/js/watch/src/broadcast.ts index 84e5ca8254..0357e572bb 100644 --- a/js/watch/src/broadcast.ts +++ b/js/watch/src/broadcast.ts @@ -205,7 +205,7 @@ export class Broadcast { if (entry.kind === "live") continue; this.#announced.mutate((active) => { if (!active) return; - if (entry.kind === "retracted") active.delete(entry.prefix); + if (entry.kind === "end") active.delete(entry.prefix); else active.add(entry.prefix); }); } diff --git a/kt/README.md b/kt/README.md index bcc78d9584..af610b0b08 100644 --- a/kt/README.md +++ b/kt/README.md @@ -31,7 +31,7 @@ import kotlinx.coroutines.flow.collect Moq.connect("https://relay.example.com").use { moq -> moq.announcements(AnnounceConfig(prefix = "demos/", filter = "*/camera")).collect { event -> // AnnounceEventLive follows the routes live at subscribe time. - if (event !is AnnounceEventAnnounced) return@collect + if (event !is AnnounceEventStart) return@collect // Prefix stays origin-relative; captures reports what * matched. println("got broadcast ${event.announce.prefix}") println("captures ${event.announce.captures}") diff --git a/kt/moq/src/jvmAndAndroidMain/kotlin/dev/moq/Aliases.kt b/kt/moq/src/jvmAndAndroidMain/kotlin/dev/moq/Aliases.kt index fd5243ae91..5561b51e1c 100644 --- a/kt/moq/src/jvmAndAndroidMain/kotlin/dev/moq/Aliases.kt +++ b/kt/moq/src/jvmAndAndroidMain/kotlin/dev/moq/Aliases.kt @@ -38,18 +38,18 @@ typealias AnnouncedBroadcast = uniffi.moq.MoqAnnouncedBroadcast /** A route over a prefix: its origin-relative path, wildcard captures, and route metadata. */ typealias Announce = uniffi.moq.MoqAnnounce /** - * What an [AnnounceConsumer] yields: [AnnounceEventAnnounced], [AnnounceEventUpdated], - * [AnnounceEventRetracted], or [AnnounceEventLive]. + * What an [AnnounceConsumer] yields: [AnnounceEventStart], [AnnounceEventUpdate], + * [AnnounceEventEnd], or [AnnounceEventLive]. */ typealias AnnounceEvent = uniffi.moq.MoqAnnounceEvent // Kotlin cannot reach a sealed class's subtypes through its typealias, so each // variant gets its own. /** A route now covers the prefix; the stream had none there. */ -typealias AnnounceEventAnnounced = uniffi.moq.MoqAnnounceEvent.Announced +typealias AnnounceEventStart = uniffi.moq.MoqAnnounceEvent.Start /** The route covering the prefix changed hops or cost. */ -typealias AnnounceEventUpdated = uniffi.moq.MoqAnnounceEvent.Updated +typealias AnnounceEventUpdate = uniffi.moq.MoqAnnounceEvent.Update /** No route covers the prefix any more; carries its last route. */ -typealias AnnounceEventRetracted = uniffi.moq.MoqAnnounceEvent.Retracted +typealias AnnounceEventEnd = uniffi.moq.MoqAnnounceEvent.End /** Every route live at subscribe time has been delivered; what follows is live changes. */ typealias AnnounceEventLive = uniffi.moq.MoqAnnounceEvent.Live // Broadcast / track / group producers and consumers. diff --git a/kt/moq/src/jvmAndAndroidTest/kotlin/dev/moq/SmokeTest.kt b/kt/moq/src/jvmAndAndroidTest/kotlin/dev/moq/SmokeTest.kt index 4e4cf0ff6b..f54fd64553 100644 --- a/kt/moq/src/jvmAndAndroidTest/kotlin/dev/moq/SmokeTest.kt +++ b/kt/moq/src/jvmAndAndroidTest/kotlin/dev/moq/SmokeTest.kt @@ -362,16 +362,16 @@ class SmokeTest { broadcast.announce(Route()) val announced = consumer.announced(AnnounceConfig()) - val first = assertIs(announced.nextRoute()) + val first = assertIs(announced.nextRoute()) assertEquals("live", first.announce.prefix) broadcast.unannounce() - val retracted = assertIs(announced.nextRoute()) + val retracted = assertIs(announced.nextRoute()) assertEquals("live", retracted.announce.prefix) assertFailsWith { consumer.requestBroadcast("live") } broadcast.announce(Route()) - assertIs(announced.nextRoute()) + assertIs(announced.nextRoute()) consumer.requestBroadcast("live") } } @@ -383,7 +383,7 @@ class SmokeTest { val announced = origin.consume().announced(AnnounceConfig(prefix = "room", filter = "*/chat")) origin.createBroadcast("room/alice/chat").use { broadcast -> broadcast.announce(Route()) - val update = assertIs(announced.nextRoute()) + val update = assertIs(announced.nextRoute()) assertEquals("room/alice/chat", update.announce.prefix) assertEquals(listOf("alice"), update.announce.captures) } @@ -404,7 +404,7 @@ class SmokeTest { val listed = consumer.announcements() .takeWhile { it !is AnnounceEventLive } - .map { (it as AnnounceEventAnnounced).announce.prefix } + .map { (it as AnnounceEventStart).announce.prefix } .toList() assertEquals(listOf("cam"), listed) } diff --git a/py/moq-rs/README.md b/py/moq-rs/README.md index bad2120889..819ce8f9e9 100644 --- a/py/moq-rs/README.md +++ b/py/moq-rs/README.md @@ -29,8 +29,8 @@ import moq async def main(): async with moq.connect("https://cdn.moq.dev/anon") as client: async for event in client.announced(): - if not isinstance(event, moq.AnnounceEventAnnounced): - continue # AnnounceEventUpdated, AnnounceEventRetracted, or AnnounceEventLive + if not isinstance(event, moq.AnnounceEventStart): + continue # AnnounceEventUpdate, AnnounceEventEnd, or AnnounceEventLive # A route covers a prefix and carries no broadcast, so resolve the path. broadcast = await client.request_broadcast(event.announce.prefix) catalog = await broadcast.catalog() @@ -205,7 +205,7 @@ Every handle whose cleanup is `cancel()` is an async context manager, so exiting - `await .requested_broadcast() → BroadcastRequest`. Call `.accept(broadcast)` to serve it, or `.reject(code)` to fail the requester. - Async iterator yielding `BroadcastRequest` - **`OriginConsumer`**. Discover broadcasts. - - `.announced(prefix, filter=None) → AnnounceConsumer` (async iterator of `AnnounceEvent`: `AnnounceEventAnnounced`, `AnnounceEventUpdated`, or `AnnounceEventRetracted` carrying an `Announce`, or `AnnounceEventLive` once caught up); `filter` is a pattern relative to the literal prefix, while each `Announce.prefix` stays origin-relative and `.captures` reports wildcard matches + - `.announced(prefix, filter=None) → AnnounceConsumer` (async iterator of `AnnounceEvent`: `AnnounceEventStart`, `AnnounceEventUpdate`, or `AnnounceEventEnd` carrying an `Announce`, or `AnnounceEventLive` once caught up); `filter` is a pattern relative to the literal prefix, while each `Announce.prefix` stays origin-relative and `.captures` reports wildcard matches - `.announced_broadcast(path) → AnnouncedBroadcast` (awaitable, waits until something serves the path) - `.request_broadcast(path) → BroadcastConsumer` (awaitable; announced now or a dynamic fallback, else raises) diff --git a/py/moq-rs/docs/index.md b/py/moq-rs/docs/index.md index 591c03c21f..c7282ed47d 100644 --- a/py/moq-rs/docs/index.md +++ b/py/moq-rs/docs/index.md @@ -17,7 +17,7 @@ import moq async def main(): async with moq.Client("https://cdn.moq.dev/anon") as client: async for event in client.announced(): - if isinstance(event, moq.AnnounceEventAnnounced): + if isinstance(event, moq.AnnounceEventStart): broadcast = await client.request_broadcast(event.announce.prefix) print(await broadcast.catalog()) @@ -103,9 +103,9 @@ asyncio.run(main()) AnnouncedBroadcast Announce AnnounceEvent - AnnounceEventAnnounced - AnnounceEventUpdated - AnnounceEventRetracted + AnnounceEventStart + AnnounceEventUpdate + AnnounceEventEnd AnnounceEventLive ``` diff --git a/py/moq-rs/examples/announced.py b/py/moq-rs/examples/announced.py index 9598c716fc..66bff90515 100644 --- a/py/moq-rs/examples/announced.py +++ b/py/moq-rs/examples/announced.py @@ -14,9 +14,9 @@ async def run(url: str, prefix: str, tls_verify: bool) -> None: async with moq.Client(url, tls_verify=tls_verify) as client: print(f"watching route announcements under {prefix!r} at {url}") async for event in client.announced(prefix): - if isinstance(event, moq.AnnounceEventAnnounced): + if isinstance(event, moq.AnnounceEventStart): print(f" + {event.announce.prefix}") - elif isinstance(event, moq.AnnounceEventRetracted): + elif isinstance(event, moq.AnnounceEventEnd): print(f" - {event.announce.prefix}") elif isinstance(event, moq.AnnounceEventLive): print(" (caught up; what follows is live)") diff --git a/py/moq-rs/moq/__init__.py b/py/moq-rs/moq/__init__.py index 57a5b10418..7c4f836e0e 100644 --- a/py/moq-rs/moq/__init__.py +++ b/py/moq-rs/moq/__init__.py @@ -13,10 +13,10 @@ AnnounceConsumer, AnnouncedBroadcast, AnnounceEvent, - AnnounceEventAnnounced, + AnnounceEventEnd, AnnounceEventLive, - AnnounceEventRetracted, - AnnounceEventUpdated, + AnnounceEventStart, + AnnounceEventUpdate, BroadcastRequest, OriginConsumer, OriginDynamic, @@ -99,10 +99,10 @@ "AnnounceConsumer", "AnnouncedBroadcast", "AnnounceEvent", - "AnnounceEventAnnounced", + "AnnounceEventEnd", "AnnounceEventLive", - "AnnounceEventRetracted", - "AnnounceEventUpdated", + "AnnounceEventStart", + "AnnounceEventUpdate", "Audio", "AudioCodec", "AudioConsumer", diff --git a/py/moq-rs/moq/origin.py b/py/moq-rs/moq/origin.py index f774ff181d..eaa4887963 100644 --- a/py/moq-rs/moq/origin.py +++ b/py/moq-rs/moq/origin.py @@ -31,18 +31,18 @@ """ AnnounceEvent = MoqAnnounceEvent -"""What :class:`AnnounceConsumer` yields: :data:`AnnounceEventAnnounced`, -:data:`AnnounceEventUpdated`, or :data:`AnnounceEventRetracted` carrying an +"""What :class:`AnnounceConsumer` yields: :data:`AnnounceEventStart`, +:data:`AnnounceEventUpdate`, or :data:`AnnounceEventEnd` carrying an :data:`Announce` as ``announce``, or :data:`AnnounceEventLive`. """ -AnnounceEventAnnounced = MoqAnnounceEvent.ANNOUNCED +AnnounceEventStart = MoqAnnounceEvent.START """A route now covers the prefix; the stream had none there.""" -AnnounceEventUpdated = MoqAnnounceEvent.UPDATED +AnnounceEventUpdate = MoqAnnounceEvent.UPDATE """The route covering the prefix changed hops or cost.""" -AnnounceEventRetracted = MoqAnnounceEvent.RETRACTED +AnnounceEventEnd = MoqAnnounceEvent.END """No route covers the prefix any more; carries its last route.""" AnnounceEventLive = MoqAnnounceEvent.LIVE diff --git a/py/moq-rs/tests/test_local.py b/py/moq-rs/tests/test_local.py index 6f96ce9f1d..4350d97d2c 100644 --- a/py/moq-rs/tests/test_local.py +++ b/py/moq-rs/tests/test_local.py @@ -17,7 +17,7 @@ def create_announced(origin: moq.OriginProducer, path: str) -> moq.BroadcastProd async def routes(announced: moq.AnnounceConsumer): """Yield each newly announced route, skipping the other events such as LIVE.""" async for event in announced: - if isinstance(event, moq.AnnounceEventAnnounced): + if isinstance(event, moq.AnnounceEventStart): yield event.announce @@ -1155,19 +1155,19 @@ async def test_broadcast_is_reachable_only_while_announced(): broadcast.announce() announced = consumer.announced() first = await next_route(announced) - assert isinstance(first, moq.AnnounceEventAnnounced) + assert isinstance(first, moq.AnnounceEventStart) assert first.announce.prefix == "live" broadcast.unannounce() retracted = await next_route(announced) - assert isinstance(retracted, moq.AnnounceEventRetracted) + assert isinstance(retracted, moq.AnnounceEventEnd) assert retracted.announce.prefix == "live" with pytest.raises(Exception): await asyncio.wait_for(consumer.request_broadcast("live"), timeout=5.0) broadcast.announce() back = await next_route(announced) - assert isinstance(back, moq.AnnounceEventAnnounced) + assert isinstance(back, moq.AnnounceEventStart) await asyncio.wait_for(consumer.request_broadcast("live"), timeout=5.0) announced.cancel() track.finish() @@ -1191,7 +1191,7 @@ async def test_announced_yields_live_once_caught_up(): async for event in announced: if isinstance(event, moq.AnnounceEventLive): break - assert isinstance(event, moq.AnnounceEventAnnounced) + assert isinstance(event, moq.AnnounceEventStart) listed.append(event.announce.prefix) assert listed == ["cam"] broadcast.close() @@ -1204,14 +1204,14 @@ async def test_announced_pattern_captures(): dynamic = origin.dynamic("room") overlap = await next_route(announced) - assert isinstance(overlap, moq.AnnounceEventAnnounced) + assert isinstance(overlap, moq.AnnounceEventStart) assert overlap.announce.prefix == "room" assert overlap.announce.captures is None audio = create_announced(origin, "room/alice/audio") chat = create_announced(origin, "room/alice/chat") match = await next_route(announced) - assert isinstance(match, moq.AnnounceEventAnnounced) + assert isinstance(match, moq.AnnounceEventStart) assert match.announce.prefix == "room/alice/chat" assert match.announce.captures == ["alice"] diff --git a/py/moq-rs/tests/test_server.py b/py/moq-rs/tests/test_server.py index b140213656..1b4596a1d3 100644 --- a/py/moq-rs/tests/test_server.py +++ b/py/moq-rs/tests/test_server.py @@ -12,7 +12,7 @@ async def routes(announced: moq.AnnounceConsumer): """Yield each newly announced route, skipping the other events such as LIVE.""" async for event in announced: - if isinstance(event, moq.AnnounceEventAnnounced): + if isinstance(event, moq.AnnounceEventStart): yield event.announce @@ -335,7 +335,7 @@ async def test_broadcast_route_over_wire(): async def test_route_update_observes_restart(): - """A route metadata update arrives as an AnnounceEventUpdated. + """A route metadata update arrives as an AnnounceEventUpdate. The publisher re-prices its announced route; the subscriber observes the new hop chain in place (no retraction), and cancelling retracts it. @@ -353,7 +353,7 @@ async def test_route_update_observes_restart(): ) as client: announced = client.announced() first = await next_route(announced) - assert isinstance(first, moq.AnnounceEventAnnounced) + assert isinstance(first, moq.AnnounceEventStart) assert first.announce.prefix == "routed" assert 42 in first.announce.route.hops assert 77 not in first.announce.route.hops @@ -361,14 +361,14 @@ async def test_route_update_observes_restart(): # The publisher advertises a longer chain: an in-place update. announce.update(moq.Route(hops=[42, 77])) updated = await next_route(announced) - assert isinstance(updated, moq.AnnounceEventUpdated) + assert isinstance(updated, moq.AnnounceEventUpdate) assert updated.announce.prefix == "routed" assert 77 in updated.announce.route.hops # Cancelling retracts the route. announce.cancel() ended = await next_route(announced) - assert isinstance(ended, moq.AnnounceEventRetracted) + assert isinstance(ended, moq.AnnounceEventEnd) assert ended.announce.prefix == "routed" finally: serve_task.cancel() diff --git a/quest/m1/ffi-shape/net.md b/quest/m1/ffi-shape/net.md index c5c7caad83..3b43630b8a 100644 --- a/quest/m1/ffi-shape/net.md +++ b/quest/m1/ffi-shape/net.md @@ -17,7 +17,7 @@ are renamed. - Objects that are only getters become records. Handles with verbs (`Request`, `TrackRequest`, `GroupRequest`) stay objects. - An enum whose variants a wrapper must name spells each variant - `` (`AnnounceEventAnnounced`, `AnnounceEventLive`) in Go, + `` (`AnnounceEventStart`, `AnnounceEventLive`) in Go, Kotlin, Dart, and Python, whatever the generated name. Swift keeps its generated `.` cases, since it cannot alias a case. - `TrackProducer` drops `name`/`is_used`/`used`/`unused` for `demand()`. diff --git a/rs/hang/examples/subscribe.rs b/rs/hang/examples/subscribe.rs index a832f96f96..e1f784652b 100644 --- a/rs/hang/examples/subscribe.rs +++ b/rs/hang/examples/subscribe.rs @@ -46,7 +46,7 @@ async fn run_subscribe(consumer: moq_net::origin::Consumer) -> anyhow::Result<() let mut announced = consumer.announced(); let path = loop { match announced.next().await.context("origin closed")? { - moq_net::announce::Event::Announced(announce) => break announce.prefix, + moq_net::announce::Event::Start(announce) => break announce.prefix, // Nothing else can come before the first announcement. event => anyhow::ensure!(matches!(event, moq_net::announce::Event::Live), "unexpected {event:?}"), } diff --git a/rs/moq-bench/src/connection.rs b/rs/moq-bench/src/connection.rs index e79f0eb1fc..569154968b 100644 --- a/rs/moq-bench/src/connection.rs +++ b/rs/moq-bench/src/connection.rs @@ -336,8 +336,8 @@ async fn subscribe( _ = &mut deadline => break, update = announced.next() => { let update = match update { - Some(moq_net::announce::Event::Announced(update) | moq_net::announce::Event::Updated(update)) => update, - Some(moq_net::announce::Event::Retracted(_) | moq_net::announce::Event::Live) => continue, + Some(moq_net::announce::Event::Start(update) | moq_net::announce::Event::Update(update)) => update, + Some(moq_net::announce::Event::End(_) | moq_net::announce::Event::Live) => continue, None => break, }; let path = update.prefix.to_string(); @@ -362,8 +362,8 @@ async fn subscribe( // there is nothing to spread over. while selected < want { let update = match announced.next().await { - Some(moq_net::announce::Event::Announced(update) | moq_net::announce::Event::Updated(update)) => update, - Some(moq_net::announce::Event::Retracted(_) | moq_net::announce::Event::Live) => continue, + Some(moq_net::announce::Event::Start(update) | moq_net::announce::Event::Update(update)) => update, + Some(moq_net::announce::Event::End(_) | moq_net::announce::Event::Live) => continue, None => break, }; let path = update.prefix.to_string(); diff --git a/rs/moq-boy/src/input.rs b/rs/moq-boy/src/input.rs index d8e48b6432..526df9f9ca 100644 --- a/rs/moq-boy/src/input.rs +++ b/rs/moq-boy/src/input.rs @@ -67,10 +67,8 @@ pub async fn handle_viewers( let mut announced = viewer_origin.announced(); loop { let (update, active) = match announced.next().await { - Some(moq_net::announce::Event::Announced(update) | moq_net::announce::Event::Updated(update)) => { - (update, true) - } - Some(moq_net::announce::Event::Retracted(update)) => (update, false), + Some(moq_net::announce::Event::Start(update) | moq_net::announce::Event::Update(update)) => (update, true), + Some(moq_net::announce::Event::End(update)) => (update, false), Some(moq_net::announce::Event::Live) => continue, None => break, }; diff --git a/rs/moq-c/README.md b/rs/moq-c/README.md index 7ac62ae033..adc0c26f9b 100644 --- a/rs/moq-c/README.md +++ b/rs/moq-c/README.md @@ -61,7 +61,7 @@ int32_t moq_origin_request_cancel(uint32_t task); int32_t moq_origin_announced_broadcast(uint32_t origin, const char *path, uintptr_t path_len, moq_status_callback on_broadcast, void *user_data); int32_t moq_origin_announced_broadcast_cancel(uint32_t task); int32_t moq_origin_announced(uint32_t origin, const moq_announce_config *config, moq_status_callback on_announce, void *user_data); -int32_t moq_origin_announced_info(uint32_t announced, moq_announce_update *dst); +int32_t moq_origin_announced_info(uint32_t announced, moq_announce_event *dst); int32_t moq_origin_announced_free(uint32_t announced); int32_t moq_origin_announced_cancel(uint32_t announced); // config (NULL for everything): filter is relative to the literal prefix, or NULL for **. hidden also lists `.`-named paths. Updates stay relative to the origin. diff --git a/rs/moq-c/src/api.rs b/rs/moq-c/src/api.rs index bc2f248cb1..02eef37ea7 100644 --- a/rs/moq-c/src/api.rs +++ b/rs/moq-c/src/api.rs @@ -715,27 +715,27 @@ pub struct moq_announce_config { pub hidden: bool, } -/// Which announce event a [moq_announce_update] reports. +/// Which announce event a [moq_announce_event] reports. #[repr(C)] #[allow(non_camel_case_types)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum moq_announce_kind { /// A route now covers the prefix; the listener had none there. - MOQ_ANNOUNCE_KIND_ANNOUNCED = 0, + MOQ_ANNOUNCE_KIND_START = 0, /// The route covering the prefix changed hops or cost. - MOQ_ANNOUNCE_KIND_UPDATED = 1, + MOQ_ANNOUNCE_KIND_UPDATE = 1, /// No route covers the prefix any more. - MOQ_ANNOUNCE_KIND_RETRACTED = 2, + MOQ_ANNOUNCE_KIND_END = 2, /// Every route live when the listener started has been delivered; what /// follows is live changes. Delivered once, with no prefix or captures. MOQ_ANNOUNCE_KIND_LIVE = 3, } -/// An announce event from an origin: a route announced, updated, or retracted, +/// An announce event from an origin: a route starting, updating, or ending, /// or the listener catching up. #[repr(C)] #[allow(non_camel_case_types)] -pub struct moq_announce_update { +pub struct moq_announce_event { /// The covered prefix, relative to the origin, NOT NULL terminated pub prefix: *const c_char, pub prefix_len: usize, @@ -1728,7 +1728,7 @@ pub extern "C" fn moq_broadcast_request_free(request: u32) -> i32 { /// Learn about broadcasts matching a pattern scope under an origin. /// /// `config` selects the paths; NULL lists every visible one. Delivered -/// [moq_announce_update] prefixes remain relative to the origin. +/// [moq_announce_event] prefixes remain relative to the origin. /// /// `on_announce` is invoked with a positive announced ID for each event (see /// [moq_announce_kind]), then exactly once more with a terminal code: `0` (stopped cleanly) or a @@ -1781,9 +1781,9 @@ pub unsafe extern "C" fn moq_origin_announced( /// Returns a zero on success, or a negative code on failure. /// /// # Safety -/// - The caller must ensure that `dst` is a valid pointer to a [moq_announce_update] struct. +/// - The caller must ensure that `dst` is a valid pointer to a [moq_announce_event] struct. #[unsafe(no_mangle)] -pub unsafe extern "C" fn moq_origin_announced_info(announced: u32, dst: *mut moq_announce_update) -> i32 { +pub unsafe extern "C" fn moq_origin_announced_info(announced: u32, dst: *mut moq_announce_event) -> i32 { ffi::enter(move || { let announced = ffi::parse_id(announced)?; let dst = unsafe { dst.as_mut() }.ok_or(Error::InvalidPointer)?; diff --git a/rs/moq-c/src/origin.rs b/rs/moq-c/src/origin.rs index 13c6fcd7a4..d251718642 100644 --- a/rs/moq-c/src/origin.rs +++ b/rs/moq-c/src/origin.rs @@ -2,7 +2,7 @@ use std::ffi::c_char; use tokio::sync::oneshot; use crate::ffi::OnStatus; -use crate::{Error, Id, NonZeroSlab, State, moq_announce_kind, moq_announce_update, moq_string}; +use crate::{Error, Id, NonZeroSlab, State, moq_announce_event, moq_announce_kind, moq_string}; /// A spawned task entry: `close` signals shutdown, `callback` delivers status. /// @@ -57,9 +57,9 @@ impl AnnouncedRecord { fn new(event: moq_net::announce::Event) -> Self { use moq_net::announce::Event; let (update, kind) = match event { - Event::Announced(update) => (update, moq_announce_kind::MOQ_ANNOUNCE_KIND_ANNOUNCED), - Event::Updated(update) => (update, moq_announce_kind::MOQ_ANNOUNCE_KIND_UPDATED), - Event::Retracted(update) => (update, moq_announce_kind::MOQ_ANNOUNCE_KIND_RETRACTED), + Event::Start(update) => (update, moq_announce_kind::MOQ_ANNOUNCE_KIND_START), + Event::Update(update) => (update, moq_announce_kind::MOQ_ANNOUNCE_KIND_UPDATE), + Event::End(update) => (update, moq_announce_kind::MOQ_ANNOUNCE_KIND_END), Event::Live => { return Self { prefix: String::new(), @@ -168,9 +168,9 @@ impl Origin { } } - pub fn announced_info(&self, announced: Id, dst: &mut moq_announce_update) -> Result<(), Error> { + pub fn announced_info(&self, announced: Id, dst: &mut moq_announce_event) -> Result<(), Error> { let announced = self.announced.get(announced).ok_or(Error::AnnouncementNotFound)?; - *dst = moq_announce_update { + *dst = moq_announce_event { prefix: announced.prefix.as_ptr().cast::(), prefix_len: announced.prefix.len(), captures: announced.capture_views.as_ptr(), diff --git a/rs/moq-c/src/test.rs b/rs/moq-c/src/test.rs index cbdb4ed547..6f2799028b 100644 --- a/rs/moq-c/src/test.rs +++ b/rs/moq-c/src/test.rs @@ -124,7 +124,7 @@ impl Callback { impl Callback { /// Wait for the next announce event that is not `LIVE`, freeing any `LIVE` ahead of it. - fn recv_route(&self) -> (u32, moq_announce_update) { + fn recv_route(&self) -> (u32, moq_announce_event) { loop { let announced = id(self.recv()); let info = announce_info(announced); @@ -149,8 +149,8 @@ impl Callback { } /// Read an announce event delivered to an `on_announce` callback. -fn announce_info(announced: u32) -> moq_announce_update { - let mut info = moq_announce_update { +fn announce_info(announced: u32) -> moq_announce_event { + let mut info = moq_announce_event { prefix: std::ptr::null(), prefix_len: 0, captures: std::ptr::null(), @@ -1876,7 +1876,7 @@ fn announced_free_lifecycle() { // The first callback is the announcement for our broadcast. let (announced, mut info) = ann_cb.recv_route(); - assert_eq!(info.kind, moq_announce_kind::MOQ_ANNOUNCE_KIND_ANNOUNCED); + assert_eq!(info.kind, moq_announce_kind::MOQ_ANNOUNCE_KIND_START); let got = unsafe { std::slice::from_raw_parts(info.prefix.cast::(), info.prefix_len) }; assert_eq!(got, path, "announced prefix should match"); @@ -2042,7 +2042,7 @@ fn announced_delivers_live_once_caught_up() { let task = id(unsafe { moq_origin_announced(origin, std::ptr::null(), Some(channel_callback), cb.ptr) }); let announced = id(cb.recv()); let info = announce_info(announced); - assert_eq!(info.kind, moq_announce_kind::MOQ_ANNOUNCE_KIND_ANNOUNCED); + assert_eq!(info.kind, moq_announce_kind::MOQ_ANNOUNCE_KIND_START); let got = unsafe { std::slice::from_raw_parts(info.prefix.cast::(), info.prefix_len) }; assert_eq!(got, path); let live = id(cb.recv()); @@ -2067,7 +2067,7 @@ fn local_announce() { let broadcast = publish_broadcast(origin, path); let (announced_id, info) = cb.recv_route(); - assert_eq!(info.kind, moq_announce_kind::MOQ_ANNOUNCE_KIND_ANNOUNCED); + assert_eq!(info.kind, moq_announce_kind::MOQ_ANNOUNCE_KIND_START); let announced_prefix = unsafe { std::str::from_utf8(std::slice::from_raw_parts(info.prefix.cast::(), info.prefix_len)).unwrap() }; @@ -2164,7 +2164,7 @@ fn announced_deactivation() { let broadcast = publish_broadcast(origin, path); let (announced_id, info) = cb.recv_route(); - assert_eq!(info.kind, moq_announce_kind::MOQ_ANNOUNCE_KIND_ANNOUNCED); + assert_eq!(info.kind, moq_announce_kind::MOQ_ANNOUNCE_KIND_START); assert_eq!(moq_origin_announced_free(announced_id), 0); // Going non-live unannounces the broadcast without tearing it down: local @@ -2172,13 +2172,13 @@ fn announced_deactivation() { assert_eq!(moq_publish_unannounce(broadcast), 0); let (deactivated_id, info) = cb.recv_route(); - assert_eq!(info.kind, moq_announce_kind::MOQ_ANNOUNCE_KIND_RETRACTED); + assert_eq!(info.kind, moq_announce_kind::MOQ_ANNOUNCE_KIND_END); assert_eq!(moq_origin_announced_free(deactivated_id), 0); assert!(request_once(origin, path) < 0, "an unannounced broadcast is unroutable"); assert_eq!(unsafe { moq_publish_announce(broadcast, std::ptr::null()) }, 0); let (reannounced_id, info) = cb.recv_route(); - assert_eq!(info.kind, moq_announce_kind::MOQ_ANNOUNCE_KIND_ANNOUNCED); + assert_eq!(info.kind, moq_announce_kind::MOQ_ANNOUNCE_KIND_START); assert_eq!(moq_origin_announced_free(reannounced_id), 0); let _ = request_broadcast(origin, path); @@ -2202,7 +2202,7 @@ fn create_broadcast_is_unroutable_until_announced() { assert_eq!(unsafe { moq_publish_announce(broadcast, std::ptr::null()) }, 0); let _ = request_broadcast(origin, path); let (announced_id, info) = cb.recv_route(); - assert_eq!(info.kind, moq_announce_kind::MOQ_ANNOUNCE_KIND_ANNOUNCED); + assert_eq!(info.kind, moq_announce_kind::MOQ_ANNOUNCE_KIND_START); assert_eq!(moq_origin_announced_free(announced_id), 0); assert_eq!(moq_origin_announced_cancel(announced_task), 0); diff --git a/rs/moq-cli/src/complete.rs b/rs/moq-cli/src/complete.rs index d495eedd08..e4346c81c9 100644 --- a/rs/moq-cli/src/complete.rs +++ b/rs/moq-cli/src/complete.rs @@ -480,12 +480,12 @@ fn broadcasts(_ctx: CompleteCtx<'_>) -> CompletionFuture<'static> { // The root broadcast is the connection path itself, which an unset // `--broadcast` already names; there is no word to insert for it. match event { - moq_net::announce::Event::Announced(announce) | moq_net::announce::Event::Updated(announce) => { + moq_net::announce::Event::Start(announce) | moq_net::announce::Event::Update(announce) => { if !announce.prefix.is_empty() { live.insert(announce.prefix.to_string()); } } - moq_net::announce::Event::Retracted(announce) => { + moq_net::announce::Event::End(announce) => { live.remove(announce.prefix.as_str()); } moq_net::announce::Event::Live => break, diff --git a/rs/moq-cli/src/ls.rs b/rs/moq-cli/src/ls.rs index 0d9f6abf8b..93f3736bc3 100644 --- a/rs/moq-cli/src/ls.rs +++ b/rs/moq-cli/src/ls.rs @@ -74,10 +74,10 @@ async fn list(moq: &MoqSide, args: &Args, net: &crate::Net, out: &mut (impl Asyn }; let (announce, active) = match event { - Event::Announced(announce) => (announce, true), - Event::Retracted(announce) => (announce, false), + Event::Start(announce) => (announce, true), + Event::End(announce) => (announce, false), // A new route for a path already live changes nothing listed. - Event::Updated(_) => continue, + Event::Update(_) => continue, Event::Live if args.follow => continue, Event::Live => break, }; diff --git a/rs/moq-ffi/src/origin.rs b/rs/moq-ffi/src/origin.rs index ca8f84d416..1fa3921e8a 100644 --- a/rs/moq-ffi/src/origin.rs +++ b/rs/moq-ffi/src/origin.rs @@ -198,11 +198,11 @@ impl From for MoqAnnounce { #[derive(Clone, Debug, PartialEq, Eq, uniffi::Enum)] pub enum MoqAnnounceEvent { /// A route now covers the prefix; the stream had none there. - Announced { announce: MoqAnnounce }, + Start { announce: MoqAnnounce }, /// The route covering the prefix changed hops or cost. - Updated { announce: MoqAnnounce }, + Update { announce: MoqAnnounce }, /// No route covers the prefix any more. Carries its last advertised route. - Retracted { announce: MoqAnnounce }, + End { announce: MoqAnnounce }, /// Every route live at subscribe time has been delivered; what follows is /// live changes. Yielded once. Live, @@ -212,13 +212,13 @@ impl From for MoqAnnounceEvent { fn from(event: moq_net::announce::Event) -> Self { use moq_net::announce::Event; match event { - Event::Announced(announce) => Self::Announced { + Event::Start(announce) => Self::Start { announce: announce.into(), }, - Event::Updated(announce) => Self::Updated { + Event::Update(announce) => Self::Update { announce: announce.into(), }, - Event::Retracted(announce) => Self::Retracted { + Event::End(announce) => Self::End { announce: announce.into(), }, Event::Live => Self::Live, @@ -514,7 +514,7 @@ impl MoqAnnounceConsumer { impl MoqAnnouncedBroadcast { /// Wait until the broadcast is announced. Returns `Closed` if cancelled or the origin is closed. /// - /// Its end arrives as a [`MoqAnnounceEvent::Retracted`] on the origin's announcements. + /// Its end arrives as a [`MoqAnnounceEvent::End`] on the origin's announcements. pub async fn available(&self) -> Result, MoqError> { self.task.run(|mut state| async move { state.available().await }).await } diff --git a/rs/moq-ffi/src/test.rs b/rs/moq-ffi/src/test.rs index 2e38d6a2a9..f1430eafe1 100644 --- a/rs/moq-ffi/src/test.rs +++ b/rs/moq-ffi/src/test.rs @@ -205,7 +205,7 @@ async fn next_announced(announced: &MoqAnnounceConsumer) -> MoqAnnounce { loop { match next_event(announced).await { MoqAnnounceEvent::Live => continue, - MoqAnnounceEvent::Announced { announce } => return announce, + MoqAnnounceEvent::Start { announce } => return announce, other => panic!("expected an announcement, got {other:?}"), } } @@ -332,7 +332,7 @@ async fn announced_route_keeps_cold_cost_on_reannounce() { // own `{warm: 0, cold: 0}`. let announced = consumer.announced(MoqAnnounceConfig::default()).unwrap(); let route = loop { - if let MoqAnnounceEvent::Announced { announce } | MoqAnnounceEvent::Updated { announce } = + if let MoqAnnounceEvent::Start { announce } | MoqAnnounceEvent::Update { announce } = next_event(&announced).await && announce.prefix == "cold-route" { @@ -1502,7 +1502,7 @@ async fn create_broadcast_is_invisible_until_announced() { .expect("announce reaches the cursor") .expect("the cursor is still open"); assert!( - matches!(&update, MoqAnnounceEvent::Announced { announce } if announce.prefix == "live"), + matches!(&update, MoqAnnounceEvent::Start { announce } if announce.prefix == "live"), "{update:?}" ); broadcast.close().unwrap(); @@ -1671,7 +1671,7 @@ async fn announced_yields_live_once_caught_up() { let announced = consumer.announced(MoqAnnounceConfig::default()).unwrap(); let first = next_event(&announced).await; assert!( - matches!(&first, MoqAnnounceEvent::Announced { announce } if announce.prefix == "cam"), + matches!(&first, MoqAnnounceEvent::Start { announce } if announce.prefix == "cam"), "{first:?}" ); assert_eq!(next_event(&announced).await, MoqAnnounceEvent::Live); @@ -1776,8 +1776,8 @@ async fn announce_and_unannounce_toggles_discovery() { async fn wait_live(announced: &MoqAnnounceConsumer, active: bool) { loop { match next_event(announced).await { - MoqAnnounceEvent::Announced { announce } if active && announce.prefix == "live" => return, - MoqAnnounceEvent::Retracted { announce } if !active && announce.prefix == "live" => return, + MoqAnnounceEvent::Start { announce } if active && announce.prefix == "live" => return, + MoqAnnounceEvent::End { announce } if !active && announce.prefix == "live" => return, _ => {} } } @@ -3274,7 +3274,7 @@ fn without_runtime() { let announcement = loop { match pollster::block_on(announced.next()).unwrap().unwrap() { MoqAnnounceEvent::Live => continue, - MoqAnnounceEvent::Announced { announce } => break announce, + MoqAnnounceEvent::Start { announce } => break announce, other => panic!("expected an announcement, got {other:?}"), } }; diff --git a/rs/moq-net/benches/origin.rs b/rs/moq-net/benches/origin.rs index 297c2c1764..4827c43e73 100644 --- a/rs/moq-net/benches/origin.rs +++ b/rs/moq-net/benches/origin.rs @@ -266,7 +266,7 @@ fn bench_subscribe(c: &mut Criterion) { b.iter(|| { let mut cursor = fleet.consumer.announced(); let mut replayed = 0; - while let Some(announce::Event::Announced(_)) = cursor.next().now_or_never().flatten() { + while let Some(announce::Event::Start(_)) = cursor.next().now_or_never().flatten() { replayed += 1; } assert_eq!(replayed, publishers); diff --git a/rs/moq-net/src/ietf/publisher.rs b/rs/moq-net/src/ietf/publisher.rs index e7dd065aaa..c48d7e0967 100644 --- a/rs/moq-net/src/ietf/publisher.rs +++ b/rs/moq-net/src/ietf/publisher.rs @@ -1881,10 +1881,10 @@ where let mut initial = std::collections::BTreeMap::new(); while let Some(event) = announced.try_next() { match event { - crate::announce::Event::Announced(update) | crate::announce::Event::Updated(update) => { + crate::announce::Event::Start(update) | crate::announce::Event::Update(update) => { initial.insert(update.prefix.clone(), update); } - crate::announce::Event::Retracted(update) => { + crate::announce::Event::End(update) => { initial.remove(&update.prefix); } crate::announce::Event::Live => {} @@ -1941,12 +1941,10 @@ where while let Poll::Ready(next) = announced.poll_next(waiter) { match next { Some(crate::announce::Event::Live) => continue, - Some( - crate::announce::Event::Announced(update) | crate::announce::Event::Updated(update), - ) => { + Some(crate::announce::Event::Start(update) | crate::announce::Event::Update(update)) => { return Poll::Ready(NamespaceEvent::Update(Some((update, true)))); } - Some(crate::announce::Event::Retracted(update)) => { + Some(crate::announce::Event::End(update)) => { return Poll::Ready(NamespaceEvent::Update(Some((update, false)))); } None => return Poll::Ready(NamespaceEvent::Update(None)), diff --git a/rs/moq-net/src/lite/publisher.rs b/rs/moq-net/src/lite/publisher.rs index c89029d84e..5dd16714de 100644 --- a/rs/moq-net/src/lite/publisher.rs +++ b/rs/moq-net/src/lite/publisher.rs @@ -710,8 +710,8 @@ impl AnnounceRun { // We use `try_next()` to synchronously get the initial updates. while let Some(event) = announced.try_next() { let (update, active) = match event { - announce::Event::Announced(update) | announce::Event::Updated(update) => (update, true), - announce::Event::Retracted(update) => (update, false), + announce::Event::Start(update) | announce::Event::Update(update) => (update, true), + announce::Event::End(update) => (update, false), // The marker only says the origin caught up; the peer learns the // initial set's end from the version's own framing. announce::Event::Live => continue, @@ -745,8 +745,8 @@ impl AnnounceRun { let mut initial: Vec<(crate::PathOwned, Hops, crate::origin::Cost)> = Vec::new(); while let Some(event) = announced.try_next() { let (update, active) = match event { - announce::Event::Announced(update) | announce::Event::Updated(update) => (update, true), - announce::Event::Retracted(update) => (update, false), + announce::Event::Start(update) | announce::Event::Update(update) => (update, true), + announce::Event::End(update) => (update, false), // The marker only says the origin caught up; the peer learns the // initial set's end from the version's own framing. announce::Event::Live => continue, @@ -819,8 +819,8 @@ impl AnnounceRun { }; let (update, active) = match next { - Some(announce::Event::Announced(update) | announce::Event::Updated(update)) => (update, true), - Some(announce::Event::Retracted(update)) => (update, false), + Some(announce::Event::Start(update) | announce::Event::Update(update)) => (update, true), + Some(announce::Event::End(update)) => (update, false), Some(announce::Event::Live) => continue, None => { // The buffer is empty (flushed at the loop top), so FIN now and diff --git a/rs/moq-net/src/model/origin.rs b/rs/moq-net/src/model/origin.rs index 846005b79a..a9345af718 100644 --- a/rs/moq-net/src/model/origin.rs +++ b/rs/moq-net/src/model/origin.rs @@ -716,21 +716,21 @@ impl OriginConsumerState { PendingUpdate::Announce(meta) => { // The consumer has seen this prefix before, so it is a metadata update. let kind = match self.delivered.insert(prefix.clone()) { - true => AnnounceEvent::Announced, - false => AnnounceEvent::Updated, + true => AnnounceEvent::Start, + false => AnnounceEvent::Update, }; (meta, kind) } PendingUpdate::Unannounce(meta) => { self.delivered.remove(&prefix); - (meta, AnnounceEvent::Retracted) + (meta, AnnounceEvent::End) } PendingUpdate::UnannounceAnnounce { old, new } => { // Deliver the retraction now; leave the trailing announce pending so // the next take returns it for the same prefix. self.delivered.remove(&prefix); self.pending.insert(prefix.clone(), PendingUpdate::Announce(new)); - (old, AnnounceEvent::Retracted) + (old, AnnounceEvent::End) } }; self.settled(&prefix); @@ -1075,11 +1075,11 @@ fn hides(heads: &[PathOwned], prefix: &Path) -> bool { #[derive(Clone, Debug)] pub enum AnnounceEvent { /// A route now covers the prefix; the cursor had none there. - Announced(Announce), + Start(Announce), /// The route covering the prefix changed hops or cost; it is delivered in place. - Updated(Announce), + Update(Announce), /// No route covers the prefix any more. Carries its last advertised route. - Retracted(Announce), + End(Announce), /// Every route live at subscribe time has been delivered; what follows is /// live changes. Yielded once, and only after each remote source feeding the /// origin at subscribe time has landed its initial set. @@ -3662,7 +3662,7 @@ impl Consumer { // discovery, not lookup, so a hidden path resolves like any other. let mut announced = consumer.untagged().with_hidden(true).announced(); loop { - if let AnnounceEvent::Announced(announce) | AnnounceEvent::Updated(announce) = announced.next().await? + if let AnnounceEvent::Start(announce) | AnnounceEvent::Update(announce) = announced.next().await? && path.has_prefix(&announce.prefix) { return Some(announce.route); @@ -3947,13 +3947,13 @@ impl AnnounceConsumer { /// Drive the egress announce guards for one update. fn hand_out(&mut self, event: AnnounceEvent) -> AnnounceEvent { match &event { - AnnounceEvent::Announced(announce) | AnnounceEvent::Updated(announce) => { + AnnounceEvent::Start(announce) | AnnounceEvent::Update(announce) => { let scope = self.stats.egress(self.root.join(&announce.prefix)); self.guards .entry(announce.prefix.clone()) .or_insert_with(|| scope.announce()); } - AnnounceEvent::Retracted(announce) => { + AnnounceEvent::End(announce) => { self.guards.remove(&announce.prefix); } AnnounceEvent::Live => {} @@ -4080,7 +4080,7 @@ impl AnnounceConsumer { /// The route of an active event at `expected`. fn active(event: AnnounceEvent, expected: &Path) -> Route { match event { - AnnounceEvent::Announced(announce) | AnnounceEvent::Updated(announce) => { + AnnounceEvent::Start(announce) | AnnounceEvent::Update(announce) => { assert_eq!(announce.prefix, *expected, "wrong prefix"); announce.route } @@ -4103,7 +4103,7 @@ impl AnnounceConsumer { /// The next update must be a retraction at `expected`. pub fn assert_next_ended(&mut self, expected: impl AsPath) { match self.next_route_now().expect("next blocked").expect("no next") { - AnnounceEvent::Retracted(announce) => assert_eq!(announce.prefix, expected.as_path(), "wrong prefix"), + AnnounceEvent::End(announce) => assert_eq!(announce.prefix, expected.as_path(), "wrong prefix"), other => panic!("should be a retraction: got {other:?}"), } } @@ -4359,7 +4359,7 @@ mod tests { loop { match announced.next().now_or_never().expect("blocked").expect("closed") { AnnounceEvent::Live => break, - AnnounceEvent::Announced(announce) => seen.push(announce.prefix.to_string()), + AnnounceEvent::Start(announce) => seen.push(announce.prefix.to_string()), other => panic!("only announcements before the marker: got {other:?}"), } } @@ -4772,19 +4772,19 @@ mod tests { .unwrap(); let mut announced = consumer.announced(); - let Some(Some(AnnounceEvent::Announced(first))) = announced.next_route_now() else { + let Some(Some(AnnounceEvent::Start(first))) = announced.next_route_now() else { panic!("expected the announcement"); }; assert_eq!(first.prefix.as_str(), ""); assert_eq!(first.captures, Some(Vec::new())); drop(exact); - let Some(Some(AnnounceEvent::Retracted(retracted))) = announced.next_route_now() else { + let Some(Some(AnnounceEvent::End(retracted))) = announced.next_route_now() else { panic!("expected the retraction"); }; assert_eq!(retracted.prefix.as_str(), ""); assert_eq!(retracted.captures, Some(Vec::new())); - let Some(Some(AnnounceEvent::Announced(replacement))) = announced.next_route_now() else { + let Some(Some(AnnounceEvent::Start(replacement))) = announced.next_route_now() else { panic!("expected the replacement"); }; assert_eq!(replacement.prefix.as_str(), ""); @@ -5329,7 +5329,7 @@ mod tests { let second = producer.dynamic("live", Route::default().with_cost(1)).unwrap(); let mut announced = producer.consume().announced(); - let Some(Some(AnnounceEvent::Announced(update))) = announced.next_route_now() else { + let Some(Some(AnnounceEvent::Start(update))) = announced.next_route_now() else { panic!("expected the announcement"); }; assert_eq!(update.prefix.as_str(), "live"); @@ -5337,7 +5337,7 @@ mod tests { announced.assert_next_wait(); drop(second); - let Some(Some(AnnounceEvent::Updated(update))) = announced.next_route_now() else { + let Some(Some(AnnounceEvent::Update(update))) = announced.next_route_now() else { panic!("expected the reprice"); }; assert_eq!(update.prefix.as_str(), "live"); @@ -5437,14 +5437,14 @@ mod tests { let server = producer.dynamic("live", Route::default()).unwrap(); let mut announced = producer.consume().announced(); let mut next = || StreamExt::next(&mut announced).now_or_never(); - let Some(Some(AnnounceEvent::Announced(update))) = next() else { + let Some(Some(AnnounceEvent::Start(update))) = next() else { panic!("expected the replayed route"); }; assert_eq!(update.prefix.as_str(), "live"); assert!(matches!(next(), Some(Some(AnnounceEvent::Live)))); assert!(next().is_none()); drop(server); - assert!(matches!(next(), Some(Some(AnnounceEvent::Retracted(_))))); + assert!(matches!(next(), Some(Some(AnnounceEvent::End(_))))); } #[tokio::test] @@ -5649,7 +5649,7 @@ mod tests { // The newest identical route wins, so the local twin takes over. let local = producer.dynamic("room", route).unwrap(); match announced.next().now_or_never().expect("next blocked").expect("no next") { - AnnounceEvent::Updated(announce) => assert_eq!(announce.route.source(), Source::Local), + AnnounceEvent::Update(announce) => assert_eq!(announce.route.source(), Source::Local), other => panic!("should be an update: got {other:?}"), } @@ -7009,7 +7009,7 @@ mod tests { let alice = producer.create_broadcast("room/alice/chat").unwrap(); alice.announce(Route::default()).unwrap(); - let Some(AnnounceEvent::Announced(update)) = announced.try_next_route() else { + let Some(AnnounceEvent::Start(update)) = announced.try_next_route() else { panic!("expected alice's chat"); }; assert_eq!(update.prefix.as_str(), "room/alice/chat"); @@ -7020,7 +7020,7 @@ mod tests { announced.assert_next_wait(); let broad = producer.announce("room", Route::default()).unwrap(); - let Some(AnnounceEvent::Announced(update)) = announced.try_next_route() else { + let Some(AnnounceEvent::Start(update)) = announced.try_next_route() else { panic!("expected the overlapping broad route"); }; assert_eq!(update.prefix.as_str(), "room"); @@ -7039,7 +7039,7 @@ mod tests { local.announce(Route::default()).unwrap(); let mut announced = producer.consume().announced(); - let Some(AnnounceEvent::Announced(update)) = announced.try_next_route() else { + let Some(AnnounceEvent::Start(update)) = announced.try_next_route() else { panic!("expected one winning route"); }; assert_eq!(update.prefix.as_str(), "room/alice"); diff --git a/rs/moq-net/tests/announce_to_serve.rs b/rs/moq-net/tests/announce_to_serve.rs index 8841e5d922..4e2bfc621b 100644 --- a/rs/moq-net/tests/announce_to_serve.rs +++ b/rs/moq-net/tests/announce_to_serve.rs @@ -42,9 +42,9 @@ fn drain(announced: &mut moq_net::announce::Consumer) -> Vec { let mut seen = Vec::new(); while let Some(event) = announced.try_next() { let (kind, announce) = match event { - Event::Announced(announce) => ("Announced", announce), - Event::Updated(announce) => ("Updated", announce), - Event::Retracted(announce) => ("Retracted", announce), + Event::Start(announce) => ("Start", announce), + Event::Update(announce) => ("Update", announce), + Event::End(announce) => ("End", announce), Event::Live => continue, }; seen.push(format!("{kind} {}", announce.prefix)); @@ -207,11 +207,11 @@ async fn lifecycle(observer: Observer) -> Vec { const EXPECTED: &[&str] = &[ "created: [] unroutable", - "announced: [\"Announced bcast\"] ok", + "announced: [\"Start bcast\"] ok", "in flight: before", - "unannounced: [\"Retracted bcast\"] unroutable closed=true", + "unannounced: [\"End bcast\"] unroutable closed=true", "draining: after then end", - "reannounced: [\"Announced bcast\"] ok fresh=true", + "reannounced: [\"Start bcast\"] ok fresh=true", "serving again: again", ]; diff --git a/rs/moq-net/tests/caught_up.rs b/rs/moq-net/tests/caught_up.rs index ded65f45c2..9f597529a5 100644 --- a/rs/moq-net/tests/caught_up.rs +++ b/rs/moq-net/tests/caught_up.rs @@ -59,7 +59,7 @@ async fn caught_up(version: &str, paths: &[&str]) -> (Vec, Duration) { loop { match announced.next().await.expect("cursor closed") { announce::Event::Live => break, - announce::Event::Announced(update) => live.push(update.prefix.to_string()), + announce::Event::Start(update) => live.push(update.prefix.to_string()), other => panic!("{version}: only announcements before the marker: got {other:?}"), } } diff --git a/rs/moq-net/tests/goaway.rs b/rs/moq-net/tests/goaway.rs index 0f8c64d085..0862d0a50a 100644 --- a/rs/moq-net/tests/goaway.rs +++ b/rs/moq-net/tests/goaway.rs @@ -443,7 +443,7 @@ async fn goaway_drains_routes(version: Version) { // re-prices the route in place, which arrives as another active update. let mut announced = sub.announced(); loop { - let (moq_net::announce::Event::Announced(update) | moq_net::announce::Event::Updated(update)) = + let (moq_net::announce::Event::Start(update) | moq_net::announce::Event::Update(update)) = announced.next().await.expect("update") else { continue; diff --git a/rs/moq-relay/src/cluster.rs b/rs/moq-relay/src/cluster.rs index d6ce31c472..c7b9539b3f 100644 --- a/rs/moq-relay/src/cluster.rs +++ b/rs/moq-relay/src/cluster.rs @@ -1664,8 +1664,8 @@ impl Cluster { tokio::select! { ann = announced.next() => { let (update, active) = match ann { - Some(moq_net::announce::Event::Announced(update) | moq_net::announce::Event::Updated(update)) => (update, true), - Some(moq_net::announce::Event::Retracted(update)) => (update, false), + Some(moq_net::announce::Event::Start(update) | moq_net::announce::Event::Update(update)) => (update, true), + Some(moq_net::announce::Event::End(update)) => (update, false), Some(moq_net::announce::Event::Live) => continue, None => return, }; @@ -2308,10 +2308,8 @@ mod tests { async fn next_update(announced: &mut moq_net::announce::Consumer) -> Option<(moq_net::announce::Announce, bool)> { loop { return match announced.next().await? { - moq_net::announce::Event::Announced(route) | moq_net::announce::Event::Updated(route) => { - Some((route, true)) - } - moq_net::announce::Event::Retracted(route) => Some((route, false)), + moq_net::announce::Event::Start(route) | moq_net::announce::Event::Update(route) => Some((route, true)), + moq_net::announce::Event::End(route) => Some((route, false)), moq_net::announce::Event::Live => continue, }; } @@ -2321,7 +2319,7 @@ mod tests { fn try_next_announced(announced: &mut moq_net::announce::Consumer) -> Option { loop { return match announced.try_next()? { - moq_net::announce::Event::Announced(route) => Some(route), + moq_net::announce::Event::Start(route) => Some(route), moq_net::announce::Event::Live => continue, other => panic!("expected an announcement: got {other:?}"), }; @@ -3121,7 +3119,7 @@ mod tests { // The self-registration route must be visible on the origin. // The watcher subscribed to an empty origin, so its marker comes first. assert!(matches!(watcher.try_next(), Some(moq_net::announce::Event::Live))); - let Some(moq_net::announce::Event::Announced(update)) = watcher.try_next() else { + let Some(moq_net::announce::Event::Start(update)) = watcher.try_next() else { panic!("self-registration must be published"); }; assert_eq!(update.prefix.as_str(), ".internal/origins/rendezvous.example.com:4443"); diff --git a/rs/moq-relay/src/internal.rs b/rs/moq-relay/src/internal.rs index be57c7c413..c56f4f5ad6 100644 --- a/rs/moq-relay/src/internal.rs +++ b/rs/moq-relay/src/internal.rs @@ -643,10 +643,8 @@ mod tests { async fn next_update(announced: &mut moq_net::announce::Consumer) -> Option<(moq_net::announce::Announce, bool)> { loop { return match announced.next().await? { - moq_net::announce::Event::Announced(route) | moq_net::announce::Event::Updated(route) => { - Some((route, true)) - } - moq_net::announce::Event::Retracted(route) => Some((route, false)), + moq_net::announce::Event::Start(route) | moq_net::announce::Event::Update(route) => Some((route, true)), + moq_net::announce::Event::End(route) => Some((route, false)), moq_net::announce::Event::Live => continue, }; } diff --git a/rs/moq-relay/src/nodes.rs b/rs/moq-relay/src/nodes.rs index 35be6a55a9..70823e9e8d 100644 --- a/rs/moq-relay/src/nodes.rs +++ b/rs/moq-relay/src/nodes.rs @@ -171,8 +171,7 @@ impl Nodes { // A retraction can land mid-drain (a re-announce is a metadata update, // not a retract-and-announce). Skip it rather than end the scan, which // would drop every node still queued behind it. - let (moq_net::announce::Event::Announced(update) | moq_net::announce::Event::Updated(update)) = event - else { + let (moq_net::announce::Event::Start(update) | moq_net::announce::Event::Update(update)) = event else { continue; }; @@ -385,7 +384,7 @@ mod tests { // Take relay-a's replayed announce, then retire it so the cursor queues a // bare unannounce ahead of relay-b's still-pending announce. - let Some(moq_net::announce::Event::Announced(first_update)) = announced.try_next() else { + let Some(moq_net::announce::Event::Start(first_update)) = announced.try_next() else { panic!("replayed announce"); }; assert_eq!( diff --git a/rs/moq-relay/src/web.rs b/rs/moq-relay/src/web.rs index 58d0add019..769ebf0a68 100644 --- a/rs/moq-relay/src/web.rs +++ b/rs/moq-relay/src/web.rs @@ -866,7 +866,7 @@ async fn serve_announced( let mut broadcasts = Vec::new(); while let Some(event) = announced.try_next() { - if let moq_net::announce::Event::Announced(update) | moq_net::announce::Event::Updated(update) = event { + if let moq_net::announce::Event::Start(update) | moq_net::announce::Event::Update(update) = event { broadcasts.push(update.prefix); } } diff --git a/rs/moq-relay/tests/auth_lifetime.rs b/rs/moq-relay/tests/auth_lifetime.rs index ac1e3a58c7..c3b66c57e4 100644 --- a/rs/moq-relay/tests/auth_lifetime.rs +++ b/rs/moq-relay/tests/auth_lifetime.rs @@ -1038,10 +1038,8 @@ async fn a_relay_without_an_auth_source_is_decided_by_the_embedder() { async fn next_update(announced: &mut moq_net::announce::Consumer) -> Option<(moq_net::announce::Announce, bool)> { loop { return match announced.next().await? { - moq_net::announce::Event::Announced(route) | moq_net::announce::Event::Updated(route) => { - Some((route, true)) - } - moq_net::announce::Event::Retracted(route) => Some((route, false)), + moq_net::announce::Event::Start(route) | moq_net::announce::Event::Update(route) => Some((route, true)), + moq_net::announce::Event::End(route) => Some((route, false)), moq_net::announce::Event::Live => continue, }; } diff --git a/rs/moq-relay/tests/cluster_unknown.rs b/rs/moq-relay/tests/cluster_unknown.rs index 80b09df684..e6b9c5b33a 100644 --- a/rs/moq-relay/tests/cluster_unknown.rs +++ b/rs/moq-relay/tests/cluster_unknown.rs @@ -369,10 +369,8 @@ async fn unknown_publisher_does_not_flap_across_a_lite04_cluster_triangle() { async fn next_update(announced: &mut moq_net::announce::Consumer) -> Option<(moq_net::announce::Announce, bool)> { loop { return match announced.next().await? { - moq_net::announce::Event::Announced(route) | moq_net::announce::Event::Updated(route) => { - Some((route, true)) - } - moq_net::announce::Event::Retracted(route) => Some((route, false)), + moq_net::announce::Event::Start(route) | moq_net::announce::Event::Update(route) => Some((route, true)), + moq_net::announce::Event::End(route) => Some((route, false)), moq_net::announce::Event::Live => continue, }; } diff --git a/rs/moq-relay/tests/drills.rs b/rs/moq-relay/tests/drills.rs index 24fb2e9a66..6ba7488364 100644 --- a/rs/moq-relay/tests/drills.rs +++ b/rs/moq-relay/tests/drills.rs @@ -759,10 +759,8 @@ lanes!(no_publisher_never_delivers); async fn next_update(announced: &mut moq_net::announce::Consumer) -> Option<(moq_net::announce::Announce, bool)> { loop { return match announced.next().await? { - moq_net::announce::Event::Announced(route) | moq_net::announce::Event::Updated(route) => { - Some((route, true)) - } - moq_net::announce::Event::Retracted(route) => Some((route, false)), + moq_net::announce::Event::Start(route) | moq_net::announce::Event::Update(route) => Some((route, true)), + moq_net::announce::Event::End(route) => Some((route, false)), moq_net::announce::Event::Live => continue, }; } diff --git a/rs/moq-relay/tests/embed.rs b/rs/moq-relay/tests/embed.rs index c456c2970a..fcba6b6c8d 100644 --- a/rs/moq-relay/tests/embed.rs +++ b/rs/moq-relay/tests/embed.rs @@ -419,10 +419,8 @@ fn embedded_cli_merges_only_relay_settings() { async fn next_update(announced: &mut moq_net::announce::Consumer) -> Option<(moq_net::announce::Announce, bool)> { loop { return match announced.next().await? { - moq_net::announce::Event::Announced(route) | moq_net::announce::Event::Updated(route) => { - Some((route, true)) - } - moq_net::announce::Event::Retracted(route) => Some((route, false)), + moq_net::announce::Event::Start(route) | moq_net::announce::Event::Update(route) => Some((route, true)), + moq_net::announce::Event::End(route) => Some((route, false)), moq_net::announce::Event::Live => continue, }; } diff --git a/rs/moq-relay/tests/goaway_cluster.rs b/rs/moq-relay/tests/goaway_cluster.rs index 4f550e340b..06c7c41f42 100644 --- a/rs/moq-relay/tests/goaway_cluster.rs +++ b/rs/moq-relay/tests/goaway_cluster.rs @@ -814,10 +814,8 @@ async fn goaway_handover_is_enforced_while_the_replacement_dial_hangs_inner() { async fn next_update(announced: &mut moq_net::announce::Consumer) -> Option<(moq_net::announce::Announce, bool)> { loop { return match announced.next().await? { - moq_net::announce::Event::Announced(route) | moq_net::announce::Event::Updated(route) => { - Some((route, true)) - } - moq_net::announce::Event::Retracted(route) => Some((route, false)), + moq_net::announce::Event::Start(route) | moq_net::announce::Event::Update(route) => Some((route, true)), + moq_net::announce::Event::End(route) => Some((route, false)), moq_net::announce::Event::Live => continue, }; } diff --git a/rs/moq-relay/tests/hidden_cluster.rs b/rs/moq-relay/tests/hidden_cluster.rs index 2868bd4045..cbcf30c7e2 100644 --- a/rs/moq-relay/tests/hidden_cluster.rs +++ b/rs/moq-relay/tests/hidden_cluster.rs @@ -77,7 +77,7 @@ async fn old_peer_keeps_hidden_paths(version: moq_net::Version) { let cluster_run = tokio::spawn(cluster.clone().start().await.expect("cluster start").run()); loop { - if let moq_net::announce::Event::Announced(announce) | moq_net::announce::Event::Updated(announce) = + if let moq_net::announce::Event::Start(announce) | moq_net::announce::Event::Update(announce) = discovered.next().await.expect("peer origin closed") && announce.prefix.as_str() == ".internal/origins/test" { diff --git a/rs/moq-relay/tests/runtime_uring.rs b/rs/moq-relay/tests/runtime_uring.rs index 1b47c6339c..2e3ae87389 100644 --- a/rs/moq-relay/tests/runtime_uring.rs +++ b/rs/moq-relay/tests/runtime_uring.rs @@ -498,10 +498,8 @@ async fn spawn_auth_server(policy: moq_auth::serve::Policy) -> url::Url { async fn next_update(announced: &mut moq_net::announce::Consumer) -> Option<(moq_net::announce::Announce, bool)> { loop { return match announced.next().await? { - moq_net::announce::Event::Announced(route) | moq_net::announce::Event::Updated(route) => { - Some((route, true)) - } - moq_net::announce::Event::Retracted(route) => Some((route, false)), + moq_net::announce::Event::Start(route) | moq_net::announce::Event::Update(route) => Some((route, true)), + moq_net::announce::Event::End(route) => Some((route, false)), moq_net::announce::Event::Live => continue, }; } diff --git a/rs/moq-relay/tests/runtime_workers.rs b/rs/moq-relay/tests/runtime_workers.rs index 535ba6ab0c..97ce387de3 100644 --- a/rs/moq-relay/tests/runtime_workers.rs +++ b/rs/moq-relay/tests/runtime_workers.rs @@ -148,10 +148,8 @@ async fn workers_serve_quic_and_share_one_origin() { async fn next_update(announced: &mut moq_net::announce::Consumer) -> Option<(moq_net::announce::Announce, bool)> { loop { return match announced.next().await? { - moq_net::announce::Event::Announced(route) | moq_net::announce::Event::Updated(route) => { - Some((route, true)) - } - moq_net::announce::Event::Retracted(route) => Some((route, false)), + moq_net::announce::Event::Start(route) | moq_net::announce::Event::Update(route) => Some((route, true)), + moq_net::announce::Event::End(route) => Some((route, false)), moq_net::announce::Event::Live => continue, }; } diff --git a/rs/moq-relay/tests/smoke.rs b/rs/moq-relay/tests/smoke.rs index 7cc9e42ded..24fc70b61b 100644 --- a/rs/moq-relay/tests/smoke.rs +++ b/rs/moq-relay/tests/smoke.rs @@ -214,7 +214,7 @@ async fn announced_until(announcements: &mut moq_net::announce::Consumer, until: .await .expect("announcement timeout") .expect("origin closed"); - if let moq_net::announce::Event::Announced(announce) | moq_net::announce::Event::Updated(announce) = event { + if let moq_net::announce::Event::Start(announce) | moq_net::announce::Event::Update(announce) = event { seen.push(announce.prefix.as_str().to_owned()); } } @@ -1115,10 +1115,8 @@ async fn connect_once( async fn next_update(announced: &mut moq_net::announce::Consumer) -> Option<(moq_net::announce::Announce, bool)> { loop { return match announced.next().await? { - moq_net::announce::Event::Announced(route) | moq_net::announce::Event::Updated(route) => { - Some((route, true)) - } - moq_net::announce::Event::Retracted(route) => Some((route, false)), + moq_net::announce::Event::Start(route) | moq_net::announce::Event::Update(route) => Some((route, true)), + moq_net::announce::Event::End(route) => Some((route, false)), moq_net::announce::Event::Live => continue, }; } diff --git a/rs/moq-room/src/room.rs b/rs/moq-room/src/room.rs index 3dac0071a5..e2a41636bd 100644 --- a/rs/moq-room/src/room.rs +++ b/rs/moq-room/src/room.rs @@ -79,8 +79,8 @@ impl Room { } let (update, active) = match ready!(self.announced.poll_next(waiter)) { - Some(announce::Event::Announced(update) | announce::Event::Updated(update)) => (update, true), - Some(announce::Event::Retracted(update)) => (update, false), + Some(announce::Event::Start(update) | announce::Event::Update(update)) => (update, true), + Some(announce::Event::End(update)) => (update, false), Some(announce::Event::Live) => continue, None => return Poll::Ready(None), }; diff --git a/rs/moq-rtc/src/lib.rs b/rs/moq-rtc/src/lib.rs index 42417d0279..fe3e275064 100644 --- a/rs/moq-rtc/src/lib.rs +++ b/rs/moq-rtc/src/lib.rs @@ -79,10 +79,8 @@ mod tests { async fn next_update(announced: &mut moq_net::announce::Consumer) -> Option<(moq_net::announce::Announce, bool)> { loop { return match announced.next().await? { - moq_net::announce::Event::Announced(route) | moq_net::announce::Event::Updated(route) => { - Some((route, true)) - } - moq_net::announce::Event::Retracted(route) => Some((route, false)), + moq_net::announce::Event::Start(route) | moq_net::announce::Event::Update(route) => Some((route, true)), + moq_net::announce::Event::End(route) => Some((route, false)), moq_net::announce::Event::Live => continue, }; } diff --git a/rs/moq-stats/src/aggregate.rs b/rs/moq-stats/src/aggregate.rs index 9f318b111c..9203ee6612 100644 --- a/rs/moq-stats/src/aggregate.rs +++ b/rs/moq-stats/src/aggregate.rs @@ -297,9 +297,9 @@ impl Merged { loop { match self.announce.poll_next(waiter) { Poll::Ready(Some( - moq_net::announce::Event::Announced(update) | moq_net::announce::Event::Updated(update), + moq_net::announce::Event::Start(update) | moq_net::announce::Event::Update(update), )) => changed |= self.apply_announce(update, true), - Poll::Ready(Some(moq_net::announce::Event::Retracted(update))) => { + Poll::Ready(Some(moq_net::announce::Event::End(update))) => { changed |= self.apply_announce(update, false) } Poll::Ready(Some(moq_net::announce::Event::Live)) => {} @@ -500,10 +500,8 @@ mod tests { async fn next_update(announced: &mut moq_net::announce::Consumer) -> Option<(moq_net::announce::Announce, bool)> { loop { return match announced.next().await? { - moq_net::announce::Event::Announced(route) | moq_net::announce::Event::Updated(route) => { - Some((route, true)) - } - moq_net::announce::Event::Retracted(route) => Some((route, false)), + moq_net::announce::Event::Start(route) | moq_net::announce::Event::Update(route) => Some((route, true)), + moq_net::announce::Event::End(route) => Some((route, false)), moq_net::announce::Event::Live => continue, }; } diff --git a/rs/moq-stats/src/consume.rs b/rs/moq-stats/src/consume.rs index 5792e02a9b..068fa32211 100644 --- a/rs/moq-stats/src/consume.rs +++ b/rs/moq-stats/src/consume.rs @@ -121,10 +121,8 @@ mod tests { async fn next_update(announced: &mut moq_net::announce::Consumer) -> Option<(moq_net::announce::Announce, bool)> { loop { return match announced.next().await? { - moq_net::announce::Event::Announced(route) | moq_net::announce::Event::Updated(route) => { - Some((route, true)) - } - moq_net::announce::Event::Retracted(route) => Some((route, false)), + moq_net::announce::Event::Start(route) | moq_net::announce::Event::Update(route) => Some((route, true)), + moq_net::announce::Event::End(route) => Some((route, false)), moq_net::announce::Event::Live => continue, }; } diff --git a/rs/moq-stats/src/produce.rs b/rs/moq-stats/src/produce.rs index bec11fc793..baecfe039a 100644 --- a/rs/moq-stats/src/produce.rs +++ b/rs/moq-stats/src/produce.rs @@ -1020,10 +1020,8 @@ mod tests { async fn next_update(announced: &mut moq_net::announce::Consumer) -> Option<(moq_net::announce::Announce, bool)> { loop { return match announced.next().await? { - moq_net::announce::Event::Announced(route) | moq_net::announce::Event::Updated(route) => { - Some((route, true)) - } - moq_net::announce::Event::Retracted(route) => Some((route, false)), + moq_net::announce::Event::Start(route) | moq_net::announce::Event::Update(route) => Some((route, true)), + moq_net::announce::Event::End(route) => Some((route, false)), moq_net::announce::Event::Live => continue, }; } diff --git a/rs/moq-tokio/examples/clock.rs b/rs/moq-tokio/examples/clock.rs index e0515c0d9a..020ca7e2ca 100644 --- a/rs/moq-tokio/examples/clock.rs +++ b/rs/moq-tokio/examples/clock.rs @@ -110,14 +110,14 @@ async fn main() -> anyhow::Result<()> { loop { tokio::select! { Some(event) = announced.next() => match event { - announce::Event::Announced(update) | announce::Event::Updated(update) => { + announce::Event::Start(update) | announce::Event::Update(update) => { tracing::info!(broadcast = %update.prefix, "broadcast is online, subscribing to track"); let broadcast = consumer.request_broadcast(&update.prefix).await?; let track = broadcast .track(&track)?.subscribe(None).await?; clock = Some(Subscriber::new(track)); } - announce::Event::Retracted(update) => { + announce::Event::End(update) => { tracing::warn!(broadcast = %update.prefix, "broadcast is offline, waiting..."); } announce::Event::Live => {} diff --git a/rs/moq-tokio/src/origin.rs b/rs/moq-tokio/src/origin.rs index adbe53cc30..48ac3aac49 100644 --- a/rs/moq-tokio/src/origin.rs +++ b/rs/moq-tokio/src/origin.rs @@ -29,10 +29,8 @@ mod tests { async fn next_update(announced: &mut moq_net::announce::Consumer) -> Option<(moq_net::announce::Announce, bool)> { loop { return match announced.next().await? { - moq_net::announce::Event::Announced(route) | moq_net::announce::Event::Updated(route) => { - Some((route, true)) - } - moq_net::announce::Event::Retracted(route) => Some((route, false)), + moq_net::announce::Event::Start(route) | moq_net::announce::Event::Update(route) => Some((route, true)), + moq_net::announce::Event::End(route) => Some((route, false)), moq_net::announce::Event::Live => continue, }; } diff --git a/rs/moq-tokio/src/server.rs b/rs/moq-tokio/src/server.rs index 6cc77d2226..4c7367dee4 100644 --- a/rs/moq-tokio/src/server.rs +++ b/rs/moq-tokio/src/server.rs @@ -1490,10 +1490,8 @@ mod tests { async fn next_update(announced: &mut moq_net::announce::Consumer) -> Option<(moq_net::announce::Announce, bool)> { loop { return match announced.next().await? { - moq_net::announce::Event::Announced(route) | moq_net::announce::Event::Updated(route) => { - Some((route, true)) - } - moq_net::announce::Event::Retracted(route) => Some((route, false)), + moq_net::announce::Event::Start(route) | moq_net::announce::Event::Update(route) => Some((route, true)), + moq_net::announce::Event::End(route) => Some((route, false)), moq_net::announce::Event::Live => continue, }; } diff --git a/rs/moq-tokio/tests/backend.rs b/rs/moq-tokio/tests/backend.rs index 06871ac776..4c56cdd73d 100644 --- a/rs/moq-tokio/tests/backend.rs +++ b/rs/moq-tokio/tests/backend.rs @@ -864,10 +864,8 @@ async fn noq_windows() { async fn next_update(announced: &mut moq_net::announce::Consumer) -> Option<(moq_net::announce::Announce, bool)> { loop { return match announced.next().await? { - moq_net::announce::Event::Announced(route) | moq_net::announce::Event::Updated(route) => { - Some((route, true)) - } - moq_net::announce::Event::Retracted(route) => Some((route, false)), + moq_net::announce::Event::Start(route) | moq_net::announce::Event::Update(route) => Some((route, true)), + moq_net::announce::Event::End(route) => Some((route, false)), moq_net::announce::Event::Live => continue, }; } diff --git a/rs/moq-tokio/tests/broadcast.rs b/rs/moq-tokio/tests/broadcast.rs index a1a469ba68..415dc8b183 100644 --- a/rs/moq-tokio/tests/broadcast.rs +++ b/rs/moq-tokio/tests/broadcast.rs @@ -1000,7 +1000,7 @@ async fn broadcast_route_migration() { // active metadata updates (the standby's chain taking over). while let Some(event) = announcements.try_next() { assert!( - !matches!(event, moq_net::announce::Event::Retracted(_)), + !matches!(event, moq_net::announce::Event::End(_)), "failover must not retract the route" ); } @@ -3695,10 +3695,8 @@ async fn abort_carries_its_code_to_the_peer() { async fn next_update(announced: &mut moq_net::announce::Consumer) -> Option<(moq_net::announce::Announce, bool)> { loop { return match announced.next().await? { - moq_net::announce::Event::Announced(route) | moq_net::announce::Event::Updated(route) => { - Some((route, true)) - } - moq_net::announce::Event::Retracted(route) => Some((route, false)), + moq_net::announce::Event::Start(route) | moq_net::announce::Event::Update(route) => Some((route, true)), + moq_net::announce::Event::End(route) => Some((route, false)), moq_net::announce::Event::Live => continue, }; } diff --git a/rs/moq-tokio/tests/max_age.rs b/rs/moq-tokio/tests/max_age.rs index f9c2fe05a7..968f625e89 100644 --- a/rs/moq-tokio/tests/max_age.rs +++ b/rs/moq-tokio/tests/max_age.rs @@ -101,7 +101,7 @@ async fn relay(version: moq_net::Version, javascript: Option) -> anyhow::R .next() .await .ok_or_else(|| anyhow::anyhow!("announcements closed"))?, - moq_net::announce::Event::Announced(_) + moq_net::announce::Event::Start(_) ) {} let front = consumer.request_broadcast("age").await?; for (i, age) in AGES.into_iter().enumerate() { diff --git a/swift/README.md b/swift/README.md index 5e5e8b4dff..e309f3a526 100644 --- a/swift/README.md +++ b/swift/README.md @@ -35,9 +35,9 @@ let session = try await client.connect(to: "https://relay.example.com") // origin between both sides. let announced = try session.consume.announced(prefix: "demos/", filter: "*/camera") for try await event in announced { - // `.live` follows the routes live at subscribe time; `.updated` and - // `.retracted` report later changes. - guard case .announced(let announcement) = event else { continue } + // `.live` follows the routes live at subscribe time; `.update` and + // `.end` report later changes. + guard case .start(let announcement) = event else { continue } // Prefix stays origin-relative; captures reports what * matched. print("got broadcast \(announcement.prefix)") print("captures \(announcement.captures ?? [])") diff --git a/swift/Sources/Moq/Aliases.swift b/swift/Sources/Moq/Aliases.swift index 29a5022eca..c0cf266018 100644 --- a/swift/Sources/Moq/Aliases.swift +++ b/swift/Sources/Moq/Aliases.swift @@ -83,7 +83,7 @@ public typealias Route = MoqFFI.MoqRoute /// wildcard matched (`captures`, `nil` for a partial overlap), and the `route` /// serving it. Resolve a path with `OriginConsumer.requestBroadcast`. public typealias Announce = MoqFFI.MoqAnnounce -/// What an `AnnounceConsumer` yields: `.announced`, `.updated`, or `.retracted` +/// What an `AnnounceConsumer` yields: `.start`, `.update`, or `.end` /// carrying an `Announce`, or `.live` once every route live at subscribe time /// has been delivered. public typealias AnnounceEvent = MoqFFI.MoqAnnounceEvent diff --git a/swift/Tests/MoqTests/SmokeTests.swift b/swift/Tests/MoqTests/SmokeTests.swift index af5e261572..866f05105b 100644 --- a/swift/Tests/MoqTests/SmokeTests.swift +++ b/swift/Tests/MoqTests/SmokeTests.swift @@ -96,13 +96,13 @@ final class SmokeTests: XCTestCase { try broadcast.announce() let announced = try consumer.announced(prefix: "") - guard case .announced(let first) = try await nextRoute(announced) else { + guard case .start(let first) = try await nextRoute(announced) else { return XCTFail("expected an announcement") } XCTAssertEqual(first.prefix, "live") try broadcast.unannounce() - guard case .retracted(let retracted) = try await nextRoute(announced) else { + guard case .end(let retracted) = try await nextRoute(announced) else { return XCTFail("expected a retraction") } XCTAssertEqual(retracted.prefix, "live") @@ -112,7 +112,7 @@ final class SmokeTests: XCTestCase { } catch {} try broadcast.announce() - guard case .announced = try await nextRoute(announced) else { + guard case .start = try await nextRoute(announced) else { return XCTFail("expected a reannouncement") } _ = try await consumer.requestBroadcast(path: "live") @@ -124,7 +124,7 @@ final class SmokeTests: XCTestCase { let chat = try origin.createBroadcast(path: "room/alice/chat") try chat.announce() - guard case .announced(let update) = try await nextRoute(announced) else { + guard case .start(let update) = try await nextRoute(announced) else { return XCTFail("expected an announcement") } XCTAssertEqual(update.prefix, "room/alice/chat") @@ -146,7 +146,7 @@ final class SmokeTests: XCTestCase { var listed: [String] = [] for try await event in try consumer.announced() { - if case .announced(let announce) = event { + if case .start(let announce) = event { listed.append(announce.prefix) } else if event == .live { break From 443a6cbbb9db5ab5b852cc4d330ca405de1290fc Mon Sep 17 00:00:00 2001 From: Luke Curley Date: Mon, 28 Sep 2026 12:07:16 -0700 Subject: [PATCH 2/2] docs(js): use the renamed announce event kinds in comments Co-Authored-By: Claude Opus 5.5 --- js/net/src/connection/reload.ts | 2 +- js/net/src/ietf/subscriber.test.ts | 4 ++-- js/net/src/ietf/subscriber.ts | 2 +- js/net/src/origin.ts | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/js/net/src/connection/reload.ts b/js/net/src/connection/reload.ts index d05f98454e..2e74b35722 100644 --- a/js/net/src/connection/reload.ts +++ b/js/net/src/connection/reload.ts @@ -438,7 +438,7 @@ export class Reload { * Subscribe to broadcast announcements matching `scope`, spanning reconnects. * * The same {@link Announce.Consumer} stream as {@link Established.announced}, but everything active - * is retracted (a `retracted` update) whenever the connection drops and re-announced on + * is retracted (an `end` event) whenever the connection drops and re-announced on * reconnect, so a consumer draining `next()` never clings to a dead route across a reconnect. * The `live` marker comes once, from the first session. * diff --git a/js/net/src/ietf/subscriber.test.ts b/js/net/src/ietf/subscriber.test.ts index d70649a79d..262fd577d1 100644 --- a/js/net/src/ietf/subscriber.test.ts +++ b/js/net/src/ietf/subscriber.test.ts @@ -399,7 +399,7 @@ test("an inline NAMESPACE that starts looping back is retracted", async () => { /** * NAMESPACE has no REQUEST_UPDATE, so a peer reprices one by re-sending it. The repeat is * neither a duplicate nor a retraction: the stored route changes and consumers hear - * `updated`. + * `update`. */ test("a repeated NAMESPACE reprices in place", async () => { const pair = createMockTransportPair(ALPN.DRAFT_19); @@ -577,7 +577,7 @@ test("a PUBLISH_NAMESPACE update that starts looping back is detached", async () /** * REQUEST_UPDATE keeps an omitted parameter, so an explicit ROUTE_COST of 0 lands on the - * path already held without disturbing the announcement. Consumers hear `updated` with the + * path already held without disturbing the announcement. Consumers hear `update` with the * new cost; the stream ending is what retracts it. */ test("a PUBLISH_NAMESPACE repricing is acknowledged in place", async () => { diff --git a/js/net/src/ietf/subscriber.ts b/js/net/src/ietf/subscriber.ts index 32fd425509..f228a9cbf0 100644 --- a/js/net/src/ietf/subscriber.ts +++ b/js/net/src/ietf/subscriber.ts @@ -240,7 +240,7 @@ export class Subscriber { /** * Replace the stored route for a path that is already announced. A no-op when the - * hops and cost did not change; otherwise consumers hear `updated` so a forwarder + * hops and cost did not change; otherwise consumers hear `update` so a forwarder * can reprice without retracting. */ #updateAnnounce(path: Path.Valid, route: Route) { diff --git a/js/net/src/origin.ts b/js/net/src/origin.ts index d036160bb3..0b79cc5ea8 100644 --- a/js/net/src/origin.ts +++ b/js/net/src/origin.ts @@ -1354,7 +1354,7 @@ export class Consumer { /** * The announced routes matching `scope`, as a live stream: every currently advertised - * route arrives first as `announced`, then the `live` marker, then changes as they + * route arrives first as `start`, then the `live` marker, then changes as they * happen. The marker also waits for every session still replaying its peer's initial * set when the stream opened, so a caller listing what is live stops there. * Any pattern is accepted. A local broadcast appears once it announces, exactly as a