@effect/platform-node-shared 4.0.0-beta.6 → 4.0.0-beta.60
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.
- package/dist/NodeChildProcessSpawner.d.ts.map +1 -1
- package/dist/NodeChildProcessSpawner.js +74 -15
- package/dist/NodeChildProcessSpawner.js.map +1 -1
- package/dist/NodeClusterSocket.d.ts.map +1 -1
- package/dist/NodeClusterSocket.js +4 -3
- package/dist/NodeClusterSocket.js.map +1 -1
- package/dist/NodeFileSystem.d.ts.map +1 -1
- package/dist/NodeFileSystem.js +19 -23
- package/dist/NodeFileSystem.js.map +1 -1
- package/dist/NodeRuntime.d.ts.map +1 -1
- package/dist/NodeRuntime.js +2 -6
- package/dist/NodeRuntime.js.map +1 -1
- package/dist/NodeSink.d.ts.map +1 -1
- package/dist/NodeSink.js +7 -1
- package/dist/NodeSink.js.map +1 -1
- package/dist/NodeSocket.d.ts +4 -4
- package/dist/NodeSocket.d.ts.map +1 -1
- package/dist/NodeSocket.js +8 -9
- package/dist/NodeSocket.js.map +1 -1
- package/dist/NodeSocketServer.d.ts +2 -2
- package/dist/NodeSocketServer.d.ts.map +1 -1
- package/dist/NodeSocketServer.js +6 -6
- package/dist/NodeSocketServer.js.map +1 -1
- package/dist/NodeStdio.d.ts +0 -3
- package/dist/NodeStdio.d.ts.map +1 -1
- package/dist/NodeStdio.js +8 -4
- package/dist/NodeStdio.js.map +1 -1
- package/dist/NodeStream.d.ts.map +1 -1
- package/dist/NodeStream.js +8 -7
- package/dist/NodeStream.js.map +1 -1
- package/dist/NodeTerminal.d.ts.map +1 -1
- package/dist/NodeTerminal.js +2 -1
- package/dist/NodeTerminal.js.map +1 -1
- package/package.json +6 -6
- package/src/NodeChildProcessSpawner.ts +98 -24
- package/src/NodeClusterSocket.ts +4 -3
- package/src/NodeFileSystem.ts +26 -25
- package/src/NodeRuntime.ts +2 -6
- package/src/NodeSink.ts +7 -0
- package/src/NodeSocket.ts +10 -11
- package/src/NodeSocketServer.ts +6 -6
- package/src/NodeStdio.ts +26 -20
- package/src/NodeStream.ts +13 -10
- package/src/NodeTerminal.ts +2 -1
|
@@ -17,7 +17,13 @@ import * as Sink from "effect/Sink"
|
|
|
17
17
|
import * as Stream from "effect/Stream"
|
|
18
18
|
import * as ChildProcess from "effect/unstable/process/ChildProcess"
|
|
19
19
|
import type { ChildProcessHandle } from "effect/unstable/process/ChildProcessSpawner"
|
|
20
|
-
import {
|
|
20
|
+
import {
|
|
21
|
+
ChildProcessSpawner,
|
|
22
|
+
ExitCode,
|
|
23
|
+
make as makeSpawner,
|
|
24
|
+
makeHandle,
|
|
25
|
+
ProcessId
|
|
26
|
+
} from "effect/unstable/process/ChildProcessSpawner"
|
|
21
27
|
import * as NodeChildProcess from "node:child_process"
|
|
22
28
|
import { handleErrnoException } from "./internal/utils.ts"
|
|
23
29
|
import * as NodeSink from "./NodeSink.ts"
|
|
@@ -337,6 +343,23 @@ const make = Effect.gen(function*() {
|
|
|
337
343
|
})
|
|
338
344
|
}
|
|
339
345
|
|
|
346
|
+
const killProcessGroupOnExit = (
|
|
347
|
+
childProcess: NodeChildProcess.ChildProcess,
|
|
348
|
+
signal: NodeJS.Signals
|
|
349
|
+
): void => {
|
|
350
|
+
if (globalThis.process.platform === "win32") {
|
|
351
|
+
NodeChildProcess.exec(`taskkill /pid ${childProcess.pid} /T /F`, () => {
|
|
352
|
+
// ignore errors during best-effort cleanup
|
|
353
|
+
})
|
|
354
|
+
return
|
|
355
|
+
}
|
|
356
|
+
try {
|
|
357
|
+
globalThis.process.kill(-childProcess.pid!, signal)
|
|
358
|
+
} catch {
|
|
359
|
+
// ignore errors during best-effort cleanup
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
|
|
340
363
|
const killProcess = (
|
|
341
364
|
command: ChildProcess.StandardCommand,
|
|
342
365
|
childProcess: NodeChildProcess.ChildProcess,
|
|
@@ -368,7 +391,7 @@ const make = Effect.gen(function*() {
|
|
|
368
391
|
? kill(command, childProcess, killSignal)
|
|
369
392
|
: Effect.timeoutOrElse(kill(command, childProcess, killSignal), {
|
|
370
393
|
duration: options.forceKillAfter,
|
|
371
|
-
|
|
394
|
+
orElse: () => kill(command, childProcess, "SIGKILL")
|
|
372
395
|
})
|
|
373
396
|
}
|
|
374
397
|
|
|
@@ -413,6 +436,8 @@ const make = Effect.gen(function*() {
|
|
|
413
436
|
const stdoutConfig = resolveOutputOption(cmd.options, "stdout")
|
|
414
437
|
const stderrConfig = resolveOutputOption(cmd.options, "stderr")
|
|
415
438
|
const resolvedAdditionalFds = resolveAdditionalFds(cmd.options)
|
|
439
|
+
let isReferenced = true
|
|
440
|
+
let cleanupOnNonZeroExit = false
|
|
416
441
|
|
|
417
442
|
const cwd = yield* resolveWorkingDirectory(cmd.options)
|
|
418
443
|
const env = resolveEnvironment(cmd.options)
|
|
@@ -438,6 +463,9 @@ const make = Effect.gen(function*() {
|
|
|
438
463
|
}
|
|
439
464
|
return yield* Effect.void
|
|
440
465
|
}
|
|
466
|
+
if (!isReferenced) {
|
|
467
|
+
return yield* Effect.void
|
|
468
|
+
}
|
|
441
469
|
// Process is still running, kill it
|
|
442
470
|
return yield* killWithTimeout((command, childProcess, signal) =>
|
|
443
471
|
Effect.catch(
|
|
@@ -452,6 +480,26 @@ const make = Effect.gen(function*() {
|
|
|
452
480
|
)
|
|
453
481
|
|
|
454
482
|
const pid = ProcessId(childProcess.pid!)
|
|
483
|
+
childProcess.on("exit", (code) => {
|
|
484
|
+
if (cleanupOnNonZeroExit && code !== 0 && Predicate.isNotNull(code)) {
|
|
485
|
+
killProcessGroupOnExit(childProcess, cmd.options.killSignal ?? "SIGTERM")
|
|
486
|
+
}
|
|
487
|
+
})
|
|
488
|
+
const reref = Effect.sync(() => {
|
|
489
|
+
if (!isReferenced) {
|
|
490
|
+
childProcess.ref()
|
|
491
|
+
isReferenced = true
|
|
492
|
+
cleanupOnNonZeroExit = false
|
|
493
|
+
}
|
|
494
|
+
})
|
|
495
|
+
const unref = Effect.sync(() => {
|
|
496
|
+
if (isReferenced) {
|
|
497
|
+
childProcess.unref()
|
|
498
|
+
isReferenced = false
|
|
499
|
+
cleanupOnNonZeroExit = true
|
|
500
|
+
}
|
|
501
|
+
return reref
|
|
502
|
+
})
|
|
455
503
|
const stdin = yield* setupChildStdin(cmd, childProcess, stdinConfig)
|
|
456
504
|
const { all, stderr, stdout } = setupChildOutputStreams(cmd, childProcess, stdoutConfig, stderrConfig)
|
|
457
505
|
const { getInputFd, getOutputFd } = yield* setupAdditionalFds(cmd, childProcess, resolvedAdditionalFds)
|
|
@@ -489,14 +537,15 @@ const make = Effect.gen(function*() {
|
|
|
489
537
|
stderr,
|
|
490
538
|
all,
|
|
491
539
|
getInputFd,
|
|
492
|
-
getOutputFd
|
|
540
|
+
getOutputFd,
|
|
541
|
+
unref
|
|
493
542
|
})
|
|
494
543
|
}
|
|
495
544
|
case "PipedCommand": {
|
|
496
545
|
const { commands, pipeOptions } = flattenCommand(cmd)
|
|
497
546
|
const [root, ...pipeline] = commands
|
|
498
547
|
|
|
499
|
-
|
|
548
|
+
const handles = [yield* spawnCommand(root)]
|
|
500
549
|
|
|
501
550
|
for (let i = 0; i < pipeline.length; i++) {
|
|
502
551
|
const command = pipeline[i]
|
|
@@ -505,7 +554,7 @@ const make = Effect.gen(function*() {
|
|
|
505
554
|
|
|
506
555
|
// Get the appropriate stream from the source based on `from` option
|
|
507
556
|
const sourceStream = Stream.unwrap(
|
|
508
|
-
Effect.
|
|
557
|
+
Effect.succeed(getSourceStream(handles[handles.length - 1], options.from))
|
|
509
558
|
)
|
|
510
559
|
|
|
511
560
|
// Determine where to pipe: stdin or custom fd
|
|
@@ -513,41 +562,66 @@ const make = Effect.gen(function*() {
|
|
|
513
562
|
|
|
514
563
|
if (toOption === "stdin") {
|
|
515
564
|
// Pipe to stdin (default behavior)
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
565
|
+
handles.push(
|
|
566
|
+
yield* spawnCommand(ChildProcess.make(command.command, command.args, {
|
|
567
|
+
...command.options,
|
|
568
|
+
stdin: { ...stdinConfig, stream: sourceStream }
|
|
569
|
+
}))
|
|
570
|
+
)
|
|
520
571
|
} else {
|
|
521
572
|
// Pipe to custom fd (fd3, fd4, etc.)
|
|
522
573
|
const fd = ChildProcess.parseFdName(toOption)
|
|
523
574
|
if (Predicate.isNotUndefined(fd)) {
|
|
524
575
|
const fdName = ChildProcess.fdName(fd) as `fd${number}`
|
|
525
576
|
const existingFds = command.options.additionalFds ?? {}
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
577
|
+
handles.push(
|
|
578
|
+
yield* spawnCommand(ChildProcess.make(command.command, command.args, {
|
|
579
|
+
...command.options,
|
|
580
|
+
additionalFds: {
|
|
581
|
+
...existingFds,
|
|
582
|
+
[fdName]: { type: "input" as const, stream: sourceStream }
|
|
583
|
+
}
|
|
584
|
+
}))
|
|
585
|
+
)
|
|
533
586
|
} else {
|
|
534
587
|
// Invalid fd name, fall back to stdin
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
588
|
+
handles.push(
|
|
589
|
+
yield* spawnCommand(ChildProcess.make(command.command, command.args, {
|
|
590
|
+
...command.options,
|
|
591
|
+
stdin: { ...stdinConfig, stream: sourceStream }
|
|
592
|
+
}))
|
|
593
|
+
)
|
|
539
594
|
}
|
|
540
595
|
}
|
|
541
596
|
}
|
|
542
597
|
|
|
543
|
-
|
|
598
|
+
const handle = handles[handles.length - 1]
|
|
599
|
+
const unref = Effect.gen(function*() {
|
|
600
|
+
const rerefs: Array<Effect.Effect<void, PlatformError.PlatformError>> = []
|
|
601
|
+
for (const handle of handles) {
|
|
602
|
+
rerefs.push(yield* handle.unref)
|
|
603
|
+
}
|
|
604
|
+
return Effect.forEach([...rerefs].reverse(), (reref) => reref, { discard: true })
|
|
605
|
+
})
|
|
606
|
+
|
|
607
|
+
return makeHandle({
|
|
608
|
+
pid: handle.pid,
|
|
609
|
+
exitCode: handle.exitCode,
|
|
610
|
+
isRunning: handle.isRunning,
|
|
611
|
+
kill: handle.kill,
|
|
612
|
+
stdin: handle.stdin,
|
|
613
|
+
stdout: handle.stdout,
|
|
614
|
+
stderr: handle.stderr,
|
|
615
|
+
all: handle.all,
|
|
616
|
+
getInputFd: handle.getInputFd,
|
|
617
|
+
getOutputFd: handle.getOutputFd,
|
|
618
|
+
unref
|
|
619
|
+
})
|
|
544
620
|
}
|
|
545
621
|
}
|
|
546
622
|
})
|
|
547
623
|
|
|
548
|
-
return
|
|
549
|
-
spawn: spawnCommand
|
|
550
|
-
})
|
|
624
|
+
return makeSpawner(spawnCommand)
|
|
551
625
|
})
|
|
552
626
|
|
|
553
627
|
/**
|
package/src/NodeClusterSocket.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import * as Effect from "effect/Effect"
|
|
5
5
|
import * as Layer from "effect/Layer"
|
|
6
|
+
import * as Option from "effect/Option"
|
|
6
7
|
import * as Runners from "effect/unstable/cluster/Runners"
|
|
7
8
|
import * as ShardingConfig from "effect/unstable/cluster/ShardingConfig"
|
|
8
9
|
import * as RpcClient from "effect/unstable/rpc/RpcClient"
|
|
@@ -48,9 +49,9 @@ export const layerSocketServer: Layer.Layer<
|
|
|
48
49
|
ShardingConfig.ShardingConfig
|
|
49
50
|
> = Effect.gen(function*() {
|
|
50
51
|
const config = yield* ShardingConfig.ShardingConfig
|
|
51
|
-
const listenAddress = config.runnerListenAddress
|
|
52
|
-
if (listenAddress
|
|
52
|
+
const listenAddress = Option.orElse(config.runnerListenAddress, () => config.runnerAddress)
|
|
53
|
+
if (Option.isNone(listenAddress)) {
|
|
53
54
|
return yield* Effect.die("layerSocketServer: ShardingConfig.runnerListenAddress is None")
|
|
54
55
|
}
|
|
55
|
-
return NodeSocketServer.layer(listenAddress)
|
|
56
|
+
return NodeSocketServer.layer(listenAddress.value)
|
|
56
57
|
}).pipe(Layer.unwrap)
|
package/src/NodeFileSystem.ts
CHANGED
|
@@ -232,7 +232,7 @@ const makeFile = (() => {
|
|
|
232
232
|
readonly fd: FileSystem.File.Descriptor
|
|
233
233
|
private readonly append: boolean
|
|
234
234
|
|
|
235
|
-
private position: bigint =
|
|
235
|
+
private position: bigint = BigInt(0)
|
|
236
236
|
|
|
237
237
|
constructor(
|
|
238
238
|
fd: FileSystem.File.Descriptor,
|
|
@@ -285,19 +285,19 @@ const makeFile = (() => {
|
|
|
285
285
|
const position = this.position
|
|
286
286
|
return Effect.map(
|
|
287
287
|
nodeReadAlloc(this.fd, { buffer, position }),
|
|
288
|
-
(bytesRead): Buffer
|
|
288
|
+
(bytesRead): Option.Option<Buffer> => {
|
|
289
289
|
if (bytesRead === 0) {
|
|
290
|
-
return
|
|
290
|
+
return Option.none()
|
|
291
291
|
}
|
|
292
292
|
|
|
293
293
|
this.position = position + BigInt(bytesRead)
|
|
294
294
|
if (bytesRead === sizeNumber) {
|
|
295
|
-
return buffer
|
|
295
|
+
return Option.some(buffer)
|
|
296
296
|
}
|
|
297
297
|
|
|
298
298
|
const dst = Buffer.allocUnsafeSlow(bytesRead)
|
|
299
299
|
buffer.copy(dst, 0, 0, bytesRead)
|
|
300
|
-
return dst
|
|
300
|
+
return Option.some(dst)
|
|
301
301
|
}
|
|
302
302
|
)
|
|
303
303
|
})
|
|
@@ -468,19 +468,19 @@ const makeFileInfo = (stat: NFS.Stats): FileSystem.File.Info => ({
|
|
|
468
468
|
stat.isSocket() ?
|
|
469
469
|
"Socket" :
|
|
470
470
|
"Unknown",
|
|
471
|
-
mtime: stat.mtime,
|
|
472
|
-
atime: stat.atime,
|
|
473
|
-
birthtime: stat.birthtime,
|
|
471
|
+
mtime: Option.fromNullishOr(stat.mtime),
|
|
472
|
+
atime: Option.fromNullishOr(stat.atime),
|
|
473
|
+
birthtime: Option.fromNullishOr(stat.birthtime),
|
|
474
474
|
dev: stat.dev,
|
|
475
|
-
rdev: stat.rdev,
|
|
476
|
-
ino: stat.ino,
|
|
475
|
+
rdev: Option.fromNullishOr(stat.rdev),
|
|
476
|
+
ino: Option.fromNullishOr(stat.ino),
|
|
477
477
|
mode: stat.mode,
|
|
478
|
-
nlink: stat.nlink,
|
|
479
|
-
uid: stat.uid,
|
|
480
|
-
gid: stat.gid,
|
|
478
|
+
nlink: Option.fromNullishOr(stat.nlink),
|
|
479
|
+
uid: Option.fromNullishOr(stat.uid),
|
|
480
|
+
gid: Option.fromNullishOr(stat.gid),
|
|
481
481
|
size: FileSystem.Size(stat.size),
|
|
482
|
-
blksize: FileSystem.Size(stat.blksize),
|
|
483
|
-
blocks: stat.blocks
|
|
482
|
+
blksize: stat.blksize !== undefined ? Option.some(FileSystem.Size(stat.blksize)) : Option.none(),
|
|
483
|
+
blocks: Option.fromNullishOr(stat.blocks)
|
|
484
484
|
})
|
|
485
485
|
const stat = (() => {
|
|
486
486
|
const nodeStat = effectify(
|
|
@@ -531,7 +531,9 @@ const watchNode = (path: string) =>
|
|
|
531
531
|
Stream.callback<FileSystem.WatchEvent, Error.PlatformError>((queue) =>
|
|
532
532
|
Effect.acquireRelease(
|
|
533
533
|
Effect.sync(() => {
|
|
534
|
-
const watcher = NFS.watch(path, {
|
|
534
|
+
const watcher = NFS.watch(path, {
|
|
535
|
+
recursive: true
|
|
536
|
+
}, (event, path) => {
|
|
535
537
|
if (!path) return
|
|
536
538
|
switch (event) {
|
|
537
539
|
case "rename": {
|
|
@@ -570,15 +572,14 @@ const watchNode = (path: string) =>
|
|
|
570
572
|
)
|
|
571
573
|
)
|
|
572
574
|
|
|
573
|
-
const watch = (backend: FileSystem.WatchBackend["Service"]
|
|
575
|
+
const watch = (backend: Option.Option<FileSystem.WatchBackend["Service"]>, path: string) =>
|
|
574
576
|
stat(path).pipe(
|
|
575
|
-
Effect.map((stat) =>
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
}),
|
|
577
|
+
Effect.map((stat) =>
|
|
578
|
+
backend.pipe(
|
|
579
|
+
Option.flatMap((_) => _.register(path, stat)),
|
|
580
|
+
Option.getOrElse(() => watchNode(path))
|
|
581
|
+
)
|
|
582
|
+
),
|
|
582
583
|
Stream.unwrap
|
|
583
584
|
)
|
|
584
585
|
|
|
@@ -628,7 +629,7 @@ const makeFileSystem = Effect.map(Effect.serviceOption(FileSystem.WatchBackend),
|
|
|
628
629
|
truncate,
|
|
629
630
|
utimes,
|
|
630
631
|
watch(path) {
|
|
631
|
-
return watch(
|
|
632
|
+
return watch(backend, path)
|
|
632
633
|
},
|
|
633
634
|
writeFile
|
|
634
635
|
}))
|
package/src/NodeRuntime.ts
CHANGED
|
@@ -37,10 +37,8 @@ export const runMain: {
|
|
|
37
37
|
let receivedSignal = false
|
|
38
38
|
|
|
39
39
|
fiber.addObserver((exit) => {
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
process.removeListener("SIGTERM", onSigint)
|
|
43
|
-
}
|
|
40
|
+
process.removeListener("SIGINT", onSigint)
|
|
41
|
+
process.removeListener("SIGTERM", onSigint)
|
|
44
42
|
teardown(exit, (code) => {
|
|
45
43
|
if (receivedSignal || code !== 0) {
|
|
46
44
|
process.exit(code)
|
|
@@ -50,8 +48,6 @@ export const runMain: {
|
|
|
50
48
|
|
|
51
49
|
function onSigint() {
|
|
52
50
|
receivedSignal = true
|
|
53
|
-
process.removeListener("SIGINT", onSigint)
|
|
54
|
-
process.removeListener("SIGTERM", onSigint)
|
|
55
51
|
fiber.interruptUnsafe(fiber.id)
|
|
56
52
|
}
|
|
57
53
|
|
package/src/NodeSink.ts
CHANGED
|
@@ -66,6 +66,13 @@ export const pullIntoWritable = <A, IE, E>(options: {
|
|
|
66
66
|
})
|
|
67
67
|
}),
|
|
68
68
|
Effect.forever({ disableYield: true }),
|
|
69
|
+
Effect.raceFirst(Effect.callback<never, E>((resume) => {
|
|
70
|
+
const onError = (error: unknown) => resume(Effect.fail(options.onError(error)))
|
|
71
|
+
options.writable.once("error", onError)
|
|
72
|
+
return Effect.sync(() => {
|
|
73
|
+
options.writable.off("error", onError)
|
|
74
|
+
})
|
|
75
|
+
})),
|
|
69
76
|
options.endOnDone !== false ?
|
|
70
77
|
Pull.catchDone((_) => {
|
|
71
78
|
if ("closed" in options.writable && options.writable.closed) {
|
package/src/NodeSocket.ts
CHANGED
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
*/
|
|
4
4
|
import type { Array } from "effect"
|
|
5
5
|
import * as Channel from "effect/Channel"
|
|
6
|
+
import * as Context from "effect/Context"
|
|
6
7
|
import * as Deferred from "effect/Deferred"
|
|
7
8
|
import type * as Duration from "effect/Duration"
|
|
8
9
|
import * as Effect from "effect/Effect"
|
|
@@ -12,7 +13,6 @@ import { identity } from "effect/Function"
|
|
|
12
13
|
import * as Latch from "effect/Latch"
|
|
13
14
|
import * as Layer from "effect/Layer"
|
|
14
15
|
import * as Scope from "effect/Scope"
|
|
15
|
-
import * as ServiceMap from "effect/ServiceMap"
|
|
16
16
|
import * as Socket from "effect/unstable/socket/Socket"
|
|
17
17
|
import * as Net from "node:net"
|
|
18
18
|
import type { Duplex } from "node:stream"
|
|
@@ -27,7 +27,7 @@ export * as NodeWS from "ws"
|
|
|
27
27
|
* @since 1.0.0
|
|
28
28
|
* @category tags
|
|
29
29
|
*/
|
|
30
|
-
export class NetSocket extends
|
|
30
|
+
export class NetSocket extends Context.Service<NetSocket, Net.Socket>()(
|
|
31
31
|
"@effect/platform-node/NodeSocket/NetSocket"
|
|
32
32
|
) {}
|
|
33
33
|
|
|
@@ -37,15 +37,15 @@ export class NetSocket extends ServiceMap.Service<NetSocket, Net.Socket>()(
|
|
|
37
37
|
*/
|
|
38
38
|
export const makeNet = (
|
|
39
39
|
options: Net.NetConnectOpts & {
|
|
40
|
-
readonly openTimeout?: Duration.
|
|
40
|
+
readonly openTimeout?: Duration.Input | undefined
|
|
41
41
|
}
|
|
42
42
|
): Effect.Effect<Socket.Socket> =>
|
|
43
43
|
fromDuplex(
|
|
44
|
-
Effect.
|
|
44
|
+
Effect.contextWith((context: Context.Context<Scope.Scope>) => {
|
|
45
45
|
let conn: Net.Socket | undefined
|
|
46
46
|
return Effect.flatMap(
|
|
47
47
|
Scope.addFinalizer(
|
|
48
|
-
|
|
48
|
+
Context.get(context, Scope.Scope),
|
|
49
49
|
Effect.sync(() => {
|
|
50
50
|
if (!conn) return
|
|
51
51
|
if (conn.closed === false) {
|
|
@@ -83,13 +83,13 @@ export const makeNet = (
|
|
|
83
83
|
export const fromDuplex = <RO>(
|
|
84
84
|
open: Effect.Effect<Duplex, Socket.SocketError, RO>,
|
|
85
85
|
options?: {
|
|
86
|
-
readonly openTimeout?: Duration.
|
|
86
|
+
readonly openTimeout?: Duration.Input | undefined
|
|
87
87
|
}
|
|
88
88
|
): Effect.Effect<Socket.Socket, never, Exclude<RO, Scope.Scope>> =>
|
|
89
89
|
Effect.withFiber<Socket.Socket, never, Exclude<RO, Scope.Scope>>((fiber) => {
|
|
90
90
|
let currentSocket: Duplex | undefined
|
|
91
91
|
const latch = Latch.makeUnsafe(false)
|
|
92
|
-
const openServices = fiber.
|
|
92
|
+
const openServices = fiber.context as Context.Context<RO>
|
|
93
93
|
|
|
94
94
|
const run = <R, E, _>(handler: (_: Uint8Array) => Effect.Effect<_, E, R> | void, opts?: {
|
|
95
95
|
readonly onOpen?: Effect.Effect<void> | undefined
|
|
@@ -113,7 +113,7 @@ export const fromDuplex = <RO>(
|
|
|
113
113
|
options?.openTimeout ?
|
|
114
114
|
Effect.timeoutOrElse({
|
|
115
115
|
duration: options.openTimeout,
|
|
116
|
-
|
|
116
|
+
orElse: () =>
|
|
117
117
|
Effect.fail(
|
|
118
118
|
new Socket.SocketError({
|
|
119
119
|
reason: new Socket.SocketOpenError({ kind: "Timeout", cause: new Error("Connection timed out") })
|
|
@@ -166,7 +166,7 @@ export const fromDuplex = <RO>(
|
|
|
166
166
|
)
|
|
167
167
|
}
|
|
168
168
|
})).pipe(
|
|
169
|
-
Effect.
|
|
169
|
+
Effect.updateContext((input: Context.Context<R>) => Context.merge(openServices, input)),
|
|
170
170
|
Effect.onExit(() =>
|
|
171
171
|
Effect.sync(() => {
|
|
172
172
|
latch.closeUnsafe()
|
|
@@ -204,8 +204,7 @@ export const fromDuplex = <RO>(
|
|
|
204
204
|
})
|
|
205
205
|
)
|
|
206
206
|
|
|
207
|
-
return Effect.succeed(Socket.
|
|
208
|
-
[Socket.TypeId]: Socket.TypeId,
|
|
207
|
+
return Effect.succeed(Socket.make({
|
|
209
208
|
run,
|
|
210
209
|
runRaw: run,
|
|
211
210
|
writer
|
package/src/NodeSocketServer.ts
CHANGED
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
* @since 1.0.0
|
|
3
3
|
*/
|
|
4
4
|
import type { Cause } from "effect/Cause"
|
|
5
|
+
import * as Context from "effect/Context"
|
|
5
6
|
import * as Deferred from "effect/Deferred"
|
|
6
7
|
import * as Effect from "effect/Effect"
|
|
7
8
|
import * as Exit from "effect/Exit"
|
|
@@ -11,7 +12,6 @@ import * as Function from "effect/Function"
|
|
|
11
12
|
import * as Layer from "effect/Layer"
|
|
12
13
|
import * as References from "effect/References"
|
|
13
14
|
import * as Scope from "effect/Scope"
|
|
14
|
-
import * as ServiceMap from "effect/ServiceMap"
|
|
15
15
|
import * as Socket from "effect/unstable/socket/Socket"
|
|
16
16
|
import * as SocketServer from "effect/unstable/socket/SocketServer"
|
|
17
17
|
import type * as Http from "node:http"
|
|
@@ -23,7 +23,7 @@ import { NodeWS } from "./NodeSocket.ts"
|
|
|
23
23
|
* @since 1.0.0
|
|
24
24
|
* @category tags
|
|
25
25
|
*/
|
|
26
|
-
export class IncomingMessage extends
|
|
26
|
+
export class IncomingMessage extends Context.Service<
|
|
27
27
|
IncomingMessage,
|
|
28
28
|
Http.IncomingMessage
|
|
29
29
|
>()("@effect/platform-node-shared/NodeSocketServer/IncomingMessage") {}
|
|
@@ -69,7 +69,7 @@ export const make = Effect.fnUntraced(function*(
|
|
|
69
69
|
|
|
70
70
|
const run = Effect.fnUntraced(function*<R, E, _>(handler: (socket: Socket.Socket) => Effect.Effect<_, E, R>) {
|
|
71
71
|
const scope = yield* Scope.make()
|
|
72
|
-
const services =
|
|
72
|
+
const services = Context.omit(Scope.Scope)(yield* Effect.context<R>()) as Context.Context<R>
|
|
73
73
|
const trackFiber = Fiber.runIn(scope)
|
|
74
74
|
const prevOnConnection = onConnection
|
|
75
75
|
onConnection = function(conn: Net.Socket) {
|
|
@@ -109,7 +109,7 @@ export const make = Effect.fnUntraced(function*(
|
|
|
109
109
|
),
|
|
110
110
|
Effect.flatMap(handler),
|
|
111
111
|
Effect.catchCause(reportUnhandledError),
|
|
112
|
-
Effect.runForkWith(
|
|
112
|
+
Effect.runForkWith(Context.add(services, NodeSocket.NetSocket, conn)),
|
|
113
113
|
trackFiber
|
|
114
114
|
)
|
|
115
115
|
}
|
|
@@ -202,7 +202,7 @@ export const makeWebSocket: (
|
|
|
202
202
|
|
|
203
203
|
const run = Effect.fnUntraced(function*<R, E, _>(handler: (socket: Socket.Socket) => Effect.Effect<_, E, R>) {
|
|
204
204
|
const scope = yield* Scope.make()
|
|
205
|
-
const services =
|
|
205
|
+
const services = Context.omit(Scope.Scope)(yield* Effect.context<R>()) as Context.Context<R>
|
|
206
206
|
const trackFiber = Fiber.runIn(scope)
|
|
207
207
|
const prevOnConnection = onConnection
|
|
208
208
|
onConnection = function(conn: globalThis.WebSocket, req: Http.IncomingMessage) {
|
|
@@ -221,7 +221,7 @@ export const makeWebSocket: (
|
|
|
221
221
|
),
|
|
222
222
|
Effect.flatMap(handler),
|
|
223
223
|
Effect.catchCause(reportUnhandledError),
|
|
224
|
-
Effect.runForkWith(
|
|
224
|
+
Effect.runForkWith(Context.makeUnsafe(map)),
|
|
225
225
|
trackFiber
|
|
226
226
|
)
|
|
227
227
|
}
|
package/src/NodeStdio.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* @since 1.0.0
|
|
3
3
|
*/
|
|
4
|
+
import * as Effect from "effect/Effect"
|
|
4
5
|
import * as Layer from "effect/Layer"
|
|
5
6
|
import { systemError } from "effect/PlatformError"
|
|
6
7
|
import * as Stdio from "effect/Stdio"
|
|
@@ -14,26 +15,31 @@ import { fromReadable } from "./NodeStream.ts"
|
|
|
14
15
|
export const layer: Layer.Layer<Stdio.Stdio> = Layer.succeed(
|
|
15
16
|
Stdio.Stdio,
|
|
16
17
|
Stdio.make({
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
18
|
+
args: Effect.sync(() => process.argv.slice(2)),
|
|
19
|
+
stdout: (options) =>
|
|
20
|
+
fromWritable({
|
|
21
|
+
evaluate: () => process.stdout,
|
|
22
|
+
onError: (cause) =>
|
|
23
|
+
systemError({
|
|
24
|
+
module: "Stdio",
|
|
25
|
+
method: "stdout",
|
|
26
|
+
_tag: "Unknown",
|
|
27
|
+
cause
|
|
28
|
+
}),
|
|
29
|
+
endOnDone: options?.endOnDone ?? false
|
|
30
|
+
}),
|
|
31
|
+
stderr: (options) =>
|
|
32
|
+
fromWritable({
|
|
33
|
+
evaluate: () => process.stderr,
|
|
34
|
+
onError: (cause) =>
|
|
35
|
+
systemError({
|
|
36
|
+
module: "Stdio",
|
|
37
|
+
method: "stderr",
|
|
38
|
+
_tag: "Unknown",
|
|
39
|
+
cause
|
|
40
|
+
}),
|
|
41
|
+
endOnDone: options?.endOnDone ?? false
|
|
42
|
+
}),
|
|
37
43
|
stdin: fromReadable({
|
|
38
44
|
evaluate: () => process.stdin,
|
|
39
45
|
onError: (cause) =>
|
package/src/NodeStream.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import * as Arr from "effect/Array"
|
|
5
5
|
import * as Cause from "effect/Cause"
|
|
6
6
|
import * as Channel from "effect/Channel"
|
|
7
|
+
import * as Context from "effect/Context"
|
|
7
8
|
import * as Effect from "effect/Effect"
|
|
8
9
|
import * as Exit from "effect/Exit"
|
|
9
10
|
import * as Fiber from "effect/Fiber"
|
|
@@ -13,7 +14,6 @@ import * as Latch from "effect/Latch"
|
|
|
13
14
|
import * as MutableRef from "effect/MutableRef"
|
|
14
15
|
import * as Pull from "effect/Pull"
|
|
15
16
|
import * as Scope from "effect/Scope"
|
|
16
|
-
import * as ServiceMap from "effect/ServiceMap"
|
|
17
17
|
import * as Stream from "effect/Stream"
|
|
18
18
|
import type { Duplex } from "node:stream"
|
|
19
19
|
import { Readable } from "node:stream"
|
|
@@ -170,7 +170,7 @@ export const pipeThroughSimple: {
|
|
|
170
170
|
*/
|
|
171
171
|
export const toReadable = <E, R>(stream: Stream.Stream<string | Uint8Array, E, R>): Effect.Effect<Readable, never, R> =>
|
|
172
172
|
Effect.map(
|
|
173
|
-
Effect.
|
|
173
|
+
Effect.context<R>(),
|
|
174
174
|
(context) => new StreamAdapter(context, stream)
|
|
175
175
|
)
|
|
176
176
|
|
|
@@ -180,7 +180,7 @@ export const toReadable = <E, R>(stream: Stream.Stream<string | Uint8Array, E, R
|
|
|
180
180
|
*/
|
|
181
181
|
export const toReadableNever = <E>(stream: Stream.Stream<string | Uint8Array, E, never>): Readable =>
|
|
182
182
|
new StreamAdapter(
|
|
183
|
-
|
|
183
|
+
Context.empty(),
|
|
184
184
|
stream
|
|
185
185
|
)
|
|
186
186
|
|
|
@@ -248,7 +248,7 @@ export const toArrayBuffer = <E = Cause.UnknownError>(
|
|
|
248
248
|
const onError = options?.onError ?? defaultOnError
|
|
249
249
|
return Effect.callback((resume) => {
|
|
250
250
|
const stream = readable() as Readable
|
|
251
|
-
|
|
251
|
+
const buffers: Array<Uint8Array> = []
|
|
252
252
|
let bytes = 0
|
|
253
253
|
stream.once("error", (err) => {
|
|
254
254
|
if ("closed" in stream && !stream.closed) {
|
|
@@ -257,13 +257,16 @@ export const toArrayBuffer = <E = Cause.UnknownError>(
|
|
|
257
257
|
resume(Effect.fail(onError(err) as E))
|
|
258
258
|
})
|
|
259
259
|
stream.once("end", () => {
|
|
260
|
-
|
|
261
|
-
|
|
260
|
+
const buffer = buffers.length === 1 ? buffers[0] : Buffer.concat(buffers)
|
|
261
|
+
if (buffer.byteOffset === 0 && buffer.buffer.byteLength === buffer.byteLength) {
|
|
262
|
+
return resume(Effect.succeed(buffer.buffer as ArrayBuffer))
|
|
262
263
|
}
|
|
263
|
-
resume(
|
|
264
|
+
resume(
|
|
265
|
+
Effect.succeed(buffer.buffer.slice(buffer.byteOffset, buffer.byteOffset + buffer.byteLength) as ArrayBuffer)
|
|
266
|
+
)
|
|
264
267
|
})
|
|
265
268
|
stream.on("data", (chunk) => {
|
|
266
|
-
|
|
269
|
+
buffers.push(chunk)
|
|
267
270
|
bytes += chunk.length
|
|
268
271
|
if (maxBytesNumber && bytes > maxBytesNumber) {
|
|
269
272
|
resume(Effect.fail(onError(new Error("maxBytes exceeded")) as E))
|
|
@@ -361,7 +364,7 @@ class StreamAdapter<E, R> extends Readable {
|
|
|
361
364
|
private fiber: Fiber.Fiber<void, E> | undefined = undefined
|
|
362
365
|
|
|
363
366
|
constructor(
|
|
364
|
-
context:
|
|
367
|
+
context: Context.Context<R>,
|
|
365
368
|
stream: Stream.Stream<Uint8Array | string, E, R>
|
|
366
369
|
) {
|
|
367
370
|
super({})
|
|
@@ -379,7 +382,7 @@ class StreamAdapter<E, R> extends Readable {
|
|
|
379
382
|
}
|
|
380
383
|
}))).pipe(
|
|
381
384
|
this.readLatch.whenOpen,
|
|
382
|
-
Effect.
|
|
385
|
+
Effect.provideContext(context),
|
|
383
386
|
Effect.runFork
|
|
384
387
|
)
|
|
385
388
|
this.fiber.addObserver((exit) => {
|
package/src/NodeTerminal.ts
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
import type * as Cause from "effect/Cause"
|
|
5
5
|
import * as Effect from "effect/Effect"
|
|
6
6
|
import * as Layer from "effect/Layer"
|
|
7
|
+
import * as Option from "effect/Option"
|
|
7
8
|
import { badArgument, type PlatformError } from "effect/PlatformError"
|
|
8
9
|
import * as Predicate from "effect/Predicate"
|
|
9
10
|
import * as Queue from "effect/Queue"
|
|
@@ -52,7 +53,7 @@ export const make: (
|
|
|
52
53
|
const queue = yield* Queue.make<Terminal.UserInput, Cause.Done>()
|
|
53
54
|
const handleKeypress = (s: string | undefined, k: readline.Key) => {
|
|
54
55
|
const userInput = {
|
|
55
|
-
input: s,
|
|
56
|
+
input: Option.fromUndefinedOr(s),
|
|
56
57
|
key: { name: k.name ?? "", ctrl: !!k.ctrl, meta: !!k.meta, shift: !!k.shift }
|
|
57
58
|
}
|
|
58
59
|
Queue.offerUnsafe(queue, userInput)
|