From dbadf287fbd410626b263a6d00ad865715fb7918 Mon Sep 17 00:00:00 2001 From: Jordan Martinez Date: Tue, 25 Jul 2023 07:45:25 -0700 Subject: [PATCH 1/9] Update node-libs to next version --- packages.dhall | 53 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/packages.dhall b/packages.dhall index c145b48..60b466d 100644 --- a/packages.dhall +++ b/packages.dhall @@ -13,3 +13,56 @@ in upstream , "prelude" , "unsafe-coerce" ] + with node-buffer.version = "v9.0.0" + with node-buffer.dependencies = + [ "arraybuffer-types" + , "effect" + , "maybe" + , "st" + , "unsafe-coerce" + , "nullable" + ] + with node-streams.version = "v8.0.0" + with node-streams.dependencies = + [ "aff" + , "effect" + , "exceptions" + , "maybe" + , "node-buffer" + , "node-event-emitter" + , "nullable" + , "prelude" + , "unsafe-coerce" + ] + with node-fs.version = "v9.0.0" + with node-fs.dependencies = + [ "datetime" + , "effect" + , "either" + , "enums" + , "exceptions" + , "functions" + , "integers" + , "js-date" + , "maybe" + , "node-buffer" + , "node-path" + , "node-streams" + , "nullable" + , "partial" + , "prelude" + , "strings" + , "unsafe-coerce" + ] + with node-process.version = "v11.1.0" + with node-process.dependencies = + [ "effect" + , "foreign-object" + , "foreign" + , "maybe" + , "node-event-emitter" + , "node-streams" + , "posix-types" + , "prelude" + , "unsafe-coerce" + ] From 863ace57e20d6b70407b8618aefb4073925be4b2 Mon Sep 17 00:00:00 2001 From: Jordan Martinez Date: Tue, 25 Jul 2023 08:56:47 -0700 Subject: [PATCH 2/9] Update most node lib code --- src/Node/Library/Execa.purs | 26 +++++++------ src/Node/Library/Execa/GetStream.js | 3 -- src/Node/Library/Execa/GetStream.purs | 17 ++++----- src/Node/Library/Execa/IsExe.js | 17 --------- src/Node/Library/Execa/IsExe.purs | 37 ++++++++----------- src/Node/Library/Execa/MergeStream.js | 15 -------- src/Node/Library/Execa/MergeStream.purs | 27 ++++++-------- src/Node/Library/Execa/StripFinalNewline.purs | 6 +-- src/Node/Library/Execa/Utils.js | 5 --- src/Node/Library/Execa/Utils.purs | 3 -- 10 files changed, 52 insertions(+), 104 deletions(-) delete mode 100644 src/Node/Library/Execa/IsExe.js delete mode 100644 src/Node/Library/Execa/MergeStream.js diff --git a/src/Node/Library/Execa.purs b/src/Node/Library/Execa.purs index 1f1b35b..ed99ab4 100644 --- a/src/Node/Library/Execa.purs +++ b/src/Node/Library/Execa.purs @@ -49,7 +49,7 @@ import Data.String.Regex as Regex import Data.String.Regex.Flags (global, noFlags) import Data.String.Regex.Unsafe (unsafeRegex) import Effect (Effect) -import Effect.Aff (Aff, Error, Milliseconds(..), effectCanceler, finally, forkAff, joinFiber, makeAff, never, nonCanceler, suspendAff) +import Effect.Aff (Aff, Error, Milliseconds(..), effectCanceler, finally, forkAff, joinFiber, makeAff, never, suspendAff) import Effect.Class (liftEffect) import Effect.Exception (throw) import Effect.Exception as Exception @@ -60,11 +60,11 @@ import Foreign (Foreign, readInt, readString, renderForeignError, unsafeToForeig import Foreign.Object (Object) import Foreign.Object as Object import Node.Buffer (unsafeThaw) +import Node.Buffer as Buffer import Node.Buffer.Immutable (ImmutableBuffer) import Node.Buffer.Immutable as ImmutableBuffer -import Node.Buffer.Internal as Buffer import Node.Encoding (Encoding(..)) -import Node.EventEmitter (EventHandle(..), once_) +import Node.EventEmitter (EventHandle(..), on, once) import Node.EventEmitter.UtilTypes (EventHandle1, EventHandle0) import Node.Library.Execa.CrossSpawn (CrossSpawnConfig) import Node.Library.Execa.CrossSpawn as CrossSpawn @@ -77,7 +77,6 @@ import Node.Library.HumanSignals (signals) import Node.Process as Process import Node.Stream (Readable, Writable, destroy) import Node.Stream as Stream -import Node.Stream as Streams import Partial.Unsafe (unsafeCrashWith) import Record as Record import Type.Proxy (Proxy(..)) @@ -373,16 +372,19 @@ execa file args buildOptions = do , windowsHide: options.windowsHide } spawnedFiber <- suspendAff $ makeAff \cb -> do - (ExecaChildProcess spawned) # once_ exitH case _ of + rmExit <- (ExecaChildProcess spawned) # once exitH case _ of Left i -> cb $ Right $ ExitCode i Right sig -> cb $ Right $ Killed sig - (ExecaChildProcess spawned) # once_ errorH \error -> do + rmError <- (ExecaChildProcess spawned) # once errorH \error -> do cb $ Right $ SpawnError error - Streams.onError (stdin spawned) \error -> do + rmStreamError <- (stdin spawned) # on Stream.errorH \error -> do cb $ Right $ StdinError error - pure nonCanceler + pure $ effectCanceler do + rmExit + rmError + rmStreamError timeoutFiber <- suspendAff do case parsed.options.timeoutWithKillSignal of Just { milliseconds, killSignal } -> do @@ -511,13 +513,13 @@ execa file args buildOptions = do { stream: stdin spawned , writeUtf8: \string -> liftEffect do buf <- Buffer.fromString string UTF8 - void $ Stream.write (stdin spawned) buf mempty + void $ Stream.write (stdin spawned) buf , writeUtf8End: \string -> liftEffect do buf <- Buffer.fromString string UTF8 - void $ Stream.write (stdin spawned) buf mempty - void $ Stream.end (stdin spawned) mempty + void $ Stream.write (stdin spawned) buf + void $ Stream.end (stdin spawned) , end: liftEffect do - void $ Stream.end (stdin spawned) mempty + void $ Stream.end (stdin spawned) , shareParentProcessStdin: liftEffect do void $ Stream.pipe Process.stdin (stdin spawned) } diff --git a/src/Node/Library/Execa/GetStream.js b/src/Node/Library/Execa/GetStream.js index 8d12be1..f154c6e 100644 --- a/src/Node/Library/Execa/GetStream.js +++ b/src/Node/Library/Execa/GetStream.js @@ -1,7 +1,4 @@ import stream from "node:stream"; -import { constants } from "node:buffer"; - -export const maxBufferLength = constants.MAX_LENGTH; export function pipeline(source, destination, cb) { return stream.pipeline([source, destination], cb); diff --git a/src/Node/Library/Execa/GetStream.purs b/src/Node/Library/Execa/GetStream.purs index 9450204..228c17b 100644 --- a/src/Node/Library/Execa/GetStream.purs +++ b/src/Node/Library/Execa/GetStream.purs @@ -13,15 +13,15 @@ import Data.Maybe (Maybe(..), fromMaybe, maybe) import Data.Nullable (Nullable, toMaybe) import Data.Number (infinity) import Effect (Effect) -import Effect.Aff (Aff, Error, error, makeAff, nonCanceler) +import Effect.Aff (Aff, Error, effectCanceler, error, makeAff) import Effect.Class (liftEffect) import Effect.Ref as Ref import Effect.Uncurried (EffectFn1, EffectFn3, mkEffectFn1, runEffectFn3) import Node.Buffer (Buffer) import Node.Buffer as Buffer import Node.Buffer.Immutable (ImmutableBuffer) -import Node.Library.Execa.Utils (newPassThroughStream) -import Node.Stream (Readable, Writable, Duplex, onData) +import Node.EventEmitter (on, on_) +import Node.Stream (Duplex, Readable, Writable, dataH, newPassThrough) import Unsafe.Coerce (unsafeCoerce) type Interface = @@ -47,7 +47,7 @@ getStreamBuffer inputStream initialOptions = do bufferedData <- interface.getBufferedValue buff <- Buffer.unsafeFreeze bufferedData cb $ Right { buffer: buff, inputError: toMaybe err } - onData interface.stream \_ -> do + rmListener <- interface.stream # on dataH \_ -> do bufferedLen <- interface.getBufferedLength when (bufferedLen > options.maxBuffer) do bufferedData <- interface.getBufferedValue @@ -59,7 +59,8 @@ getStreamBuffer inputStream initialOptions = do (\size -> "Max buffer size exceeded. Buffer size was: " <> show size) initialOptions.maxBuffer } - pure nonCanceler + pure $ effectCanceler do + rmListener where -- PureScript implementation note: -- - object mode == false due to 'buffer' usage @@ -67,8 +68,8 @@ getStreamBuffer inputStream initialOptions = do bufferStream = do chunksRef <- Ref.new [] lengthRef <- Ref.new 0.0 - stream <- newPassThroughStream - onData stream \buf -> do + stream <- newPassThrough + stream # on_ dataH \buf -> do Ref.modify_ (\chunks -> Array.snoc chunks buf) chunksRef bufLen <- Buffer.size buf Ref.modify_ (_ + (toNumber bufLen)) lengthRef @@ -87,6 +88,4 @@ getStreamBuffer inputStream initialOptions = do , stream } -foreign import maxBufferLength :: Number - foreign import pipeline :: forall w r. EffectFn3 (Readable w) (Writable r) (EffectFn1 (Nullable Error) Unit) Unit diff --git a/src/Node/Library/Execa/IsExe.js b/src/Node/Library/Execa/IsExe.js deleted file mode 100644 index 2139cc7..0000000 --- a/src/Node/Library/Execa/IsExe.js +++ /dev/null @@ -1,17 +0,0 @@ -import process from "process"; - -export const getUidImpl = (nothing, just) => { - if (process.getuid) { - return just(process.getuid()); - } else { - return nothing; - } -}; - -export const getGidImpl = (nothing, just) => { - if (process.getgid) { - return just(process.getgid()); - } else { - return nothing; - } -} diff --git a/src/Node/Library/Execa/IsExe.purs b/src/Node/Library/Execa/IsExe.purs index 5e09d03..fa259fa 100644 --- a/src/Node/Library/Execa/IsExe.purs +++ b/src/Node/Library/Execa/IsExe.purs @@ -24,13 +24,16 @@ import Data.Tuple (Tuple(..), fst) import Effect (Effect) import Effect.Aff (Aff, Error, makeAff, nonCanceler) import Effect.Exception (try) -import Effect.Uncurried (EffectFn2, runEffectFn2) import Node.FS.Async as FsAsync -import Node.FS.Stats (Stats(..), isFile, isSymbolicLink) +import Node.FS.Stats (Stats) +import Node.FS.Stats as Stats import Node.FS.Sync as FsSync import Node.Library.Execa.Utils (envKey) import Node.Platform (Platform(..)) import Node.Process (platform) +import Node.Process as Process +import Data.Posix (Gid(..), Uid(..)) +import Safe.Coerce (coerce) import Unsafe.Coerce (unsafeCoerce) type IsExeOptions = @@ -51,7 +54,7 @@ defaultIsExeOptions = isExe :: String -> IsExeOptions -> Aff (Tuple (Maybe Error) Boolean) isExe path options = do let - core = case platform of + core = case Process.platform of Just Win32 -> coreWindows _ -> coreNonWindows @@ -93,8 +96,8 @@ coreWindows = where checkStat :: Stats -> String -> IsExeOptions -> Effect Boolean checkStat stat path options - | not (isSymbolicLink stat) - , not (isFile stat) = pure false + | not (Stats.isSymbolicLink stat) + , not (Stats.isFile stat) = pure false | otherwise = checkPathExt path options checkPathExt :: String -> IsExeOptions -> Effect Boolean @@ -151,17 +154,17 @@ coreNonWindows = checkStat :: Stats -> IsExeOptions -> Effect Boolean checkStat stat options = do b <- checkMode stat options - pure $ (isFile stat) && b + pure $ (Stats.isFile stat) && b checkMode :: Stats -> IsExeOptions -> Effect Boolean - checkMode (Stats statObj) options = do + checkMode statsObj options = do let - mode = floor statObj.mode - uid = floor statObj.uid - gid = floor statObj.gid + mode = floor $ Stats.mode statsObj + uid = floor $ Stats.uid statsObj + gid = floor $ Stats.gid statsObj - processMbUid <- getUid - processMbGid <- getGid + processMbUid :: Maybe Int <- coerce $ Process.getUid + processMbGid :: Maybe Int <- coerce $ Process.getGid let myUid = options.uid <|> processMbUid @@ -179,13 +182,3 @@ coreNonWindows = , truthy (mode .&. u) && maybe false (eq uid) myUid , truthy (mode .&. ug) && maybe false (eq 0) myUid ] - -foreign import getUidImpl :: EffectFn2 (forall a. Maybe a) (forall a. a -> Maybe a) (Maybe Int) - -foreign import getGidImpl :: EffectFn2 (forall a. Maybe a) (forall a. a -> Maybe a) (Maybe Int) - -getUid :: Effect (Maybe Int) -getUid = runEffectFn2 getUidImpl Nothing Just - -getGid :: Effect (Maybe Int) -getGid = runEffectFn2 getGidImpl Nothing Just diff --git a/src/Node/Library/Execa/MergeStream.js b/src/Node/Library/Execa/MergeStream.js deleted file mode 100644 index 9eb3ba1..0000000 --- a/src/Node/Library/Execa/MergeStream.js +++ /dev/null @@ -1,15 +0,0 @@ -export function onceEnd(source, removeFn) { - source.once("end", removeFn); -} - -export function onceError(source, output) { - source.once("error", output.emit.bind(output, "error")); -} - -export function pipeImpl(source, output, options) { - source.pipe(output, options); -} - -export function readable(stream) { - return stream.readable; -} diff --git a/src/Node/Library/Execa/MergeStream.purs b/src/Node/Library/Execa/MergeStream.purs index 3ed45b2..ce87119 100644 --- a/src/Node/Library/Execa/MergeStream.purs +++ b/src/Node/Library/Execa/MergeStream.purs @@ -14,9 +14,10 @@ import Data.Array as Array import Effect (Effect) import Effect.Ref (Ref) import Effect.Ref as Ref -import Effect.Uncurried (EffectFn1, EffectFn2, EffectFn3, runEffectFn1, runEffectFn2, runEffectFn3) -import Node.Stream (Readable, Duplex, end) -import Node.Library.Execa.Utils (newPassThroughStream) +import Effect.Uncurried (runEffectFn2) +import Node.EventEmitter (once_, unsafeEmitFn) +import Node.Stream (Duplex, Readable, end, endH, errorH, pipe') +import Node.Stream as Stream import Unsafe.Coerce (unsafeCoerce) import Unsafe.Reference (unsafeRefEq) @@ -34,20 +35,21 @@ mergeStreams useInterface = do buildInterface :: Effect Interface buildInterface = do sources <- Ref.new [] - output <- newPassThroughStream + output <- Stream.newPassThrough pure $ Interface { sources, output } add :: forall w. Readable w -> Interface -> Effect Boolean add source iface@(Interface r) = do ifM - (runEffectFn1 readable r.output) + (Stream.readable r.output) ( do let readStream = toReadableOnlyStream source Ref.modify_ (flip Array.snoc readStream) r.sources - runEffectFn2 onceEnd readStream (remove readStream iface) - runEffectFn2 onceError readStream r.output - runEffectFn3 pipeImpl readStream r.output { end: false } + readStream # once_ endH (remove readStream iface) + readStream # once_ errorH \err -> do + void $ runEffectFn2 (unsafeEmitFn $ Stream.toEventEmitter r.output) "error" err + pipe' readStream r.output { end: false } pure true ) (pure false) @@ -56,14 +58,9 @@ remove :: forall w. Readable w -> Interface -> Effect Unit remove source (Interface r) = do arr <- Ref.read r.sources let arr' = Array.filter (not <<< unsafeRefEq (toReadableOnlyStream source)) arr - stillReadable <- runEffectFn1 readable r.output + stillReadable <- Stream.readable r.output when (Array.length arr' == 0 && stillReadable) do - end r.output mempty + end r.output toReadableOnlyStream :: forall w. Readable w -> Readable () toReadableOnlyStream = unsafeCoerce - -foreign import onceEnd :: EffectFn2 (Readable ()) (Effect Unit) Unit -foreign import onceError :: EffectFn2 (Readable ()) Duplex Unit -foreign import pipeImpl :: EffectFn3 (Readable ()) Duplex { end :: Boolean } Unit -foreign import readable :: EffectFn1 (Duplex) Boolean diff --git a/src/Node/Library/Execa/StripFinalNewline.purs b/src/Node/Library/Execa/StripFinalNewline.purs index 47fc60d..6a0963f 100644 --- a/src/Node/Library/Execa/StripFinalNewline.purs +++ b/src/Node/Library/Execa/StripFinalNewline.purs @@ -9,9 +9,9 @@ import Prelude import Data.Char (toCharCode) import Data.Int as Int import Data.String as String -import Node.Buffer (class MutableBuffer, Buffer, BufferValueType(..), slice, unsafeFreeze) +import Effect (Effect) +import Node.Buffer (Buffer, BufferValueType(..), slice, unsafeFreeze, read, size) import Node.Buffer.Immutable (ImmutableBuffer) -import Node.Buffer.Internal (read, size) stripFinalNewline :: String -> String stripFinalNewline s = do @@ -26,7 +26,7 @@ stripFinalNewline s = do "\r" -> sDrop1 _ -> s -stripFinalNewlineBuf :: forall m. MutableBuffer Buffer m => Buffer -> m ImmutableBuffer +stripFinalNewlineBuf :: Buffer -> Effect ImmutableBuffer stripFinalNewlineBuf b = do len <- size b -- PureScript implementation note: diff --git a/src/Node/Library/Execa/Utils.js b/src/Node/Library/Execa/Utils.js index 2640e23..0b5eb24 100644 --- a/src/Node/Library/Execa/Utils.js +++ b/src/Node/Library/Execa/Utils.js @@ -1,8 +1,3 @@ -import stream from "node:stream"; - export const buildCustomErrorImpl = (msg, obj) => Object.assign(new Error(msg), obj); -export function newPassThroughStream() { - return new stream.PassThrough({ objectMode: false }); -} diff --git a/src/Node/Library/Execa/Utils.purs b/src/Node/Library/Execa/Utils.purs index d1e2ad2..30f9402 100644 --- a/src/Node/Library/Execa/Utils.purs +++ b/src/Node/Library/Execa/Utils.purs @@ -23,7 +23,6 @@ import Node.Buffer.Immutable as ImmutableBuffer import Node.Encoding (Encoding(..)) import Node.Platform (Platform(..)) import Node.Process as Process -import Node.Stream (Duplex) import Prim.Row as Row import Record as Record import Type.Proxy (Proxy) @@ -80,5 +79,3 @@ envKey' env key | Process.platform == Just Win32 = findMapWithIndex (\k v -> v <$ guard (String.toUpper k == String.toUpper key)) env | otherwise = Object.lookup key env - -foreign import newPassThroughStream :: Effect Duplex From db7085bfdb6ce359a07e074a9c9ae638ce4cf2df Mon Sep 17 00:00:00 2001 From: Jordan Martinez Date: Tue, 25 Jul 2023 16:32:15 -0700 Subject: [PATCH 3/9] Add dep on child-process; drop CP FFI --- packages.dhall | 39 ++ spago.dhall | 1 + src/Node/Library/Execa.js | 85 --- src/Node/Library/Execa.purs | 662 +++++------------- src/Node/Library/Execa/GetStream.purs | 9 +- src/Node/Library/Execa/StripFinalNewline.purs | 17 +- 6 files changed, 217 insertions(+), 596 deletions(-) diff --git a/packages.dhall b/packages.dhall index 60b466d..fd82cc2 100644 --- a/packages.dhall +++ b/packages.dhall @@ -66,3 +66,42 @@ in upstream , "prelude" , "unsafe-coerce" ] + with node-child-process.version = "5af300b576fc7cc42a63d4d41b6c7afc97d0e9d3" + with node-child-process.dependencies = + [ "exceptions" + , "node-event-emitter" + , "foreign" + , "foreign-object" + , "functions" + , "node-fs" + , "node-streams" + , "node-os" + , "nullable" + , "posix-types" + , "unsafe-coerce" + ] + with node-os = + { dependencies = + [ "arrays" + , "bifunctors" + , "console" + , "control" + , "datetime" + , "effect" + , "either" + , "exceptions" + , "foldable-traversable" + , "foreign" + , "foreign-object" + , "functions" + , "maybe" + , "node-buffer" + , "nullable" + , "partial" + , "posix-types" + , "prelude" + , "unsafe-coerce" + ] + , repo = "https://github.com/purescript-node/purescript-node-os.git" + , version = "v5.1.0" + } diff --git a/spago.dhall b/spago.dhall index ac7a380..d58f31d 100644 --- a/spago.dhall +++ b/spago.dhall @@ -14,6 +14,7 @@ , "js-timers" , "maybe" , "node-buffer" + , "node-child-process" , "node-event-emitter" , "node-fs" , "node-human-signals" diff --git a/src/Node/Library/Execa.js b/src/Node/Library/Execa.js index 29380de..18c9d97 100644 --- a/src/Node/Library/Execa.js +++ b/src/Node/Library/Execa.js @@ -1,13 +1,3 @@ -// Unless otherwise stated, FFI here is -// for child process-related things -import { Readable } from "node:stream"; - -export { - spawn as spawnImpl, - spawnSync as spawnSyncImpl -} from "node:child_process"; - -// For execa-specifically. export function monkeyPatchKill(cp, killFn) { cp.kill = killFn.bind(null, cp.kill.bind(cp)) } @@ -21,81 +11,6 @@ export function setTimeoutImpl(timeout, cb) { return t.unref ? t : { unref: () => {} }; } -export function channelImpl(cp) { - return cp.channel; -} - -export function connectedImpl(cp) { - return cp.connected; -} - -export function disconnectImpl(cp) { - return cp.disconnect(); -} - -export function exitCodeImpl(cp) { - return cp.exitCode; -} - export function killImpl(cp, signal, options) { return cp.kill(signal, options); } - -export function pidExistsImpl(cp) { - return cp.kill(0); -} - -export function killedImpl(cp) { - return cp.killed; -} - -export function pidImpl(cp) { - return cp.pid; -} - -export function refImpl(cp) { - return cp.ref(); -} - -export function unrefImpl(cp) { - return cp.unref(); -} - -export function sendImpl(cp, msg, handle, options, cb) { - return cp.send(msg, handle, options, cb); -} - -export function signalCodeImpl(cp) { - return cp.signalCode; -} - -export function spawnArgs(cp) { - return cp.spawnArgs; -} - -export function spawnFile(cp) { - return cp.spawnFile; -} - -export function stderr(cp) { - return cp.stderr; -} - -export function stdin(cp) { - return cp.stdin; -} - -export function stdioImpl(cp) { - return cp.stdio; -} - -export function stdout(cp) { - return cp.stdout; -} - -const _undefined = undefined; -export { _undefined as undefined }; - -export function bufferToReadStream(buf) { - return Readable.from(buf); -} diff --git a/src/Node/Library/Execa.purs b/src/Node/Library/Execa.purs index ed99ab4..83987ee 100644 --- a/src/Node/Library/Execa.purs +++ b/src/Node/Library/Execa.purs @@ -9,40 +9,28 @@ module Node.Library.Execa , ExecaProcess , ExecaSuccess , ExecaChildProcess - , closeH - , disconnectH - , errorH - , exitH - , messageH - , spawnH - , KillSignal + , toChildProcess , execa , ExecaSyncOptions , ExecaSyncResult , execaSync , execaCommand , execaCommandSync - , Handle - , ChildProcessError ) where import Prelude import Control.Alternative ((<|>), guard) -import Control.Monad.Except (runExcept) import Control.Parallel (parOneOf) import Data.Array as Array import Data.Either (Either(..), either) import Data.Foldable (for_, sequence_) -import Data.Foldable as Foldable -import Data.Generic.Rep (class Generic) import Data.Int (floor, toNumber) import Data.Lens (Prism', is, preview, prism) import Data.Map as Map import Data.Maybe (Maybe(..), fromMaybe, isJust, maybe) import Data.Nullable (Nullable, toMaybe, toNullable) -import Data.Posix (Pid) -import Data.Show.Generic (genericShow) +import Data.Posix (Gid, Pid, Uid) import Data.String as String import Data.String.Regex (Regex, test) import Data.String.Regex as Regex @@ -55,17 +43,18 @@ import Effect.Exception (throw) import Effect.Exception as Exception import Effect.Ref as Ref import Effect.Timer (clearTimeout, setTimeout) -import Effect.Uncurried (EffectFn1, EffectFn2, EffectFn3, EffectFn5, mkEffectFn1, mkEffectFn2, mkEffectFn3, runEffectFn1, runEffectFn2, runEffectFn3, runEffectFn5) -import Foreign (Foreign, readInt, readString, renderForeignError, unsafeToForeign) +import Effect.Uncurried (EffectFn1, EffectFn2, EffectFn3, mkEffectFn3, runEffectFn1, runEffectFn2, runEffectFn3) import Foreign.Object (Object) import Foreign.Object as Object -import Node.Buffer (unsafeThaw) +import Node.Buffer (Buffer) import Node.Buffer as Buffer -import Node.Buffer.Immutable (ImmutableBuffer) -import Node.Buffer.Immutable as ImmutableBuffer +import Node.ChildProcess (ChildProcess) +import Node.ChildProcess as CP +import Node.ChildProcess.Types (Exit(..), KillSignal, StdIO, customShell, fromKillSignal, intSignal, stringSignal) import Node.Encoding (Encoding(..)) -import Node.EventEmitter (EventHandle(..), on, once) -import Node.EventEmitter.UtilTypes (EventHandle1, EventHandle0) +import Node.Errors.SystemError (SystemError) +import Node.Errors.SystemError as SystemError +import Node.EventEmitter (on, once) import Node.Library.Execa.CrossSpawn (CrossSpawnConfig) import Node.Library.Execa.CrossSpawn as CrossSpawn import Node.Library.Execa.GetStream (getStreamBuffer) @@ -77,7 +66,7 @@ import Node.Library.HumanSignals (signals) import Node.Process as Process import Node.Stream (Readable, Writable, destroy) import Node.Stream as Stream -import Partial.Unsafe (unsafeCrashWith) +import Node.UnsafeChildProcess.Unsafe as Unsafe import Record as Record import Type.Proxy (Proxy(..)) import Unsafe.Coerce (unsafeCoerce) @@ -137,12 +126,12 @@ type ExecaOptions = , env :: Maybe (Object String) , encoding :: Maybe Encoding , argv0 :: Maybe String - , stdioExtra :: Maybe (Array Foreign) + , stdioExtra :: Maybe (Array StdIO) , detached :: Maybe Boolean - , uid :: Maybe Int - , gid :: Maybe Int + , uid :: Maybe Uid + , gid :: Maybe Gid , shell :: Maybe String - , timeout :: Maybe { milliseconds :: Number, killSignal :: Either Int String } + , timeout :: Maybe { milliseconds :: Milliseconds, killSignal :: KillSignal } , maxBuffer :: Maybe Number , windowsVerbatimArguments :: Maybe Boolean , windowsHide :: Maybe Boolean @@ -226,7 +215,7 @@ handleArguments file args initOptions = do let -- validateTimeout { timeout, killSignal, timeoutWithKillSignal } = case initOptions.timeout of - Just r | r.milliseconds > 0.0 -> + Just r | r.milliseconds > Milliseconds 0.0 -> { timeout: Just r.milliseconds, killSignal: Just r.killSignal, timeoutWithKillSignal: Just r } _ -> { timeout: Nothing, killSignal: Nothing, timeoutWithKillSignal: Nothing } @@ -261,21 +250,21 @@ handleArguments file args initOptions = do -- | `stdin.end` - End the child process' `stdin` type ExecaProcess = { cancel :: Aff Unit - , channel :: Aff (Maybe { ref :: Effect Unit, unref :: Effect Unit }) + , unsafeChannelRef :: Aff Unit + , unsafeChannelUnref :: Aff Unit , connected :: Aff Boolean , disconnect :: Aff Unit , exitCode :: Aff (Maybe Int) , kill :: Aff Boolean , killForced :: Milliseconds -> Aff Boolean - , killForcedWithSignal :: Either Int String -> Milliseconds -> Aff Boolean - , killWithSignal :: Either Int String -> Aff Boolean + , killForcedWithSignal :: KillSignal -> Milliseconds -> Aff Boolean + , killWithSignal :: KillSignal -> Aff Boolean , killed :: Aff Boolean , childProcess :: ExecaChildProcess , pid :: Aff (Maybe Pid) , pidExists :: Aff Boolean , ref :: Aff Unit , result :: Aff (Either ExecaError ExecaSuccess) - , send :: Foreign -> Handle -> ({ keepOpen :: Maybe Boolean } -> { keepOpen :: Maybe Boolean }) -> Effect Unit -> Aff Boolean , signalCode :: Aff (Maybe String) , spawnArgs :: Array String , spawnFile :: String @@ -296,7 +285,6 @@ type ExecaProcess = , output :: Aff { text :: String, error :: Maybe Exception.Error } , pipeToParentStderr :: Aff Unit } - , stdio :: Aff (Array Foreign) , unref :: Aff Unit } @@ -308,33 +296,10 @@ type ExecaSuccess = , stdout :: String } -newtype ExecaChildProcess = ExecaChildProcess ChildProcess +newtype ExecaChildProcess = ExecaChildProcess CP.ChildProcess -closeH :: EventHandle ExecaChildProcess (Either Int String -> Effect Unit) (EffectFn2 (Nullable Int) (Nullable String) Unit) -closeH = EventHandle "close" \cb -> mkEffectFn2 \code sig -> do - case toMaybe code, toMaybe sig of - Just c, _ -> cb $ Left c - _, Just s -> cb $ Right s - _, _ -> unsafeCrashWith $ "Impossible. Got both an exit code and error signal. Code=" <> show code <> "; Signal=" <> show sig - -disconnectH :: EventHandle0 ExecaChildProcess -disconnectH = EventHandle "disconnect" identity - -errorH :: EventHandle1 ExecaChildProcess ChildProcessError -errorH = EventHandle "error" mkEffectFn1 - -exitH :: EventHandle ExecaChildProcess (Either Int (Either Int String) -> Effect Unit) (EffectFn2 (Nullable Int) (Nullable KillSignal) Unit) -exitH = EventHandle "exit" \cb -> mkEffectFn2 \code sig -> do - case toMaybe code, map fromKillSignal $ toMaybe sig of - Just c, _ -> cb $ Left c - _, Just s -> cb $ Right s - _, _ -> unsafeCrashWith $ "Impossible. Got neither or both an exit code and error signal. Code=" <> show code <> "; Signal=" <> (unsafeCoerce sig :: String) - -messageH :: EventHandle ExecaChildProcess (Foreign -> Maybe Handle -> Effect Unit) (EffectFn2 Foreign (Nullable Handle) Unit) -messageH = EventHandle "message" \cb -> mkEffectFn2 \a b -> cb a (toMaybe b) - -spawnH :: EventHandle0 ExecaChildProcess -spawnH = EventHandle "spawn" identity +toChildProcess :: ExecaChildProcess -> ChildProcess +toChildProcess (ExecaChildProcess cp) = cp -- | Replacement for `childProcess.spawn`. Since this is asynchronous, -- | the returned value will not provide any results until one calls `spawned.result`: @@ -358,43 +323,57 @@ execa file args buildOptions = do let command = joinCommand file args escapedCommand = getEscapedCommand file args - spawned <- liftEffect $ spawn parsed.file parsed.args - { cwd: options.cwd - , env: options.env - , argv0: options.argv0 - , stdioExtra: options.stdioExtra - , detached: options.detached - , uid: options.uid - , gid: options.gid - , serialization: Nothing - , shell: options.shell - , windowsVerbatimArguments: options.windowsVerbatimArguments - , windowsHide: options.windowsHide - } + spawned <- liftEffect $ CP.spawn' parsed.file parsed.args + ( _ + { cwd = options.cwd + , env = options.env + , argv0 = options.argv0 + , appendStdio = options.stdioExtra + , detached = options.detached + , uid = options.uid + , gid = options.gid + , serialization = Nothing + , shell = map customShell options.shell + , windowsVerbatimArguments = options.windowsVerbatimArguments + , windowsHide = options.windowsHide + } + ) spawnedFiber <- suspendAff $ makeAff \cb -> do - rmExit <- (ExecaChildProcess spawned) # once exitH case _ of - Left i -> cb $ Right $ ExitCode i - Right sig -> cb $ Right $ Killed sig - - rmError <- (ExecaChildProcess spawned) # once errorH \error -> do + exitRef <- Ref.new mempty + errorRef <- Ref.new mempty + streamRef <- Ref.new mempty + let + removeListeners = do + join $ Ref.read exitRef + join $ Ref.read errorRef + join $ Ref.read streamRef + rmExit <- spawned # once CP.exitH \res -> do + removeListeners + cb $ Right case res of + Normally i -> ExitCode i + BySignal sig -> Killed sig + Ref.write rmExit exitRef + + rmError <- spawned # once CP.errorH \error -> do + removeListeners cb $ Right $ SpawnError error + Ref.write rmError errorRef - rmStreamError <- (stdin spawned) # on Stream.errorH \error -> do + rmStream <- (CP.stdin spawned) # on Stream.errorH \error -> do + removeListeners cb $ Right $ StdinError error - pure $ effectCanceler do - rmExit - rmError - rmStreamError + Ref.write rmStream streamRef + pure $ effectCanceler removeListeners timeoutFiber <- suspendAff do case parsed.options.timeoutWithKillSignal of - Just { milliseconds, killSignal } -> do + Just { milliseconds, killSignal: signal } -> do makeAff \cb -> do - tid <- setTimeout ((unsafeCoerce :: Number -> Int) milliseconds) do - void $ kill'' (toKillSignal killSignal) Nothing spawned - void $ destroy (stdin spawned) - void $ destroy (stdout spawned) - void $ destroy (stderr spawned) - cb $ Right $ TimedOut killSignal + tid <- setTimeout ((unsafeCoerce :: Milliseconds -> Int) milliseconds) do + void $ execaKill signal Nothing spawned + void $ destroy (CP.stdin spawned) + void $ destroy (CP.stdout spawned) + void $ destroy (CP.stderr spawned) + cb $ Right $ TimedOut signal pure $ effectCanceler do clearTimeout tid _ -> @@ -415,7 +394,7 @@ execa file args buildOptions = do ( do liftEffect do removal <- SignalExit.onExit \_ _ -> do - void $ kill'' (stringKillSignal "SIGTERM") Nothing spawned + void $ execaKill (stringSignal "SIGTERM") Nothing spawned Ref.write (Just removal) removeHandlerRef joinFiber mainFiber ) @@ -429,24 +408,24 @@ execa file args buildOptions = do let cancel :: Aff Unit cancel = liftEffect do - killSucceeded <- kill spawned + killSucceeded <- execaKill (stringSignal "SIGTERM") Nothing spawned when killSucceeded do Ref.write true isCanceledRef - bufferToString = ImmutableBuffer.toString parsed.options.encoding + bufferToString = Buffer.toString parsed.options.encoding mkStdIoFiber stream = forkAff do streamResult <- getStreamBuffer stream { maxBuffer: Just parsed.options.maxBuffer } text <- liftEffect do - text <- bufferToString <$> handleOutput { stripFinalNewline: parsed.options.stripFinalNewline } streamResult.buffer + text <- bufferToString =<< handleOutput { stripFinalNewline: parsed.options.stripFinalNewline } streamResult.buffer when (isJust streamResult.inputError) do destroy stream pure text pure { text, error: streamResult.inputError } runFiber <- forkAff $ joinFiber processDoneFiber - stdoutFiber <- mkStdIoFiber (stdout spawned) - stderrFiber <- mkStdIoFiber (stderr spawned) + stdoutFiber <- mkStdIoFiber (CP.stdout spawned) + stderrFiber <- mkStdIoFiber (CP.stderr spawned) let getSpawnResult = do @@ -468,7 +447,7 @@ execa file args buildOptions = do } someError, stdoutErr, stderrErr -> liftEffect do isCanceled <- Ref.read isCanceledRef - killed' <- killed spawned + killed' <- CP.killed spawned pure $ Left $ mkError { error: preview _SpawnError someError , stdinErr: preview _StdinError someError @@ -487,55 +466,55 @@ execa file args buildOptions = do } pure - { channel: liftEffect $ channel spawned - , connected: liftEffect $ connected spawned - , disconnect: liftEffect $ disconnect spawned - , exitCode: liftEffect $ exitCode spawned - , kill: liftEffect $ kill spawned + { unsafeChannelRef: liftEffect $ Unsafe.unsafeChannelRef $ CP.toUnsafeChildProcess spawned + , unsafeChannelUnref: liftEffect $ Unsafe.unsafeChannelUnref $ CP.toUnsafeChildProcess spawned + , connected: liftEffect $ CP.connected spawned + , disconnect: liftEffect $ CP.disconnect spawned + , exitCode: liftEffect $ CP.exitCode spawned + , kill: liftEffect $ execaKill (stringSignal "SIGTERM") Nothing spawned , killWithSignal: \signal -> liftEffect do - kill' (toKillSignal signal) spawned + execaKill signal Nothing spawned , killForced: \forceKillAfterTimeout -> liftEffect do - kill'' (stringKillSignal "SIGTERM") (Just forceKillAfterTimeout) spawned + execaKill (stringSignal "SIGTERM") (Just forceKillAfterTimeout) spawned , killForcedWithSignal: \signal forceKillAfterTimeout -> liftEffect do - kill'' (toKillSignal signal) (Just forceKillAfterTimeout) spawned - , pidExists: liftEffect $ pidExists spawned - , killed: liftEffect $ killed spawned - , pid: liftEffect $ pid spawned - , unref: liftEffect $ unref spawned - , ref: liftEffect $ ref spawned - , send: \foreignData handle mkOptions cb -> liftEffect do - send spawned foreignData handle mkOptions cb - , signalCode: liftEffect $ signalCode spawned - , spawnArgs: spawnArgs spawned - , spawnFile: spawnFile spawned + execaKill signal (Just forceKillAfterTimeout) spawned + , pidExists: liftEffect $ CP.pidExists spawned + , killed: liftEffect $ CP.killed spawned + , pid: liftEffect $ CP.pid spawned + , unref: liftEffect $ CP.unref spawned + , ref: liftEffect $ CP.ref spawned + -- , send: \foreignData handle mkOptions cb -> liftEffect do + -- unsafeSendOptsCb foreignData (toNullable handle) mkOptions cb $ CP.toUnsafeChildProcess spawned + , signalCode: liftEffect $ CP.signalCode spawned + , spawnArgs: CP.spawnArgs spawned + , spawnFile: CP.spawnFile spawned , childProcess: ExecaChildProcess spawned , stdin: - { stream: stdin spawned + { stream: CP.stdin spawned , writeUtf8: \string -> liftEffect do buf <- Buffer.fromString string UTF8 - void $ Stream.write (stdin spawned) buf + void $ Stream.write (CP.stdin spawned) buf , writeUtf8End: \string -> liftEffect do buf <- Buffer.fromString string UTF8 - void $ Stream.write (stdin spawned) buf - void $ Stream.end (stdin spawned) + void $ Stream.write (CP.stdin spawned) buf + void $ Stream.end (CP.stdin spawned) , end: liftEffect do - void $ Stream.end (stdin spawned) + void $ Stream.end (CP.stdin spawned) , shareParentProcessStdin: liftEffect do - void $ Stream.pipe Process.stdin (stdin spawned) + void $ Stream.pipe Process.stdin (CP.stdin spawned) } , stdout: - { stream: stdout spawned + { stream: CP.stdout spawned , output: joinFiber stdoutFiber , pipeToParentStdout: liftEffect do - void $ Stream.pipe (stdout spawned) Process.stdout + void $ Stream.pipe (CP.stdout spawned) Process.stdout } , stderr: - { stream: stderr spawned + { stream: CP.stderr spawned , output: joinFiber stderrFiber , pipeToParentStderr: liftEffect do - void $ Stream.pipe (stderr spawned) Process.stderr + void $ Stream.pipe (CP.stderr spawned) Process.stderr } - , stdio: liftEffect $ stdio spawned , cancel , result: joinFiber run } @@ -575,13 +554,13 @@ type ExecaSyncOptions = , cwd :: Maybe String , env :: Maybe (Object String) , argv0 :: Maybe String - , input :: Maybe ImmutableBuffer - , stdioExtra :: Maybe (Array Foreign) + , input :: Maybe Buffer + , stdioExtra :: Maybe (Array StdIO) , detached :: Maybe Boolean - , uid :: Maybe Int - , gid :: Maybe Int + , uid :: Maybe Uid + , gid :: Maybe Gid , shell :: Maybe String - , timeout :: Maybe { milliseconds :: Number, killSignal :: Either Int String } + , timeout :: Maybe { milliseconds :: Milliseconds, killSignal :: KillSignal } , maxBuffer :: Maybe Number , encoding :: Maybe Encoding , windowsVerbatimArguments :: Maybe Boolean @@ -629,34 +608,40 @@ execaSync file args buildOptions = do let command = joinCommand file args escapedCommand = getEscapedCommand file args - result <- spawnSync parsed.file parsed.args - { cwd: Just parsed.options.cwd - , input: options.input - , argv0: parsed.options.argv0 - , stdioExtra: Just parsed.options.stdioExtra - , env: Just parsed.options.env - , uid: parsed.options.uid - , gid: parsed.options.gid - , timeout: parsed.options.timeout - , killSignal: parsed.options.killSignal - , maxBuffer: Just parsed.options.maxBuffer - , shell: parsed.options.shell - , windowsVerbatimArguments: Just parsed.options.windowsVerbatimArguments - , windowsHide: Just parsed.options.windowsHide - } + result <- CP.spawnSync' parsed.file parsed.args + ( _ + { cwd = Just parsed.options.cwd + , input = options.input + , argv0 = parsed.options.argv0 + , appendStdio = Just parsed.options.stdioExtra + , env = Just parsed.options.env + , uid = parsed.options.uid + , gid = parsed.options.gid + , timeout = parsed.options.timeout + , killSignal = parsed.options.killSignal + , maxBuffer = Just parsed.options.maxBuffer + , shell = map customShell parsed.options.shell + , windowsVerbatimArguments = Just parsed.options.windowsVerbatimArguments + , windowsHide = Just parsed.options.windowsHide + } + ) let stripOption = fromMaybe true options.stripFinalNewline encoding = fromMaybe defaultOptions.encoding options.encoding - bufferToString = ImmutableBuffer.toString encoding - stdout' <- bufferToString <$> handleOutput { stripFinalNewline: stripOption } result.stdout - stderr' <- bufferToString <$> handleOutput { stripFinalNewline: stripOption } result.stderr + bufferToString = Buffer.toString encoding + stdout' <- bufferToString =<< handleOutput { stripFinalNewline: stripOption } result.stdout + stderr' <- bufferToString =<< handleOutput { stripFinalNewline: stripOption } result.stderr let - resultError = toMaybe result.error - resultSignal = map fromKillSignal $ toMaybe result.signal - hasNonZeroExit = case toMaybe result.status of - Just n | n /= 0 -> true + resultSignal = case result.exitStatus of + BySignal s -> Just s + _ -> Nothing + resultExitCode = case result.exitStatus of + Normally n -> Just n + _ -> Nothing + hasNonZeroExit = case result.exitStatus of + Normally n | n /= 0 -> true _ -> false - if isJust resultError || hasNonZeroExit || isJust resultSignal then + if isJust result.error || hasNonZeroExit || isJust resultSignal then pure $ Left $ mkError { command , escapedCommand @@ -665,11 +650,11 @@ execaSync file args buildOptions = do , stdinErr: Nothing , stdoutErr: Nothing , stderrErr: Nothing - , error: resultError + , error: result.error , signal: resultSignal - , exitCode: toMaybe result.status + , exitCode: resultExitCode , execaOptions: parsed.options - , timedOut: Just "ETIMEDOUT" == (map _.code resultError) + , timedOut: Just "ETIMEDOUT" == (map SystemError.code result.error) , isCanceled: false , killed: isJust resultSignal } @@ -690,10 +675,9 @@ type ExecaSyncResult = , stderr :: String } -handleOutput :: forall r. { stripFinalNewline :: Boolean | r } -> ImmutableBuffer -> Effect ImmutableBuffer +handleOutput :: forall r. { stripFinalNewline :: Boolean | r } -> Buffer -> Effect Buffer handleOutput options value - | options.stripFinalNewline = - unsafeThaw value >>= stripFinalNewlineBuf + | options.stripFinalNewline = stripFinalNewlineBuf value | otherwise = pure value joinCommand :: String -> Array String -> String @@ -709,22 +693,22 @@ getEscapedCommand file args = do data SpawnResult = ExitCode Int - | Killed (Either Int String) - | SpawnError ChildProcessError + | Killed KillSignal + | SpawnError SystemError | StdinError Error - | TimedOut (Either Int String) + | TimedOut KillSignal _ExitCode :: Prism' SpawnResult Int _ExitCode = prism ExitCode case _ of ExitCode i -> Right i other -> Left other -_Killed :: Prism' SpawnResult (Either Int String) +_Killed :: Prism' SpawnResult KillSignal _Killed = prism Killed case _ of Killed sig -> Right sig other -> Left other -_SpawnError :: Prism' SpawnResult ChildProcessError +_SpawnError :: Prism' SpawnResult SystemError _SpawnError = prism SpawnError case _ of SpawnError a -> Right a other -> Left other @@ -734,7 +718,7 @@ _StdinError = prism StdinError case _ of StdinError a -> Right a other -> Left other -_TimedOut :: Prism' SpawnResult (Either Int String) +_TimedOut :: Prism' SpawnResult KillSignal _TimedOut = prism TimedOut case _ of TimedOut a -> Right a other -> Left other @@ -758,7 +742,7 @@ spawnedKill = mkEffectFn3 \killFn numOrStringSignal forceKillAfterTimeout -> do signal = case toMaybe numOrStringSignal of Nothing -> Right "SIGTERM" Just numOrStr -> fromKillSignal numOrStr - killSignalSucceeded <- runEffectFn1 killFn $ either intKillSignal stringKillSignal signal + killSignalSucceeded <- runEffectFn1 killFn $ either intSignal stringSignal signal let mbTimeout = do guard $ isSigTerm signal @@ -766,7 +750,7 @@ spawnedKill = mkEffectFn3 \killFn numOrStringSignal forceKillAfterTimeout -> do toMaybe forceKillAfterTimeout for_ mbTimeout \(Milliseconds timeout) -> do t <- runEffectFn2 setTimeoutImpl (floor timeout) do - void $ runEffectFn1 killFn $ stringKillSignal "SIGKILL" + void $ runEffectFn1 killFn $ stringSignal "SIGKILL" t.unref pure killSignalSucceeded where @@ -791,7 +775,7 @@ foreign import setTimeoutImpl :: EffectFn2 Int (Effect Unit) { unref :: Effect U type ExecaRunOptions = -- execa options { cleanup :: Boolean - , stdioExtra :: Array Foreign + , stdioExtra :: Array StdIO , stripFinalNewline :: Boolean , encoding :: Encoding -- child process spawn options: @@ -799,12 +783,12 @@ type ExecaRunOptions = , env :: Object String -- , argv0 :: Maybe String , detached :: Boolean - , uid :: Maybe Int - , gid :: Maybe Int + , uid :: Maybe Uid + , gid :: Maybe Gid , shell :: Maybe String - , timeout :: Maybe Number - , killSignal :: Maybe (Either Int String) - , timeoutWithKillSignal :: Maybe { milliseconds :: Number, killSignal :: Either Int String } + , timeout :: Maybe Milliseconds + , killSignal :: Maybe KillSignal + , timeoutWithKillSignal :: Maybe { milliseconds :: Milliseconds, killSignal :: KillSignal } , maxBuffer :: Number , windowsVerbatimArguments :: Boolean , windowsHide :: Boolean @@ -816,7 +800,7 @@ type ExecaError = , shortMessage :: String , escapedCommand :: String , exitCode :: Maybe Int - , signal :: Maybe (Either Int String) + , signal :: Maybe KillSignal , signalDescription :: Maybe String , stdout :: String , stderr :: String @@ -829,11 +813,11 @@ type ExecaError = mkError :: { stdout :: String , stderr :: String - , error :: Maybe ChildProcessError + , error :: Maybe SystemError , stdinErr :: Maybe Exception.Error , stdoutErr :: Maybe Exception.Error , stderrErr :: Maybe Exception.Error - , signal :: Maybe (Either Int String) + , signal :: Maybe KillSignal , exitCode :: Maybe Int , command :: String , escapedCommand :: String @@ -844,7 +828,7 @@ mkError } -> ExecaError mkError r = - { originalMessage: (r.error >>= _.message >>> toMaybe) <|> (map Exception.message $ r.stdinErr <|> r.stdoutErr <|> r.stderrErr) + { originalMessage: (r.error <#> SystemError.message) <|> (map Exception.message $ r.stdinErr <|> r.stdoutErr <|> r.stderrErr) , message , shortMessage , escapedCommand: r.escapedCommand @@ -859,10 +843,10 @@ mkError r = , killed: r.killed && not r.timedOut } where - signalDescription = r.signal >>= case _ of + signalDescription = r.signal >>= fromKillSignal >>> case _ of Left i -> map _.description $ Map.lookup i signals.byNumber Right s -> map _.description $ Object.lookup s signals.byString - errorCode = map _.code r.error + errorCode = map SystemError.code r.error prefix | r.timedOut , Just timeout <- r.execaOptions.timeout = @@ -873,7 +857,7 @@ mkError r = "failed with " <> code | Just signal' <- r.signal , Just description <- signalDescription = - "was killed with " <> show signal' <> " (" <> description <> ")" + "was killed with " <> (either show show $ fromKillSignal signal') <> " (" <> description <> ")" | Just exit <- r.exitCode = "failed with exit code " <> show exit | Just err <- r.stdinErr = @@ -885,7 +869,7 @@ mkError r = | otherwise = "failed" execaMessage = "Command " <> prefix <> ": " <> r.command - shortMessage = execaMessage <> (maybe "" (append "\n") $ (toMaybe <<< _.message) =<< r.error) + shortMessage = execaMessage <> (maybe "" (append "\n") $ map SystemError.message r.error) message = Array.intercalate "\n" [ shortMessage , r.stderr @@ -928,55 +912,9 @@ execaCommandSync s buildOptions = do Nothing -> liftEffect $ throw $ "Command " <> show s <> " could not be parsed into `{ file :: String, args :: Array String }` value." -{- - -### Child Process Module Start ### - --} - --- | A handle for inter-process communication (IPC). -foreign import data Handle :: Type - --- | Opaque type returned by `spawn`. --- | Needed as input for most methods in this module. -foreign import data ChildProcess :: Type - -channel - :: ChildProcess - -> Effect (Maybe { ref :: Effect Unit, unref :: Effect Unit }) -channel cp = toMaybe <$> runEffectFn1 channelImpl cp - -foreign import channelImpl :: EffectFn1 (ChildProcess) (Nullable { ref :: Effect Unit, unref :: Effect Unit }) - --- | Indicates whether it is still possible to send and receive --- | messages from the child process. -connected - :: ChildProcess - -> Effect Boolean -connected cp = runEffectFn1 connectedImpl cp - -foreign import connectedImpl :: EffectFn1 (ChildProcess) Boolean - --- | Closes the IPC channel between parent and child. -disconnect :: ChildProcess -> Effect Unit -disconnect cp = runEffectFn1 disconnectImpl cp - -foreign import disconnectImpl :: EffectFn1 (ChildProcess) Unit - -exitCode :: ChildProcess -> Effect (Maybe Int) -exitCode cp = toMaybe <$> runEffectFn1 exitCodeImpl cp - -foreign import exitCodeImpl :: EffectFn1 (ChildProcess) (Nullable Int) - -- | Same as `kill' SIGTERM` -kill :: ChildProcess -> Effect Boolean -kill = kill' (stringKillSignal "SIGTERM") - -kill' :: KillSignal -> ChildProcess -> Effect Boolean -kill' sig cp = kill'' sig Nothing cp - -kill'' :: KillSignal -> Maybe Milliseconds -> ChildProcess -> Effect Boolean -kill'' sig forceKillAfterTimeout cp = runEffectFn3 killImpl cp sig (toNullable forceKillAfterTimeout) +execaKill :: KillSignal -> Maybe Milliseconds -> ChildProcess -> Effect Boolean +execaKill sig forceKillAfterTimeout cp = runEffectFn3 killImpl cp sig (toNullable forceKillAfterTimeout) -- | Send a signal to a child process. In the same way as the -- | [unix kill(2) system call](https://linux.die.net/man/2/kill), @@ -991,271 +929,3 @@ kill'' sig forceKillAfterTimeout cp = runEffectFn3 killImpl cp sig (toNullable f -- | the kill signal was successful, `childProcess.kill "SIGKILL"` -- | will be called once the timeout is reached. foreign import killImpl :: EffectFn3 (ChildProcess) KillSignal (Nullable Milliseconds) Boolean - -pidExists :: ChildProcess -> Effect Boolean -pidExists cp = runEffectFn1 pidExistsImpl cp - -foreign import pidExistsImpl :: EffectFn1 (ChildProcess) Boolean - -killed :: ChildProcess -> Effect Boolean -killed cp = runEffectFn1 killedImpl cp - -foreign import killedImpl :: EffectFn1 (ChildProcess) Boolean - --- | The process ID of a child process. Note that if the process has already --- | exited, another process may have taken the same ID, so be careful! -pid :: ChildProcess -> Effect (Maybe Pid) -pid cp = toMaybe <$> runEffectFn1 pidImpl cp - -foreign import pidImpl :: EffectFn1 (ChildProcess) (Nullable Pid) - -ref :: ChildProcess -> Effect Unit -ref cp = runEffectFn1 refImpl cp - -foreign import refImpl :: EffectFn1 (ChildProcess) Unit - -unref :: ChildProcess -> Effect Unit -unref cp = runEffectFn1 unrefImpl cp - -foreign import unrefImpl :: EffectFn1 (ChildProcess) Unit - -type SendOptions = - { keepOpen :: Maybe Boolean - } - -type JsSendOptions = - { keepOpen :: Boolean - } - --- | Send messages to the (`nodejs`) child process. --- | --- | See the [node documentation](https://nodejs.org/api/child_process.html#child_process_subprocess_send_message_sendhandle_options_callback) --- | for in-depth documentation. -send - :: ChildProcess - -> Foreign - -> Handle - -> (SendOptions -> SendOptions) - -> Effect Unit - -> Effect Boolean -send cp msg handle buildOptions cb = runEffectFn5 sendImpl cp msg handle jsOptions cb - where - options = buildOptions { keepOpen: Nothing } - jsOptions = { keepOpen: fromMaybe undefined options.keepOpen } - -foreign import sendImpl :: EffectFn5 (ChildProcess) (Foreign) (Handle) (JsSendOptions) (Effect Unit) (Boolean) - -signalCode - :: ChildProcess - -> Effect (Maybe String) -signalCode cp = map toMaybe $ runEffectFn1 signalCodeImpl cp - -foreign import signalCodeImpl :: EffectFn1 (ChildProcess) (Nullable String) - -foreign import spawnArgs :: ChildProcess -> Array String - -foreign import spawnFile :: ChildProcess -> String - --- | The standard input stream of a child process. -foreign import stdin :: ChildProcess -> Writable () - -stdio :: ChildProcess -> Effect (Array Foreign) -stdio cp = runEffectFn1 stdioImpl cp - -foreign import stdioImpl :: EffectFn1 (ChildProcess) (Array Foreign) - --- | The standard output stream of a child process. -foreign import stdout :: ChildProcess -> Readable () - --- | The standard error stream of a child process. -foreign import stderr :: ChildProcess -> Readable () - --- | either Int or String -foreign import data KillSignal :: Type - -toKillSignal :: Either Int String -> KillSignal -toKillSignal = either intKillSignal stringKillSignal - -intKillSignal :: Int -> KillSignal -intKillSignal = unsafeCoerce - -stringKillSignal :: String -> KillSignal -stringKillSignal = unsafeCoerce - -fromKillSignal :: KillSignal -> Either Int String -fromKillSignal ks = do - let - ksFor :: Foreign - ksFor = unsafeCoerce ks - renderError errs = unsafeCrashWith - $ append "Unexpected kill signal. Value should be String or Int but got these errors: " - $ Foldable.intercalate "; " - $ map renderForeignError errs - - either renderError identity $ runExcept $ (Left <$> readInt ksFor) <|> (Right <$> readString ksFor) - -data SerializationOption - = SerializeJson - | SerializeAdvanced - -derive instance Eq SerializationOption -derive instance Generic SerializationOption _ -instance Show SerializationOption where - show x = genericShow x - -toJsSerialization :: SerializationOption -> String -toJsSerialization = case _ of - SerializeJson -> "json" - SerializeAdvanced -> "advanced" - --- Note: `signal` option intentionally not supported. -type SpawnOptions = - { cwd :: Maybe String - , env :: Maybe (Object String) - , argv0 :: Maybe String - , stdioExtra :: Maybe (Array Foreign) - , detached :: Maybe Boolean - , uid :: Maybe Int - , gid :: Maybe Int - , serialization :: Maybe SerializationOption - , shell :: Maybe String - , windowsVerbatimArguments :: Maybe Boolean - , windowsHide :: Maybe Boolean - } - --- Note: due to overwriting the `kill` function, so that --- it takes another argument, passing `timeout` and `killSignal` --- to the child process is problematic as it will call `kill` --- without that additional argument and fail. --- I found that evern changing the type to `Nullable` --- and keeping these args did not fix the issue. --- Since we already implement a timeout in `execa`, --- the args seems unnecessary for at least `spawn`. -type JsSpawnOptions = - { cwd :: String - , env :: Object String - , argv0 :: String - , stdio :: Array Foreign - , detached :: Boolean - , uid :: Int - , gid :: Int - , serialization :: String - , shell :: String - , windowsVerbatimArguments :: Boolean - , windowsHide :: Boolean - } - -spawn - :: String - -> Array String - -> SpawnOptions - -> Effect ChildProcess -spawn cmd args options = do - runEffectFn3 spawnImpl cmd args - { cwd: fromMaybe undefined options.cwd - , env: fromMaybe undefined options.env - , argv0: fromMaybe undefined options.argv0 - , detached: fromMaybe undefined options.detached - , uid: fromMaybe undefined options.uid - , gid: fromMaybe undefined options.gid - , serialization: maybe undefined toJsSerialization options.serialization - , stdio: [ pipe, pipe, pipe, ipc ] <> fromMaybe [] options.stdioExtra - , shell: fromMaybe undefined options.shell - , windowsHide: fromMaybe undefined options.windowsHide - , windowsVerbatimArguments: fromMaybe undefined options.windowsVerbatimArguments - } - where - pipe = unsafeToForeign "pipe" - ipc = unsafeToForeign "ipc" - -foreign import spawnImpl - :: EffectFn3 - String - (Array String) - (JsSpawnOptions) - (ChildProcess) - -type SpawnSyncOptions = - { cwd :: Maybe String - , input :: Maybe ImmutableBuffer - , argv0 :: Maybe String - , stdioExtra :: Maybe (Array Foreign) - , env :: Maybe (Object String) - , uid :: Maybe Int - , gid :: Maybe Int - , timeout :: Maybe Number - , killSignal :: Maybe (Either Int String) - , maxBuffer :: Maybe Number - , shell :: Maybe String - , windowsVerbatimArguments :: Maybe Boolean - , windowsHide :: Maybe Boolean - } - -type JsSpawnSyncOptions = - { cwd :: String - , argv0 :: String - , input :: ImmutableBuffer - , stdio :: Array Foreign - , env :: Object String - , uid :: Int - , gid :: Int - , timeout :: Number - , killSignal :: KillSignal - , maxBuffer :: Number - , encoding :: String - , shell :: String - , windowsVerbatimArguments :: Boolean - , windowsHide :: Boolean - } - -type JsSpawnSyncResult = - { pid :: Pid - , output :: Array Foreign - , stdout :: ImmutableBuffer - , stderr :: ImmutableBuffer - , status :: Nullable Int - , signal :: Nullable KillSignal - , error :: Nullable ChildProcessError - } - -spawnSync :: String -> Array String -> SpawnSyncOptions -> Effect JsSpawnSyncResult -spawnSync file args options = do - runEffectFn3 spawnSyncImpl file args jsOptions - where - pipe = unsafeToForeign "pipe" - ignore = unsafeToForeign "ignore" - jsOptions = - { cwd: fromMaybe undefined options.cwd - , argv0: fromMaybe undefined options.argv0 - , input: fromMaybe undefined options.input - , stdio: [ pipe, pipe, pipe, ignore ] <> fromMaybe [] options.stdioExtra - , env: fromMaybe undefined options.env - , uid: fromMaybe undefined options.uid - , gid: fromMaybe undefined options.gid - , timeout: fromMaybe undefined options.timeout - , killSignal: fromMaybe undefined $ map (either intKillSignal stringKillSignal) options.killSignal - , maxBuffer: fromMaybe undefined options.maxBuffer - , encoding: "buffer" -- force stdout/stderr in callback to be Buffers - , shell: fromMaybe undefined options.shell - , windowsHide: fromMaybe undefined options.windowsHide - , windowsVerbatimArguments: fromMaybe undefined options.windowsVerbatimArguments - } - -foreign import spawnSyncImpl - :: EffectFn3 - String - (Array String) - (JsSpawnSyncOptions) - JsSpawnSyncResult - -foreign import undefined :: forall a. a - --- | An error which occurred inside a child process. -type ChildProcessError = - { code :: String - , errno :: String - , syscall :: String - , message :: Nullable String - } - -foreign import bufferToReadStream :: ImmutableBuffer -> Readable () diff --git a/src/Node/Library/Execa/GetStream.purs b/src/Node/Library/Execa/GetStream.purs index 228c17b..7016bb2 100644 --- a/src/Node/Library/Execa/GetStream.purs +++ b/src/Node/Library/Execa/GetStream.purs @@ -19,7 +19,6 @@ import Effect.Ref as Ref import Effect.Uncurried (EffectFn1, EffectFn3, mkEffectFn1, runEffectFn3) import Node.Buffer (Buffer) import Node.Buffer as Buffer -import Node.Buffer.Immutable (ImmutableBuffer) import Node.EventEmitter (on, on_) import Node.Stream (Duplex, Readable, Writable, dataH, newPassThrough) import Unsafe.Coerce (unsafeCoerce) @@ -34,7 +33,7 @@ getStreamBuffer :: forall r . Readable r -> { maxBuffer :: Maybe Number } - -> Aff { buffer :: ImmutableBuffer, inputError :: Maybe Error } + -> Aff { buffer :: Buffer, inputError :: Maybe Error } getStreamBuffer inputStream initialOptions = do let options = { maxBuffer: fromMaybe infinity initialOptions.maxBuffer } interface <- liftEffect bufferStream @@ -45,15 +44,13 @@ getStreamBuffer inputStream initialOptions = do makeAff \cb -> do runEffectFn3 pipeline inputStream interface.stream $ mkEffectFn1 \err -> do bufferedData <- interface.getBufferedValue - buff <- Buffer.unsafeFreeze bufferedData - cb $ Right { buffer: buff, inputError: toMaybe err } + cb $ Right { buffer: bufferedData, inputError: toMaybe err } rmListener <- interface.stream # on dataH \_ -> do bufferedLen <- interface.getBufferedLength when (bufferedLen > options.maxBuffer) do bufferedData <- interface.getBufferedValue - buff <- Buffer.unsafeFreeze bufferedData cb $ Right - { buffer: buff + { buffer: bufferedData , inputError: Just $ error $ maybe ("Max buffer exceeded") (\size -> "Max buffer size exceeded. Buffer size was: " <> show size) diff --git a/src/Node/Library/Execa/StripFinalNewline.purs b/src/Node/Library/Execa/StripFinalNewline.purs index 6a0963f..d1811dc 100644 --- a/src/Node/Library/Execa/StripFinalNewline.purs +++ b/src/Node/Library/Execa/StripFinalNewline.purs @@ -10,8 +10,7 @@ import Data.Char (toCharCode) import Data.Int as Int import Data.String as String import Effect (Effect) -import Node.Buffer (Buffer, BufferValueType(..), slice, unsafeFreeze, read, size) -import Node.Buffer.Immutable (ImmutableBuffer) +import Node.Buffer (Buffer, BufferValueType(..), read, size, slice) stripFinalNewline :: String -> String stripFinalNewline s = do @@ -26,7 +25,7 @@ stripFinalNewline s = do "\r" -> sDrop1 _ -> s -stripFinalNewlineBuf :: Buffer -> Effect ImmutableBuffer +stripFinalNewlineBuf :: Buffer -> Effect Buffer stripFinalNewlineBuf b = do len <- size b -- PureScript implementation note: @@ -43,22 +42,22 @@ stripFinalNewlineBuf b = do -- Source (v14): https://nodejs.org/docs/latest-v14.x/api/buffer.html#buffer_new_buffer_blob_sources_options case len of 0 -> - unsafeFreeze b + pure b 1 -> do lastChar <- read UInt8 (len - 1) b if lastChar == charN || lastChar == charR then do - unsafeFreeze $ slice 0 (len - 1) b + pure $ slice 0 (len - 1) b else do - unsafeFreeze b + pure b _ -> do lastChar <- read UInt8 (len - 1) b sndLastChar <- read UInt8 (len - 2) b if lastChar == charN && sndLastChar == charR then do - unsafeFreeze $ slice 0 (len - 2) b + pure $ slice 0 (len - 2) b else if lastChar == charN || lastChar == charR then do - unsafeFreeze $ slice 0 (len - 1) b + pure $ slice 0 (len - 1) b else do - unsafeFreeze b + pure b where charN = Int.toNumber $ toCharCode '\n' charR = Int.toNumber $ toCharCode '\r' From c3d33c65bd488f4831ef0271265751ebec8b560c Mon Sep 17 00:00:00 2001 From: Jordan Martinez Date: Tue, 25 Jul 2023 16:57:35 -0700 Subject: [PATCH 4/9] Update deps one more time --- packages.dhall | 2 +- spago.dhall | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/packages.dhall b/packages.dhall index fd82cc2..ed9a7e0 100644 --- a/packages.dhall +++ b/packages.dhall @@ -66,7 +66,7 @@ in upstream , "prelude" , "unsafe-coerce" ] - with node-child-process.version = "5af300b576fc7cc42a63d4d41b6c7afc97d0e9d3" + with node-child-process.version = "e9072711bc8f0a96978aaf1fb392220d57d1c034" with node-child-process.dependencies = [ "exceptions" , "node-event-emitter" diff --git a/spago.dhall b/spago.dhall index d58f31d..1dde145 100644 --- a/spago.dhall +++ b/spago.dhall @@ -7,7 +7,6 @@ , "either" , "exceptions" , "foldable-traversable" - , "foreign" , "foreign-object" , "functions" , "integers" @@ -18,6 +17,7 @@ , "node-event-emitter" , "node-fs" , "node-human-signals" + , "node-os" , "node-path" , "node-process" , "node-streams" @@ -35,7 +35,6 @@ , "safe-coerce" , "strings" , "tailrec" - , "transformers" , "tuples" , "unsafe-coerce" , "unsafe-reference" From b382ad07a80bfb99ca783f481197290ad103a1ef Mon Sep 17 00:00:00 2001 From: Jordan Martinez Date: Tue, 25 Jul 2023 17:22:24 -0700 Subject: [PATCH 5/9] Get tests to compile (currently fail) --- packages.dhall | 2 +- src/Node/Library/Execa/Utils.purs | 9 --------- test.dhall | 4 +++- test/Test/Node/Library/Execa.purs | 23 ++++++++++++++--------- 4 files changed, 18 insertions(+), 20 deletions(-) diff --git a/packages.dhall b/packages.dhall index ed9a7e0..9649f9d 100644 --- a/packages.dhall +++ b/packages.dhall @@ -66,7 +66,7 @@ in upstream , "prelude" , "unsafe-coerce" ] - with node-child-process.version = "e9072711bc8f0a96978aaf1fb392220d57d1c034" + with node-child-process.version = "4d27b66969ee11717104a1619c264b077e597d9e" with node-child-process.dependencies = [ "exceptions" , "node-event-emitter" diff --git a/src/Node/Library/Execa/Utils.purs b/src/Node/Library/Execa/Utils.purs index 30f9402..0de05c2 100644 --- a/src/Node/Library/Execa/Utils.purs +++ b/src/Node/Library/Execa/Utils.purs @@ -18,9 +18,6 @@ import Effect (Effect) import Effect.Exception (Error) import Foreign.Object (Object) import Foreign.Object as Object -import Node.Buffer.Immutable (ImmutableBuffer) -import Node.Buffer.Immutable as ImmutableBuffer -import Node.Encoding (Encoding(..)) import Node.Platform (Platform(..)) import Node.Process as Process import Prim.Row as Row @@ -28,12 +25,6 @@ import Record as Record import Type.Proxy (Proxy) import Unsafe.Coerce (unsafeCoerce) -utf8 :: { toString :: ImmutableBuffer -> String, toBuffer :: String -> ImmutableBuffer } -utf8 = { toString, toBuffer } - where - toString = ImmutableBuffer.toString UTF8 - toBuffer = flip ImmutableBuffer.fromString UTF8 - newtype CustomError :: forall k. k -> Type newtype CustomError rows = CustomError Error diff --git a/test.dhall b/test.dhall index ed57cc8..68df06e 100644 --- a/test.dhall +++ b/test.dhall @@ -2,4 +2,6 @@ let config = ./spago.dhall in config with sources = config.sources # [ "test/**/*.purs" ] with dependencies = config.dependencies # - [ "spec" ] + [ "spec" + , "datetime" + ] diff --git a/test/Test/Node/Library/Execa.purs b/test/Test/Node/Library/Execa.purs index 134e45c..9ffef0b 100644 --- a/test/Test/Node/Library/Execa.purs +++ b/test/Test/Node/Library/Execa.purs @@ -4,9 +4,12 @@ import Prelude import Data.Either (Either(..)) import Data.Maybe (Maybe(..)) +import Data.Time.Duration (Milliseconds(..)) import Effect.Class (liftEffect) +import Node.Buffer as Buffer +import Node.ChildProcess.Types (fromKillSignal, intSignal, stringSignal) +import Node.Encoding (Encoding(..)) import Node.Library.Execa (execa, execaCommand, execaCommandSync, execaSync) -import Node.Library.Execa.Utils (utf8) import Node.Library.HumanSignals (signals) import Node.Path as Path import Test.Node.Library.Utils (isWindows, itNix) @@ -51,30 +54,30 @@ spec = describe "execa" do Left e -> e.isCanceled `shouldEqual` true it "basic kill (string) produces error" do spawned <- execa shellCmd [ sleepFile, "1" ] identity - _ <- spawned.killWithSignal (Right "SIGTERM") + _ <- spawned.killWithSignal (stringSignal "SIGTERM") result <- spawned.result case result of Right _ -> fail "Cancelling should work" - Left e -> e.signal `shouldEqual` (Just $ Right "SIGTERM") + Left e -> e.signal `shouldEqual` (Just $ stringSignal "SIGTERM") it "basic kill (int) produces error" do spawned <- execa shellCmd [ sleepFile, "1" ] identity - _ <- spawned.killWithSignal (Left signals.byName."SIGTERM".number) + _ <- spawned.killWithSignal (intSignal signals.byName."SIGTERM".number) result <- spawned.result case result of Right _ -> fail "Cancelling should work" - Left e -> case e.signal of + Left e -> case map fromKillSignal e.signal of Just (Left i) -> i `shouldEqual` signals.byName."SIGTERM".number Just (Right s) -> s `shouldEqual` signals.byName."SIGTERM".name _ -> fail "Did not get a kill signal" describe "timeout produces error" do it "basic timeout produces error" do spawned <- execa shellCmd [ sleepFile, "10" ] - (_ { timeout = Just { milliseconds: 400.0, killSignal: Right "SIGTERM" } }) + (_ { timeout = Just { milliseconds: Milliseconds 400.0, killSignal: stringSignal "SIGTERM" } }) result <- spawned.result case result of Right _ -> fail "Timeout should work" Left e -> do - e.signal `shouldEqual` (Just $ Right "SIGTERM") + e.signal `shouldEqual` (Just $ stringSignal "SIGTERM") e.timedOut `shouldEqual` true describe "execaSync" do describe "`cat` tests" do @@ -84,7 +87,8 @@ spec = describe "execa" do Right r -> r.stdout `shouldContain` "let config =" Left e -> fail e.message itNix "input is buffer" do - result <- liftEffect $ execaSync "cat" [ "-" ] (_ { input = Just $ utf8.toBuffer "test" }) + input <- liftEffect $ Buffer.fromString "test" UTF8 + result <- liftEffect $ execaSync "cat" [ "-" ] (_ { input = Just input }) case result of Right r -> r.stdout `shouldEqual` "test" Left e -> fail e.message @@ -111,7 +115,8 @@ spec = describe "execa" do Right r -> r.stdout `shouldContain` "let config =" Left e -> fail e.message itNix "input is buffer" do - result <- liftEffect $ execaCommandSync "cat" (_ { input = Just $ utf8.toBuffer "test" }) + input <- liftEffect $ Buffer.fromString "test" UTF8 + result <- liftEffect $ execaCommandSync "cat" (_ { input = Just input }) case result of Right r -> r.stdout `shouldEqual` "test" Left e -> fail e.message From 7cdab8cef5da575d3d0f0fb844c2eb0f6d87ba7e Mon Sep 17 00:00:00 2001 From: Jordan Martinez Date: Tue, 25 Jul 2023 17:46:25 -0700 Subject: [PATCH 6/9] Bump process to v11.1.1 --- packages.dhall | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages.dhall b/packages.dhall index 9649f9d..2cda937 100644 --- a/packages.dhall +++ b/packages.dhall @@ -54,7 +54,7 @@ in upstream , "strings" , "unsafe-coerce" ] - with node-process.version = "v11.1.0" + with node-process.version = "v11.1.1" with node-process.dependencies = [ "effect" , "foreign-object" From 82cb9fcafcf791f5fb1bad97d48bd7500b672db9 Mon Sep 17 00:00:00 2001 From: Jordan Martinez Date: Wed, 26 Jul 2023 07:41:37 -0700 Subject: [PATCH 7/9] Drop FFI for pipeline --- packages.dhall | 2 +- src/Node/Library/Execa/GetStream.js | 5 ----- src/Node/Library/Execa/GetStream.purs | 17 ++++++++--------- 3 files changed, 9 insertions(+), 15 deletions(-) delete mode 100644 src/Node/Library/Execa/GetStream.js diff --git a/packages.dhall b/packages.dhall index 2cda937..26faf38 100644 --- a/packages.dhall +++ b/packages.dhall @@ -22,7 +22,7 @@ in upstream , "unsafe-coerce" , "nullable" ] - with node-streams.version = "v8.0.0" + with node-streams.version = "3909b1682ff7fe877b7c548dcf59bc301bacd06e" with node-streams.dependencies = [ "aff" , "effect" diff --git a/src/Node/Library/Execa/GetStream.js b/src/Node/Library/Execa/GetStream.js deleted file mode 100644 index f154c6e..0000000 --- a/src/Node/Library/Execa/GetStream.js +++ /dev/null @@ -1,5 +0,0 @@ -import stream from "node:stream"; - -export function pipeline(source, destination, cb) { - return stream.pipeline([source, destination], cb); -} diff --git a/src/Node/Library/Execa/GetStream.purs b/src/Node/Library/Execa/GetStream.purs index 7016bb2..06725cf 100644 --- a/src/Node/Library/Execa/GetStream.purs +++ b/src/Node/Library/Execa/GetStream.purs @@ -10,17 +10,16 @@ import Data.Array as Array import Data.Either (Either(..)) import Data.Int (toNumber) import Data.Maybe (Maybe(..), fromMaybe, maybe) -import Data.Nullable (Nullable, toMaybe) import Data.Number (infinity) import Effect (Effect) import Effect.Aff (Aff, Error, effectCanceler, error, makeAff) import Effect.Class (liftEffect) import Effect.Ref as Ref -import Effect.Uncurried (EffectFn1, EffectFn3, mkEffectFn1, runEffectFn3) import Node.Buffer (Buffer) import Node.Buffer as Buffer -import Node.EventEmitter (on, on_) -import Node.Stream (Duplex, Readable, Writable, dataH, newPassThrough) +import Node.EventEmitter (on) +import Node.Stream (Duplex, Readable, dataH, newPassThrough) +import Node.Stream as Stream import Unsafe.Coerce (unsafeCoerce) type Interface = @@ -42,9 +41,9 @@ getStreamBuffer inputStream initialOptions = do -- fails or not. It also destroys the input stream if an error occurs -- but we'll handle that outside of this function. makeAff \cb -> do - runEffectFn3 pipeline inputStream interface.stream $ mkEffectFn1 \err -> do + Stream.pipeline inputStream [] interface.stream \err -> do bufferedData <- interface.getBufferedValue - cb $ Right { buffer: bufferedData, inputError: toMaybe err } + cb $ Right { buffer: bufferedData, inputError: err } rmListener <- interface.stream # on dataH \_ -> do bufferedLen <- interface.getBufferedLength when (bufferedLen > options.maxBuffer) do @@ -57,6 +56,7 @@ getStreamBuffer inputStream initialOptions = do initialOptions.maxBuffer } pure $ effectCanceler do + interface.unsubcribe rmListener where -- PureScript implementation note: @@ -66,7 +66,7 @@ getStreamBuffer inputStream initialOptions = do chunksRef <- Ref.new [] lengthRef <- Ref.new 0.0 stream <- newPassThrough - stream # on_ dataH \buf -> do + rmData <- stream # on dataH \buf -> do Ref.modify_ (\chunks -> Array.snoc chunks buf) chunksRef bufLen <- Buffer.size buf Ref.modify_ (_ + (toNumber bufLen)) lengthRef @@ -83,6 +83,5 @@ getStreamBuffer inputStream initialOptions = do Buffer.concat' chunks $ asTooLargeInt len , getBufferedLength: Ref.read lengthRef , stream + , unsubcribe: rmData } - -foreign import pipeline :: forall w r. EffectFn3 (Readable w) (Writable r) (EffectFn1 (Nullable Error) Unit) Unit From e2502ee5de22e68a79f3fcbe530ced090864f7a6 Mon Sep 17 00:00:00 2001 From: Jordan Martinez Date: Wed, 26 Jul 2023 14:33:21 -0700 Subject: [PATCH 8/9] Update child-process to latest --- packages.dhall | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages.dhall b/packages.dhall index 26faf38..7ad1c09 100644 --- a/packages.dhall +++ b/packages.dhall @@ -66,7 +66,7 @@ in upstream , "prelude" , "unsafe-coerce" ] - with node-child-process.version = "4d27b66969ee11717104a1619c264b077e597d9e" + with node-child-process.version = "8e031fefa99056f8eec2e80815abf90a4c7d186f" with node-child-process.dependencies = [ "exceptions" , "node-event-emitter" From 06e5c2cec97085e338db54f9d71d04a01edd8e06 Mon Sep 17 00:00:00 2001 From: Jordan Martinez Date: Wed, 26 Jul 2023 14:56:15 -0700 Subject: [PATCH 9/9] Update packages to released versions --- packages.dhall | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/packages.dhall b/packages.dhall index 7ad1c09..12af450 100644 --- a/packages.dhall +++ b/packages.dhall @@ -22,7 +22,7 @@ in upstream , "unsafe-coerce" , "nullable" ] - with node-streams.version = "3909b1682ff7fe877b7c548dcf59bc301bacd06e" + with node-streams.version = "v9.0.0" with node-streams.dependencies = [ "aff" , "effect" @@ -34,7 +34,7 @@ in upstream , "prelude" , "unsafe-coerce" ] - with node-fs.version = "v9.0.0" + with node-fs.version = "v9.1.0" with node-fs.dependencies = [ "datetime" , "effect" @@ -54,7 +54,7 @@ in upstream , "strings" , "unsafe-coerce" ] - with node-process.version = "v11.1.1" + with node-process.version = "v11.2.0" with node-process.dependencies = [ "effect" , "foreign-object" @@ -66,7 +66,7 @@ in upstream , "prelude" , "unsafe-coerce" ] - with node-child-process.version = "8e031fefa99056f8eec2e80815abf90a4c7d186f" + with node-child-process.version = "v11.0.0" with node-child-process.dependencies = [ "exceptions" , "node-event-emitter"