diff --git a/.gitignore b/.gitignore index b14e3ce..3a7e65b 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,5 @@ *.orig node_modules -lib dist npm-debug.log -docs/literate \ No newline at end of file +docs diff --git a/.hgignore b/.hgignore index c26f9ec..5033d2b 100644 --- a/.hgignore +++ b/.hgignore @@ -1,7 +1,6 @@ syntax: glob *.orig node_modules -lib dist npm-debug.log -docs/literate \ No newline at end of file +docs diff --git a/Makefile b/Makefile index e00310b..90d11b2 100644 --- a/Makefile +++ b/Makefile @@ -1,52 +1,69 @@ bin = $(shell npm bin) -lsc = $(bin)/lsc +sjs = $(bin)/sjs browserify = $(bin)/browserify -groc = $(bin)/groc +jsdoc = $(bin)/jsdoc uglify = $(bin)/uglifyjs VERSION = $(shell node -e 'console.log(require("./package.json").version)') +# -- Configuration ----------------------------------------------------- +PACKGE = control.async +EXPORTS = folktale.control.async -lib: src/*.ls - $(lsc) -o lib -c src/*.ls +LIB_DIR = lib +TEST_DIR = test/specs-src +TEST_BLD = test/specs +TEST_SRC = $(wildcard $(TEST_DIR)/*.sjs) +TEST_TGT = ${TEST_SRC:$(TEST_DIR)/%.sjs=$(TEST_BLD)/%.js} + + +# -- Compilation ------------------------------------------------------- dist: - mkdir -p dist + mkdir -p $@ + +dist/$(PACKAGE).umd.js: $(LIB_DIR)/index.js dist + $(browserify) $< --standalone $(EXPORTS) > $@ -dist/control.async.umd.js: compile dist - $(browserify) lib/index.js --standalone Async > $@ +dist/$(PACKAGE).umd.min.js: dist/$(PACKAGE).umd.js + $(uglify) --mangle - < $< > $@ -dist/control.async.umd.min.js: dist/control.async.umd.js - $(uglify) --mangle - < $^ > $@ +$(TEST_BLD)/%.js: $(TEST_DIR)/%.sjs + mkdir -p $(dir $@) + $(sjs) --readable-names \ + --module alright/macros \ + --output $@ \ + $< -# ---------------------------------------------------------------------- -bundle: dist/control.async.umd.js -minify: dist/control.async.umd.min.js +# -- Tasks ------------------------------------------------------------- +bundle: dist/$(PACKAGE).umd.js -compile: lib +minify: dist/$(PACKAGE).umd.min.js documentation: - $(groc) --index "README.md" \ - --out "docs/literate" \ - src/*.ls test/*.ls test/specs/**.ls README.md + $(jsdoc) --configure jsdoc.conf.json + ABSPATH=$(shell cd "$(dirname "$0")"; pwd) $(MAKE) clean-docs + +clean-docs: + perl -pi -e "s?$$ABSPATH/??g" ./docs/*.html clean: - rm -rf dist build lib - -test: - $(lsc) test/tap.ls - -package: compile documentation bundle minify - mkdir -p dist/control.async-$(VERSION) - cp -r docs/literate dist/control.async-$(VERSION)/docs - cp -r lib dist/control.async-$(VERSION) - cp dist/*.js dist/control.async-$(VERSION) - cp package.json dist/control.async-$(VERSION) - cp README.md dist/control.async-$(VERSION) - cp LICENCE dist/control.async-$(VERSION) - cd dist && tar -czf control.async-$(VERSION).tar.gz control.async-$(VERSION) - -publish: clean + rm -rf dist $(TEST_BLD) + +test: $(TEST_TGT) + node test/node.js + +package: documentation bundle minify + mkdir -p dist/$(PACKAGE)-$(VERSION) + cp -r docs dist/$(PACKAGE)-$(VERSION) + cp -r lib dist/$(PACKAGE)-$(VERSION) + cp dist/*.js dist/$(PACKAGE)-$(VERSION) + cp package.json dist/$(PACKAGE)-$(VERSION) + cp README.md dist/$(PACKAGE)-$(VERSION) + cp LICENCE dist/$(PACKAGE)-$(VERSION) + cd dist && tar -czf $(PACKAGE)-$(VERSION).tar.gz $(PACKAGE)-$(VERSION) + +publish: clean $(TGT) npm install npm publish @@ -59,5 +76,4 @@ bump-feature: bump-major: VERSION_BUMP=MAJOR $(MAKE) bump - -.PHONY: test +.PHONY: test bump bump-feature bump-major publish package clean documentation diff --git a/jsdoc.conf.json b/jsdoc.conf.json new file mode 100644 index 0000000..19a7a6c --- /dev/null +++ b/jsdoc.conf.json @@ -0,0 +1,23 @@ +{ + "source": { + "include": [ "lib", "README.md" ] + }, + "plugins": [ "plugins/markdown" ], + "markdown": { + "parser": "gfm" + }, + "opts": { + "destination": "./docs/", + "template": "./node_modules/ink-docstrap/template" + }, + "templates": { + "systemName": "control.async", + "theme": "cerulean", + "linenums": true, + "navType": "vertical", + "copyright": "© 2014 Quildreen Motta", + "default": { + "outputSourceFiles": true + } + } +} diff --git a/lib/core.js b/lib/core.js new file mode 100644 index 0000000..ed9c45b --- /dev/null +++ b/lib/core.js @@ -0,0 +1,90 @@ +// Copyright (c) 2014 Quildreen Motta +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation files +// (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +/** + * Core operations for asynchronous control flow. + * + * @module async/core + */ + +// -- Dependencies ----------------------------------------------------- +var Future = require('data.future') + + +// -- Implementation --------------------------------------------------- + +/** + * Returns an action that always fails with the given data. + * + * @method + * @summary α → Future[α, β] + */ +exports.fail = fail +function fail(a) { + return new Future(function(reject, resolve) { reject(a) }) +} + + +/** + * Resolves all futures in parallel, and collects all of their values. + * + * @method + * @summary Array[Future[α, β]] → Future[α, Array[β]] + */ +exports.parallel = parallel +function parallel(xs) { return new Future(function(reject, resolve) { + var len = xs.length + var result = new Array(len) + var resolved = false + + xs.forEach(runComputation) + + function runComputation(x, i) { + return x.fork( function(e) { + if (resolved) return + resolved = true + reject(e) } + + , function(v) { + if (resolved) return + result[i] = v + len = len - 1 + if (len === 0) { resolved = true + resolve(result) }})} +})} + + +/** + * Returns the value of the first resolved or rejected future. + * + * @method + * @summary Array[Future[α, β]] → Future[α, β] + */ +exports.nondeterministicChoice = nondeterministicChoice +function nondeterministicChoice(xs){ return new Future(function(reject, resolve) { + var resolved = false + xs.forEach(function(x){ x.fork( function(e){ transition(reject, e) } + , function(v){ transition(resolve, v) }) }) + + function transition(f, a) { + if (!resolved) { resolved = true + f(a) }} +})} \ No newline at end of file diff --git a/lib/index.js b/lib/index.js new file mode 100644 index 0000000..1081d3a --- /dev/null +++ b/lib/index.js @@ -0,0 +1,29 @@ +// Copyright (c) 2014 Quildreen Motta +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation files +// (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +/** + * @module async + */ +var extend = require('xtend') + +module.exports = extend( require('./core') + , require('./time') + ) \ No newline at end of file diff --git a/lib/time.js b/lib/time.js new file mode 100644 index 0000000..cce8788 --- /dev/null +++ b/lib/time.js @@ -0,0 +1,76 @@ +// Copyright (c) 2014 Quildreen Motta +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation files +// (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +/** + * Asynchronous actions related to time. + * + * @module async/time + */ + +// -- Dependencies ----------------------------------------------------- +var Future = require('data.future') +var flaw = require('flaw') + + +// -- Helpers ---------------------------------------------------------- + +/** + * Timeout, in ms. + * + * @private + * @summary Number → Error + */ +function TimeoutError(n) { + return flaw( 'TimeoutError' + , 'Timeoutted after ' + n + ' milliseconds.') +} + +// -- Implementation --------------------------------------------------- + +/** + * Returns a future that gets resolved after N milliseconds. + * + * The value of the future will be the delta of the time from its execution to + * the resolution. + * + * @method + * @summary Number → Future[α, Number] + */ +exports.delay = delay +function delay(n) { return new Future(function(reject, resolve) { + var s = new Date + setTimeout( function(){ resolve(new Date - s) } + , n) +})} + + +/** + * Returns a future that fails after N milliseconds. + * + * @method + * @summary Number → Future[TimeoutError, α] + */ +exports.timeout = timeout +function timeout(n) { return new Future(function(reject, resolve) { + var s = new Date + setTimeout( function(){ reject(TimeoutError(n)) } + , n) +})} \ No newline at end of file diff --git a/package.json b/package.json index 6e0cff9..155c2ec 100644 --- a/package.json +++ b/package.json @@ -4,8 +4,7 @@ "description": "Operations for asynchronous control flow.", "main": "lib/index.js", "scripts": { - "test": "make test", - "prepublish": "make compile" + "test": "make test" }, "repository": { "type": "git", @@ -13,6 +12,9 @@ }, "keywords": [ "fantasy-land", + "concurrency", + "parallelism", + "async", "folktale" ], "author": "Quildreen Motta", @@ -22,16 +24,17 @@ }, "dependencies": { "flaw": "~0.1.0", - "data.future": "^2.0.0" + "data.future": "^2.0.0", + "xtend": "^3.0.0", + "jsdoc": "^3.3.0-alpha5", + "browserify": "^4.1.2" }, "devDependencies": { - "browserify": "git://github.com/robotlolita/node-browserify", - "groc": "git://github.com/robotlolita/groc", - "LiveScript": "~1.2.0", - "hifive-tap": "~0.1.0", "hifive": "~0.1.0", "uglify-js": "~2.4.3", - "laws": "~0.2.0", - "claire": "~0.4.1" + "hifive-spec": "^0.1.1", + "sweet.js": "^0.6.0", + "alright": "^1.0.0-alpha2", + "ink-docstrap": "git://github.com/robotlolita/docstrap" } } diff --git a/src/index.ls b/src/index.ls deleted file mode 100644 index 063ec75..0000000 --- a/src/index.ls +++ /dev/null @@ -1,93 +0,0 @@ -# # control.async - -/** ^ - * Copyright (c) 2013-2014 Quildreen Motta - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -# Operations for asynchronous control flow. - -Future = require 'data.future' -flaw = require 'flaw' - -TimeoutError = (n) -> flaw 'TimeoutError', "Timeoutted after #n milliseconds." - - -# # Function: delay -# -# Returns a promise that gets resolved after X milliseconds -# -# + type: Int -> Future(a, Int) -export delay = (n) -> new Future (reject, resolve) !-> - s = new Date - set-timeout (-> resolve (new Date - s)), n - - -# # Function: timeout -# -# Returns a promise that fails after X milliseconds -# -# + type: Int -> Future(Error, a) -export timeout = (n) -> new Future (reject, resolve) !-> - s = new Date - set-timeout (-> reject (TimeoutError n)), n - - -# # Function: parallel -# -# Resolves all futures in parallel. -# -# + type: [Future(a, b)] -> Future(a, [b]) -export parallel = (xs) -> new Future (reject, resolve) !-> - len = xs.length - result = new Array len - resolved = false - - for x,i in xs => compute x, i - - function compute(x, i) => x.fork do - * (e) -> do - if resolved => return - resolved := true - reject e - * (v) -> do - if resolved => return - result[i] := v - len := len - 1 - if len is 0 => do - resolved := true - resolve result - - -# # Function: nondeterministic-choice -# -# Returns the value of the first resolved or rejected future. -# -# + type: [Future(a, b)] -> Future(a, b) -export nondeterministic-choice = (xs) -> new Future (reject, resolve) !-> - resolved = false - for x,i in xs => x.fork do - * (e) -> transition reject, e - * (v) -> transition resolve, v - - function transition(f, a) => if not resolved - resolved := true - f a diff --git a/test/node.js b/test/node.js new file mode 100644 index 0000000..0547617 --- /dev/null +++ b/test/node.js @@ -0,0 +1,28 @@ +// Copyright (c) 2014 Quildreen Motta +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation files +// (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +var hifive = require('hifive') +var reporter = require('hifive-spec') +var specs = require('./specs') + +hifive.run(specs, reporter()).otherwise(function() { + process.exit(1) +}) \ No newline at end of file diff --git a/test/specs-src/index.sjs b/test/specs-src/index.sjs new file mode 100644 index 0000000..b8eca9d --- /dev/null +++ b/test/specs-src/index.sjs @@ -0,0 +1,24 @@ +// Copyright (c) 2014 Quildreen Motta +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation files +// (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +module.exports = [ + +] \ No newline at end of file diff --git a/test/specs/index.js b/test/specs/index.js new file mode 100644 index 0000000..e278e9a --- /dev/null +++ b/test/specs/index.js @@ -0,0 +1,21 @@ +// Copyright (c) 2014 Quildreen Motta +// +// Permission is hereby granted, free of charge, to any person +// obtaining a copy of this software and associated documentation files +// (the "Software"), to deal in the Software without restriction, +// including without limitation the rights to use, copy, modify, merge, +// publish, distribute, sublicense, and/or sell copies of the Software, +// and to permit persons to whom the Software is furnished to do so, +// subject to the following conditions: +// +// The above copyright notice and this permission notice shall be +// included in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +// EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +// NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +// LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +// OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +// WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +module.exports = []; \ No newline at end of file diff --git a/test/specs/index.ls b/test/specs/index.ls deleted file mode 100644 index 85e62e8..0000000 --- a/test/specs/index.ls +++ /dev/null @@ -1,29 +0,0 @@ -# # Entry point for the specifications - -/** ^ - * Copyright (c) 2013-2014 Quildreen Motta - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -module.exports = [ - # The specification objects go here - # See: http://hifivejs.github.io/hifive/getting-started.html -] diff --git a/test/tap.ls b/test/tap.ls deleted file mode 100644 index f02e781..0000000 --- a/test/tap.ls +++ /dev/null @@ -1,34 +0,0 @@ -# # A node test runner - -/** ^ - * Copyright (c) 2013-2014 Quildreen Motta - * - * Permission is hereby granted, free of charge, to any person - * obtaining a copy of this software and associated documentation files - * (the "Software"), to deal in the Software without restriction, - * including without limitation the rights to use, copy, modify, merge, - * publish, distribute, sublicense, and/or sell copies of the Software, - * and to permit persons to whom the Software is furnished to do so, - * subject to the following conditions: - * - * The above copyright notice and this permission notice shall be - * included in all copies or substantial portions of the Software. - * - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, - * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF - * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND - * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE - * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION - * OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION - * WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - */ - -# This will run the tests in a Node environment, and present the results -# with the TAP reporter. -hifive = require 'hifive' -tap = require 'hifive-tap' -specs = require './specs' - -# If we, for any reason, fail any of the tests, signal that with an -# error exit status. -(hifive.run specs, tap!).otherwise -> process?.exit 1