@effect/platform-node-shared 0.29.4 → 0.30.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.
Files changed (39) hide show
  1. package/NodeClusterRunnerSocket/package.json +6 -0
  2. package/NodeClusterShardManagerSocket/package.json +6 -0
  3. package/NodeClusterSocketCommon/package.json +6 -0
  4. package/NodeSocketServer/package.json +6 -0
  5. package/dist/cjs/NodeClusterRunnerSocket.js +49 -0
  6. package/dist/cjs/NodeClusterRunnerSocket.js.map +1 -0
  7. package/dist/cjs/NodeClusterShardManagerSocket.js +38 -0
  8. package/dist/cjs/NodeClusterShardManagerSocket.js.map +1 -0
  9. package/dist/cjs/NodeClusterSocketCommon.js +34 -0
  10. package/dist/cjs/NodeClusterSocketCommon.js.map +1 -0
  11. package/dist/cjs/NodeSocket.js +2 -1
  12. package/dist/cjs/NodeSocket.js.map +1 -1
  13. package/dist/cjs/NodeSocketServer.js +149 -0
  14. package/dist/cjs/NodeSocketServer.js.map +1 -0
  15. package/dist/dts/NodeClusterRunnerSocket.d.ts +24 -0
  16. package/dist/dts/NodeClusterRunnerSocket.d.ts.map +1 -0
  17. package/dist/dts/NodeClusterShardManagerSocket.d.ts +22 -0
  18. package/dist/dts/NodeClusterShardManagerSocket.d.ts.map +1 -0
  19. package/dist/dts/NodeClusterSocketCommon.d.ts +12 -0
  20. package/dist/dts/NodeClusterSocketCommon.d.ts.map +1 -0
  21. package/dist/dts/NodeSocket.d.ts.map +1 -1
  22. package/dist/dts/NodeSocketServer.d.ts +44 -0
  23. package/dist/dts/NodeSocketServer.d.ts.map +1 -0
  24. package/dist/esm/NodeClusterRunnerSocket.js +39 -0
  25. package/dist/esm/NodeClusterRunnerSocket.js.map +1 -0
  26. package/dist/esm/NodeClusterShardManagerSocket.js +28 -0
  27. package/dist/esm/NodeClusterShardManagerSocket.js.map +1 -0
  28. package/dist/esm/NodeClusterSocketCommon.js +25 -0
  29. package/dist/esm/NodeClusterSocketCommon.js.map +1 -0
  30. package/dist/esm/NodeSocket.js +2 -1
  31. package/dist/esm/NodeSocket.js.map +1 -1
  32. package/dist/esm/NodeSocketServer.js +137 -0
  33. package/dist/esm/NodeSocketServer.js.map +1 -0
  34. package/package.json +40 -4
  35. package/src/NodeClusterRunnerSocket.ts +78 -0
  36. package/src/NodeClusterShardManagerSocket.ts +54 -0
  37. package/src/NodeClusterSocketCommon.ts +35 -0
  38. package/src/NodeSocket.ts +47 -48
  39. package/src/NodeSocketServer.ts +225 -0
@@ -0,0 +1,225 @@
1
+ /**
2
+ * @since 1.0.0
3
+ */
4
+ import * as Socket from "@effect/platform/Socket"
5
+ import * as SocketServer from "@effect/platform/SocketServer"
6
+ import type { Cause } from "effect/Cause"
7
+ import * as Context from "effect/Context"
8
+ import * as Effect from "effect/Effect"
9
+ import * as Exit from "effect/Exit"
10
+ import * as FiberRef from "effect/FiberRef"
11
+ import * as FiberSet from "effect/FiberSet"
12
+ import { pipe } from "effect/Function"
13
+ import * as Layer from "effect/Layer"
14
+ import * as Scope from "effect/Scope"
15
+ import type * as Http from "node:http"
16
+ import * as Net from "node:net"
17
+ import * as WS from "ws"
18
+ import * as NodeSocket from "./NodeSocket.js"
19
+
20
+ /**
21
+ * @since 1.0.0
22
+ * @category tags
23
+ */
24
+ export class IncomingMessage extends Context.Tag("@effect/platform-node-shared/NodeSocketServer/IncomingMessage")<
25
+ IncomingMessage,
26
+ Http.IncomingMessage
27
+ >() {}
28
+
29
+ /**
30
+ * @since 1.0.0
31
+ * @category constructors
32
+ */
33
+ export const make = Effect.fnUntraced(function*(
34
+ options: Net.ServerOpts & Net.ListenOptions
35
+ ) {
36
+ const server = yield* Effect.acquireRelease(
37
+ Effect.sync(() => Net.createServer(options)),
38
+ (server) =>
39
+ Effect.async<void>((resume) => {
40
+ server.close(() => resume(Effect.void))
41
+ })
42
+ )
43
+
44
+ yield* Effect.async<void, SocketServer.SocketServerError>((resume) => {
45
+ server.once("error", (cause) => {
46
+ resume(Effect.fail(
47
+ new SocketServer.SocketServerError({
48
+ reason: "Open",
49
+ cause
50
+ })
51
+ ))
52
+ })
53
+ server.listen(options, () => {
54
+ resume(Effect.void)
55
+ })
56
+ })
57
+
58
+ const run = Effect.fnUntraced(function*<R, E, _>(handler: (socket: Socket.Socket) => Effect.Effect<_, E, R>) {
59
+ const scope = yield* Scope.make()
60
+ const fiberSet = yield* FiberSet.make().pipe(
61
+ Scope.extend(scope)
62
+ )
63
+ const run = yield* FiberSet.runtime(fiberSet)<R>()
64
+ function onConnection(conn: Net.Socket) {
65
+ pipe(
66
+ NodeSocket.fromDuplex(
67
+ Effect.acquireRelease(
68
+ Effect.succeed(conn),
69
+ (conn) =>
70
+ Effect.sync(() => {
71
+ if (conn.closed === false) {
72
+ conn.destroySoon()
73
+ }
74
+ })
75
+ )
76
+ ),
77
+ Effect.flatMap(handler),
78
+ Effect.catchAllCause(reportUnhandledError),
79
+ Effect.provideService(NodeSocket.NetSocket, conn),
80
+ run
81
+ )
82
+ }
83
+ return yield* Effect.async<never>((_resume) => {
84
+ server.on("connection", onConnection)
85
+ return Effect.sync(() => {
86
+ server.off("connection", onConnection)
87
+ })
88
+ }).pipe(
89
+ Effect.ensuring(Scope.close(scope, Exit.void))
90
+ )
91
+ })
92
+
93
+ const address = server.address()!
94
+ return SocketServer.SocketServer.of({
95
+ address: typeof address === "string" ?
96
+ {
97
+ _tag: "UnixAddress",
98
+ path: address
99
+ } :
100
+ {
101
+ _tag: "TcpAddress",
102
+ hostname: address.address,
103
+ port: address.port
104
+ },
105
+ run
106
+ })
107
+ })
108
+
109
+ /**
110
+ * @since 1.0.0
111
+ * @category layers
112
+ */
113
+ export const layer = (
114
+ options: Net.ServerOpts & Net.ListenOptions
115
+ ): Layer.Layer<SocketServer.SocketServer, SocketServer.SocketServerError> =>
116
+ Layer.scoped(
117
+ SocketServer.SocketServer,
118
+ make(options)
119
+ )
120
+
121
+ /**
122
+ * @since 1.0.0
123
+ * @category constructors
124
+ */
125
+ export const makeWebSocket: (
126
+ options: WS.ServerOptions<typeof WS.WebSocket, typeof Http.IncomingMessage>
127
+ ) => Effect.Effect<
128
+ SocketServer.SocketServer["Type"],
129
+ SocketServer.SocketServerError,
130
+ Scope.Scope
131
+ > = Effect.fnUntraced(function*(
132
+ options: WS.ServerOptions
133
+ ) {
134
+ const server = yield* Effect.acquireRelease(
135
+ Effect.sync(() => new WS.WebSocketServer(options)),
136
+ (server) =>
137
+ Effect.async<void>((resume) => {
138
+ server.close(() => resume(Effect.void))
139
+ })
140
+ )
141
+
142
+ yield* Effect.async<void, SocketServer.SocketServerError>((resume) => {
143
+ server.once("error", (error) => {
144
+ resume(Effect.fail(
145
+ new SocketServer.SocketServerError({
146
+ reason: "Open",
147
+ cause: error
148
+ })
149
+ ))
150
+ })
151
+ server.once("listening", () => {
152
+ resume(Effect.void)
153
+ })
154
+ })
155
+
156
+ const run = Effect.fnUntraced(function*<R, E, _>(handler: (socket: Socket.Socket) => Effect.Effect<_, E, R>) {
157
+ const scope = yield* Scope.make()
158
+ const fiberSet = yield* FiberSet.make().pipe(
159
+ Scope.extend(scope)
160
+ )
161
+ const run = yield* FiberSet.runtime(fiberSet)<R>()
162
+ function onConnection(conn: Net.Socket, req: Http.IncomingMessage) {
163
+ pipe(
164
+ Socket.fromWebSocket(
165
+ Effect.acquireRelease(
166
+ Effect.succeed(conn as unknown as globalThis.WebSocket),
167
+ (conn) =>
168
+ Effect.sync(() => {
169
+ conn.close()
170
+ })
171
+ )
172
+ ),
173
+ Effect.flatMap(handler),
174
+ Effect.catchAllCause(reportUnhandledError),
175
+ Effect.provideService(Socket.WebSocket, conn as any),
176
+ Effect.provideService(IncomingMessage, req),
177
+ run
178
+ )
179
+ }
180
+ return yield* Effect.async<never>((_resume) => {
181
+ server.on("connection", onConnection)
182
+ return Effect.sync(() => {
183
+ server.off("connection", onConnection)
184
+ })
185
+ }).pipe(
186
+ Effect.ensuring(Scope.close(scope, Exit.void))
187
+ )
188
+ })
189
+
190
+ const address = server.address()!
191
+ return SocketServer.SocketServer.of({
192
+ address: typeof address === "string" ?
193
+ {
194
+ _tag: "UnixAddress",
195
+ path: address
196
+ } :
197
+ {
198
+ _tag: "TcpAddress",
199
+ hostname: address.address,
200
+ port: address.port
201
+ },
202
+ run
203
+ })
204
+ })
205
+
206
+ /**
207
+ * @since 1.0.0
208
+ * @category layers
209
+ */
210
+ export const layerWebSocket = (
211
+ options: WS.ServerOptions
212
+ ): Layer.Layer<SocketServer.SocketServer, SocketServer.SocketServerError> =>
213
+ Layer.scoped(
214
+ SocketServer.SocketServer,
215
+ makeWebSocket(options)
216
+ )
217
+
218
+ const reportUnhandledError = <E>(cause: Cause<E>) =>
219
+ Effect.withFiberRuntime<void>((fiber) => {
220
+ const unhandledLogLevel = fiber.getFiberRef(FiberRef.unhandledErrorLogLevel)
221
+ if (unhandledLogLevel._tag === "Some") {
222
+ return Effect.logWithLevel(unhandledLogLevel.value, cause, "Unhandled error in SocketServer")
223
+ }
224
+ return Effect.void
225
+ })