feat(core-realtime): InMemoryRealtimeBroadcaster (test/dev impl)

This commit is contained in:
2026-05-08 21:14:33 +02:00
parent 6c5484b874
commit 5e57c66140
2 changed files with 62 additions and 0 deletions

View File

@@ -0,0 +1,34 @@
import { describe, it, expect } from "vitest";
import { z } from "zod";
import { InMemoryRealtimeBroadcaster } from "@/in-memory-realtime-broadcaster";
import { defineRealtimeChannel } from "@/realtime-channel";
const ch = defineRealtimeChannel(
"a.b",
z.object({ x: z.number() }).strict(),
{ scope: "public" },
);
describe("InMemoryRealtimeBroadcaster", () => {
it("validates payload via the descriptor schema", async () => {
const b = new InMemoryRealtimeBroadcaster();
await expect(
b.broadcast(ch, { x: "not a number" } as never),
).rejects.toThrow();
});
it("delivers to subscribers in order", async () => {
const b = new InMemoryRealtimeBroadcaster();
const got: number[] = [];
b.subscribe(ch, async (p) => { got.push(p.x); });
await b.broadcast(ch, { x: 1 });
await b.broadcast(ch, { x: 2 });
expect(got).toEqual([1, 2]);
});
it("does nothing when no subscribers", async () => {
const b = new InMemoryRealtimeBroadcaster();
await b.broadcast(ch, { x: 1 });
// does not throw
});
});

View File

@@ -0,0 +1,28 @@
import type { z } from "zod";
import type { IRealtimeBroadcaster } from "./realtime-broadcaster.interface";
import type { RealtimeChannelDescriptor } from "./realtime-channel";
type Listener<T> = (payload: T) => Promise<void> | void;
export class InMemoryRealtimeBroadcaster implements IRealtimeBroadcaster {
private readonly listeners = new Map<string, Listener<unknown>[]>();
async broadcast<T>(
descriptor: RealtimeChannelDescriptor<string, z.ZodType<T>>,
payload: T,
): Promise<void> {
descriptor.schema.parse(payload);
const arr = this.listeners.get(descriptor.name) ?? [];
for (const l of arr) await l(payload);
}
// Test-friendly: lets unit tests subscribe directly without a Socket.IO server.
subscribe<T>(
descriptor: RealtimeChannelDescriptor<string, z.ZodType<T>>,
listener: Listener<T>,
): void {
const arr = this.listeners.get(descriptor.name) ?? [];
arr.push(listener as Listener<unknown>);
this.listeners.set(descriptor.name, arr);
}
}