@effect/platform-node 0.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.
Files changed (83) hide show
  1. package/Console.d.ts +15 -0
  2. package/Console.d.ts.map +1 -0
  3. package/Console.js +19 -0
  4. package/Console.js.map +1 -0
  5. package/Effectify.d.ts +19 -0
  6. package/Effectify.d.ts.map +1 -0
  7. package/Effectify.js +13 -0
  8. package/Effectify.js.map +1 -0
  9. package/Error.d.ts +30 -0
  10. package/Error.d.ts.map +1 -0
  11. package/Error.js +25 -0
  12. package/Error.js.map +1 -0
  13. package/FileSystem.d.ts +68 -0
  14. package/FileSystem.d.ts.map +1 -0
  15. package/FileSystem.js +33 -0
  16. package/FileSystem.js.map +1 -0
  17. package/LICENSE +21 -0
  18. package/Runtime.d.ts +24 -0
  19. package/Runtime.d.ts.map +1 -0
  20. package/Runtime.js +27 -0
  21. package/Runtime.js.map +1 -0
  22. package/Sink.d.ts +22 -0
  23. package/Sink.d.ts.map +1 -0
  24. package/Sink.js +20 -0
  25. package/Sink.js.map +1 -0
  26. package/Stream.d.ts +22 -0
  27. package/Stream.d.ts.map +1 -0
  28. package/Stream.js +16 -0
  29. package/Stream.js.map +1 -0
  30. package/internal/fileSystem.d.ts +2 -0
  31. package/internal/fileSystem.d.ts.map +1 -0
  32. package/internal/fileSystem.js +322 -0
  33. package/internal/fileSystem.js.map +1 -0
  34. package/internal/runtime.d.ts +2 -0
  35. package/internal/runtime.d.ts.map +1 -0
  36. package/internal/runtime.js +35 -0
  37. package/internal/runtime.js.map +1 -0
  38. package/internal/sink.d.ts +2 -0
  39. package/internal/sink.d.ts.map +1 -0
  40. package/internal/sink.js +40 -0
  41. package/internal/sink.js.map +1 -0
  42. package/internal/stream.d.ts +2 -0
  43. package/internal/stream.d.ts.map +1 -0
  44. package/internal/stream.js +38 -0
  45. package/internal/stream.js.map +1 -0
  46. package/mjs/Console.mjs +15 -0
  47. package/mjs/Console.mjs.map +1 -0
  48. package/mjs/Effectify.mjs +10 -0
  49. package/mjs/Effectify.mjs.map +1 -0
  50. package/mjs/Error.mjs +20 -0
  51. package/mjs/Error.mjs.map +1 -0
  52. package/mjs/FileSystem.mjs +21 -0
  53. package/mjs/FileSystem.mjs.map +1 -0
  54. package/mjs/Runtime.mjs +16 -0
  55. package/mjs/Runtime.mjs.map +1 -0
  56. package/mjs/Sink.mjs +10 -0
  57. package/mjs/Sink.mjs.map +1 -0
  58. package/mjs/Stream.mjs +7 -0
  59. package/mjs/Stream.mjs.map +1 -0
  60. package/mjs/internal/fileSystem.mjs +313 -0
  61. package/mjs/internal/fileSystem.mjs.map +1 -0
  62. package/mjs/internal/runtime.mjs +26 -0
  63. package/mjs/internal/runtime.mjs.map +1 -0
  64. package/mjs/internal/sink.mjs +31 -0
  65. package/mjs/internal/sink.mjs.map +1 -0
  66. package/mjs/internal/stream.mjs +29 -0
  67. package/mjs/internal/stream.mjs.map +1 -0
  68. package/package.json +31 -0
  69. package/src/Console.ts +16 -0
  70. package/src/Effectify.ts +22 -0
  71. package/src/Error.ts +31 -0
  72. package/src/FileSystem.ts +74 -0
  73. package/src/Runtime.ts +32 -0
  74. package/src/Sink.ts +27 -0
  75. package/src/Stream.ts +27 -0
  76. package/src/internal/fileSystem.ts +527 -0
  77. package/src/internal/runtime.ts +39 -0
  78. package/src/internal/sink.ts +62 -0
  79. package/src/internal/stream.ts +57 -0
  80. package/tsconfig.build.json +11 -0
  81. package/tsconfig.examples.json +12 -0
  82. package/tsconfig.json +11 -0
  83. package/tsconfig.test.json +17 -0
package/src/Runtime.ts ADDED
@@ -0,0 +1,32 @@
1
+ /**
2
+ * @since 1.0.0
3
+ */
4
+ import * as internal from "@effect/platform-node/internal/runtime"
5
+ import type { RunMain } from "@effect/platform/Runtime"
6
+
7
+ export type {
8
+ /**
9
+ * @category model
10
+ * @since 1.0.0
11
+ */
12
+ RunMain,
13
+ /**
14
+ * @category model
15
+ * @since 1.0.0
16
+ */
17
+ Teardown
18
+ } from "@effect/platform/Runtime"
19
+
20
+ export {
21
+ /**
22
+ * @category teardown
23
+ * @since 1.0.0
24
+ */
25
+ defaultTeardown
26
+ } from "@effect/platform/Runtime"
27
+
28
+ /**
29
+ * @since 1.0.0
30
+ * @category runtime
31
+ */
32
+ export const runMain: RunMain = internal.runMain
package/src/Sink.ts ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @since 1.0.0
3
+ */
4
+
5
+ import type { LazyArg } from "@effect/data/Function"
6
+ import * as internal from "@effect/platform-node/internal/sink"
7
+ import type { Sink } from "@effect/stream/Sink"
8
+ import type { Writable } from "stream"
9
+
10
+ /**
11
+ * @category model
12
+ * @since 1.0.0
13
+ */
14
+ export interface FromWritableOptions {
15
+ readonly endOnClose?: boolean
16
+ readonly encoding?: BufferEncoding
17
+ }
18
+
19
+ /**
20
+ * @category constructor
21
+ * @since 1.0.0
22
+ */
23
+ export const fromWritable: <E, A>(
24
+ evaluate: LazyArg<Writable>,
25
+ onError: (error: unknown) => E,
26
+ options?: FromWritableOptions
27
+ ) => Sink<never, E, A, never, void> = internal.fromWritable
package/src/Stream.ts ADDED
@@ -0,0 +1,27 @@
1
+ /**
2
+ * @since 1.0.0
3
+ */
4
+ import type { LazyArg } from "@effect/data/Function"
5
+ import * as internal from "@effect/platform-node/internal/stream"
6
+ import type { Size } from "@effect/platform/FileSystem"
7
+ import type { Stream } from "@effect/stream/Stream"
8
+ import type { Readable } from "stream"
9
+
10
+ /**
11
+ * @category model
12
+ * @since 1.0.0
13
+ */
14
+ export interface FromReadableOptions {
15
+ /** Defaults to 64kb */
16
+ readonly chunkSize?: Size
17
+ }
18
+
19
+ /**
20
+ * @category constructor
21
+ * @since 1.0.0
22
+ */
23
+ export const fromReadable: <E, A>(
24
+ evaluate: LazyArg<Readable>,
25
+ onError: (error: unknown) => E,
26
+ options?: FromReadableOptions
27
+ ) => Stream<never, E, A> = internal.fromReadable
@@ -0,0 +1,527 @@
1
+ import { pipe } from "@effect/data/Function"
2
+ import * as Option from "@effect/data/Option"
3
+ import * as Effect from "@effect/io/Effect"
4
+ import * as Layer from "@effect/io/Layer"
5
+ import { effectify } from "@effect/platform/Effectify"
6
+ import * as Error from "@effect/platform/Error"
7
+ import * as FileSystem from "@effect/platform/FileSystem"
8
+ import * as File from "@effect/platform/FileSystem/File"
9
+ import * as Crypto from "node:crypto"
10
+ import * as NFS from "node:fs"
11
+ import * as OS from "node:os"
12
+ import * as Path from "node:path"
13
+
14
+ // == errors
15
+
16
+ const handleErrnoException = (method: string) =>
17
+ (
18
+ err: NodeJS.ErrnoException,
19
+ [path]: [path: NFS.PathLike | number, ...args: Array<any>]
20
+ ) => {
21
+ let reason: Error.SystemErrorReason = "Unknown"
22
+
23
+ switch (err.code) {
24
+ case "ENOENT":
25
+ reason = "NotFound"
26
+ break
27
+
28
+ case "EACCES":
29
+ reason = "PermissionDenied"
30
+ break
31
+
32
+ case "EEXIST":
33
+ reason = "AlreadyExists"
34
+ break
35
+
36
+ case "EISDIR":
37
+ reason = "BadResource"
38
+ break
39
+
40
+ case "ENOTDIR":
41
+ reason = "BadResource"
42
+ break
43
+
44
+ case "EBUSY":
45
+ reason = "Busy"
46
+ break
47
+
48
+ case "ELOOP":
49
+ reason = "BadResource"
50
+ break
51
+ }
52
+
53
+ return Error.SystemError({
54
+ reason,
55
+ module: "FileSystem",
56
+ method,
57
+ pathOrDescriptor: path.toString(),
58
+ syscall: err.syscall,
59
+ message: err.message
60
+ })
61
+ }
62
+
63
+ const handleBadArgument = (method: string) =>
64
+ (err: unknown) =>
65
+ Error.BadArgument({
66
+ module: "FileSystem",
67
+ method,
68
+ message: (err as Error).message ?? String(err)
69
+ })
70
+
71
+ // == access
72
+
73
+ const access = (() => {
74
+ const nodeAccess = effectify(
75
+ NFS.access,
76
+ handleErrnoException("access"),
77
+ handleBadArgument("access")
78
+ )
79
+ return (path: string, options?: FileSystem.AccessFileOptions) => {
80
+ let mode = NFS.constants.F_OK
81
+ if (options?.readable) {
82
+ mode |= NFS.constants.R_OK
83
+ }
84
+ if (options?.writable) {
85
+ mode |= NFS.constants.W_OK
86
+ }
87
+ return nodeAccess(path, mode)
88
+ }
89
+ })()
90
+
91
+ // == copyFile
92
+
93
+ const copyFile = (() => {
94
+ const nodeCopyFile = effectify(
95
+ NFS.copyFile,
96
+ handleErrnoException("copyFile"),
97
+ handleBadArgument("copyFile")
98
+ )
99
+ return (fromPath: string, toPath: string) => nodeCopyFile(fromPath, toPath)
100
+ })()
101
+
102
+ // == chmod
103
+
104
+ const chmod = (() => {
105
+ const nodeChmod = effectify(
106
+ NFS.chmod,
107
+ handleErrnoException("chmod"),
108
+ handleBadArgument("chmod")
109
+ )
110
+ return (path: string, mode: number) => nodeChmod(path, mode)
111
+ })()
112
+
113
+ // == chown
114
+
115
+ const chown = (() => {
116
+ const nodeChown = effectify(
117
+ NFS.chown,
118
+ handleErrnoException("chown"),
119
+ handleBadArgument("chown")
120
+ )
121
+ return (path: string, uid: number, gid: number) => nodeChown(path, uid, gid)
122
+ })()
123
+
124
+ // == link
125
+
126
+ const link = (() => {
127
+ const nodeLink = effectify(
128
+ NFS.link,
129
+ handleErrnoException("link"),
130
+ handleBadArgument("link")
131
+ )
132
+ return (existingPath: string, newPath: string) => nodeLink(existingPath, newPath)
133
+ })()
134
+
135
+ // == makeDirectory
136
+
137
+ const makeDirectory = (() => {
138
+ const nodeMkdir = effectify(
139
+ NFS.mkdir,
140
+ handleErrnoException("makeDirectory"),
141
+ handleBadArgument("makeDirectory")
142
+ )
143
+ return (path: string, options?: FileSystem.MakeDirectoryOptions) =>
144
+ nodeMkdir(path, {
145
+ recursive: options?.recursive ?? false,
146
+ mode: options?.mode
147
+ })
148
+ })()
149
+
150
+ // == makeTempDirectory
151
+
152
+ const makeTempDirectoryFactory = (method: string) => {
153
+ const nodeMkdtemp = effectify(
154
+ NFS.mkdtemp,
155
+ handleErrnoException(method),
156
+ handleBadArgument(method)
157
+ )
158
+ return (options?: FileSystem.MakeTempDirectoryOptions) =>
159
+ Effect.suspend(() => {
160
+ const prefix = options?.prefix ?? ""
161
+ const directory = typeof options?.directory === "string"
162
+ ? Path.join(options.directory, ".")
163
+ : OS.tmpdir()
164
+
165
+ return nodeMkdtemp(prefix ? Path.join(directory, prefix) : directory)
166
+ })
167
+ }
168
+ const makeTempDirectory = makeTempDirectoryFactory("makeTempDirectory")
169
+
170
+ // == remove
171
+
172
+ const removeFactory = (method: string) => {
173
+ const nodeRm = effectify(
174
+ NFS.rm,
175
+ handleErrnoException(method),
176
+ handleBadArgument(method)
177
+ )
178
+ return (path: string, options?: FileSystem.RemoveOptions) => nodeRm(path, { recursive: options?.recursive ?? false })
179
+ }
180
+ const remove = removeFactory("remove")
181
+
182
+ // == makeTempDirectoryScoped
183
+
184
+ const makeTempDirectoryScoped = (() => {
185
+ const makeDirectory = makeTempDirectoryFactory("makeTempDirectoryScoped")
186
+ const removeDirectory = removeFactory("makeTempDirectoryScoped")
187
+ return (
188
+ options?: FileSystem.MakeTempDirectoryOptions
189
+ ) =>
190
+ Effect.acquireRelease(
191
+ makeDirectory(options),
192
+ (directory) => Effect.orDie(removeDirectory(directory, { recursive: true }))
193
+ )
194
+ })()
195
+
196
+ // == makeTempFile
197
+
198
+ const makeTempFile = (() => {
199
+ const makeDirectory = makeTempDirectoryFactory("makeTempFile")
200
+ const randomHexString = (bytes: number) => Effect.sync(() => Crypto.randomBytes(bytes).toString("hex"))
201
+ return (options?: FileSystem.MakeTempFileOptions) =>
202
+ pipe(
203
+ Effect.zip(makeDirectory(options), randomHexString(6)),
204
+ Effect.map(([directory, random]) => Path.join(directory, random)),
205
+ Effect.flatMap((path) => open(path, { flag: "w+" }))
206
+ )
207
+ })()
208
+
209
+ // == open
210
+
211
+ const open = (() => {
212
+ const nodeOpen = effectify(
213
+ NFS.open,
214
+ handleErrnoException("open"),
215
+ handleBadArgument("open")
216
+ )
217
+ const nodeClose = effectify(
218
+ NFS.close,
219
+ handleErrnoException("open"),
220
+ handleBadArgument("open")
221
+ )
222
+
223
+ return (path: string, options?: FileSystem.OpenFileOptions) =>
224
+ pipe(
225
+ Effect.acquireRelease(
226
+ nodeOpen(path, options?.flag ?? "r", options?.mode),
227
+ (fd) => Effect.orDie(nodeClose(fd))
228
+ ),
229
+ Effect.map((fd) => makeFile(File.Descriptor(fd)))
230
+ )
231
+ })()
232
+
233
+ const makeFile = (() => {
234
+ const nodeReadFactory = (method: string) =>
235
+ effectify(
236
+ NFS.read,
237
+ handleErrnoException(method),
238
+ handleBadArgument(method)
239
+ )
240
+ const nodeRead = nodeReadFactory("read")
241
+ const nodeReadAlloc = nodeReadFactory("readAlloc")
242
+ const nodeStat = effectify(
243
+ NFS.fstat,
244
+ handleErrnoException("stat"),
245
+ handleBadArgument("stat")
246
+ )
247
+ const nodeTruncate = effectify(
248
+ NFS.ftruncate,
249
+ handleErrnoException("truncate"),
250
+ handleBadArgument("truncate")
251
+ )
252
+
253
+ const nodeWriteFactory = (method: string) =>
254
+ effectify(
255
+ NFS.write,
256
+ handleErrnoException(method),
257
+ handleBadArgument(method)
258
+ )
259
+ const nodeWrite = nodeWriteFactory("write")
260
+ const nodeWriteAll = nodeWriteFactory("writeAll")
261
+
262
+ class FileImpl {
263
+ readonly [File.FileTypeId] = File.FileTypeId
264
+
265
+ constructor(
266
+ readonly fd: File.File.Descriptor
267
+ ) {}
268
+
269
+ get stat() {
270
+ return Effect.map(nodeStat(this.fd), makeFileInfo)
271
+ }
272
+
273
+ read(
274
+ buffer: Uint8Array,
275
+ options?: File.FileReadOptions
276
+ ) {
277
+ return Effect.map(
278
+ nodeRead(this.fd, {
279
+ buffer,
280
+ length: options?.length ? Number(options.length) : undefined,
281
+ offset: options?.offset ? Number(options.offset) : undefined
282
+ }),
283
+ FileSystem.Size
284
+ )
285
+ }
286
+
287
+ readAlloc(size: FileSystem.Size, options?: File.FileReadOptions | undefined) {
288
+ return Effect.flatMap(
289
+ Effect.sync(() => Buffer.allocUnsafeSlow(Number(size))),
290
+ (buffer) =>
291
+ Effect.map(
292
+ nodeReadAlloc(this.fd, {
293
+ buffer,
294
+ length: options?.length ? Number(options.length) : undefined,
295
+ offset: options?.offset ? Number(options.offset) : undefined
296
+ }),
297
+ (bytesRead) => {
298
+ if (bytesRead === 0) {
299
+ return Option.none()
300
+ }
301
+
302
+ if (bytesRead === Number(size)) {
303
+ return Option.some(buffer)
304
+ }
305
+
306
+ const dst = Buffer.allocUnsafeSlow(bytesRead)
307
+ buffer.copy(dst, 0, 0, bytesRead)
308
+ return Option.some(dst)
309
+ }
310
+ )
311
+ )
312
+ }
313
+
314
+ truncate(length?: FileSystem.Size) {
315
+ return nodeTruncate(this.fd, Number(length))
316
+ }
317
+
318
+ write(buffer: Uint8Array) {
319
+ return Effect.map(nodeWrite(this.fd, buffer), FileSystem.Size)
320
+ }
321
+
322
+ writeAll(buffer: Uint8Array): Effect.Effect<never, Error.PlatformError, void> {
323
+ return Effect.flatMap(
324
+ nodeWriteAll(this.fd, buffer),
325
+ (bytesWritten) => {
326
+ if (bytesWritten === buffer.length) {
327
+ return Effect.unit()
328
+ }
329
+ return this.writeAll(buffer.subarray(bytesWritten))
330
+ }
331
+ )
332
+ }
333
+ }
334
+
335
+ return (fd: File.File.Descriptor) => new FileImpl(fd) as File.File
336
+ })()
337
+
338
+ // == readDirectory
339
+
340
+ const readDirectory = (() => {
341
+ const nodeReadDirectory = effectify(
342
+ NFS.readdir,
343
+ handleErrnoException("readDirectory"),
344
+ handleBadArgument("readDirectory")
345
+ )
346
+
347
+ return (path: string, options?: FileSystem.ReadDirectoryOptions) =>
348
+ nodeReadDirectory(path, options) as Effect.Effect<never, Error.PlatformError, ReadonlyArray<string>>
349
+ })()
350
+
351
+ // == readFile
352
+
353
+ const readFile = (path: string) =>
354
+ Effect.asyncInterrupt<never, Error.PlatformError, Uint8Array>((resume) => {
355
+ const controller = new AbortController()
356
+
357
+ try {
358
+ NFS.readFile(path, { signal: controller.signal }, (err, data) => {
359
+ if (err) {
360
+ resume(Effect.fail(handleErrnoException("readFile")(err, [path])))
361
+ } else {
362
+ resume(Effect.succeed(data))
363
+ }
364
+ })
365
+ } catch (err) {
366
+ resume(Effect.fail(handleBadArgument("readFile")(err)))
367
+ }
368
+
369
+ return Effect.sync(() => controller.abort())
370
+ })
371
+
372
+ // == readLink
373
+
374
+ const readLink = (() => {
375
+ const nodeReadLink = effectify(
376
+ NFS.readlink,
377
+ handleErrnoException("readLink"),
378
+ handleBadArgument("readLink")
379
+ )
380
+ return (path: string) => nodeReadLink(path)
381
+ })()
382
+
383
+ // == realPath
384
+
385
+ const realPath = (() => {
386
+ const nodeRealPath = effectify(
387
+ NFS.realpath,
388
+ handleErrnoException("realPath"),
389
+ handleBadArgument("realPath")
390
+ )
391
+ return (path: string) => nodeRealPath(path)
392
+ })()
393
+
394
+ // == rename
395
+
396
+ const rename = (() => {
397
+ const nodeRename = effectify(
398
+ NFS.rename,
399
+ handleErrnoException("rename"),
400
+ handleBadArgument("rename")
401
+ )
402
+ return (oldPath: string, newPath: string) => nodeRename(oldPath, newPath)
403
+ })()
404
+
405
+ // == stat
406
+
407
+ const makeFileInfo = (stat: NFS.Stats): File.File.Info => ({
408
+ type: stat.isFile() ?
409
+ "File" :
410
+ stat.isDirectory() ?
411
+ "Directory" :
412
+ stat.isSymbolicLink() ?
413
+ "SymbolicLink" :
414
+ stat.isBlockDevice() ?
415
+ "BlockDevice" :
416
+ stat.isCharacterDevice() ?
417
+ "CharacterDevice" :
418
+ stat.isFIFO() ?
419
+ "FIFO" :
420
+ stat.isSocket() ?
421
+ "Socket" :
422
+ "Unknown",
423
+ mtime: Option.fromNullable(stat.mtime),
424
+ atime: Option.fromNullable(stat.atime),
425
+ birthtime: Option.fromNullable(stat.birthtime),
426
+ dev: stat.dev,
427
+ rdev: Option.fromNullable(stat.rdev),
428
+ ino: Option.fromNullable(stat.ino),
429
+ mode: stat.mode,
430
+ nlink: Option.fromNullable(stat.nlink),
431
+ uid: Option.fromNullable(stat.uid),
432
+ gid: Option.fromNullable(stat.gid),
433
+ size: FileSystem.Size(stat.size),
434
+ blksize: Option.fromNullable(FileSystem.Size(stat.blksize)),
435
+ blocks: Option.fromNullable(stat.blocks)
436
+ })
437
+ const stat = (() => {
438
+ const nodeStat = effectify(
439
+ NFS.stat,
440
+ handleErrnoException("stat"),
441
+ handleBadArgument("stat")
442
+ )
443
+ return (path: string) => Effect.map(nodeStat(path), makeFileInfo)
444
+ })()
445
+
446
+ // == symlink
447
+
448
+ const symlink = (() => {
449
+ const nodeSymlink = effectify(
450
+ NFS.symlink,
451
+ handleErrnoException("symlink"),
452
+ handleBadArgument("symlink")
453
+ )
454
+ return (target: string, path: string) => nodeSymlink(target, path)
455
+ })()
456
+
457
+ // == truncate
458
+
459
+ const truncate = (() => {
460
+ const nodeTruncate = effectify(
461
+ NFS.truncate,
462
+ handleErrnoException("truncate"),
463
+ handleBadArgument("truncate")
464
+ )
465
+ return (path: string, length?: FileSystem.Size) => nodeTruncate(path, Number(length))
466
+ })()
467
+
468
+ // == utime
469
+
470
+ const utime = (() => {
471
+ const nodeUtime = effectify(
472
+ NFS.utimes,
473
+ handleErrnoException("utime"),
474
+ handleBadArgument("utime")
475
+ )
476
+ return (path: string, atime: number | Date, mtime: number | Date) => nodeUtime(path, atime, mtime)
477
+ })()
478
+
479
+ // == writeFile
480
+
481
+ const writeFile = (path: string, data: Uint8Array, options?: FileSystem.WriteFileOptions) =>
482
+ Effect.asyncInterrupt<never, Error.PlatformError, void>((resume) => {
483
+ const controller = new AbortController()
484
+ try {
485
+ NFS.writeFile(path, data, {
486
+ signal: controller.signal,
487
+ flag: options?.flag,
488
+ mode: options?.mode
489
+ }, (err) => {
490
+ if (err) {
491
+ resume(Effect.fail(handleErrnoException("writeFile")(err, [path])))
492
+ } else {
493
+ resume(Effect.unit())
494
+ }
495
+ })
496
+ } catch (err) {
497
+ resume(Effect.fail(handleBadArgument("writeFile")(err)))
498
+ }
499
+ return Effect.sync(() => controller.abort())
500
+ })
501
+
502
+ const fileSystemImpl = FileSystem.make({
503
+ access,
504
+ copyFile,
505
+ chmod,
506
+ chown,
507
+ link,
508
+ makeDirectory,
509
+ makeTempDirectory,
510
+ makeTempDirectoryScoped,
511
+ makeTempFile,
512
+ open,
513
+ readDirectory,
514
+ readFile,
515
+ readLink,
516
+ realPath,
517
+ remove,
518
+ rename,
519
+ stat,
520
+ symlink,
521
+ truncate,
522
+ utime,
523
+ writeFile
524
+ })
525
+
526
+ /** @internal */
527
+ export const layer = Layer.succeed(FileSystem.FileSystem, fileSystemImpl)
@@ -0,0 +1,39 @@
1
+ import * as Effect from "@effect/io/Effect"
2
+ import * as Fiber from "@effect/io/Fiber"
3
+ import type * as FiberId from "@effect/io/Fiber/Id"
4
+ import { defaultTeardown, type RunMain } from "@effect/platform/Runtime"
5
+
6
+ /** @internal */
7
+ export const runMain: RunMain = <E, A>(
8
+ effect: Effect.Effect<never, E, A>,
9
+ teardown = defaultTeardown
10
+ ) => {
11
+ const fiber = Effect.runFork(effect)
12
+
13
+ fiber.unsafeAddObserver((exit) =>
14
+ teardown(exit, (code) => {
15
+ Effect.runCallback(interruptAll(fiber.id()), () => {
16
+ process.exit(code)
17
+ })
18
+ })
19
+ )
20
+
21
+ function onSigint() {
22
+ process.removeListener("SIGINT", onSigint)
23
+ process.removeListener("SIGTERM", onSigint)
24
+
25
+ Effect.runFork(fiber.interruptAsFork(fiber.id()))
26
+ }
27
+
28
+ process.once("SIGINT", onSigint)
29
+ process.once("SIGTERM", onSigint)
30
+ }
31
+
32
+ const interruptAll = (id: FiberId.FiberId) =>
33
+ Effect.flatMap(Fiber.roots(), (roots) => {
34
+ if (roots.length === 0) {
35
+ return Effect.unit()
36
+ }
37
+
38
+ return Fiber.interruptAllAs(roots, id)
39
+ })
@@ -0,0 +1,62 @@
1
+ import type { LazyArg } from "@effect/data/Function"
2
+ import { pipe } from "@effect/data/Function"
3
+ import * as Effect from "@effect/io/Effect"
4
+ import type { FromWritableOptions } from "@effect/platform-node/Sink"
5
+ import * as Sink from "@effect/stream/Sink"
6
+ import type { Writable } from "node:stream"
7
+
8
+ /** @internal */
9
+ export const fromWritable = <E, A>(
10
+ evaluate: LazyArg<Writable>,
11
+ onError: (error: unknown) => E,
12
+ { encoding, endOnClose = true }: FromWritableOptions = {}
13
+ ): Sink.Sink<never, E, A, never, void> =>
14
+ endOnClose ?
15
+ makeSinkWithRelease<E, A>(evaluate, onError, encoding) :
16
+ makeSink<E, A>(evaluate, onError, encoding)
17
+
18
+ const makeSink = <E, A>(stream: LazyArg<Writable>, onError: (error: unknown) => E, encoding?: BufferEncoding) =>
19
+ pipe(
20
+ Effect.sync(stream),
21
+ Effect.map((stream) => Sink.forEach(write<E, A>(stream, onError, encoding))),
22
+ Sink.unwrap
23
+ )
24
+
25
+ const makeSinkWithRelease = <E, A>(
26
+ stream: LazyArg<Writable>,
27
+ onError: (error: unknown) => E,
28
+ encoding?: BufferEncoding
29
+ ) =>
30
+ pipe(
31
+ Effect.acquireRelease(Effect.sync(stream), endWritable),
32
+ Effect.map((stream) => Sink.forEach(write<E, A>(stream, onError, encoding))),
33
+ Sink.unwrapScoped
34
+ )
35
+
36
+ const endWritable = (stream: Writable) =>
37
+ Effect.async<never, never, void>((resume) => {
38
+ if (stream.closed) {
39
+ resume(Effect.unit())
40
+ return
41
+ }
42
+
43
+ stream.end(() => resume(Effect.unit()))
44
+ })
45
+
46
+ const write = <E, A>(stream: Writable, onError: (error: unknown) => E, encoding?: BufferEncoding) =>
47
+ (_: A) =>
48
+ Effect.async<never, E, void>((resume) => {
49
+ const cb = (err?: Error | null) => {
50
+ if (err) {
51
+ resume(Effect.fail(onError(err)))
52
+ } else {
53
+ resume(Effect.unit())
54
+ }
55
+ }
56
+
57
+ if (encoding) {
58
+ stream.write(_, encoding, cb)
59
+ } else {
60
+ stream.write(_, cb)
61
+ }
62
+ })