From b5a9d1fe9dc7a9e85be4fd2873f485b16613af1d Mon Sep 17 00:00:00 2001 From: Danijel Martinek Date: Sat, 9 May 2026 12:35:48 +0200 Subject: [PATCH] feat(core-shared): bind-protocols (event bus / realtime / realtime registry) --- .../core-shared/src/di/bind-protocols.test.ts | 30 +++++++++++++++++ packages/core-shared/src/di/bind-protocols.ts | 33 +++++++++++++++++++ 2 files changed, 63 insertions(+) create mode 100644 packages/core-shared/src/di/bind-protocols.test.ts create mode 100644 packages/core-shared/src/di/bind-protocols.ts diff --git a/packages/core-shared/src/di/bind-protocols.test.ts b/packages/core-shared/src/di/bind-protocols.test.ts new file mode 100644 index 0000000..2d2bbec --- /dev/null +++ b/packages/core-shared/src/di/bind-protocols.test.ts @@ -0,0 +1,30 @@ +import { describe, it, expectTypeOf } from "vitest"; +import type { + EventBusProtocol, + RealtimeBroadcasterProtocol, + RealtimeRegistryProtocol, +} from "./bind-protocols"; + +describe("EventBusProtocol", () => { + it("requires publish(event, payload) and subscribe(event, consumer, handler)", () => { + type Bus = EventBusProtocol; + expectTypeOf().toBeFunction(); + expectTypeOf().toBeFunction(); + }); +}); + +describe("RealtimeBroadcasterProtocol", () => { + it("requires broadcast(channel, payload)", () => { + type Rt = RealtimeBroadcasterProtocol; + expectTypeOf().toBeFunction(); + }); +}); + +describe("RealtimeRegistryProtocol", () => { + it("requires register, registerChannel, listChannels", () => { + type Reg = RealtimeRegistryProtocol; + expectTypeOf().toBeFunction(); + expectTypeOf().toBeFunction(); + expectTypeOf().toBeFunction(); + }); +}); diff --git a/packages/core-shared/src/di/bind-protocols.ts b/packages/core-shared/src/di/bind-protocols.ts new file mode 100644 index 0000000..8d37379 --- /dev/null +++ b/packages/core-shared/src/di/bind-protocols.ts @@ -0,0 +1,33 @@ +/** + * Minimal protocol surfaces used by feature binders to interact with optional + * cross-cutting infrastructure (event bus, realtime broadcaster, realtime + * handler registry). Lives in `core-shared` so `BindContext` can reference + * these unconditionally — features depend on `core-shared`, never on the + * optional packages directly. + * + * The optional packages' full interfaces (`IEventBus`, `IRealtimeBroadcaster`, + * `IRealtimeHandlerRegistry`) `extends` these — typechecks fail if a refactor + * narrows the protocol surface in a way the full interface would lose. + */ + +export type EventBusProtocol = { + publish(event: { name: string }, payload: T): Promise; + subscribe( + event: { name: string }, + consumer: string, + handler: (payload: T) => Promise, + ): void; +}; + +export type RealtimeBroadcasterProtocol = { + broadcast( + channel: { name: string; key?: unknown }, + payload: T, + ): Promise; +}; + +export type RealtimeRegistryProtocol = { + register(entry: { descriptor: unknown; handler: unknown }): void; + registerChannel(descriptor: unknown): void; + listChannels(): unknown[]; +};