batch3 T3: broker user channel + /api/ws/user + project/session/inference emits
- broker.subscribeUser/publishUser via separate user topics map - /api/ws/user WS route subscribes to the user channel - projects/sessions POST/DELETE handlers emit lifecycle frames - inference 3 terminal-state sites emit session_updated with RETURNING Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -4,35 +4,53 @@ export type Listener = (frame: Frame) => void;
|
||||
export interface Broker {
|
||||
publish(sessionId: string, frame: Frame): void;
|
||||
subscribe(sessionId: string, listener: Listener): () => void;
|
||||
publishUser(user: string, frame: Frame): void;
|
||||
subscribeUser(user: string, listener: Listener): () => void;
|
||||
}
|
||||
|
||||
export function createBroker(): Broker {
|
||||
const topics = new Map<string, Set<Listener>>();
|
||||
const userTopics = new Map<string, Set<Listener>>();
|
||||
|
||||
function publishTo(map: Map<string, Set<Listener>>, key: string, frame: Frame): void {
|
||||
const set = map.get(key);
|
||||
if (!set) return;
|
||||
for (const listener of set) {
|
||||
try {
|
||||
listener(frame);
|
||||
} catch {
|
||||
// ignore listener errors so one bad subscriber doesn't break the rest
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function subscribeTo(map: Map<string, Set<Listener>>, key: string, listener: Listener): () => void {
|
||||
let set = map.get(key);
|
||||
if (!set) {
|
||||
set = new Set();
|
||||
map.set(key, set);
|
||||
}
|
||||
set.add(listener);
|
||||
return () => {
|
||||
const s = map.get(key);
|
||||
if (!s) return;
|
||||
s.delete(listener);
|
||||
if (s.size === 0) map.delete(key);
|
||||
};
|
||||
}
|
||||
|
||||
return {
|
||||
publish(sessionId, frame) {
|
||||
const set = topics.get(sessionId);
|
||||
if (!set) return;
|
||||
for (const listener of set) {
|
||||
try {
|
||||
listener(frame);
|
||||
} catch {
|
||||
// ignore listener errors so one bad subscriber doesn't break the rest
|
||||
}
|
||||
}
|
||||
publishTo(topics, sessionId, frame);
|
||||
},
|
||||
subscribe(sessionId, listener) {
|
||||
let set = topics.get(sessionId);
|
||||
if (!set) {
|
||||
set = new Set();
|
||||
topics.set(sessionId, set);
|
||||
}
|
||||
set.add(listener);
|
||||
return () => {
|
||||
const s = topics.get(sessionId);
|
||||
if (!s) return;
|
||||
s.delete(listener);
|
||||
if (s.size === 0) topics.delete(sessionId);
|
||||
};
|
||||
return subscribeTo(topics, sessionId, listener);
|
||||
},
|
||||
publishUser(user, frame) {
|
||||
publishTo(userTopics, user, frame);
|
||||
},
|
||||
subscribeUser(user, listener) {
|
||||
return subscribeTo(userTopics, user, listener);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user