diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d25b9e0
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,4 @@
+/.nyc_output
+/dist
+/node_modules
+/test/dist
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..7fef1c7
--- /dev/null
+++ b/README.md
@@ -0,0 +1,70 @@
+# Monapt
+
+[![npm version](https://badge.fury.io/js/monapt.svg)](http://badge.fury.io/js/monapt)
+[![Build Status](https://circleci.com/gh/jiaweihli/monapt/tree/1.0.svg?style=shield)](https://circleci.com/gh/jiaweihli/monapt/tree/1.0)
+[![Coverage Status](https://coveralls.io/repos/github/jiaweihli/monapt/badge.svg?branch=1.0)](https://coveralls.io/github/jiaweihli/monapt?branch=1.0)
+
+[![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
+[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-green.svg)](https://conventionalcommits.org)
+
+Monapt helps you better manage `null`, `undefined`, exceptions, and other mildly interesting
+phenomena. It handles them through the
+[`Option`](http://danielwestheide.com/blog/2012/12/19/the-neophytes-guide-to-scala-part-5-the-option-type.html),
+[`Try`](http://danielwestheide.com/blog/2012/12/26/the-neophytes-guide-to-scala-part-6-error-handling-with-try.html),
+and [`Future`](http://danielwestheide.com/blog/2013/01/09/the-neophytes-guide-to-scala-part-8-welcome-to-the-future.html)
+abstractions.
+
+
+## Setup
+
+```bash
+$ npm install monapt
+```
+
+## APIs
+
+### Usage
+
+```typescript
+import { Option } from 'monapt';
+
+Option(1)
+ .map((x) => x * 2)
+ .getOrElse(() => 4);
+```
+
+Docs are undergoing a redesign, and will be published on a separate site.
+In the meantime, the sources for the [`Option`](https://github.com/jiaweihli/monapt/tree/master/src/option),
+[`Future`](https://github.com/jiaweihli/monapt/tree/master/src/future), and
+[`Try`](https://github.com/jiaweihli/monapt/tree/master/src/try) classes are readable.
+
+You can also take a look at the [tests](https://github.com/jiaweihli/monapt/tree/master/test) to get
+a feel for how to use them.
+
+## Changes in 1.0
+
+1.0 was a complete rewrite of Monapt - including everything from the implementation to the tooling
+to the tests. The result is almost the same API, but more true to the original Scala interface.
+
+## Migrating from 0.7.1
+
+### Breaking Changes
+
+- All default exports have been removed [to avoid ambiguity](https://github.com/palantir/tslint/issues/1182#issue-151780453).
+- `Future` now depends on `when.Promise`, and uses it internally when representing promises.
+- `Future#onFailure` has been removed.
+- `Future#onSuccess` has been removed.
+- `Future#reject` has been removed.
+- `Monapt::flatten` has been renamed to `Option::flatten`.
+- `Monapt::future` has been renamed to `Future::create`. It now accepts a
+ `when.Promise | when.Thenable | A`.
+- `Option#reject` has been renamed to `Option#filterNot`.
+- `Try#reject` has been removed.
+
+These are all backed by type definitions, so compiling your code via TypeScript should reveal any
+breakages.
+
+## Credits
+
+This repo couldn't have been possible without [yaakaito/monapt](https://github.com/yaakaito/monapt).
+In his absence, I'll continue improving upon his hard work.
diff --git a/build.webpack.config.js b/build.webpack.config.js
new file mode 100644
index 0000000..b96c081
--- /dev/null
+++ b/build.webpack.config.js
@@ -0,0 +1,25 @@
+const path = require('path');
+
+module.exports = {
+ entry: './src/monapt.ts',
+ output: {
+ filename: 'monapt.js',
+ library: 'Monapt',
+ libraryTarget: 'umd',
+ path: path.resolve(__dirname, 'dist')
+ },
+ module: {
+ rules: [
+ {
+ test: /\.ts$/,
+ loader: 'ts-loader'
+ }
+ ]
+ },
+ resolve: {
+ modules: [
+ 'node_modules'
+ ],
+ extensions: ['.ts', '.js']
+ },
+};
diff --git a/circle.yml b/circle.yml
new file mode 100644
index 0000000..3bce1f6
--- /dev/null
+++ b/circle.yml
@@ -0,0 +1,25 @@
+version: 2
+jobs:
+ build:
+ working_directory: ~/monapt
+ docker:
+ - image: node:boron
+ environment:
+ - COVERALLS_REPO_TOKEN: "mMDS1eSfES0hTguOkxWQOLDHbLJdfZLAa"
+ steps:
+ - checkout
+ - restore_cache:
+ key: node-{{ .Branch }}-{{ checksum "package.json" }}
+ - run:
+ name: Installing JS dependencies
+ command: npm install
+ - save_cache:
+ key: node-{{ .Branch }}-{{ checksum "package.json" }}
+ paths:
+ - "node_modules"
+ - run:
+ name: Running tests
+ command: npm run test
+ - run:
+ name: Reporting code coverage to Coveralls
+ command: npm run coverage
diff --git a/package.json b/package.json
new file mode 100644
index 0000000..7f7738c
--- /dev/null
+++ b/package.json
@@ -0,0 +1,102 @@
+{
+ "name": "monapt",
+ "version": "1.0.0",
+ "description": "Eliminate null/undefined errors in JS...and more!",
+ "author": {
+ "name": "Kevin Li",
+ "email": "jiawei.h.li+npm@gmail.com",
+ "url": "https://jiawei.li/"
+ },
+ "license": "MIT",
+ "homepage": "https://github.com/jiaweihli/monapt#readme",
+ "bugs": {
+ "url": "https://github.com/jiaweihli/monapt/issues"
+ },
+ "repository": {
+ "type": "git",
+ "url": "git+https://github.com/jiaweihli/monapt.git"
+ },
+ "scripts": {
+ "build": "webpack --config build.webpack.config.js",
+ "commit": "git-cz",
+ "commitmsg": "validate-commit-msg",
+ "coverage": "nyc report --reporter=text-lcov | coveralls",
+ "lint:ts": "tslint --format verbose --project ./src/tsconfig.json --type-check './src/**/*.ts' && tslint --format verbose --config ./test/tslint.json --project ./test/tsconfig.json --type-check --exclude './test/dist/**/*.ts' './test/**/*.ts'",
+ "smoke:ts": "tsc --noEmit --pretty --project ./src/tsconfig.json && tsc --noEmit --pretty --project ./test/tsconfig.json",
+ "test": "webpack --config test.webpack.config.js && nyc ava --verbose",
+ "watch": "webpack --config build.webpack.config.js --watch"
+ },
+ "main": "dist/monapt.js",
+ "typings": "dist/src/monapt.d.ts",
+ "files": [
+ "dist/",
+ "README.md"
+ ],
+ "keywords": [
+ "option",
+ "optional",
+ "future",
+ "try",
+ "monad",
+ "functional",
+ "util",
+ "scala",
+ "type",
+ "types"
+ ],
+ "dependencies": {
+ "when": "3.7.8"
+ },
+ "devDependencies": {
+ "@types/when": "2.4.29",
+ "ava": "0.20.0",
+ "commitizen": "2.9.6",
+ "conventional-changelog-cli": "1.3.1",
+ "conventional-commit-types": "2.1.0",
+ "coveralls": "2.13.1",
+ "cracks": "3.1.2",
+ "cz-conventional-changelog": "2.0.0",
+ "glob": "7.1.2",
+ "husky": "0.14.1",
+ "istanbul-instrumenter-loader": "2.0.0",
+ "nyc": "11.0.3",
+ "semantic-release": "6.3.6",
+ "ts-loader": "2.2.1",
+ "tslint": "5.1.0",
+ "typescript": "2.3.4",
+ "validate-commit-msg": "2.12.2",
+ "webpack": "2.6.1"
+ },
+ "config": {
+ "commitizen": {
+ "path": "./node_modules/cz-conventional-changelog"
+ },
+ "validate-commit-msg": {
+ "types": "conventional-commit-types",
+ "scope": {
+ "required": false,
+ "allowed": [
+ "*"
+ ],
+ "validate": true,
+ "multiple": false
+ },
+ "warnOnFail": true,
+ "maxSubjectLength": 100,
+ "subjectPattern": ".+",
+ "subjectPatternErrorMsg": "subject does not match subject pattern!",
+ "helpMessage": "",
+ "autoFix": false
+ }
+ },
+ "release": {
+ "verifyRelease": {
+ "path": "cracks",
+ "paths": [
+ "test",
+ "package.json"
+ ],
+ "silent": true
+ }
+ }
+}
diff --git a/src/future/future.ts b/src/future/future.ts
new file mode 100644
index 0000000..2046e06
--- /dev/null
+++ b/src/future/future.ts
@@ -0,0 +1,170 @@
+import * as when from 'when';
+
+import { None } from '../option/none';
+import { Option } from '../option/option';
+import { Try } from '../try/try';
+
+/* tslint:disable:no-any */
+(when as any).Promise.onPotentiallyUnhandledRejection = (): void => {
+ // :TRICKY: Ignore potentially unhandled exceptions since they're intentional.
+ // :LINK: http://stackoverflow.com/a/29692412
+};
+/* tslint:enable */
+
+/**
+ * A Future represents a value which may or may not be available. It may be asynchronously filled
+ * in the future.
+ */
+class Future {
+ static create(value: when.Promise | when.Thenable | A): Future {
+ return new Future(
+ when(value) as when.Promise
+ );
+}
+
+ /* tslint:disable:variable-name */
+ protected promise_: when.Promise;
+ protected value_: Option>;
+ /* tslint:enable:variable-name */
+
+ get isCompleted(): boolean {
+ return this.value.isDefined;
+ }
+
+ get promise(): when.Promise {
+ return this.promise_;
+ }
+
+ get value(): Option> {
+ return this.value_;
+ }
+
+ protected constructor(promise: when.Promise) {
+ this.value_ = None;
+
+ this.promise_ = promise
+ .tap((value: A): void => {
+ this.value_ = Option(Try(() => value));
+ })
+ .catch((error: Error): when.Promise => {
+ this.value_ = Option(Try(() => { throw error; }));
+
+ throw error;
+ });
+ }
+
+ filter(predicate: (a: A) => boolean): Future {
+ return new Future(
+ when.promise(
+ (resolve: (a: A) => void, reject: (error: Error) => void): void => {
+ this.promise
+ .then((value: A): void => {
+ if (predicate(value)) {
+ resolve(value);
+ }
+ else {
+ reject(new Error('Future.filter predicate is not satisfied'));
+ }
+ });
+ }
+ )
+ );
+ }
+
+ flatMap(mapper: (a: A) => Future): Future {
+ return new Future(
+ when.promise(
+ (resolve: (b: B) => void, reject: (error: Error) => void): void => {
+ this.promise
+ .then((value: A): void => {
+ try {
+ mapper(value)
+ .foreach((b: B): void => {
+ resolve(b);
+ });
+ }
+ catch (error) {
+ reject(error as Error);
+ }
+ })
+ .catch((error: Error): void => {
+ reject(error);
+ });
+ }
+ )
+ );
+ }
+
+ foreach(run: (a: A) => B): void {
+ this.promise
+ .then((value: A): void => {
+ run(value);
+ });
+ }
+
+ map(mapper: (a: A) => B): Future {
+ return new Future(
+ this.promise
+ .then((value: A): B => {
+ return mapper(value);
+ })
+ );
+ }
+
+ onComplete(run: (t: Try) => B): void {
+ this.promise
+ .then((value: A): void => {
+ run(Try(() => value));
+ })
+ .catch((error: Error): void => {
+ run(Try(() => { throw error; }));
+ });
+ }
+
+ recover(this: Future, f: (e: Error) => B): Future {
+ return new Future(
+ when.promise(
+ (resolve: (b: B) => void, reject: (error: Error) => void): void => {
+ this.promise
+ .then((value: A): void => {
+ resolve(value);
+ })
+ .catch((error: Error): void => {
+ try {
+ resolve(f(error));
+ }
+ catch (error) {
+ reject(error as Error);
+ }
+ });
+ }
+ )
+ );
+ }
+
+ recoverWith(this: Future, f: (e: Error) => Future): Future {
+ return new Future(
+ when.promise(
+ (resolve: (b: B) => void, reject: (error: Error) => void): void => {
+ this.promise
+ .then((value: A): void => {
+ resolve(value);
+ })
+ .catch((error: Error): void => {
+ try {
+ f(error)
+ .foreach((b: B): void => {
+ resolve(b);
+ });
+ }
+ catch (error) {
+ reject(error as Error);
+ }
+ });
+ }
+ )
+ );
+ }
+}
+
+export { Future };
diff --git a/src/monapt.ts b/src/monapt.ts
new file mode 100644
index 0000000..b783026
--- /dev/null
+++ b/src/monapt.ts
@@ -0,0 +1,7 @@
+export { Failure } from './try/failure';
+export { Future } from './future/future';
+export { None } from './option/none';
+export { Option } from './option/option';
+export { Some } from './option/some';
+export { Success } from './try/success';
+export { Try } from './try/try';
diff --git a/src/option/none.ts b/src/option/none.ts
new file mode 100644
index 0000000..415bff7
--- /dev/null
+++ b/src/option/none.ts
@@ -0,0 +1,69 @@
+import { Option } from './option';
+
+/**
+ * None is a singleton class that represents emptiness. It signals that a value that may not exist
+ * doesn't exist.
+ */
+/* tslint:disable:class-name */
+class None_ implements Option {
+/* tslint:enable:class-name */
+ static get INSTANCE(): None_ {
+ return new None_();
+ }
+
+ get isDefined(): boolean {
+ return false;
+ }
+
+ get isEmpty(): boolean {
+ return true;
+ }
+
+ private constructor() {
+ // :TRICKY: We don't want to do anything except mark constructor as private.
+ }
+
+ equals(other: Option): boolean {
+ return (other instanceof None_);
+ }
+
+ filter(predicate: (value: A) => boolean): Option {
+ return this;
+ }
+
+ filterNot(predicate: (value: A) => boolean): Option {
+ return this;
+ }
+
+ flatMap(flatMapper: (value: A) => Option): Option {
+ return None_.INSTANCE;
+ }
+
+ foreach(run: (value: A) => void): void {
+ // :TRICKY: Don't do anything.
+ }
+
+ get(): A {
+ throw new Option.NoSuchElementError();
+ }
+
+ getOrElse(this: None_, defaultValue: () => B): B {
+ return defaultValue();
+ }
+
+ map(mapper: (value: A) => B): Option {
+ return None_.INSTANCE;
+ }
+
+ match(matcher: { Some: (a: A) => B, None: () => B }): B {
+ return matcher.None();
+ }
+
+ orElse(this: None_, alternative: () => Option): Option {
+ return alternative();
+ }
+}
+
+const instance: None_ = None_.INSTANCE;
+
+export { None_, instance as None };
diff --git a/src/option/option.ts b/src/option/option.ts
new file mode 100644
index 0000000..78a52f2
--- /dev/null
+++ b/src/option/option.ts
@@ -0,0 +1,60 @@
+import { None } from './none';
+import { Some } from './some';
+
+/**
+ * An Option represents a collection of size 0 or 1. Traditionally it's used as a replacement for
+ * `null` or `undefined` to explicitly encode if a value can be empty or not.
+ */
+interface Option {
+ isDefined: boolean;
+ isEmpty: boolean;
+
+ equals(other: Option): boolean;
+ filter(predicate: (value: A) => boolean): Option;
+ filterNot(predicate: (value: A) => boolean): Option;
+ flatMap(flatMapper: (value: A) => Option): Option;
+ foreach(run: (value: A) => void): void;
+ get(): A;
+ getOrElse(this: Option, defaultValue: () => B): B;
+ map(mapper: (value: A) => B): Option;
+ match(matcher: { Some: (a: A) => B, None: () => B }): B;
+ orElse(this: Option, alternative: () => Option): Option;
+}
+
+/* tslint:disable:no-null-keyword only-arrow-functions */
+function Option(value: A | null | undefined): Option {
+ if ((value === null) || (value === undefined)) {
+ return None;
+ }
+ else {
+ return new Some(value);
+ }
+}
+/* tslint:enable */
+
+/* tslint:disable:no-namespace */
+namespace Option {
+ /**
+ * This error is thrown when `None.get()` is called.
+ */
+ export class NoSuchElementError extends Error {
+ constructor() {
+ super();
+
+ this.name = 'NoSuchElementError';
+ this.message = 'Monapt.Option.get(): No such element.';
+ this.stack = (new Error()).stack;
+ }
+ }
+
+ /* tslint:disable:only-arrow-functions */
+ export function flatten(options: Array