@almadar/runtime 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,118 @@
1
+ import { I as IEventBus } from './types-JFCKD0FU.js';
2
+
3
+ /**
4
+ * ServerBridge - Client-Server Trait Communication
5
+ *
6
+ * Bridges the client EventBus with the server OrbitalRuntime:
7
+ * 1. Forwards specified events from client to server
8
+ * 2. Receives events from server (via polling or WebSocket)
9
+ * 3. Puts server events onto client EventBus
10
+ *
11
+ * This enables cross-orbital communication between client and server traits.
12
+ *
13
+ * @example
14
+ * ```typescript
15
+ * import { ServerBridge } from '@kflow-builder/shared/runtime';
16
+ * import { useEventBus } from './hooks/useEventBus';
17
+ *
18
+ * // In your app initialization
19
+ * const eventBus = useEventBus();
20
+ * const bridge = new ServerBridge({
21
+ * eventBus,
22
+ * serverUrl: '/api/orbitals',
23
+ * // Events to forward from client to server
24
+ * forwardEvents: ['ORDER_PLACED', 'PAYMENT_COMPLETED'],
25
+ * });
26
+ *
27
+ * bridge.connect();
28
+ *
29
+ * // Now when client emits ORDER_PLACED, server traits receive it
30
+ * eventBus.emit('ORDER_PLACED', { orderId: '123' });
31
+ * ```
32
+ *
33
+ * @packageDocumentation
34
+ */
35
+
36
+ interface ServerBridgeConfig {
37
+ /** Client EventBus to bridge */
38
+ eventBus: IEventBus;
39
+ /** Base URL for orbital server API */
40
+ serverUrl: string;
41
+ /** Events to forward from client to server */
42
+ forwardEvents?: string[];
43
+ /** Forward all events matching pattern (e.g., 'Order*', '*') */
44
+ forwardPattern?: string;
45
+ /** Target orbital for forwarded events (or 'broadcast' for all) */
46
+ targetOrbital?: string;
47
+ /** Enable WebSocket for real-time server events */
48
+ useWebSocket?: boolean;
49
+ /** Polling interval in ms (if not using WebSocket) */
50
+ pollInterval?: number;
51
+ /** Enable debug logging */
52
+ debug?: boolean;
53
+ /** Custom fetch function (for testing or custom auth) */
54
+ fetch?: typeof fetch;
55
+ }
56
+ interface ServerBridgeState {
57
+ connected: boolean;
58
+ lastError?: string;
59
+ eventsForwarded: number;
60
+ eventsReceived: number;
61
+ }
62
+ /**
63
+ * Bridges client EventBus with server OrbitalRuntime
64
+ */
65
+ declare class ServerBridge {
66
+ private config;
67
+ private state;
68
+ private unsubscribes;
69
+ private pollTimer?;
70
+ private ws?;
71
+ private fetchFn;
72
+ constructor(config: ServerBridgeConfig);
73
+ /**
74
+ * Connect the bridge - start forwarding events
75
+ */
76
+ connect(): void;
77
+ /**
78
+ * Disconnect the bridge
79
+ */
80
+ disconnect(): void;
81
+ /**
82
+ * Get current bridge state
83
+ */
84
+ getState(): ServerBridgeState;
85
+ private setupEventForwarding;
86
+ private forwardToServer;
87
+ private sendEventToOrbital;
88
+ private fetchOrbitals;
89
+ private setupPolling;
90
+ private setupWebSocket;
91
+ /**
92
+ * Send an event directly to a specific orbital (bypassing EventBus)
93
+ */
94
+ sendEvent(orbitalName: string, event: string, payload?: Record<string, unknown>): Promise<{
95
+ success: boolean;
96
+ states?: Record<string, string>;
97
+ emittedEvents?: Array<{
98
+ event: string;
99
+ payload?: unknown;
100
+ }>;
101
+ error?: string;
102
+ }>;
103
+ /**
104
+ * Get current state of an orbital's traits
105
+ */
106
+ getOrbitalState(orbitalName: string): Promise<{
107
+ success: boolean;
108
+ states?: Record<string, string>;
109
+ error?: string;
110
+ }>;
111
+ private log;
112
+ }
113
+ /**
114
+ * Create a ServerBridge instance
115
+ */
116
+ declare function createServerBridge(config: ServerBridgeConfig): ServerBridge;
117
+
118
+ export { ServerBridge, type ServerBridgeConfig, type ServerBridgeState, createServerBridge };
@@ -0,0 +1,242 @@
1
+ // src/ServerBridge.ts
2
+ var ServerBridge = class {
3
+ config;
4
+ state = {
5
+ connected: false,
6
+ eventsForwarded: 0,
7
+ eventsReceived: 0
8
+ };
9
+ unsubscribes = [];
10
+ pollTimer;
11
+ ws;
12
+ fetchFn;
13
+ constructor(config) {
14
+ this.config = {
15
+ pollInterval: 5e3,
16
+ ...config
17
+ };
18
+ this.fetchFn = config.fetch || fetch.bind(globalThis);
19
+ }
20
+ // ==========================================================================
21
+ // Connection Management
22
+ // ==========================================================================
23
+ /**
24
+ * Connect the bridge - start forwarding events
25
+ */
26
+ connect() {
27
+ if (this.state.connected) {
28
+ this.log("warn", "Already connected");
29
+ return;
30
+ }
31
+ this.log("info", "Connecting bridge...");
32
+ this.setupEventForwarding();
33
+ if (this.config.useWebSocket) {
34
+ this.setupWebSocket();
35
+ } else if (this.config.pollInterval && this.config.pollInterval > 0) {
36
+ this.setupPolling();
37
+ }
38
+ this.state.connected = true;
39
+ this.log("info", "Bridge connected");
40
+ }
41
+ /**
42
+ * Disconnect the bridge
43
+ */
44
+ disconnect() {
45
+ for (const unsub of this.unsubscribes) {
46
+ unsub();
47
+ }
48
+ this.unsubscribes = [];
49
+ if (this.pollTimer) {
50
+ clearInterval(this.pollTimer);
51
+ this.pollTimer = void 0;
52
+ }
53
+ if (this.ws) {
54
+ this.ws.close();
55
+ this.ws = void 0;
56
+ }
57
+ this.state.connected = false;
58
+ this.log("info", "Bridge disconnected");
59
+ }
60
+ /**
61
+ * Get current bridge state
62
+ */
63
+ getState() {
64
+ return { ...this.state };
65
+ }
66
+ // ==========================================================================
67
+ // Event Forwarding (Client -> Server)
68
+ // ==========================================================================
69
+ setupEventForwarding() {
70
+ const { eventBus, forwardEvents, forwardPattern } = this.config;
71
+ if (forwardEvents && forwardEvents.length > 0) {
72
+ for (const eventType of forwardEvents) {
73
+ const unsub = eventBus.on(eventType, (event) => {
74
+ this.forwardToServer(event);
75
+ });
76
+ this.unsubscribes.push(unsub);
77
+ }
78
+ } else if (forwardPattern) {
79
+ if (forwardPattern === "*") {
80
+ const unsub = eventBus.on("*", (event) => {
81
+ if (!event.type.startsWith("UI:") && !event.type.startsWith("BRIDGE:")) {
82
+ this.forwardToServer(event);
83
+ }
84
+ });
85
+ this.unsubscribes.push(unsub);
86
+ } else {
87
+ const prefix = forwardPattern.replace("*", "");
88
+ const unsub = eventBus.on("*", (event) => {
89
+ if (event.type.startsWith(prefix)) {
90
+ this.forwardToServer(event);
91
+ }
92
+ });
93
+ this.unsubscribes.push(unsub);
94
+ }
95
+ }
96
+ }
97
+ async forwardToServer(event) {
98
+ const { serverUrl, targetOrbital } = this.config;
99
+ try {
100
+ if (targetOrbital && targetOrbital !== "broadcast") {
101
+ await this.sendEventToOrbital(targetOrbital, event);
102
+ } else {
103
+ const orbitals = await this.fetchOrbitals();
104
+ for (const orbital of orbitals) {
105
+ await this.sendEventToOrbital(orbital.name, event);
106
+ }
107
+ }
108
+ this.state.eventsForwarded++;
109
+ this.log("debug", `Forwarded event: ${event.type}`);
110
+ } catch (error) {
111
+ this.state.lastError = String(error);
112
+ this.log("error", `Failed to forward event: ${event.type}`, error);
113
+ }
114
+ }
115
+ async sendEventToOrbital(orbitalName, event) {
116
+ const { serverUrl } = this.config;
117
+ const url = `${serverUrl}/${orbitalName}/events`;
118
+ const response = await this.fetchFn(url, {
119
+ method: "POST",
120
+ headers: { "Content-Type": "application/json" },
121
+ body: JSON.stringify({
122
+ event: event.type,
123
+ payload: event.payload
124
+ })
125
+ });
126
+ if (!response.ok) {
127
+ throw new Error(`Server returned ${response.status}`);
128
+ }
129
+ const result = await response.json();
130
+ if (result.emittedEvents && result.emittedEvents.length > 0) {
131
+ for (const emitted of result.emittedEvents) {
132
+ this.config.eventBus.emit(`SERVER:${emitted.event}`, emitted.payload);
133
+ this.state.eventsReceived++;
134
+ }
135
+ }
136
+ }
137
+ async fetchOrbitals() {
138
+ const response = await this.fetchFn(this.config.serverUrl);
139
+ if (!response.ok) {
140
+ throw new Error(`Failed to fetch orbitals: ${response.status}`);
141
+ }
142
+ const data = await response.json();
143
+ return data.orbitals || [];
144
+ }
145
+ // ==========================================================================
146
+ // Server -> Client (Polling)
147
+ // ==========================================================================
148
+ setupPolling() {
149
+ this.log("debug", "Polling mode - events received via forward response");
150
+ }
151
+ // ==========================================================================
152
+ // Server -> Client (WebSocket)
153
+ // ==========================================================================
154
+ setupWebSocket() {
155
+ const { serverUrl } = this.config;
156
+ const wsUrl = serverUrl.replace(/^http:/, "ws:").replace(/^https:/, "wss:").replace(/\/api\/orbitals$/, "/ws/orbitals");
157
+ try {
158
+ this.ws = new WebSocket(wsUrl);
159
+ this.ws.onopen = () => {
160
+ this.log("info", "WebSocket connected");
161
+ };
162
+ this.ws.onmessage = (msg) => {
163
+ try {
164
+ const data = JSON.parse(msg.data);
165
+ if (data.type === "event") {
166
+ this.config.eventBus.emit(`SERVER:${data.event}`, data.payload);
167
+ this.state.eventsReceived++;
168
+ this.log("debug", `Received server event: ${data.event}`);
169
+ }
170
+ } catch (error) {
171
+ this.log("error", "Failed to parse WebSocket message", error);
172
+ }
173
+ };
174
+ this.ws.onerror = (error) => {
175
+ this.state.lastError = "WebSocket error";
176
+ this.log("error", "WebSocket error", error);
177
+ };
178
+ this.ws.onclose = () => {
179
+ this.log("info", "WebSocket closed");
180
+ };
181
+ } catch (error) {
182
+ this.log("error", "Failed to create WebSocket", error);
183
+ }
184
+ }
185
+ // ==========================================================================
186
+ // Direct Methods
187
+ // ==========================================================================
188
+ /**
189
+ * Send an event directly to a specific orbital (bypassing EventBus)
190
+ */
191
+ async sendEvent(orbitalName, event, payload) {
192
+ const { serverUrl } = this.config;
193
+ const url = `${serverUrl}/${orbitalName}/events`;
194
+ try {
195
+ const response = await this.fetchFn(url, {
196
+ method: "POST",
197
+ headers: { "Content-Type": "application/json" },
198
+ body: JSON.stringify({ event, payload })
199
+ });
200
+ return await response.json();
201
+ } catch (error) {
202
+ return { success: false, error: String(error) };
203
+ }
204
+ }
205
+ /**
206
+ * Get current state of an orbital's traits
207
+ */
208
+ async getOrbitalState(orbitalName) {
209
+ const { serverUrl } = this.config;
210
+ const url = `${serverUrl}/${orbitalName}`;
211
+ try {
212
+ const response = await this.fetchFn(url);
213
+ const data = await response.json();
214
+ if (data.success && data.orbital) {
215
+ const states = {};
216
+ for (const trait of data.orbital.traits) {
217
+ states[trait.name] = trait.currentState;
218
+ }
219
+ return { success: true, states };
220
+ }
221
+ return { success: false, error: data.error };
222
+ } catch (error) {
223
+ return { success: false, error: String(error) };
224
+ }
225
+ }
226
+ // ==========================================================================
227
+ // Utilities
228
+ // ==========================================================================
229
+ log(level, message, data) {
230
+ if (!this.config.debug && level === "debug") return;
231
+ const prefix = "[ServerBridge]";
232
+ const logFn = level === "error" ? console.error : level === "warn" ? console.warn : console.log;
233
+ logFn(prefix, message, data !== void 0 ? data : "");
234
+ }
235
+ };
236
+ function createServerBridge(config) {
237
+ return new ServerBridge(config);
238
+ }
239
+
240
+ export { ServerBridge, createServerBridge };
241
+ //# sourceMappingURL=ServerBridge.js.map
242
+ //# sourceMappingURL=ServerBridge.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/ServerBridge.ts"],"names":[],"mappings":"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","file":"ServerBridge.js","sourcesContent":["/**\n * ServerBridge - Client-Server Trait Communication\n *\n * Bridges the client EventBus with the server OrbitalRuntime:\n * 1. Forwards specified events from client to server\n * 2. Receives events from server (via polling or WebSocket)\n * 3. Puts server events onto client EventBus\n *\n * This enables cross-orbital communication between client and server traits.\n *\n * @example\n * ```typescript\n * import { ServerBridge } from '@kflow-builder/shared/runtime';\n * import { useEventBus } from './hooks/useEventBus';\n *\n * // In your app initialization\n * const eventBus = useEventBus();\n * const bridge = new ServerBridge({\n * eventBus,\n * serverUrl: '/api/orbitals',\n * // Events to forward from client to server\n * forwardEvents: ['ORDER_PLACED', 'PAYMENT_COMPLETED'],\n * });\n *\n * bridge.connect();\n *\n * // Now when client emits ORDER_PLACED, server traits receive it\n * eventBus.emit('ORDER_PLACED', { orderId: '123' });\n * ```\n *\n * @packageDocumentation\n */\n\nimport type { IEventBus, RuntimeEvent } from \"./types.js\";\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface ServerBridgeConfig {\n /** Client EventBus to bridge */\n eventBus: IEventBus;\n /** Base URL for orbital server API */\n serverUrl: string;\n /** Events to forward from client to server */\n forwardEvents?: string[];\n /** Forward all events matching pattern (e.g., 'Order*', '*') */\n forwardPattern?: string;\n /** Target orbital for forwarded events (or 'broadcast' for all) */\n targetOrbital?: string;\n /** Enable WebSocket for real-time server events */\n useWebSocket?: boolean;\n /** Polling interval in ms (if not using WebSocket) */\n pollInterval?: number;\n /** Enable debug logging */\n debug?: boolean;\n /** Custom fetch function (for testing or custom auth) */\n fetch?: typeof fetch;\n}\n\nexport interface ServerBridgeState {\n connected: boolean;\n lastError?: string;\n eventsForwarded: number;\n eventsReceived: number;\n}\n\n// ============================================================================\n// ServerBridge\n// ============================================================================\n\n/**\n * Bridges client EventBus with server OrbitalRuntime\n */\nexport class ServerBridge {\n private config: ServerBridgeConfig;\n private state: ServerBridgeState = {\n connected: false,\n eventsForwarded: 0,\n eventsReceived: 0,\n };\n private unsubscribes: Array<() => void> = [];\n private pollTimer?: ReturnType<typeof setInterval>;\n private ws?: WebSocket;\n private fetchFn: typeof fetch;\n\n constructor(config: ServerBridgeConfig) {\n this.config = {\n pollInterval: 5000,\n ...config,\n };\n this.fetchFn = config.fetch || fetch.bind(globalThis);\n }\n\n // ==========================================================================\n // Connection Management\n // ==========================================================================\n\n /**\n * Connect the bridge - start forwarding events\n */\n connect(): void {\n if (this.state.connected) {\n this.log(\"warn\", \"Already connected\");\n return;\n }\n\n this.log(\"info\", \"Connecting bridge...\");\n\n // Subscribe to events to forward\n this.setupEventForwarding();\n\n // Set up server -> client channel\n if (this.config.useWebSocket) {\n this.setupWebSocket();\n } else if (this.config.pollInterval && this.config.pollInterval > 0) {\n this.setupPolling();\n }\n\n this.state.connected = true;\n this.log(\"info\", \"Bridge connected\");\n }\n\n /**\n * Disconnect the bridge\n */\n disconnect(): void {\n // Clean up event subscriptions\n for (const unsub of this.unsubscribes) {\n unsub();\n }\n this.unsubscribes = [];\n\n // Clean up polling\n if (this.pollTimer) {\n clearInterval(this.pollTimer);\n this.pollTimer = undefined;\n }\n\n // Clean up WebSocket\n if (this.ws) {\n this.ws.close();\n this.ws = undefined;\n }\n\n this.state.connected = false;\n this.log(\"info\", \"Bridge disconnected\");\n }\n\n /**\n * Get current bridge state\n */\n getState(): ServerBridgeState {\n return { ...this.state };\n }\n\n // ==========================================================================\n // Event Forwarding (Client -> Server)\n // ==========================================================================\n\n private setupEventForwarding(): void {\n const { eventBus, forwardEvents, forwardPattern } = this.config;\n\n if (forwardEvents && forwardEvents.length > 0) {\n // Forward specific events\n for (const eventType of forwardEvents) {\n const unsub = eventBus.on(eventType, (event: RuntimeEvent) => {\n this.forwardToServer(event);\n });\n this.unsubscribes.push(unsub);\n }\n } else if (forwardPattern) {\n // Forward events matching pattern\n if (forwardPattern === \"*\") {\n // Forward all events\n const unsub = eventBus.on(\"*\", (event: RuntimeEvent) => {\n // Don't forward internal events\n if (\n !event.type.startsWith(\"UI:\") &&\n !event.type.startsWith(\"BRIDGE:\")\n ) {\n this.forwardToServer(event);\n }\n });\n this.unsubscribes.push(unsub);\n } else {\n // Pattern matching (simple prefix match)\n const prefix = forwardPattern.replace(\"*\", \"\");\n const unsub = eventBus.on(\"*\", (event: RuntimeEvent) => {\n if (event.type.startsWith(prefix)) {\n this.forwardToServer(event);\n }\n });\n this.unsubscribes.push(unsub);\n }\n }\n }\n\n private async forwardToServer(event: RuntimeEvent): Promise<void> {\n const { serverUrl, targetOrbital } = this.config;\n\n try {\n // Determine which orbital(s) to send to\n if (targetOrbital && targetOrbital !== \"broadcast\") {\n // Send to specific orbital\n await this.sendEventToOrbital(targetOrbital, event);\n } else {\n // Broadcast to all orbitals\n const orbitals = await this.fetchOrbitals();\n for (const orbital of orbitals) {\n await this.sendEventToOrbital(orbital.name, event);\n }\n }\n\n this.state.eventsForwarded++;\n this.log(\"debug\", `Forwarded event: ${event.type}`);\n } catch (error) {\n this.state.lastError = String(error);\n this.log(\"error\", `Failed to forward event: ${event.type}`, error);\n }\n }\n\n private async sendEventToOrbital(\n orbitalName: string,\n event: RuntimeEvent,\n ): Promise<void> {\n const { serverUrl } = this.config;\n const url = `${serverUrl}/${orbitalName}/events`;\n\n const response = await this.fetchFn(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({\n event: event.type,\n payload: event.payload,\n }),\n });\n\n if (!response.ok) {\n throw new Error(`Server returned ${response.status}`);\n }\n\n const result = (await response.json()) as {\n emittedEvents?: Array<{ event: string; payload?: Record<string, unknown> }>;\n };\n\n // If server emitted events, put them on client EventBus\n if (result.emittedEvents && result.emittedEvents.length > 0) {\n for (const emitted of result.emittedEvents) {\n this.config.eventBus.emit(`SERVER:${emitted.event}`, emitted.payload);\n this.state.eventsReceived++;\n }\n }\n }\n\n private async fetchOrbitals(): Promise<Array<{ name: string }>> {\n const response = await this.fetchFn(this.config.serverUrl);\n if (!response.ok) {\n throw new Error(`Failed to fetch orbitals: ${response.status}`);\n }\n const data = (await response.json()) as { orbitals?: Array<{ name: string }> };\n return data.orbitals || [];\n }\n\n // ==========================================================================\n // Server -> Client (Polling)\n // ==========================================================================\n\n private setupPolling(): void {\n // For now, polling is handled by the response to forwarded events\n // A more sophisticated implementation would poll a /events endpoint\n this.log(\"debug\", \"Polling mode - events received via forward response\");\n }\n\n // ==========================================================================\n // Server -> Client (WebSocket)\n // ==========================================================================\n\n private setupWebSocket(): void {\n const { serverUrl } = this.config;\n\n // Convert HTTP URL to WebSocket URL\n const wsUrl = serverUrl\n .replace(/^http:/, \"ws:\")\n .replace(/^https:/, \"wss:\")\n .replace(/\\/api\\/orbitals$/, \"/ws/orbitals\");\n\n try {\n this.ws = new WebSocket(wsUrl);\n\n this.ws.onopen = () => {\n this.log(\"info\", \"WebSocket connected\");\n };\n\n this.ws.onmessage = (msg) => {\n try {\n const data = JSON.parse(msg.data);\n if (data.type === \"event\") {\n // Server pushed an event - put it on client EventBus\n this.config.eventBus.emit(`SERVER:${data.event}`, data.payload);\n this.state.eventsReceived++;\n this.log(\"debug\", `Received server event: ${data.event}`);\n }\n } catch (error) {\n this.log(\"error\", \"Failed to parse WebSocket message\", error);\n }\n };\n\n this.ws.onerror = (error) => {\n this.state.lastError = \"WebSocket error\";\n this.log(\"error\", \"WebSocket error\", error);\n };\n\n this.ws.onclose = () => {\n this.log(\"info\", \"WebSocket closed\");\n // Could implement reconnection logic here\n };\n } catch (error) {\n this.log(\"error\", \"Failed to create WebSocket\", error);\n }\n }\n\n // ==========================================================================\n // Direct Methods\n // ==========================================================================\n\n /**\n * Send an event directly to a specific orbital (bypassing EventBus)\n */\n async sendEvent(\n orbitalName: string,\n event: string,\n payload?: Record<string, unknown>,\n ): Promise<{\n success: boolean;\n states?: Record<string, string>;\n emittedEvents?: Array<{ event: string; payload?: unknown }>;\n error?: string;\n }> {\n const { serverUrl } = this.config;\n const url = `${serverUrl}/${orbitalName}/events`;\n\n try {\n const response = await this.fetchFn(url, {\n method: \"POST\",\n headers: { \"Content-Type\": \"application/json\" },\n body: JSON.stringify({ event, payload }),\n });\n\n return (await response.json()) as {\n success: boolean;\n states?: Record<string, string>;\n emittedEvents?: Array<{ event: string; payload?: unknown }>;\n error?: string;\n };\n } catch (error) {\n return { success: false, error: String(error) };\n }\n }\n\n /**\n * Get current state of an orbital's traits\n */\n async getOrbitalState(orbitalName: string): Promise<{\n success: boolean;\n states?: Record<string, string>;\n error?: string;\n }> {\n const { serverUrl } = this.config;\n const url = `${serverUrl}/${orbitalName}`;\n\n try {\n const response = await this.fetchFn(url);\n const data = (await response.json()) as {\n success: boolean;\n orbital?: { traits: Array<{ name: string; currentState: string }> };\n error?: string;\n };\n\n if (data.success && data.orbital) {\n const states: Record<string, string> = {};\n for (const trait of data.orbital.traits) {\n states[trait.name] = trait.currentState;\n }\n return { success: true, states };\n }\n\n return { success: false, error: data.error };\n } catch (error) {\n return { success: false, error: String(error) };\n }\n }\n\n // ==========================================================================\n // Utilities\n // ==========================================================================\n\n private log(\n level: \"debug\" | \"info\" | \"warn\" | \"error\",\n message: string,\n data?: unknown,\n ): void {\n if (!this.config.debug && level === \"debug\") return;\n\n const prefix = \"[ServerBridge]\";\n const logFn =\n level === \"error\"\n ? console.error\n : level === \"warn\"\n ? console.warn\n : console.log;\n logFn(prefix, message, data !== undefined ? data : \"\");\n }\n}\n\n/**\n * Create a ServerBridge instance\n */\nexport function createServerBridge(config: ServerBridgeConfig): ServerBridge {\n return new ServerBridge(config);\n}\n"]}