@effect/platform-node 0.0.0-snapshot-c0ae728e57df2c572ea803e1bb7121088cd67b49

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 (181) hide show
  1. package/Http/Platform/package.json +6 -0
  2. package/Http/Server/package.json +6 -0
  3. package/Http/ServerRequest/package.json +6 -0
  4. package/LICENSE +21 -0
  5. package/NodeCommandExecutor/package.json +6 -0
  6. package/NodeContext/package.json +6 -0
  7. package/NodeFileSystem/package.json +6 -0
  8. package/NodeHttpClient/package.json +6 -0
  9. package/NodeHttpServer/package.json +6 -0
  10. package/NodeKeyValueStore/package.json +6 -0
  11. package/NodePath/package.json +6 -0
  12. package/NodeRuntime/package.json +6 -0
  13. package/NodeSink/package.json +6 -0
  14. package/NodeStream/package.json +6 -0
  15. package/NodeTerminal/package.json +6 -0
  16. package/NodeWorker/package.json +6 -0
  17. package/NodeWorkerRunner/package.json +6 -0
  18. package/README.md +64 -0
  19. package/dist/cjs/Http/Platform.js +43 -0
  20. package/dist/cjs/Http/Platform.js.map +1 -0
  21. package/dist/cjs/Http/Server.js +58 -0
  22. package/dist/cjs/Http/Server.js.map +1 -0
  23. package/dist/cjs/Http/ServerRequest.js +38 -0
  24. package/dist/cjs/Http/ServerRequest.js.map +1 -0
  25. package/dist/cjs/NodeCommandExecutor.js +42 -0
  26. package/dist/cjs/NodeCommandExecutor.js.map +1 -0
  27. package/dist/cjs/NodeContext.js +48 -0
  28. package/dist/cjs/NodeContext.js.map +1 -0
  29. package/dist/cjs/NodeFileSystem.js +42 -0
  30. package/dist/cjs/NodeFileSystem.js.map +1 -0
  31. package/dist/cjs/NodeHttpClient.js +73 -0
  32. package/dist/cjs/NodeHttpClient.js.map +1 -0
  33. package/dist/cjs/NodeHttpServer.js +40 -0
  34. package/dist/cjs/NodeHttpServer.js.map +1 -0
  35. package/dist/cjs/NodeKeyValueStore.js +42 -0
  36. package/dist/cjs/NodeKeyValueStore.js.map +1 -0
  37. package/dist/cjs/NodePath.js +52 -0
  38. package/dist/cjs/NodePath.js.map +1 -0
  39. package/dist/cjs/NodeRuntime.js +42 -0
  40. package/dist/cjs/NodeRuntime.js.map +1 -0
  41. package/dist/cjs/NodeSink.js +17 -0
  42. package/dist/cjs/NodeSink.js.map +1 -0
  43. package/dist/cjs/NodeStream.js +17 -0
  44. package/dist/cjs/NodeStream.js.map +1 -0
  45. package/dist/cjs/NodeTerminal.js +47 -0
  46. package/dist/cjs/NodeTerminal.js.map +1 -0
  47. package/dist/cjs/NodeWorker.js +63 -0
  48. package/dist/cjs/NodeWorker.js.map +1 -0
  49. package/dist/cjs/NodeWorkerRunner.js +48 -0
  50. package/dist/cjs/NodeWorkerRunner.js.map +1 -0
  51. package/dist/cjs/index.js +58 -0
  52. package/dist/cjs/index.js.map +1 -0
  53. package/dist/cjs/internal/http/client.js +216 -0
  54. package/dist/cjs/internal/http/client.js.map +1 -0
  55. package/dist/cjs/internal/http/incomingMessage.js +91 -0
  56. package/dist/cjs/internal/http/incomingMessage.js.map +1 -0
  57. package/dist/cjs/internal/http/platform.js +77 -0
  58. package/dist/cjs/internal/http/platform.js.map +1 -0
  59. package/dist/cjs/internal/http/server.js +270 -0
  60. package/dist/cjs/internal/http/server.js.map +1 -0
  61. package/dist/cjs/internal/worker.js +89 -0
  62. package/dist/cjs/internal/worker.js.map +1 -0
  63. package/dist/cjs/internal/workerRunner.js +85 -0
  64. package/dist/cjs/internal/workerRunner.js.map +1 -0
  65. package/dist/dts/Http/Platform.d.ts +19 -0
  66. package/dist/dts/Http/Platform.d.ts.map +1 -0
  67. package/dist/dts/Http/Server.d.ts +50 -0
  68. package/dist/dts/Http/Server.d.ts.map +1 -0
  69. package/dist/dts/Http/ServerRequest.d.ts +12 -0
  70. package/dist/dts/Http/ServerRequest.d.ts.map +1 -0
  71. package/dist/dts/NodeCommandExecutor.d.ts +9 -0
  72. package/dist/dts/NodeCommandExecutor.d.ts.map +1 -0
  73. package/dist/dts/NodeContext.d.ts +17 -0
  74. package/dist/dts/NodeContext.d.ts.map +1 -0
  75. package/dist/dts/NodeFileSystem.d.ts +8 -0
  76. package/dist/dts/NodeFileSystem.d.ts.map +1 -0
  77. package/dist/dts/NodeHttpClient.d.ts +67 -0
  78. package/dist/dts/NodeHttpClient.d.ts.map +1 -0
  79. package/dist/dts/NodeHttpServer.d.ts +37 -0
  80. package/dist/dts/NodeHttpServer.d.ts.map +1 -0
  81. package/dist/dts/NodeKeyValueStore.d.ts +9 -0
  82. package/dist/dts/NodeKeyValueStore.d.ts.map +1 -0
  83. package/dist/dts/NodePath.d.ts +21 -0
  84. package/dist/dts/NodePath.d.ts.map +1 -0
  85. package/dist/dts/NodeRuntime.d.ts +7 -0
  86. package/dist/dts/NodeRuntime.d.ts.map +1 -0
  87. package/dist/dts/NodeSink.d.ts +8 -0
  88. package/dist/dts/NodeSink.d.ts.map +1 -0
  89. package/dist/dts/NodeStream.d.ts +8 -0
  90. package/dist/dts/NodeStream.d.ts.map +1 -0
  91. package/dist/dts/NodeTerminal.d.ts +15 -0
  92. package/dist/dts/NodeTerminal.d.ts.map +1 -0
  93. package/dist/dts/NodeWorker.d.ts +42 -0
  94. package/dist/dts/NodeWorker.d.ts.map +1 -0
  95. package/dist/dts/NodeWorkerRunner.d.ts +24 -0
  96. package/dist/dts/NodeWorkerRunner.d.ts.map +1 -0
  97. package/dist/dts/index.d.ts +53 -0
  98. package/dist/dts/index.d.ts.map +1 -0
  99. package/dist/dts/internal/http/client.d.ts +2 -0
  100. package/dist/dts/internal/http/client.d.ts.map +1 -0
  101. package/dist/dts/internal/http/incomingMessage.d.ts +2 -0
  102. package/dist/dts/internal/http/incomingMessage.d.ts.map +1 -0
  103. package/dist/dts/internal/http/platform.d.ts +2 -0
  104. package/dist/dts/internal/http/platform.d.ts.map +1 -0
  105. package/dist/dts/internal/http/server.d.ts +2 -0
  106. package/dist/dts/internal/http/server.d.ts.map +1 -0
  107. package/dist/dts/internal/worker.d.ts +2 -0
  108. package/dist/dts/internal/worker.d.ts.map +1 -0
  109. package/dist/dts/internal/workerRunner.d.ts +2 -0
  110. package/dist/dts/internal/workerRunner.d.ts.map +1 -0
  111. package/dist/esm/Http/Platform.js +12 -0
  112. package/dist/esm/Http/Platform.js.map +1 -0
  113. package/dist/esm/Http/Server.js +27 -0
  114. package/dist/esm/Http/Server.js.map +1 -0
  115. package/dist/esm/Http/ServerRequest.js +7 -0
  116. package/dist/esm/Http/ServerRequest.js.map +1 -0
  117. package/dist/esm/NodeCommandExecutor.js +10 -0
  118. package/dist/esm/NodeCommandExecutor.js.map +1 -0
  119. package/dist/esm/NodeContext.js +16 -0
  120. package/dist/esm/NodeContext.js.map +1 -0
  121. package/dist/esm/NodeFileSystem.js +10 -0
  122. package/dist/esm/NodeFileSystem.js.map +1 -0
  123. package/dist/esm/NodeHttpClient.js +42 -0
  124. package/dist/esm/NodeHttpClient.js.map +1 -0
  125. package/dist/esm/NodeHttpServer.js +37 -0
  126. package/dist/esm/NodeHttpServer.js.map +1 -0
  127. package/dist/esm/NodeKeyValueStore.js +10 -0
  128. package/dist/esm/NodeKeyValueStore.js.map +1 -0
  129. package/dist/esm/NodePath.js +20 -0
  130. package/dist/esm/NodePath.js.map +1 -0
  131. package/dist/esm/NodeRuntime.js +10 -0
  132. package/dist/esm/NodeRuntime.js.map +1 -0
  133. package/dist/esm/NodeSink.js +8 -0
  134. package/dist/esm/NodeSink.js.map +1 -0
  135. package/dist/esm/NodeStream.js +8 -0
  136. package/dist/esm/NodeStream.js.map +1 -0
  137. package/dist/esm/NodeTerminal.js +15 -0
  138. package/dist/esm/NodeTerminal.js.map +1 -0
  139. package/dist/esm/NodeWorker.js +32 -0
  140. package/dist/esm/NodeWorker.js.map +1 -0
  141. package/dist/esm/NodeWorkerRunner.js +17 -0
  142. package/dist/esm/NodeWorkerRunner.js.map +1 -0
  143. package/dist/esm/index.js +53 -0
  144. package/dist/esm/index.js.map +1 -0
  145. package/dist/esm/internal/http/client.js +183 -0
  146. package/dist/esm/internal/http/client.js.map +1 -0
  147. package/dist/esm/internal/http/incomingMessage.js +59 -0
  148. package/dist/esm/internal/http/incomingMessage.js.map +1 -0
  149. package/dist/esm/internal/http/platform.js +41 -0
  150. package/dist/esm/internal/http/platform.js.map +1 -0
  151. package/dist/esm/internal/http/server.js +233 -0
  152. package/dist/esm/internal/http/server.js.map +1 -0
  153. package/dist/esm/internal/worker.js +58 -0
  154. package/dist/esm/internal/worker.js.map +1 -0
  155. package/dist/esm/internal/workerRunner.js +52 -0
  156. package/dist/esm/internal/workerRunner.js.map +1 -0
  157. package/dist/esm/package.json +4 -0
  158. package/package.json +162 -0
  159. package/src/Http/Platform.ts +21 -0
  160. package/src/Http/Server.ts +86 -0
  161. package/src/Http/ServerRequest.ts +12 -0
  162. package/src/NodeCommandExecutor.ts +13 -0
  163. package/src/NodeContext.ts +40 -0
  164. package/src/NodeFileSystem.ts +12 -0
  165. package/src/NodeHttpClient.ts +76 -0
  166. package/src/NodeHttpServer.ts +38 -0
  167. package/src/NodeKeyValueStore.ts +15 -0
  168. package/src/NodePath.ts +25 -0
  169. package/src/NodeRuntime.ts +11 -0
  170. package/src/NodeSink.ts +8 -0
  171. package/src/NodeStream.ts +8 -0
  172. package/src/NodeTerminal.ts +20 -0
  173. package/src/NodeWorker.ts +58 -0
  174. package/src/NodeWorkerRunner.ts +38 -0
  175. package/src/index.ts +64 -0
  176. package/src/internal/http/client.ts +258 -0
  177. package/src/internal/http/incomingMessage.ts +81 -0
  178. package/src/internal/http/platform.ts +46 -0
  179. package/src/internal/http/server.ts +375 -0
  180. package/src/internal/worker.ts +74 -0
  181. package/src/internal/workerRunner.ts +74 -0
@@ -0,0 +1,375 @@
1
+ import * as Etag from "@effect/platform-node-shared/Http/Etag"
2
+ import * as MultipartNode from "@effect/platform-node-shared/Http/Multipart"
3
+ import * as FileSystem from "@effect/platform/FileSystem"
4
+ import * as App from "@effect/platform/Http/App"
5
+ import type * as Headers from "@effect/platform/Http/Headers"
6
+ import * as IncomingMessage from "@effect/platform/Http/IncomingMessage"
7
+ import type { Method } from "@effect/platform/Http/Method"
8
+ import * as Middleware from "@effect/platform/Http/Middleware"
9
+ import type * as Multipart from "@effect/platform/Http/Multipart"
10
+ import * as Server from "@effect/platform/Http/Server"
11
+ import * as Error from "@effect/platform/Http/ServerError"
12
+ import * as ServerRequest from "@effect/platform/Http/ServerRequest"
13
+ import type * as ServerResponse from "@effect/platform/Http/ServerResponse"
14
+ import type * as Path from "@effect/platform/Path"
15
+ import * as Cause from "effect/Cause"
16
+ import * as Config from "effect/Config"
17
+ import * as Effect from "effect/Effect"
18
+ import { type LazyArg } from "effect/Function"
19
+ import * as Layer from "effect/Layer"
20
+ import * as Option from "effect/Option"
21
+ import * as Runtime from "effect/Runtime"
22
+ import type * as Scope from "effect/Scope"
23
+ import * as Stream from "effect/Stream"
24
+ import type * as Http from "node:http"
25
+ import type * as Net from "node:net"
26
+ import { Readable } from "node:stream"
27
+ import { pipeline } from "node:stream/promises"
28
+ import * as NodeContext from "../../NodeContext.js"
29
+ import * as NodeSink from "../../NodeSink.js"
30
+ import { IncomingMessageImpl } from "./incomingMessage.js"
31
+ import * as internalPlatform from "./platform.js"
32
+
33
+ /** @internal */
34
+ export const make = (
35
+ evaluate: LazyArg<Http.Server>,
36
+ options: Net.ListenOptions
37
+ ): Effect.Effect<Server.Server, Error.ServeError, Scope.Scope> =>
38
+ Effect.gen(function*(_) {
39
+ const server = yield* _(Effect.acquireRelease(
40
+ Effect.sync(evaluate),
41
+ (server) =>
42
+ Effect.async<void, never, never>((resume) => {
43
+ server.close((error) => {
44
+ if (error) {
45
+ resume(Effect.die(error))
46
+ } else {
47
+ resume(Effect.unit)
48
+ }
49
+ })
50
+ })
51
+ ))
52
+
53
+ yield* _(Effect.async<void, Error.ServeError, never>((resume) => {
54
+ server.on("error", (error) => {
55
+ resume(Effect.fail(Error.ServeError({ error })))
56
+ })
57
+ server.listen(options, () => {
58
+ resume(Effect.unit)
59
+ })
60
+ }))
61
+
62
+ const address = server.address()!
63
+
64
+ return Server.make({
65
+ address: typeof address === "string" ?
66
+ {
67
+ _tag: "UnixAddress",
68
+ path: address
69
+ } :
70
+ {
71
+ _tag: "TcpAddress",
72
+ hostname: address.address === "::" ? "0.0.0.0" : address.address,
73
+ port: address.port
74
+ },
75
+ serve: (httpApp, middleware) =>
76
+ Effect.gen(function*(_) {
77
+ const handler = yield* _(makeHandler(httpApp, middleware!))
78
+ yield* _(Effect.addFinalizer(() =>
79
+ Effect.sync(() => {
80
+ server.off("request", handler)
81
+ })
82
+ ))
83
+ server.on("request", handler)
84
+ })
85
+ })
86
+ }).pipe(
87
+ Effect.locally(
88
+ IncomingMessage.maxBodySize,
89
+ Option.some(FileSystem.Size(1024 * 1024 * 10))
90
+ )
91
+ )
92
+
93
+ /** @internal */
94
+ export const makeHandler: {
95
+ <R, E>(httpApp: App.Default<R, E>): Effect.Effect<
96
+ (nodeRequest: Http.IncomingMessage, nodeResponse: Http.ServerResponse) => void,
97
+ never,
98
+ Exclude<R, ServerRequest.ServerRequest | Scope.Scope>
99
+ >
100
+ <R, E, App extends App.Default<any, any>>(
101
+ httpApp: App.Default<R, E>,
102
+ middleware: Middleware.Middleware.Applied<R, E, App>
103
+ ): Effect.Effect<
104
+ (nodeRequest: Http.IncomingMessage, nodeResponse: Http.ServerResponse) => void,
105
+ never,
106
+ Exclude<Effect.Effect.Context<App>, ServerRequest.ServerRequest | Scope.Scope>
107
+ >
108
+ } = <R, E>(httpApp: App.Default<R, E>, middleware?: Middleware.Middleware) => {
109
+ const handledApp = Effect.scoped(
110
+ middleware
111
+ ? middleware(App.withDefaultMiddleware(respond(httpApp)))
112
+ : App.withDefaultMiddleware(respond(httpApp))
113
+ )
114
+ return Effect.map(Effect.runtime<R>(), (runtime) => {
115
+ const runFork = Runtime.runFork(runtime)
116
+ return function handler(nodeRequest: Http.IncomingMessage, nodeResponse: Http.ServerResponse) {
117
+ const fiber = runFork(
118
+ Effect.provideService(
119
+ handledApp,
120
+ ServerRequest.ServerRequest,
121
+ new ServerRequestImpl(nodeRequest, nodeResponse)
122
+ )
123
+ )
124
+ nodeResponse.on("close", () => {
125
+ if (!nodeResponse.writableEnded) {
126
+ if (!nodeResponse.headersSent) {
127
+ nodeResponse.writeHead(499)
128
+ }
129
+ nodeResponse.end()
130
+ runFork(fiber.interruptAsFork(Error.clientAbortFiberId))
131
+ }
132
+ })
133
+ }
134
+ })
135
+ }
136
+
137
+ const respond = Middleware.make((httpApp) =>
138
+ Effect.uninterruptibleMask((restore) =>
139
+ Effect.flatMap(ServerRequest.ServerRequest, (request) =>
140
+ Effect.tapErrorCause(
141
+ restore(
142
+ Effect.tap(
143
+ Effect.flatMap(
144
+ httpApp,
145
+ (response) => Effect.flatMap(App.preResponseHandler, (f) => f(request, response))
146
+ ),
147
+ (response) => handleResponse(request, response)
148
+ )
149
+ ),
150
+ (cause) =>
151
+ Effect.sync(() => {
152
+ const nodeResponse = (request as ServerRequestImpl).response
153
+ if (!nodeResponse.headersSent) {
154
+ nodeResponse.writeHead(Cause.isInterruptedOnly(cause) ? 503 : 500)
155
+ }
156
+ if (!nodeResponse.writableEnded) {
157
+ nodeResponse.end()
158
+ }
159
+ })
160
+ ))
161
+ )
162
+ )
163
+
164
+ class ServerRequestImpl extends IncomingMessageImpl<Error.RequestError> implements ServerRequest.ServerRequest {
165
+ readonly [ServerRequest.TypeId]: ServerRequest.TypeId
166
+
167
+ constructor(
168
+ readonly source: Http.IncomingMessage,
169
+ readonly response: Http.ServerResponse,
170
+ readonly url = source.url!,
171
+ private headersOverride?: Headers.Headers,
172
+ remoteAddressOverride?: string
173
+ ) {
174
+ super(source, (_) =>
175
+ Error.RequestError({
176
+ request: this,
177
+ reason: "Decode",
178
+ error: _
179
+ }), remoteAddressOverride)
180
+ this[ServerRequest.TypeId] = ServerRequest.TypeId
181
+ }
182
+
183
+ modify(
184
+ options: {
185
+ readonly url?: string | undefined
186
+ readonly headers?: Headers.Headers | undefined
187
+ readonly remoteAddress?: string | undefined
188
+ }
189
+ ) {
190
+ return new ServerRequestImpl(
191
+ this.source,
192
+ this.response,
193
+ options.url ?? this.url,
194
+ options.headers ?? this.headersOverride,
195
+ options.remoteAddress ?? this.remoteAddressOverride
196
+ )
197
+ }
198
+
199
+ get originalUrl(): string {
200
+ return this.source.url!
201
+ }
202
+
203
+ get method(): Method {
204
+ return this.source.method!.toUpperCase() as Method
205
+ }
206
+
207
+ get headers(): Headers.Headers {
208
+ this.headersOverride ??= this.source.headers as Headers.Headers
209
+ return this.headersOverride
210
+ }
211
+
212
+ private multipartEffect:
213
+ | Effect.Effect<
214
+ Multipart.Persisted,
215
+ Multipart.MultipartError,
216
+ Scope.Scope | FileSystem.FileSystem | Path.Path
217
+ >
218
+ | undefined
219
+ get multipart(): Effect.Effect<
220
+ Multipart.Persisted,
221
+ Multipart.MultipartError,
222
+ Scope.Scope | FileSystem.FileSystem | Path.Path
223
+ > {
224
+ if (this.multipartEffect) {
225
+ return this.multipartEffect
226
+ }
227
+ this.multipartEffect = Effect.runSync(Effect.cached(
228
+ MultipartNode.persisted(this.source, this.source.headers)
229
+ ))
230
+ return this.multipartEffect
231
+ }
232
+
233
+ get multipartStream(): Stream.Stream<Multipart.Part, Multipart.MultipartError> {
234
+ return MultipartNode.stream(this.source, this.source.headers)
235
+ }
236
+
237
+ toString(): string {
238
+ return `ServerRequest(${this.method} ${this.url})`
239
+ }
240
+
241
+ toJSON(): unknown {
242
+ return {
243
+ _tag: "ServerRequest",
244
+ method: this.method,
245
+ url: this.url,
246
+ originalUrl: this.originalUrl,
247
+ headers: this.headers
248
+ }
249
+ }
250
+ }
251
+
252
+ /** @internal */
253
+ export const layerServer = (
254
+ evaluate: LazyArg<Http.Server>,
255
+ options: Net.ListenOptions
256
+ ) => Layer.scoped(Server.Server, make(evaluate, options))
257
+
258
+ /** @internal */
259
+ export const layer = (
260
+ evaluate: LazyArg<Http.Server>,
261
+ options: Net.ListenOptions
262
+ ) =>
263
+ Layer.mergeAll(
264
+ Layer.scoped(Server.Server, make(evaluate, options)),
265
+ internalPlatform.layer,
266
+ Etag.layerWeak,
267
+ NodeContext.layer
268
+ )
269
+
270
+ /** @internal */
271
+ export const layerConfig = (
272
+ evaluate: LazyArg<Http.Server>,
273
+ options: Config.Config.Wrap<Net.ListenOptions>
274
+ ) =>
275
+ Layer.mergeAll(
276
+ Layer.scoped(
277
+ Server.Server,
278
+ Effect.flatMap(Config.unwrap(options), (options) => make(evaluate, options))
279
+ ),
280
+ internalPlatform.layer,
281
+ Etag.layerWeak,
282
+ NodeContext.layer
283
+ )
284
+
285
+ const handleResponse = (request: ServerRequest.ServerRequest, response: ServerResponse.ServerResponse) =>
286
+ Effect.suspend((): Effect.Effect<void, Error.ResponseError> => {
287
+ const nodeResponse = (request as ServerRequestImpl).response
288
+ if (request.method === "HEAD") {
289
+ nodeResponse.writeHead(response.status, response.headers)
290
+ nodeResponse.end()
291
+ return Effect.unit
292
+ }
293
+ const body = response.body
294
+ switch (body._tag) {
295
+ case "Empty": {
296
+ nodeResponse.writeHead(response.status, response.headers)
297
+ nodeResponse.end()
298
+ return Effect.unit
299
+ }
300
+ case "Raw": {
301
+ nodeResponse.writeHead(response.status, response.headers)
302
+ if (
303
+ typeof body.body === "object" && body.body !== null && "pipe" in body.body &&
304
+ typeof body.body.pipe === "function"
305
+ ) {
306
+ return Effect.tryPromise({
307
+ try: (signal) => pipeline(body.body as any, nodeResponse, { signal, end: true }),
308
+ catch: (error) =>
309
+ Error.ResponseError({
310
+ request,
311
+ response,
312
+ reason: "Decode",
313
+ error
314
+ })
315
+ })
316
+ }
317
+ nodeResponse.end(body.body)
318
+ return Effect.unit
319
+ }
320
+ case "Uint8Array": {
321
+ nodeResponse.writeHead(response.status, response.headers)
322
+ nodeResponse.end(body.body)
323
+ return Effect.unit
324
+ }
325
+ case "FormData": {
326
+ return Effect.async<void, Error.ResponseError, never>((resume) => {
327
+ const r = new Response(body.formData)
328
+ const headers = {
329
+ ...response.headers,
330
+ ...Object.fromEntries(r.headers)
331
+ }
332
+ nodeResponse.writeHead(response.status, headers)
333
+ Readable.fromWeb(r.body as any)
334
+ .pipe(nodeResponse)
335
+ .on("error", (error) => {
336
+ resume(Effect.fail(Error.ResponseError({
337
+ request,
338
+ response,
339
+ reason: "Decode",
340
+ error
341
+ })))
342
+ })
343
+ .once("finish", () => {
344
+ resume(Effect.unit)
345
+ })
346
+ })
347
+ }
348
+ case "Stream": {
349
+ nodeResponse.writeHead(response.status, response.headers)
350
+ return Stream.run(
351
+ Stream.mapError(
352
+ body.stream,
353
+ (error) =>
354
+ Error.ResponseError({
355
+ request,
356
+ response,
357
+ reason: "Decode",
358
+ error
359
+ })
360
+ ),
361
+ NodeSink.fromWritable(() => nodeResponse, (error) =>
362
+ Error.ResponseError({
363
+ request,
364
+ response,
365
+ reason: "Decode",
366
+ error
367
+ }))
368
+ )
369
+ }
370
+ }
371
+ })
372
+
373
+ /** @internal */
374
+ export const requestSource = (self: ServerRequest.ServerRequest): Http.IncomingMessage =>
375
+ (self as ServerRequestImpl).source
@@ -0,0 +1,74 @@
1
+ import * as Worker from "@effect/platform/Worker"
2
+ import { WorkerError } from "@effect/platform/WorkerError"
3
+ import * as Effect from "effect/Effect"
4
+ import { pipe } from "effect/Function"
5
+ import * as Layer from "effect/Layer"
6
+ import * as Queue from "effect/Queue"
7
+ import type * as WorkerThreads from "node:worker_threads"
8
+
9
+ const platformWorkerImpl = Worker.PlatformWorker.of({
10
+ [Worker.PlatformWorkerTypeId]: Worker.PlatformWorkerTypeId,
11
+ spawn<I, O>(worker_: unknown) {
12
+ return Effect.gen(function*(_) {
13
+ const worker = worker_ as WorkerThreads.Worker
14
+ yield* _(Effect.addFinalizer(() =>
15
+ pipe(
16
+ Effect.async<void, never, never>((resume) => {
17
+ worker.once("exit", () => {
18
+ resume(Effect.unit)
19
+ })
20
+ worker.postMessage([1])
21
+ }),
22
+ Effect.timeout(5000),
23
+ Effect.orElse(() => Effect.sync(() => worker.terminate()))
24
+ )
25
+ ))
26
+ const queue = yield* _(Queue.unbounded<Worker.BackingWorker.Message<O>>())
27
+ yield* _(Effect.addFinalizer(() => Queue.shutdown(queue)))
28
+ const fiber = yield* _(
29
+ Effect.async<never, WorkerError, never>((resume) => {
30
+ worker.on("message", (message: Worker.BackingWorker.Message<O>) => {
31
+ queue.unsafeOffer(message)
32
+ })
33
+ worker.on("messageerror", (error) => {
34
+ resume(Effect.fail(WorkerError("decode", error.message, error.stack)))
35
+ })
36
+ worker.on("error", (error) => {
37
+ resume(Effect.fail(WorkerError("unknown", error.message, error.stack)))
38
+ })
39
+ worker.on("exit", (code) => {
40
+ resume(Effect.fail(WorkerError("unknown", new Error(`exited with code ${code}`))))
41
+ })
42
+ }),
43
+ Effect.interruptible,
44
+ Effect.forkScoped
45
+ )
46
+ const send = (message: I, transfers?: ReadonlyArray<unknown>) =>
47
+ Effect.try({
48
+ try: () => worker.postMessage([0, message], transfers as any),
49
+ catch: (error) => WorkerError("send", (error as any).message, (error as any).stack)
50
+ })
51
+ return { fiber, queue, send }
52
+ })
53
+ }
54
+ })
55
+
56
+ /** @internal */
57
+ export const layerWorker = Layer.succeed(Worker.PlatformWorker, platformWorkerImpl)
58
+
59
+ /** @internal */
60
+ export const layerManager = Layer.provide(Worker.layerManager, layerWorker)
61
+
62
+ /** @internal */
63
+ export const makePool = Worker.makePool<WorkerThreads.Worker>()
64
+
65
+ /** @internal */
66
+ export const makePoolLayer = Worker.makePoolLayer<WorkerThreads.Worker>(layerManager)
67
+
68
+ /** @internal */
69
+ export const makePoolSerialized = Worker.makePoolSerialized<WorkerThreads.Worker>()
70
+
71
+ /** @internal */
72
+ export const makePoolSerializedLayer = Worker.makePoolSerializedLayer<WorkerThreads.Worker>(
73
+ layerManager
74
+ )
@@ -0,0 +1,74 @@
1
+ import { WorkerError } from "@effect/platform/WorkerError"
2
+ import * as Runner from "@effect/platform/WorkerRunner"
3
+ import type * as Schema from "@effect/schema/Schema"
4
+ import * as Cause from "effect/Cause"
5
+ import * as Effect from "effect/Effect"
6
+ import * as Layer from "effect/Layer"
7
+ import * as Queue from "effect/Queue"
8
+ import * as Schedule from "effect/Schedule"
9
+ import type * as Stream from "effect/Stream"
10
+ import * as WorkerThreads from "node:worker_threads"
11
+
12
+ const platformRunnerImpl = Runner.PlatformRunner.of({
13
+ [Runner.PlatformRunnerTypeId]: Runner.PlatformRunnerTypeId,
14
+ start<I, O>(shutdown: Effect.Effect<void>) {
15
+ return Effect.gen(function*(_) {
16
+ if (!WorkerThreads.parentPort) {
17
+ return yield* _(Effect.fail(WorkerError("spawn", "not in worker")))
18
+ }
19
+ const port = WorkerThreads.parentPort
20
+ const queue = yield* _(Queue.unbounded<I>())
21
+ yield* _(
22
+ Effect.async<never, WorkerError, never>((resume) => {
23
+ port.on("message", (message: Runner.BackingRunner.Message<I>) => {
24
+ if (message[0] === 0) {
25
+ queue.unsafeOffer(message[1])
26
+ } else {
27
+ Effect.runFork(shutdown)
28
+ }
29
+ })
30
+ port.on("messageerror", (error) => {
31
+ resume(Effect.fail(WorkerError("decode", error.message, error.stack)))
32
+ })
33
+ port.on("error", (error) => {
34
+ resume(Effect.fail(WorkerError("unknown", error.message, error.stack)))
35
+ })
36
+ }),
37
+ Effect.tapErrorCause((cause) => Cause.isInterruptedOnly(cause) ? Effect.unit : Effect.logDebug(cause)),
38
+ Effect.retry(Schedule.forever),
39
+ Effect.annotateLogs({
40
+ package: "@effect/platform-node",
41
+ module: "WorkerRunner"
42
+ }),
43
+ Effect.interruptible,
44
+ Effect.forkScoped
45
+ )
46
+ const send = (message: O, transfers?: ReadonlyArray<unknown>) =>
47
+ Effect.sync(() => port.postMessage([1, message], transfers as any))
48
+ // ready
49
+ port.postMessage([0])
50
+ return { queue, send }
51
+ })
52
+ }
53
+ })
54
+
55
+ /** @internal */
56
+ export const layerPlatform = Layer.succeed(Runner.PlatformRunner, platformRunnerImpl)
57
+
58
+ /** @internal */
59
+ export const layer = <I, R, E, O>(
60
+ process: (request: I) => Stream.Stream<O, E, R>,
61
+ options?: Runner.Runner.Options<I, E, O>
62
+ ): Layer.Layer<never, WorkerError, R> => Layer.provide(Runner.layer(process, options), layerPlatform)
63
+
64
+ /** @internal */
65
+ export const layerSerialized = <
66
+ A extends Schema.TaggedRequest.Any,
67
+ I,
68
+ R,
69
+ Handlers extends Runner.SerializedRunner.Handlers<A>
70
+ >(
71
+ schema: Schema.Schema<A, I, R>,
72
+ handlers: Handlers
73
+ ): Layer.Layer<never, WorkerError, R | Runner.SerializedRunner.HandlersContext<Handlers>> =>
74
+ Layer.provide(Runner.layerSerialized(schema, handlers), layerPlatform)