[
  {
    "path": ".eslintrc.json",
    "content": "{\n  \"root\": true,\n  \"extends\": [\"./node_modules/sanctuary-style/eslint-es6.json\"],\n  \"parserOptions\": {\"ecmaVersion\": 8},\n  \"env\": {\"node\": true},\n  \"rules\": {\n    \"max-len\": [\"error\", {\"code\": 79, \"ignoreComments\": true}]\n  }\n}\n"
  },
  {
    "path": ".github/CODEOWNERS",
    "content": "# This is a CODEOWNERS file.\n#\n# Please consult https://help.github.com/articles/about-codeowners/\n# for documentation.\n\n# Plaid codeowners requirements.\n* @plaid/developer-relations\n"
  },
  {
    "path": ".gitignore",
    "content": "/node_modules/\n"
  },
  {
    "path": ".npmrc",
    "content": "package-lock=false\n"
  },
  {
    "path": "README.md",
    "content": "# JavaScript has a problem\n\nWe like to say that JavaScript has a callback problem<sup>†</sup>.\nThis project considers various approaches to the following problem:\n\n*Given a path to a directory containing an index file, __index.txt__, and\nzero or more other files, read the index file (which contains one filename\nper line), then read each of the files listed in the index concurrently,\nconcat the resulting strings (in the order specified by the index), and\nwrite the result to stdout.*\n\n*The program's exit code should be 0 if the entire operation is successful;\n1 otherwise.*\n\nAll side effects must be isolated to the `main` function. Impure functions\nmay be defined at the top level but may only be invoked in `main`.\n\nThe solutions are best read in the following order:\n\n  - [__synchronous.js__](./synchronous.js)\n  - [__callbacks.js__](./callbacks.js)\n  - [__node-streams.js__](./node-streams.js)\n  - [__async.js__](./async.js)\n  - [__righto.js__](./righto.js)\n  - [__promises.js__](./promises.js)\n  - [__promises-pipe.js__](./promises-pipe.js)\n  - [__bluebird-promisell.js__](./bluebird-promisell.js)\n  - [__most.js__](./most.js)\n  - [__coroutines-co.js__](./coroutines-co.js)\n  - [__coroutines-bluebird.js__](./coroutines-bluebird.js)\n  - [__await.js__](./await.js)\n  - [__futures.js__](./futures.js)\n  - [__callbacks-revenge.js__](./callbacks-revenge.js)\n\nThe above ordering is suggested reading order only – not grade. Solutions are\ngraded on the following criteria:\n\n  - the size of `main` (smaller is better); and\n  - the degree to which the approach facilitates breaking the problem into\n    manageable subproblems.\n\n---\n\n† Perhaps in five years we'll be saying that JavaScript has a promise problem.\n"
  },
  {
    "path": "async.js",
    "content": "'use strict';\n\nconst async         = require ('async');\nconst S             = require ('sanctuary');\n\nconst exit0         = require ('./common/exit0');\nconst exit1         = require ('./common/exit1');\nconst join          = require ('./common/join');\nconst readFile      = require ('./common/read-file-callback');\n\n\nconst main = () => {\n  const path = join (process.argv[2]);\n  readFile (path ('index.txt')) ((err, data) => {\n    if (err != null) exit1 (err);\n    async.map (\n      S.map (path) (S.lines (data)),\n      (filename, callback) => readFile (filename) (callback),\n      (err, results) => {\n        if (err == null) exit0 (S.joinWith ('') (results)); else exit1 (err);\n      }\n    );\n  });\n};\n\nif (process.mainModule.filename === __filename) main ();\n"
  },
  {
    "path": "await.js",
    "content": "'use strict';\n\nconst exit0         = require ('./common/exit0');\nconst exit1         = require ('./common/exit1');\nconst join          = require ('./common/join');\nconst readFile      = require ('./common/read-file-promise');\nconst S             = require ('./common/sanctuary');\n\n\n//  concatFiles :: (String -> String) -> Promise Error String\nasync function concatFiles(path) {\n  const index = await readFile (path ('index.txt'));\n  const filenames = S.map (path) (S.lines (index));\n  const results = await Promise.all (S.map (readFile) (filenames));\n  return S.joinWith ('') (results);\n}\n\n\nconst main = () => {\n  concatFiles (join (process.argv[2]))\n  .then (exit0, exit1);\n};\n\nif (process.mainModule.filename === __filename) main ();\n"
  },
  {
    "path": "bluebird-promisell.js",
    "content": "'use strict';\n\nconst P             = require ('bluebird-promisell');\n\nconst exit0         = require ('./common/exit0');\nconst exit1         = require ('./common/exit1');\nconst join          = require ('./common/join');\nconst readFile      = require ('./common/read-file-promise');\nconst S             = require ('./common/sanctuary');\n\n\n//    concatFiles :: (String -> String) -> Promise Error String\nconst concatFiles = path => {\n  //    readFileRel :: String -> Promise Error String\n  const readFileRel = S.compose (readFile) (path);\n  //    indexP :: Promise Error String\n  const indexP = readFileRel ('index.txt');\n  //    filenamesP :: Promise Error (Array String)\n  const filenamesP = P.liftp1 (S.lines) (indexP);\n  //    resultsP :: Promise Error (Array String)\n  const resultsP = P.liftp1 (P.traversep (readFileRel)) (filenamesP);\n  //    (return value) :: Promise Error String\n  return P.liftp1 (S.joinWith ('')) (resultsP);\n};\n\n\nconst main = () => {\n  concatFiles (join (process.argv[2]))\n  .then (exit0, exit1);\n};\n\nif (process.mainModule.filename === __filename) main ();\n"
  },
  {
    "path": "callbacks-revenge.js",
    "content": "'use strict';\n\nconst fs = require ('fs');\n\nconst exit0 = require ('./common/exit0');\nconst exit1 = require ('./common/exit1');\nconst {\n  Arr,\n  Fn,\n  Str,\n  Path,\n  NodeEither,\n  GenericEitherT,\n  Cont,\n} = require ('./common/revengeutils');\n\nconst getpath = Path.combine (process.argv[2]);\n\n// A monad for working with continuations that contain NodeEithers\n// :: type NC r e x = GenericEither (Cont r) NodeEither e x\n// ::               = Cont r (NodeEither e x)\nconst NC = GenericEitherT (NodeEither) (Cont);\n\n// Standard NodeJS APIs need a little massaging to be valid continuations\n// - Callback must be a curried, final argument\n// - Can only pass a single argument to callback (an [err, ...data] array is fine)\n// :: Path -> NC () String String\nconst readFile = path => cb =>\n  fs.readFile (path, {encoding: 'utf8'}, (...args) => cb (args));\n\n// Main\n// :: String -> NC () String String\nconst readAllFiles = Fn.pipe ([\n  getpath,                                  // :: Path\n  readFile,                                 // :: NC () String String\n  NC.map (Str.lines),                       // :: NC () String [String]\n  NC.map (Arr.map (getpath)),               // :: NC () String [Path]\n  NC.chain (Arr.traverse (NC) (readFile)),  // :: NC () String [String]\n  NC.map (Str.join ('')),                   // :: NC () String String\n]);\n\n// :: NC () String String\nconst result = readAllFiles ('index.txt');\n\n// Remember that:\n// :: type NC r e x = Cont r (NodeEither e x)\n// so...\n// :: NC () String String = Cont () (NodeEither String String)\n// ::                     = ((NodeEither String String) -> ()) -> ()\n\n// :: NodeEither String String -> ⊥\nconst fork = NodeEither.match ({Left: exit1, Right: exit0});\nconst main = () => result (fork); // :: ()\nmain ();\n"
  },
  {
    "path": "callbacks.js",
    "content": "'use strict';\n\nconst S             = require ('sanctuary');\n\nconst exit0         = require ('./common/exit0');\nconst exit1         = require ('./common/exit1');\nconst join          = require ('./common/join');\nconst readFile      = require ('./common/read-file-callback');\n\n\nconst main = () => {\n  const path = join (process.argv[2]);\n  readFile (path ('index.txt')) ((err, data) => {\n    if (err != null) exit1 (err);\n    const filenames = S.lines (data);\n    const $results = [];\n    let filesRead = 0;\n    filenames.forEach ((file, idx) => {\n      readFile (path (file)) ((err, data) => {\n        if (err != null) exit1 (err);\n        $results[idx] = data;\n        filesRead += 1;\n        if (filesRead === filenames.length) exit0 (S.joinWith ('') ($results));\n      });\n    });\n  });\n};\n\nif (process.mainModule.filename === __filename) main ();\n"
  },
  {
    "path": "common/exit0.js",
    "content": "'use strict';\n\nmodule.exports = s => {\n  process.stdout.write (s);\n  process.exit (0);\n};\n"
  },
  {
    "path": "common/exit1.js",
    "content": "'use strict';\n\nmodule.exports = err => {\n  process.stderr.write (`${err}\\n`);\n  process.exit (1);\n};\n"
  },
  {
    "path": "common/join.js",
    "content": "'use strict';\n\nconst path          = require ('path');\n\nconst S             = require ('sanctuary');\n\n//  join :: String -> String -> String\nmodule.exports = S.curry2 (path.join);\n"
  },
  {
    "path": "common/read-file-callback.js",
    "content": "'use strict';\n\nconst fs            = require ('fs');\n\n//  readFile :: String -> ((Error?, String?) -> Undefined) -> Undefined\nmodule.exports = filename => callback =>\n  fs.readFile (filename, {encoding: 'utf8'}, callback);\n"
  },
  {
    "path": "common/read-file-promise.js",
    "content": "'use strict';\n\nconst fs            = require ('fs');\n\n//  readFile :: String -> Promise Error String\nmodule.exports = filename =>\n  new Promise ((res, rej) => {\n    fs.readFile (filename, {encoding: 'utf8'}, (err, data) => {\n      if (err == null) res (data); else rej (err);\n    });\n  });\n"
  },
  {
    "path": "common/revengeutils.js",
    "content": "'use strict';\n\nconst path = require ('path');\n\n\nconst derivemonad = M => {\n  const {of, chain} = M;\n\n  const map = f => chain (x => of (f (x)));\n  const lift2 = f => mx => my =>\n    chain (x => chain (y => of (f (x) (y))) (my)) (mx);\n\n  return Object.assign ({map, lift2}, M);\n};\n\nconst Arr = (() => {\n  const of = x => [x];\n  const map = f => x => x.map (f);\n\n  const foldl = f => z => xs => xs.reduce ((p, c) => f (p) (c), z);\n\n  const empty = [];\n  const append = a => b => [...a, ...b];\n\n  const sequence = A =>\n    foldl (A.lift2 (b => a => append (b) (of (a)))) (A.of (empty));\n  const traverse = A => f => xs => sequence (A) (map (f) (xs));\n\n  return {map, foldl, traverse};\n}) ();\n\nconst Fn = (() => {\n  const id = x => x;\n  const compose = f => g => a => f (g (a));\n  const flip = f => x => y => f (y) (x);\n  const pipe = Arr.foldl (flip (compose)) (id);\n\n  return {id, compose, flip, pipe, '.': compose};\n}) ();\n\nconst Str = (() => {\n  const trim = s => s.trim ();\n  const join = sep => arr => arr.join (sep);\n  const split = sep => s => s.split (sep);\n  const lines = Fn['.'] (split ('\\n')) (trim);\n\n  return {split, lines, join, trim};\n}) ();\n\nconst Path = (() => {\n  const combine = base => sub => path.join (base, sub);\n  return {combine};\n}) ();\n\n// An Either interpretation of Node style first-element-falsy arrays\n// :: type NodeEither e d = [Maybe e, ...d]\nconst NodeEither = (() => {\n  const Left = l => [l];\n  const Right = (...r) => [null, ...r];\n\n  const match = ({Left, Right}) => ([e, ...x]) => e ? Left (e) : Right (...x);\n\n  return {Left, Right, match};\n}) ();\n\n// The GenericEitherT monad transformer\n// :: type GenericEitherT e m l r = m (e l r)\nconst GenericEitherT = E => M => {\n  const {Left, Right, match} = E;\n\n  // :: x -> m (e l x)\n  const of = x => M.of (Right (x));\n\n  // :: (a -> m (e l b)) -> m (e l a) -> m (e l b)\n  const chain = f =>\n    Fn['.'] (M.chain) (match) ({\n      Left: l => M.of (Left (l)),\n      Right: f,\n    });\n\n  return derivemonad ({of, chain});\n};\n\n// The continuation monad\n// :: type Cont r a = (a -> r) -> r\nconst Cont = (() => {\n  const of = x => cb => cb (x);\n  const chain = f => m => cb => m (x => f (x) (cb));\n\n  return derivemonad ({of, chain});\n}) ();\n\nmodule.exports = {Arr, Fn, Str, Path, NodeEither, GenericEitherT, Cont};\n"
  },
  {
    "path": "common/sanctuary.js",
    "content": "'use strict';\n\nconst FutureTypes   = require ('fluture-sanctuary-types');\nconst S             = require ('sanctuary');\nconst $             = require ('sanctuary-def');\n\n\n//    PromiseType :: Type\nconst PromiseType = $.NullaryType\n  ('Promise')\n  ('')\n  ([])\n  (x => x != null && x.constructor === Promise);\n\nmodule.exports = S.create ({\n  checkTypes: true,\n  env: S.env.concat (FutureTypes.env.concat ([PromiseType])),\n});\n"
  },
  {
    "path": "coroutines-bluebird.js",
    "content": "'use strict';\n\nconst bluebird      = require ('bluebird');\n\nconst exit0         = require ('./common/exit0');\nconst exit1         = require ('./common/exit1');\nconst join          = require ('./common/join');\nconst readFile      = require ('./common/read-file-promise');\nconst S             = require ('./common/sanctuary');\n\n\n//    concatFiles :: (String -> String) -> Promise Error String\nconst concatFiles = bluebird.coroutine (function* generator(path) {\n  const index = yield readFile (path ('index.txt'));\n  const filenames = S.map (path) (S.lines (index));\n  const results = yield bluebird.all (S.map (readFile) (filenames));\n  return S.joinWith ('') (results);\n});\n\n\nconst main = () => {\n  concatFiles (join (process.argv[2]))\n  .then (exit0, exit1);\n};\n\nif (process.mainModule.filename === __filename) main ();\n"
  },
  {
    "path": "coroutines-co.js",
    "content": "'use strict';\n\nconst co            = require ('co');\n\nconst exit0         = require ('./common/exit0');\nconst exit1         = require ('./common/exit1');\nconst join          = require ('./common/join');\nconst readFile      = require ('./common/read-file-promise');\nconst S             = require ('./common/sanctuary');\n\n\n//    concatFiles :: (String -> String) -> Promise Error String\nconst concatFiles = path =>\n  co (function* generator() {\n    const index = yield readFile (path ('index.txt'));\n    const filenames = S.map (path) (S.lines (index));\n    const results = yield Promise.all (S.map (readFile) (filenames));\n    return S.joinWith ('') (results);\n  });\n\n\nconst main = () => {\n  concatFiles (join (process.argv[2]))\n  .then (exit0, exit1);\n};\n\nif (process.mainModule.filename === __filename) main ();\n"
  },
  {
    "path": "futures.js",
    "content": "'use strict';\n\nconst fs            = require ('fs');\n\nconst Future        = require ('fluture');\n\nconst exit0         = require ('./common/exit0');\nconst exit1         = require ('./common/exit1');\nconst join          = require ('./common/join');\nconst S             = require ('./common/sanctuary');\n\n\n//    readFile :: String -> Future Error String\nconst readFile = S.flip (Future.encaseN2 (fs.readFile)) ({encoding: 'utf8'});\n\n//    readFilePar :: String -> ConcurrentFuture Error String\nconst readFilePar = S.compose (Future.Par) (readFile);\n\n//    concatFiles :: (String -> String) -> Future Error String\nconst concatFiles = path =>\n  S.pipe ([path,                                            // :: String\n           readFile,                                        // :: Future Error String\n           S.map (S.lines),                                 // :: Future Error (Array String)\n           S.map (S.map (path)),                            // :: Future Error (Array String)\n           S.map (S.traverse (Future.Par) (readFilePar)),   // :: Future Error (ConcurrentFuture Error (Array String))\n           S.chain (Future.seq),                            // :: Future Error (Array String)\n           S.map (S.joinWith (''))])                        // :: Future Error String\n         ('index.txt');\n\n\nconst main = () => {\n  concatFiles (join (process.argv[2]))\n  .fork (exit1, exit0);\n};\n\nif (process.mainModule.filename === __filename) main ();\n"
  },
  {
    "path": "input/bar.txt",
    "content": "BAR\n"
  },
  {
    "path": "input/baz.txt",
    "content": "BAZ\n"
  },
  {
    "path": "input/foo.txt",
    "content": "FOO\n"
  },
  {
    "path": "input/index.txt",
    "content": "foo.txt\nbar.txt\nbaz.txt\n"
  },
  {
    "path": "most.js",
    "content": "'use strict';\n\nconst most          = require ('most');\n\nconst exit0         = require ('./common/exit0');\nconst exit1         = require ('./common/exit1');\nconst join          = require ('./common/join');\nconst readFile      = require ('./common/read-file-promise');\nconst S             = require ('./common/sanctuary');\n\n\n//    concatFiles :: (String -> String) -> Promise Error String\nconst concatFiles = path =>\n  most.fromPromise (readFile (path ('index.txt')))\n  .map (S.lines)\n  .map (S.map (path))\n  .map (S.map (readFile))\n  .map (Promise.all.bind (Promise))\n  .awaitPromises ()\n  .map (S.joinWith (''))\n  .reduce ((x, y) => x + y, '');\n\n\nconst main = () => {\n  concatFiles (join (process.argv[2]))\n  .then (exit0, exit1);\n};\n\nif (process.mainModule.filename === __filename) main ();\n"
  },
  {
    "path": "node-streams.js",
    "content": "'use strict';\n\nconst fs            = require ('fs');\n\nconst miss          = require ('mississippi');\nconst split         = require ('split2');\n\nconst exit0         = require ('./common/exit0');\nconst exit1         = require ('./common/exit1');\nconst join          = require ('./common/join');\nconst readFile      = require ('./common/read-file-callback');\n\nconst main = () => {\n  const path = join (process.argv[2]);\n  let results;\n\n  miss.pipe (\n    fs.createReadStream (path ('index.txt')),\n    split (),\n    miss.parallel (123, (filename, cb) => readFile (path (filename)) (cb)),\n    miss.concat (data => { results = data; }),\n    err => { if (err != null) exit1 (err); else exit0 (results); }\n  );\n};\n\nif (process.mainModule.filename === __filename) main ();\n"
  },
  {
    "path": "package.json",
    "content": "{\n  \"name\": \"async-problem\",\n  \"private\": true,\n  \"scripts\": {\n    \"lint\": \"eslint -- \\\"**/*.js\\\"\",\n    \"test\": \"./test\"\n  },\n  \"dependencies\": {\n    \"async\": \"3.1.0\",\n    \"bluebird\": \"3.7.1\",\n    \"bluebird-promisell\": \"0.7.0\",\n    \"co\": \"4.6.0\",\n    \"fluture\": \"11.0.3\",\n    \"fluture-sanctuary-types\": \"4.1.1\",\n    \"mississippi\": \"4.0.0\",\n    \"most\": \"1.7.3\",\n    \"righto\": \"6.0.0\",\n    \"sanctuary\": \"2.0.0\",\n    \"sanctuary-def\": \"0.20.0\",\n    \"split2\": \"3.1.1\"\n  },\n  \"devDependencies\": {\n    \"eslint\": \"5.15.x\",\n    \"sanctuary-style\": \"3.0.0\"\n  }\n}\n"
  },
  {
    "path": "promises-pipe.js",
    "content": "'use strict';\n\nconst exit0         = require ('./common/exit0');\nconst exit1         = require ('./common/exit1');\nconst join          = require ('./common/join');\nconst readFile      = require ('./common/read-file-promise');\nconst S             = require ('./common/sanctuary');\n\n\n//    then :: (a -> (b | Promise e b)) -> Promise e a -> Promise e b\nconst then = f => p => p.then (f);\n\n//    concatFiles :: (String -> String) -> Promise Error String\nconst concatFiles = path =>\n  S.pipe ([path,\n           readFile,\n           then (S.lines),\n           then (S.map (path)),\n           then (S.map (readFile)),\n           then (Promise.all.bind (Promise)),\n           then (S.joinWith (''))])\n         ('index.txt');\n\n\nconst main = () => {\n  concatFiles (join (process.argv[2]))\n  .then (exit0, exit1);\n};\n\nif (process.mainModule.filename === __filename) main ();\n"
  },
  {
    "path": "promises.js",
    "content": "'use strict';\n\nconst exit0         = require ('./common/exit0');\nconst exit1         = require ('./common/exit1');\nconst join          = require ('./common/join');\nconst readFile      = require ('./common/read-file-promise');\nconst S             = require ('./common/sanctuary');\n\n\n//    concatFiles :: (String -> String) -> Promise Error String\nconst concatFiles = path =>\n  Promise.resolve ('index.txt')\n  .then (path)\n  .then (readFile)\n  .then (S.lines)\n  .then (S.map (path))\n  .then (S.map (readFile))\n  .then (Promise.all.bind (Promise))\n  .then (S.joinWith (''));\n\n\nconst main = () => {\n  concatFiles (join (process.argv[2]))\n  .then (exit0, exit1);\n};\n\nif (process.mainModule.filename === __filename) main ();\n"
  },
  {
    "path": "righto.js",
    "content": "'use strict';\n\nconst fs            = require ('fs');\n\nconst righto        = require ('righto');\nconst S             = require ('sanctuary');\n\nconst exit0         = require ('./common/exit0');\nconst exit1         = require ('./common/exit1');\nconst join          = require ('./common/join');\n\n\n//    readFile :: String -> Righto String\nconst readFile = filename =>\n  righto (fs.readFile, filename, {encoding: 'utf8'});\n\n//    concatFiles :: (String -> String) -> Righto String\nconst concatFiles = path => {\n  const readFileRel = S.compose (readFile) (path);\n  const index = readFileRel ('index.txt');\n  const files = righto.sync (S.compose (S.map (readFileRel)) (S.lines), index);\n  return righto.sync (S.joinWith (''), righto.all (files));\n};\n\n\nconst main = () => {\n  concatFiles\n    (join (process.argv[2]))\n    ((err, data) => { if (err == null) exit0 (data); else exit1 (err); });\n};\n\nif (process.mainModule.filename === __filename) main ();\n"
  },
  {
    "path": "synchronous.js",
    "content": "'use strict';\n\nconst fs            = require ('fs');\n\nconst S             = require ('sanctuary');\n\nconst exit0         = require ('./common/exit0');\nconst exit1         = require ('./common/exit1');\nconst join          = require ('./common/join');\n\n\nconst readFile = filename => {\n  try {\n    return fs.readFileSync (filename, {encoding: 'utf8'});\n  } catch (err) {\n    exit1 (err);\n  }\n};\n\n\nconst main = () => {\n  const path = join (process.argv[2]);\n  S.pipe ([path,\n           readFile,\n           S.lines,\n           S.map (path),\n           S.map (readFile),\n           S.joinWith (''),\n           exit0])\n         ('index.txt');\n};\n\nif (process.mainModule.filename === __filename) main ();\n"
  },
  {
    "path": "test",
    "content": "#!/usr/bin/env bash\n\npassed=\"\\033[0;32mpassed\\033[0m\\n\"\nfailed=\"\\033[0;31mfailed\\033[0m\\n\"\n\ntest() {\n  local actual\n  local expected\n\n  printf \"Testing \\033[0;36m%s\\033[0m...\\n\" \"$1\"\n\n  printf %s \"- correct directory name...     \"\n  expected=\"$(printf \"FOO\\nBAR\\nBAZ\\n\")\"\n  if ! actual=\"$(node \"$1\" input)\" ; then\n    printf \"$failed\"\n  elif [[ \"$actual\" == \"$expected\" ]] ; then\n    printf \"$passed\"\n  else\n    printf \"$failed\"\n  fi\n\n  printf %s \"- incorrect directory name...   \"\n  expected=\"^Error: ENOENT[,:] .* 'XXX/index.txt'$\"\n  if actual=\"$(node \"$1\" XXX 2>&1 1>/dev/null)\" ; then\n    printf \"$failed\"\n  elif [[ $? != 1 ]] ; then\n    printf \"$failed\"\n  elif [[ \"$actual\" =~ $expected ]] ; then\n    printf \"$passed\"\n  else\n    printf \"$failed\"\n  fi\n\n  printf %s \"- inaccurate index...           \"\n  rm -f -- input/baz.txt\n  expected=\"^Error: ENOENT[,:] .* 'input/baz.txt'$\"\n  if actual=\"$(node \"$1\" input 2>&1 1>/dev/null)\" ; then\n    printf \"$failed\"\n  elif [[ $? != 1 ]] ; then\n    printf \"$failed\"\n  elif [[ \"$actual\" =~ $expected ]] ; then\n    printf \"$passed\"\n  else\n    printf \"$failed\"\n  fi\n  git checkout -- input/baz.txt\n\n  echo\n}\n\ntest synchronous.js\ntest callbacks.js\ntest node-streams.js\ntest async.js\ntest righto.js\ntest promises.js\ntest promises-pipe.js\ntest bluebird-promisell.js\ntest most.js\ntest coroutines-co.js\ntest coroutines-bluebird.js\ntest await.js\ntest futures.js\ntest callbacks-revenge.js\n"
  }
]