fix(bus): topic segments can't contain dots — service.added → service_added
Bus topic segments are NATS-style tokens and the validator at bus/topics.py:402 rejects '.', '*', '>', whitespace. My W3 constants 'service.added' / 'service.removed' tripped this on every live add/remove call: ValueError: topic segment 'service.added' may not contain '.', ... Renamed both to underscore form: DECKY_SERVICE_ADDED = 'service_added'. Aligned the SSE forwarder's name mapping (decky.<name>.service_added → SSE event 'decky.service_added') and the frontend's useTopologyStream listener + MazeNET.tsx event handler. Also updated the wiki entry with a note about the underscore.
This commit is contained in:
@@ -88,8 +88,8 @@ DECKY_MUTATION = "mutation"
|
|||||||
# ``topology_id``, and ``services`` (the post-mutation list). Consumers
|
# ``topology_id``, and ``services`` (the post-mutation list). Consumers
|
||||||
# that watch substrate shape (correlator, dashboard, profiler) reconcile
|
# that watch substrate shape (correlator, dashboard, profiler) reconcile
|
||||||
# off these without waiting for the next decnet-state.json snapshot.
|
# off these without waiting for the next decnet-state.json snapshot.
|
||||||
DECKY_SERVICE_ADDED = "service.added"
|
DECKY_SERVICE_ADDED = "service_added"
|
||||||
DECKY_SERVICE_REMOVED = "service.removed"
|
DECKY_SERVICE_REMOVED = "service_removed"
|
||||||
|
|
||||||
# Attacker event types (second token under the ``attacker`` root). First
|
# Attacker event types (second token under the ``attacker`` root). First
|
||||||
# sighting, session boundary transitions, and score-threshold crossings
|
# sighting, session boundary transitions, and score-threshold crossings
|
||||||
|
|||||||
@@ -177,10 +177,15 @@ def _sse_name_for(topic: str) -> str:
|
|||||||
|
|
||||||
``topology.<id>.mutation.applied`` → ``mutation.applied``
|
``topology.<id>.mutation.applied`` → ``mutation.applied``
|
||||||
``topology.<id>.status`` → ``status``
|
``topology.<id>.status`` → ``status``
|
||||||
``decky.<name>.service.added`` → ``decky.service.added``
|
``decky.<name>.service_added`` → ``decky.service_added``
|
||||||
``decky.<name>.service.removed`` → ``decky.service.removed``
|
``decky.<name>.service_removed`` → ``decky.service_removed``
|
||||||
Anything else is passed through unchanged so future topic families
|
Anything else is passed through unchanged so future topic families
|
||||||
don't silently collapse onto a generic bucket.
|
don't silently collapse onto a generic bucket.
|
||||||
|
|
||||||
|
Bus topic segments are NATS-style tokens — no dots inside a segment
|
||||||
|
— which is why the leaf is ``service_added`` (underscore) here and
|
||||||
|
on the wire, not ``service.added``. The frontend's
|
||||||
|
``useTopologyStream`` listens on the underscore form too.
|
||||||
"""
|
"""
|
||||||
parts = topic.split(".", 2)
|
parts = topic.split(".", 2)
|
||||||
if len(parts) < 3:
|
if len(parts) < 3:
|
||||||
|
|||||||
@@ -679,8 +679,8 @@ const MazeNET: React.FC = () => {
|
|||||||
// patch local state so the chip set reflects shape without a full
|
// patch local state so the chip set reflects shape without a full
|
||||||
// re-hydrate. The post-mutation services list lives on the
|
// re-hydrate. The post-mutation services list lives on the
|
||||||
// payload; same shape the actor's POST/DELETE response carries.
|
// payload; same shape the actor's POST/DELETE response carries.
|
||||||
if (event.name === 'decky.service.added'
|
if (event.name === 'decky.service_added'
|
||||||
|| event.name === 'decky.service.removed') {
|
|| event.name === 'decky.service_removed') {
|
||||||
const p = event.payload ?? {};
|
const p = event.payload ?? {};
|
||||||
const deckyName = typeof p.decky_name === 'string' ? p.decky_name : null;
|
const deckyName = typeof p.decky_name === 'string' ? p.decky_name : null;
|
||||||
const services = Array.isArray(p.services) ? p.services as string[] : null;
|
const services = Array.isArray(p.services) ? p.services as string[] : null;
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ export type TopologyStreamEventName =
|
|||||||
// server. The payload carries decky_name + service_name + the
|
// server. The payload carries decky_name + service_name + the
|
||||||
// post-mutation services list, so a second tab can reconcile shape
|
// post-mutation services list, so a second tab can reconcile shape
|
||||||
// without a refetch.
|
// without a refetch.
|
||||||
| 'decky.service.added'
|
| 'decky.service_added'
|
||||||
| 'decky.service.removed';
|
| 'decky.service_removed';
|
||||||
|
|
||||||
export interface TopologyStreamEvent {
|
export interface TopologyStreamEvent {
|
||||||
name: TopologyStreamEventName | string;
|
name: TopologyStreamEventName | string;
|
||||||
@@ -46,8 +46,8 @@ const NAMED_EVENTS: TopologyStreamEventName[] = [
|
|||||||
'mutation.applied',
|
'mutation.applied',
|
||||||
'mutation.failed',
|
'mutation.failed',
|
||||||
'status',
|
'status',
|
||||||
'decky.service.added',
|
'decky.service_added',
|
||||||
'decky.service.removed',
|
'decky.service_removed',
|
||||||
];
|
];
|
||||||
|
|
||||||
export function useTopologyStream({
|
export function useTopologyStream({
|
||||||
|
|||||||
Reference in New Issue
Block a user