authorgravatar for julius0216@outlook.comJulius Marminge <julius0216@outlook.com> 2026-02-25 17:51:56-08:00
committergravatar for jmarminge@gmail.comJulius Marminge <jmarminge@gmail.com> 2026-02-26 13:20:36-08:00
logb211d059d2b88e3ffce38b5e9b59cdc7b2e0259a
tree71d326369e9aaf9c634023598edb8de70bd6393f
parent52681634cdf71e05a8df069664953b42352a388c

fix


1 files changed, 39 insertions(+), 33 deletions(-)

apps/server/src/wsServer.ts+39-33
...@@ -16,7 +16,7 @@ import {...@@ -16,7 +16,7 @@ import {
16 WsResponse,16 WsResponse,
17} from "@t3tools/contracts";17} from "@t3tools/contracts";
18import { NodeHttpServer } from "@effect/platform-node";18import { NodeHttpServer } from "@effect/platform-node";
19import { Cause, Effect, Exit, Layer, Schema, Scope, ServiceMap, Stream, Struct } from "effect";19import { Cause, Effect, Exit, Layer, Ref, Schema, Scope, ServiceMap, Stream, Struct } from "effect";
20import { WebSocketServer, type WebSocket } from "ws";20import { WebSocketServer, type WebSocket } from "ws";
2121
22import { createLogger } from "./logger";22import { createLogger } from "./logger";
...@@ -171,7 +171,7 @@ export const createServer = Effect.fn(function* (options: ServerOptions = {}): E...@@ -171,7 +171,7 @@ export const createServer = Effect.fn(function* (options: ServerOptions = {}): E
171 const keybindingsManager = yield* Keybindings;171 const keybindingsManager = yield* Keybindings;
172 const git = yield* GitCore;172 const git = yield* GitCore;
173173
174 const clients = new Set<WebSocket>();174 const clients = yield* Ref.make(new Set<WebSocket>());
175 const logger = createLogger("ws");175 const logger = createLogger("ws");
176176
177 function logOutgoingPush(push: WsPush, recipients: number) {177 function logOutgoingPush(push: WsPush, recipients: number) {
...@@ -183,22 +183,23 @@ export const createServer = Effect.fn(function* (options: ServerOptions = {}): E...@@ -183,22 +183,23 @@ export const createServer = Effect.fn(function* (options: ServerOptions = {}): E
183 });183 });
184 }184 }
185185
186 const onTerminalEvent = (event: TerminalEvent) => {186 const onTerminalEvent = (event: TerminalEvent) =>
187 const push: WsPush = {187 Effect.gen(function* () {
188 type: "push",188 const push: WsPush = {
189 channel: WS_CHANNELS.terminalEvent,189 type: "push",
190 data: event,190 channel: WS_CHANNELS.terminalEvent,
191 };191 data: event,
192 const message = JSON.stringify(push);192 };
193 let recipients = 0;193 const message = yield* Schema.encodeEffect(Schema.fromJsonString(WsPush))(push);
194 for (const client of clients) {194 let recipients = 0;
195 if (client.readyState === client.OPEN) {195 for (const client of yield* Ref.get(clients)) {
196 client.send(message);196 if (client.readyState === client.OPEN) {
197 recipients += 1;197 client.send(message);
198 recipients += 1;
199 }
198 }200 }
199 }201 logOutgoingPush(push, recipients);
200 logOutgoingPush(push, recipients);202 }).pipe(Effect.runPromise);
201 };
202203
203 // HTTP server — serves static files or redirects to Vite dev server204 // HTTP server — serves static files or redirects to Vite dev server
204 const httpServer = http.createServer((req, res) => {205 const httpServer = http.createServer((req, res) => {
...@@ -273,12 +274,10 @@ export const createServer = Effect.fn(function* (options: ServerOptions = {}): E...@@ -273,12 +274,10 @@ export const createServer = Effect.fn(function* (options: ServerOptions = {}): E
273 });274 });
274 });275 });
275276
276 const closeAllClients = () => {277 const closeAllClients = Ref.get(clients).pipe(
277 for (const client of clients) {278 Effect.flatMap(Effect.forEach((client) => Effect.sync(() => client.close()))),
278 client.close();279 Effect.flatMap(() => Ref.set(clients, new Set())),
279 }280 );
280 clients.clear();
281 };
282281
283 const listenOptions = host ? { host, port } : { port };282 const listenOptions = host ? { host, port } : { port };
284283
...@@ -295,15 +294,15 @@ export const createServer = Effect.fn(function* (options: ServerOptions = {}): E...@@ -295,15 +294,15 @@ export const createServer = Effect.fn(function* (options: ServerOptions = {}): E
295 );294 );
296295
297 yield* Stream.runForEach(orchestrationEngine.streamDomainEvents, (event) =>296 yield* Stream.runForEach(orchestrationEngine.streamDomainEvents, (event) =>
298 Effect.sync(() => {297 Effect.gen(function* () {
299 const push: WsPush = {298 const push: WsPush = {
300 type: "push",299 type: "push",
301 channel: ORCHESTRATION_WS_CHANNELS.domainEvent,300 channel: ORCHESTRATION_WS_CHANNELS.domainEvent,
302 data: event,301 data: event,
303 };302 };
304 const message = JSON.stringify(push);303 const message = yield* Schema.encodeEffect(Schema.fromJsonString(WsPush))(push);
305 let recipients = 0;304 let recipients = 0;
306 for (const client of clients) {305 for (const client of yield* Ref.get(clients)) {
307 if (client.readyState === client.OPEN) {306 if (client.readyState === client.OPEN) {
308 client.send(message);307 client.send(message);
309 recipients += 1;308 recipients += 1;
...@@ -532,7 +531,7 @@ export const createServer = Effect.fn(function* (options: ServerOptions = {}): E...@@ -532,7 +531,7 @@ export const createServer = Effect.fn(function* (options: ServerOptions = {}): E
532 });531 });
533532
534 wss.on("connection", (ws) => {533 wss.on("connection", (ws) => {
535 clients.add(ws);534 void Effect.runPromise(Ref.update(clients, (clients) => clients.add(ws)));
536535
537 const segments = cwd.split(/[/\\]/).filter(Boolean);536 const segments = cwd.split(/[/\\]/).filter(Boolean);
538 const projectName = segments[segments.length - 1] ?? "project";537 const projectName = segments[segments.length - 1] ?? "project";
...@@ -550,11 +549,21 @@ export const createServer = Effect.fn(function* (options: ServerOptions = {}): E...@@ -550,11 +549,21 @@ export const createServer = Effect.fn(function* (options: ServerOptions = {}): E
550 });549 });
551550
552 ws.on("close", () => {551 ws.on("close", () => {
553 clients.delete(ws);552 void Effect.runPromise(
553 Ref.update(clients, (clients) => {
554 clients.delete(ws);
555 return clients;
556 }),
557 );
554 });558 });
555559
556 ws.on("error", () => {560 ws.on("error", () => {
557 clients.delete(ws);561 void Effect.runPromise(
562 Ref.update(clients, (clients) => {
563 clients.delete(ws);
564 return clients;
565 }),
566 );
558 });567 });
559 });568 });
560569
...@@ -575,10 +584,7 @@ export const createServer = Effect.fn(function* (options: ServerOptions = {}): E...@@ -575,10 +584,7 @@ export const createServer = Effect.fn(function* (options: ServerOptions = {}): E
575 yield* NodeHttpServer.make(() => httpServer, listenOptions);584 yield* NodeHttpServer.make(() => httpServer, listenOptions);
576585
577 yield* Effect.addFinalizer(() =>586 yield* Effect.addFinalizer(() =>
578 Effect.all([587 Effect.all([closeAllClients, closeWebSocketServer.pipe(Effect.orElseSucceed(() => undefined))]),
579 Effect.sync(() => closeAllClients()),
580 closeWebSocketServer.pipe(Effect.orElseSucceed(() => undefined)),
581 ]),
582 );588 );
583589
584 return httpServer;590 return httpServer;